OXYGENHUB
◆ main openmsx / src / memory / AmdFlash.cc
32.8 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "AmdFlash.hh"
2
3
#include "Rom.hh"
4
#include "SRAM.hh"
5
6
#include "MSXCPU.hh"
7
#include "MSXCliComm.hh"
8
#include "MSXDevice.hh"
9
#include "MSXException.hh"
10
#include "MSXMotherBoard.hh"
11
12
#include "narrow.hh"
13
#include "one_of.hh"
14
#include "ranges.hh"
15
#include "serialize.hh"
16
#include "serialize_stl.hh"
17
#include "xrange.hh"
18
19
#include <algorithm>
20
#include <bit>
21
#include <cassert>
22
#include <iterator>
23
#include <memory>
24
#include <utility>
25
26
namespace openmsx {
27
28
AmdFlash::AmdFlash(const Rom& rom, const ValidatedChip& validatedChip,
29
                   std::span<const bool> writeProtectSectors,
30
                   DeviceConfig& config)
31
	: AmdFlash(rom.getName() + "_flash", validatedChip, &rom, writeProtectSectors, config, {})
32
{
33
}
34
35
AmdFlash::AmdFlash(const std::string& name, const ValidatedChip& validatedChip,
36
                   std::span<const bool> writeProtectSectors,
37
                   DeviceConfig& config, std::string_view id)
38
	: AmdFlash(name, validatedChip, nullptr, writeProtectSectors, config, id)
39
{
40
}
41
42
AmdFlash::AmdFlash(const std::string& name, const ValidatedChip& validatedChip,
43
                   const Rom* rom, std::span<const bool> writeProtectSectors,
44
                   DeviceConfig& config, std::string_view id)
45
	: motherBoard(config.getMotherBoard())
46
	, chip(validatedChip.chip)
47
	, syncOperation(motherBoard.getScheduler())
48
	, syncSuspend(motherBoard.getScheduler())
49
{
50
	cmd.reserve(5 + chip.program.bufferPageSize); // longest command is BufferProgram
51
52
	assert(writeProtectSectors.size() <= chip.geometry.sectorCount);
53
	sectors.reserve(chip.geometry.sectorCount);
54
	for (size_t address = 0; const Region& region : chip.geometry.regions) {
55
		for (size_t regionSector = 0; regionSector < region.count; ++regionSector, address += region.size) {
56
			sectors.push_back({
57
				.index = sectors.size(),
58
				.address = address,
59
				.size = region.size,
60
				.writeProtect = sectors.size() < writeProtectSectors.size() && writeProtectSectors[sectors.size()]
61
			});
62
		}
63
	}
64
65
	size_t writableSize = 0;
66
	size_t readOnlySize = 0;
67
	for (Sector& sector : sectors) {
68
		if (sector.writeProtect) {
69
			sector.writeAddress = -1;
70
			readOnlySize += sector.size;
71
		} else {
72
			sector.writeAddress = narrow<ptrdiff_t>(writableSize);
73
			writableSize += sector.size;
74
		}
75
	}
76
	assert((writableSize + readOnlySize) == size());
77
78
	bool loaded = false;
79
	if (writableSize) {
80
		ram = std::make_unique<SRAM>(
81
			name, "flash rom",
82
			writableSize, config, nullptr, &loaded);
83
	}
84
	if (readOnlySize) {
85
		// If some part of the flash is read-only we require a ROM
86
		// constructor parameter.
87
		assert(rom);
88
	}
89
90
	std::unique_ptr<Rom> rom_;
91
	if (!rom && !loaded) {
92
		// If we don't have a ROM constructor parameter and there was
93
		// no sram content loaded (= previous persistent flash
94
		// content), then try to load some initial content. This
95
		// represents the original content of the flash when the device
96
		// ships. This ROM is optional, if it's not found, then the
97
		// initial flash content is all 0xFF.
98
		try {
99
			rom_ = std::make_unique<Rom>(
100
				"", "", // dummy name and description
101
				config, id);
102
			rom = rom_.get();
103
			config.getCliComm().printInfo(
104
				"Loaded initial content for flash ROM from ",
105
				rom->getFilename());
106
		} catch (MSXException& e) {
107
			// ignore error
108
			assert(rom == nullptr); // 'rom' remains nullptr
109
			// only if an actual sha1sum was given, tell the user we failed to use it
110
			const auto* romTag = config.getXML()->findChild("rom");
111
			const bool initialContentSpecified = romTag && romTag->findChild("sha1");
112
			if (initialContentSpecified) {
113
				config.getCliComm().printWarning(
114
					"Could not load specified initial content "
115
					"for flash ROM: ", e.getMessage());
116
			}
117
		}
118
	}
119
120
	const size_t romSize = rom ? rom->size() : 0;
121
	size_t offset = 0;
122
	for (Sector& sector : sectors) {
123
		if (sector.writeAddress != -1) { // don't use isWritable() here
124
			sector.readAddress = &(*ram)[sector.writeAddress];
125
			if (!loaded) {
126
				auto ramBlock = ram->getWriteBackdoor().subspan(sector.writeAddress, sector.size);
127
				if (offset >= romSize) {
128
					// completely past end of rom
129
					std::ranges::fill(ramBlock, 0xFF);
130
				} else if (offset + sector.size >= romSize) {
131
					// partial overlap
132
					const size_t last = romSize - offset;
133
					const uint8_t* romPtr = &(*rom)[offset];
134
					copy_to_range(std::span{romPtr, last}, ramBlock);
135
					std::ranges::fill(ramBlock.subspan(last), 0xFF);
136
				} else {
137
					// completely before end of rom
138
					const uint8_t* romPtr = &(*rom)[offset];
139
					copy_to_range(std::span{romPtr, sector.size}, ramBlock);
140
				}
141
			}
142
		} else {
143
			assert(rom); // must have rom constructor parameter
144
			if ((offset + sector.size) <= romSize) {
145
				sector.readAddress = &(*rom)[offset];
146
			} else {
147
				sector.readAddress = nullptr;
148
			}
149
		}
150
		offset += sector.size;
151
	}
152
	assert(offset == size());
153
154
	reset();
155
}
156
157
AmdFlash::~AmdFlash() = default;
158
159
size_t AmdFlash::getSectorIndex(size_t address) const
160
{
161
	size_t sectorIndex = 0;
162
	for (const Region& region : chip.geometry.regions) {
163
		if (address < region.count * region.size) {
164
			return sectorIndex + address / region.size;
165
		} else {
166
			address -= region.count * region.size;
167
			sectorIndex += region.count;
168
		}
169
	}
170
	UNREACHABLE;
171
}
172
173
bool AmdFlash::isWritable(const Sector& sector) const
174
{
175
	if (vppWpPinLow) {
176
		const auto range = chip.geometry.writeProtectPinRange;
177
		if ((range > 0 && sector <= sectors[range - 1uz]) ||
178
		    (range < 0 && sector >= sectors[range + chip.geometry.sectorCount])) {
179
			return false;
180
		}
181
	}
182
	return !sector.writeProtect;
183
}
184
185
bool AmdFlash::getReadyPin() const
186
{
187
	return !chip.misc.readyPin || (statusRegister.ready && !status.error && !status.abort);
188
}
189
190
void AmdFlash::reset()
191
{
192
	cmd.clear();
193
	status = {};
194
	statusRegister = {};
195
	setState(State::READ);
196
	syncOperation.removeSyncPoint();
197
	syncSuspend.removeSyncPoint();
198
	erase.reset();
199
	program.reset();
200
}
201
202
void AmdFlash::softReset()
203
{
204
	clearStatus();
205
	setState(State::READ);
206
}
207
208
void AmdFlash::clearStatus()
209
{
210
	status.dataPolling = false;
211
	status.busyToggle = false;
212
	status.error = false;
213
	status.eraseTimer = false;
214
	status.eraseToggle = false;
215
	status.abort = false;
216
	statusRegister.eraseStatus = false;
217
	statusRegister.programStatus = false;
218
	statusRegister.bufferAbort = false;
219
	statusRegister.sectorLock = false;
220
	statusRegister.continuity = false;
221
}
222
223
void AmdFlash::setState(State newState)
224
{
225
	if (state == newState) return;
226
	state = newState;
227
	motherBoard.getCPU().invalidateAllSlotsRWCache(0x0000, 0x10000);
228
}
229
230
uint8_t AmdFlash::read(size_t address, EmuTime time)
231
{
232
	address %= size();
233
	const uint8_t value = peek(address, time);
234
	if (state == State::READ) {
235
		if (statusRegister.eraseSuspend && erase.isPending(getSector(address))) {
236
			status.eraseToggle = !status.eraseToggle;
237
		}
238
	} else if (state == State::STATUS) {
239
		setState(State::READ);
240
	} else if (state == one_of(State::PROGRAM, State::ERROR)) {
241
		status.busyToggle = !status.busyToggle;
242
	} else if (state == State::ERASE_SECTOR) {
243
		status.busyToggle = !status.busyToggle;
244
		if (erase.isPending(getSector(address))) {
245
			status.eraseToggle = !status.eraseToggle;
246
		}
247
	} else if (state == State::ERASE_CHIP) {
248
		status.busyToggle = !status.busyToggle;
249
		status.eraseToggle = !status.eraseToggle;
250
	}
251
	return value;
252
}
253
254
uint8_t AmdFlash::peek(size_t address, EmuTime /*time*/) const
255
{
256
	address %= size();
257
	if (state == State::READ) {
258
		const Sector& sector = getSector(address);
259
		if (erase.isPending(sector)) {
260
			return status.raw;
261
		} else {
262
			return sector.readAddress ? sector.readAddress[address - sector.address] : 0xFF;
263
		}
264
	} else if (state == State::AUTOSELECT) {
265
		if (chip.geometry.deviceInterface == DeviceInterface::x8x16) {
266
			if (chip.autoSelect.oddZero && (address & 1)) {
267
				// some devices return zero for odd bits instead of mirroring
268
				return 0x00;
269
			}
270
			// convert byte address to native address
271
			address >>= 1;
272
		}
273
		return narrow_cast<uint8_t>(peekAutoSelect(address, chip.autoSelect.undefined));
274
	} else if (state == State::CFI) {
275
		if (chip.geometry.deviceInterface == DeviceInterface::x8x16) {
276
			// convert byte address to native address
277
			const uint16_t result = peekCFI(address >> 1);
278
			return narrow_cast<uint8_t>(address & 1 ? result >> 8 : result);
279
		} else {
280
			return narrow_cast<uint8_t>(peekCFI(address));
281
		}
282
	} else if (state == State::STATUS) {
283
		return statusRegister.raw;
284
	} else if (state == one_of(State::ERASE_SECTOR, State::ERASE_CHIP, State::PROGRAM, State::ERROR)) {
285
		return status.raw;
286
	} else {
287
		UNREACHABLE;
288
	}
289
}
290
291
uint16_t AmdFlash::peekAutoSelect(size_t address, uint16_t undefined) const
292
{
293
	switch (address & chip.autoSelect.readMask) {
294
	case 0x0:
295
		return std::to_underlying(chip.autoSelect.manufacturer);
296
	case 0x1:
297
		return chip.autoSelect.device.size() == 1 ? chip.autoSelect.device[0] | 0x2200 : 0x227E;
298
	case 0x2:
299
		if (address & 0x40) {
300
			return undefined;
301
		}
302
		if (chip.geometry.deviceInterface == DeviceInterface::x8x16) {
303
			// convert native address to byte address
304
			address <<= 1;
305
		}
306
		return isWritable(getSector(address)) ? 0 : 1;
307
	case 0x3:
308
		// On AM29F040 it indicates "Autoselect Device Unprotect Code".
309
		// Datasheet does not elaborate. Value is 0x01 according to
310
		//  https://www.msx.org/forum/semi-msx-talk/emulation/matra-ink-emulated
311
		//
312
		// On M29W640G it indicates "Extended Block Verify Code".
313
		// Bit 7: 0: Extended memory block not factory locked
314
		//        1: Extended memory block factory locked
315
		// Other bits are 0x18 on GL, GT and GB models, and 0x01 on GH model.
316
		// Datasheet does not explain their meaning.
317
		// Actual value is 0x08 on GB model (verified on hardware).
318
		//
319
		// On S29GL064S it indicates "Secure Silicon Region Factory Protect":
320
		// Bit 4: 0: WP# protects lowest address (bottom boot)
321
		//        1: WP# protects highest address (top boot)
322
		// Bit 7: 0: Secure silicon region not factory locked
323
		//        1: Secure silicon region factory locked
324
		// Other bits are 0xFF0A. Datasheet does not explain their meaning.
325
		//
326
		// On Alliance Memory AS29CF160B it indicates Continuation ID.
327
		// Datasheet does not elaborate. Value is 0x7F.
328
		return chip.autoSelect.extraCode;
329
	case 0xE:
330
		return chip.autoSelect.device.size() == 2 ? chip.autoSelect.device[0] | 0x2200 : undefined;
331
	case 0xF:
332
		return chip.autoSelect.device.size() == 2 ? chip.autoSelect.device[1] | 0x2200 : undefined;
333
	default:
334
		// some devices return zero instead of 0xFFFF (typical)
335
		return undefined;
336
	}
337
}
338
339
uint16_t AmdFlash::peekCFI(size_t address) const
340
{
341
	const size_t maskedAddress = address & chip.cfi.readMask;
342
343
	if (maskedAddress < 0x10) {
344
		if (chip.cfi.withManufacturerDevice) {
345
			// M29W640GB exposes manufacturer & device ID below 0x10 (as 16-bit values)
346
			switch (maskedAddress) {
347
			case 0x0:
348
				return std::to_underlying(chip.autoSelect.manufacturer);
349
			case 0x1:
350
				return chip.autoSelect.device.size() == 1 ? chip.autoSelect.device[0] | 0x2200 : 0x227E;
351
			case 0x2:
352
				return chip.autoSelect.device.size() == 2 ? chip.autoSelect.device[0] | 0x2200 : 0xFFFF;
353
			case 0x3:
354
				return chip.autoSelect.device.size() == 2 ? chip.autoSelect.device[1] | 0x2200 : 0xFFFF;
355
			default:
356
				return 0xFFFF;
357
			}
358
		} else if (chip.cfi.withAutoSelect) {
359
			// S29GL064S exposes auto select data below 0x10 (as 16-bit values)
360
			return peekAutoSelect(address | 0x40);
361
		}
362
	}
363
364
	switch (maskedAddress) {
365
	case 0x10:
366
		return 'Q';
367
	case 0x11:
368
		return 'R';
369
	case 0x12:
370
		return 'Y';
371
	case 0x13:
372
		return 0x02;  // AMD compatible
373
	case 0x14:
374
		return 0x00;
375
	case 0x15:
376
		return 0x40;
377
	case 0x16:
378
	case 0x17:
379
	case 0x18:
380
	case 0x19:
381
	case 0x1A:
382
		return 0x00;
383
	case 0x1B:
384
		return chip.cfi.systemInterface.supply.minVcc;
385
	case 0x1C:
386
		return chip.cfi.systemInterface.supply.maxVcc;
387
	case 0x1D:
388
		return chip.cfi.systemInterface.supply.minVpp;
389
	case 0x1E:
390
		return chip.cfi.systemInterface.supply.maxVpp;
391
	case 0x1F:
392
		return chip.cfi.systemInterface.typTimeout.singleProgram.exponent;
393
	case 0x20:
394
		return chip.cfi.systemInterface.typTimeout.multiProgram.exponent;
395
	case 0x21:
396
		return chip.cfi.systemInterface.typTimeout.sectorErase.exponent;
397
	case 0x22:
398
		return chip.cfi.systemInterface.typTimeout.chipErase.exponent;
399
	case 0x23:
400
		return chip.cfi.systemInterface.maxTimeoutMult.singleProgram.exponent;
401
	case 0x24:
402
		return chip.cfi.systemInterface.maxTimeoutMult.multiProgram.exponent;
403
	case 0x25:
404
		return chip.cfi.systemInterface.maxTimeoutMult.sectorErase.exponent;
405
	case 0x26:
406
		return chip.cfi.systemInterface.maxTimeoutMult.chipErase.exponent;
407
	case 0x27:
408
		return chip.geometry.size.exponent;
409
	case 0x28:
410
		return narrow<uint8_t>(std::to_underlying(chip.geometry.deviceInterface) & 0xFF);
411
	case 0x29:
412
		return narrow<uint8_t>(std::to_underlying(chip.geometry.deviceInterface) >> 8);
413
	case 0x2A:
414
		return std::max(chip.program.fastPageSize.exponent, chip.program.bufferPageSize.exponent);
415
	case 0x2B:
416
		return 0x00;
417
	case 0x2C:
418
		return narrow<uint8_t>(chip.geometry.regions.size());
419
	case 0x2D:
420
	case 0x31:
421
	case 0x35:
422
	case 0x39:
423
		if (const size_t index = (maskedAddress - 0x2D) >> 2; index < chip.geometry.regions.size()) {
424
			return narrow<uint8_t>((chip.geometry.regions[index].count - 1) & 0xFF);
425
		}
426
		return 0x00;
427
	case 0x2E:
428
	case 0x32:
429
	case 0x36:
430
	case 0x3A:
431
		if (const size_t index = (maskedAddress - 0x2E) >> 2; index < chip.geometry.regions.size()) {
432
			return narrow<uint8_t>((chip.geometry.regions[index].count - 1) >> 8);
433
		}
434
		return 0x00;
435
	case 0x2F:
436
	case 0x33:
437
	case 0x37:
438
	case 0x3B:
439
		if (const size_t index = (maskedAddress - 0x2F) >> 2; index < chip.geometry.regions.size()) {
440
			return narrow<uint8_t>((chip.geometry.regions[index].size >> 8) & 0xFF);
441
		}
442
		return 0x00;
443
	case 0x30:
444
	case 0x34:
445
	case 0x38:
446
	case 0x3C:
447
		if (const size_t index = (maskedAddress - 0x30) >> 2; index < chip.geometry.regions.size()) {
448
			return narrow<uint8_t>((chip.geometry.regions[index].size >> 8) >> 8);
449
		}
450
		return 0x00;
451
	case 0x40:
452
		return 'P';
453
	case 0x41:
454
		return 'R';
455
	case 0x42:
456
		return 'I';
457
	case 0x43:
458
		return '0' + chip.cfi.primaryAlgorithm.version.major;
459
	case 0x44:
460
		return '0' + chip.cfi.primaryAlgorithm.version.minor;
461
	case 0x45:
462
		return narrow<uint8_t>(chip.cfi.primaryAlgorithm.addressSensitiveUnlock | (chip.cfi.primaryAlgorithm.siliconRevision << 2));
463
	case 0x46:
464
		return chip.cfi.primaryAlgorithm.eraseSuspend;
465
	case 0x47:
466
		return chip.cfi.primaryAlgorithm.sectorProtect;
467
	case 0x48:
468
		return chip.cfi.primaryAlgorithm.sectorTemporaryUnprotect;
469
	case 0x49:
470
		return chip.cfi.primaryAlgorithm.sectorProtectScheme;
471
	case 0x4A:
472
		return chip.cfi.primaryAlgorithm.simultaneousOperation;
473
	case 0x4B:
474
		return chip.cfi.primaryAlgorithm.burstMode;
475
	case 0x4C:
476
		return chip.cfi.primaryAlgorithm.pageMode;
477
	case 0x4D:
478
		return chip.cfi.primaryAlgorithm.version.minor >= 1 ? chip.cfi.primaryAlgorithm.supply.minAcc : 0xFFFF;
479
	case 0x4E:
480
		return chip.cfi.primaryAlgorithm.version.minor >= 1 ? chip.cfi.primaryAlgorithm.supply.maxAcc : 0xFFFF;
481
	case 0x4F:
482
		return chip.cfi.primaryAlgorithm.version.minor >= 1 ? chip.cfi.primaryAlgorithm.bootBlockFlag : 0xFFFF;
483
	case 0x50:
484
		return chip.cfi.primaryAlgorithm.version.minor >= 2 ? chip.cfi.primaryAlgorithm.programSuspend : 0xFFFF;
485
	default:
486
		// TODO
487
		// 0x61-0x64 Unique 64-bit device number / security code for M29W800DB and M29W640GB.
488
		// M29W640GB also has unknown data in 0x65-0x6A, as well as some in the 0x80-0xFF range.
489
		return 0xFFFF;
490
	}
491
}
492
493
const uint8_t* AmdFlash::getReadCacheLine(size_t address) const
494
{
495
	address %= size();
496
	if (state == State::READ) {
497
		const Sector& sector = getSector(address);
498
		if (erase.isPending(sector)) {
499
			return nullptr;
500
		} else {
501
			const uint8_t* addr = sector.readAddress;
502
			return addr ? &addr[address - sector.address] : MSXDevice::unmappedRead.data();
503
		}
504
	} else {
505
		return nullptr;
506
	}
507
}
508
509
void AmdFlash::write(size_t address, uint8_t value, EmuTime time)
510
{
511
	address %= size();
512
	cmd.push_back({.addr = address, .value = value});
513
514
	if (state == State::READ) {
515
		if (checkCommandAutoSelect() ||
516
		    checkCommandEraseSector(time) ||
517
		    checkCommandEraseChip(time) ||
518
		    checkCommandProgram(time) ||
519
		    checkCommandDoubleByteProgram(time) ||
520
		    checkCommandQuadrupleByteProgram(time) ||
521
		    checkCommandBufferProgram(time) ||
522
		    checkCommandResume(time) ||
523
		    checkCommandCFIQuery() ||
524
		    checkCommandContinuityCheck() ||
525
		    checkCommandStatusRead() ||
526
		    checkCommandStatusClear() ||
527
		    checkCommandLongReset() ||
528
		    checkCommandReset()) {
529
			// do nothing, we're still matching a command, but it is not complete yet
530
		} else {
531
			cmd.clear();
532
		}
533
	} else if (state == State::AUTOSELECT) {
534
		if (checkCommandCFIQuery() ||
535
		    checkCommandLongReset() ||
536
		    checkCommandReset()) {
537
			// do nothing, we're still matching a command, but it is not complete yet
538
		} else {
539
			cmd.clear();
540
		}
541
	} else if (state == State::CFI) {
542
		if (checkCommandLongReset() ||
543
		    checkCommandCFIExit() ||
544
		    checkCommandReset()) {
545
			// do nothing, we're still matching a command, but it is not complete yet
546
		} else {
547
			cmd.clear();
548
		}
549
	} else if (state == State::STATUS) {
550
		// TODO confirm. S29GL064S datasheet: "it is not recommended".
551
		if (checkCommandLongReset() ||
552
		    checkCommandReset()) {
553
			// do nothing, we're still matching a command, but it is not complete yet
554
		} else {
555
			cmd.clear();
556
		}
557
	} else if (state == State::ERROR) {
558
		const bool shortReset = !status.abort || chip.program.shortAbortReset;
559
		if (checkCommandLongReset() ||
560
		    (shortReset && checkCommandReset())) {
561
			// do nothing, we're still matching a command, but it is not complete yet
562
		} else {
563
			cmd.clear();
564
		}
565
	} else if (state == State::ERASE_SECTOR) {
566
		if (checkCommandEraseAdditionalSector(time) ||
567
		    checkCommandSuspend(time)) {
568
			// do nothing, we're still matching a command, but it is not complete yet
569
		} else {
570
			cmd.clear();
571
		}
572
	} else if (state == State::ERASE_CHIP) {
573
		if (false) {
574
			// do nothing, we're still matching a command, but it is not complete yet
575
		} else {
576
			cmd.clear();
577
		}
578
	} else if (state == State::PROGRAM) {
579
		if (checkCommandSuspend(time)) {
580
			// do nothing, we're still matching a command, but it is not complete yet
581
		} else {
582
			cmd.clear();
583
		}
584
	} else {
585
		UNREACHABLE;
586
	}
587
}
588
589
// The checkCommandXXX() methods below return
590
//   true  -> if the command sequence still matches, but is not complete yet
591
//   false -> if the command was fully matched or does not match with
592
//            the current command sequence.
593
// If there was a full match, the command is also executed.
594
bool AmdFlash::checkCommandReset()
595
{
596
	if (cmd[0].value == 0xf0) {
597
		softReset();
598
	}
599
	return false;
600
}
601
602
bool AmdFlash::checkCommandLongReset()
603
{
604
	static constexpr std::array<uint8_t, 3> cmdSeq = {0xaa, 0x55, 0xf0};
605
	if (partialMatch(cmdSeq)) {
606
		if (cmd.size() < 3) return true;
607
		softReset();
608
	}
609
	return false;
610
}
611
612
bool AmdFlash::checkCommandAutoSelect()
613
{
614
	static constexpr std::array<uint8_t, 3> cmdSeq = {0xaa, 0x55, 0x90};
615
	if (partialMatch(cmdSeq)) {
616
		if (cmd.size() < 3) return true;
617
		setState(State::AUTOSELECT);
618
	}
619
	return false;
620
}
621
622
bool AmdFlash::checkCommandCFIQuery()
623
{
624
	// convert byte address to native address
625
	const size_t addr = (chip.geometry.deviceInterface == DeviceInterface::x8x16) ? cmd[0].addr >> 1 : cmd[0].addr;
626
	if (chip.cfi.command && (addr & chip.cfi.commandMask) == 0x55 && cmd[0].value == 0x98) {
627
		setState(State::CFI);
628
	}
629
	return false;
630
}
631
632
bool AmdFlash::checkCommandCFIExit()
633
{
634
	if (chip.cfi.exitCommand && cmd[0].value == 0xff) {
635
		softReset();
636
	}
637
	return false;
638
}
639
640
bool AmdFlash::checkCommandStatusRead()
641
{
642
	static constexpr std::array<uint8_t, 1> cmdSeq = {0x70};
643
	if (chip.misc.statusCommand && partialMatch(cmdSeq)) {
644
		setState(State::STATUS);
645
	}
646
	return false;
647
}
648
649
bool AmdFlash::checkCommandStatusClear()
650
{
651
	static constexpr std::array<uint8_t, 1> cmdSeq = {0x71};
652
	if (chip.misc.statusCommand && partialMatch(cmdSeq)) {
653
		softReset();
654
	}
655
	return false;
656
}
657
658
bool AmdFlash::checkCommandContinuityCheck()
659
{
660
	if (chip.misc.continuityCommand && cmd[0] == AddressValue{.addr = 0x5554AB, .value = 0xFF}) {
661
		if (cmd.size() < 2) return true;
662
		if (cmd.size() == 2 && cmd[1] == AddressValue{.addr = 0x2AAB54, .value = 0x00}) {
663
			statusRegister.continuity = true;
664
		}
665
	}
666
	return false;
667
}
668
669
bool AmdFlash::checkCommandEraseSector(EmuTime time)
670
{
671
	static constexpr std::array<uint8_t, 5> cmdSeq = {0xaa, 0x55, 0x80, 0xaa, 0x55};
672
	if (!statusRegister.eraseSuspend && !statusRegister.programSuspend && partialMatch(cmdSeq)) {
673
		if (cmd.size() < 6) return true;
674
		if (cmd[5].value == 0x30) {
675
			clearStatus();
676
677
			if (const Sector& sector = getSector(cmd[5].addr);
678
			    isWritable(sector)) {
679
				erase.markPending(sector);
680
			} else {
681
				statusRegister.sectorLock = true;
682
			}
683
684
			statusRegister.ready = false;
685
			status.dataPolling = false;
686
			setState(State::ERASE_SECTOR);
687
			scheduleEraseOperation(time);
688
		}
689
	}
690
	return false;
691
}
692
693
bool AmdFlash::checkCommandEraseAdditionalSector(EmuTime time)
694
{
695
	assert(state == State::ERASE_SECTOR);
696
	if (!status.eraseTimer && cmd[0].value == 0x30) {
697
		if (const Sector& sector = getSector(cmd[0].addr);
698
			isWritable(sector)) {
699
			erase.markPending(sector);
700
		} else {
701
			statusRegister.sectorLock = true;
702
		}
703
704
		scheduleEraseOperation(time);
705
	}
706
	return false;
707
}
708
709
bool AmdFlash::checkCommandEraseChip(EmuTime time)
710
{
711
	static constexpr std::array<uint8_t, 5> cmdSeq = {0xaa, 0x55, 0x80, 0xaa, 0x55};
712
	if (!statusRegister.eraseSuspend && !statusRegister.programSuspend && partialMatch(cmdSeq)) {
713
		if (cmd.size() < 6) return true;
714
		if (cmd[5].value == 0x10) {
715
			clearStatus();
716
717
			for (const Sector& sector : sectors) {
718
				if (isWritable(sector)) {
719
					erase.markPending(sector);
720
				} else {
721
					statusRegister.sectorLock = true;
722
				}
723
			}
724
725
			statusRegister.ready = false;
726
			status.dataPolling = false;
727
			setState(State::ERASE_CHIP);
728
			scheduleEraseOperation(time);
729
		}
730
	}
731
	return false;
732
}
733
734
void AmdFlash::scheduleEraseOperation(EmuTime time)
735
{
736
	if (state == State::ERASE_SECTOR && !status.eraseTimer) {
737
		syncOperation.removeSyncPoint();
738
		syncOperation.setSyncPoint(time + chip.erase.timerDuration);
739
	} else if (std::ranges::any_of(erase.buffer, std::identity{})) {
740
		assert(!syncOperation.isPending());
741
		syncOperation.setSyncPoint(time + chip.erase.duration);
742
	} else {
743
		assert(!syncOperation.isPending());
744
		syncOperation.setSyncPoint(time + chip.erase.minimumDuration);
745
	}
746
}
747
748
void AmdFlash::execEraseOperation(EmuTime time)
749
{
750
	assert(state == one_of(State::ERASE_SECTOR, State::ERASE_CHIP));
751
	if (state == State::ERASE_SECTOR && !status.eraseTimer) {
752
		status.eraseTimer = true;
753
		scheduleEraseOperation(time);
754
	} else {
755
		while (erase.index < sectors.size()) {
756
			const Sector& sector = sectors[erase.index];
757
			const bool pending = erase.isPending(sector);
758
			++erase.index;
759
			if (pending) {
760
				assert(!sector.writeProtect);
761
				ram->memset(sector.writeAddress, 0xff, sector.size);
762
				break;
763
			}
764
		}
765
766
		if (erase.index >= sectors.size()) {
767
			erase.reset();
768
			statusRegister.ready = true;
769
			status.dataPolling = true;
770
			status.eraseTimer = false;
771
			setState(State::READ);
772
		} else {
773
			syncOperation.setSyncPoint(time + chip.erase.duration);
774
		}
775
	}
776
}
777
778
bool AmdFlash::EraseOperation::isPending(const Sector& sector) const
779
{
780
	return index <= sector.index && sector.index < buffer.size() && buffer[sector.index];
781
}
782
783
void AmdFlash::EraseOperation::markPending(const Sector& sector)
784
{
785
	if (sector.index >= buffer.size()) {
786
		buffer.resize(sector.index + 1, false);
787
	}
788
	buffer[sector.index] = true;
789
}
790
791
void AmdFlash::EraseOperation::reset()
792
{
793
	index = 0;
794
	buffer.clear();
795
}
796
797
bool AmdFlash::checkCommandSuspend(EmuTime time)
798
{
799
	if (state == State::ERASE_SECTOR) {
800
		if (chip.erase.suspend) {
801
			if (cmd[0].value == 0xB0) {
802
				if (!status.eraseTimer) {
803
					syncOperation.removeSyncPoint();
804
					execSuspend(time);
805
				} else {
806
					if (!syncSuspend.isPending()) {
807
						syncOperation.removeSyncPoint();
808
						syncSuspend.setSyncPoint(time + chip.erase.suspendDuration);
809
					}
810
				}
811
			}
812
		}
813
	} else if (state == State::PROGRAM) {
814
		if (chip.program.suspend) {
815
			if (cmd[0].value == 0xB0 || (chip.program.enhancedSuspend && cmd[0].value == 0x51)) {
816
				if (!syncSuspend.isPending()) {
817
					syncOperation.removeSyncPoint();
818
					syncSuspend.setSyncPoint(time + chip.program.suspendDuration);
819
				}
820
			}
821
		}
822
	} else {
823
		UNREACHABLE;
824
	}
825
	return false;
826
}
827
828
void AmdFlash::execSuspend(EmuTime /*time*/)
829
{
830
	if (state == State::ERASE_SECTOR) {
831
		statusRegister.ready = true;
832
		statusRegister.eraseSuspend = true;
833
		status.dataPolling = true;
834
		status.eraseTimer = true;
835
		setState(State::READ);
836
	} else if (state == State::PROGRAM) {
837
		statusRegister.ready = true;
838
		statusRegister.programSuspend = true;
839
		setState(State::READ);
840
	} else {
841
		UNREACHABLE;
842
	}
843
}
844
845
bool AmdFlash::checkCommandResume(EmuTime time)
846
{
847
	if (statusRegister.programSuspend) {
848
		if (cmd[0].value == 0x30 || (chip.program.enhancedSuspend && cmd[0].value == 0x50)) {
849
			statusRegister.ready = false;
850
			statusRegister.programSuspend = false;
851
			setState(State::PROGRAM);
852
			scheduleProgramOperation(time);
853
		}
854
	} else if (statusRegister.eraseSuspend) {
855
		if (cmd[0].value == 0x30) {
856
			statusRegister.ready = false;
857
			statusRegister.eraseSuspend = false;
858
			status.dataPolling = false;
859
			setState(State::ERASE_SECTOR);
860
			scheduleEraseOperation(time);
861
		}
862
	}
863
	return false;
864
}
865
866
bool AmdFlash::checkCommandProgram(EmuTime time)
867
{
868
	static constexpr std::array<uint8_t, 3> cmdSeq = {0xaa, 0x55, 0xa0};
869
	return checkCommandProgramHelper(1, cmdSeq, time);
870
}
871
872
bool AmdFlash::checkCommandDoubleByteProgram(EmuTime time)
873
{
874
	static constexpr std::array<uint8_t, 1> cmdSeq = {0x50};
875
	return checkCommandProgramHelper(2, cmdSeq, time);
876
}
877
878
bool AmdFlash::checkCommandQuadrupleByteProgram(EmuTime time)
879
{
880
	static constexpr std::array<uint8_t, 1> cmdSeq = {0x56};
881
	return checkCommandProgramHelper(4, cmdSeq, time);
882
}
883
884
bool AmdFlash::checkCommandProgramHelper(size_t numBytes, std::span<const uint8_t> cmdSeq, EmuTime time)
885
{
886
	if (numBytes <= chip.program.fastPageSize && partialMatch(cmdSeq)) {
887
		if (cmd.size() == cmdSeq.size()) clearStatus();
888
		if (cmd.size() <= cmdSeq.size()) return true;
889
		status.dataPolling = ~cmd.back().value & 0x80;
890
		if (cmd.size() < (cmdSeq.size() + numBytes)) return true;
891
		const Sector& sector = getSector(cmd.back().addr);
892
		if (isWritable(sector) && !erase.isPending(sector)) {
893
			const size_t pageMask = chip.program.fastPageSize - 1;
894
			program.address = cmd.back().addr & ~pageMask;
895
			program.buffer.assign(chip.program.fastPageSize, 0xFFFF);
896
897
			// For fast program commands, if you write 0x7F and then 0xF7 to the same empty
898
			// flash memory location within the same command, the final value is 0x77.
899
			// Tested on: M29W640GB.
900
			for (size_t i : xrange(cmdSeq.size(), cmdSeq.size() + numBytes)) {
901
				program.buffer[cmd[i].addr & pageMask] &= cmd[i].value;
902
			}
903
904
			statusRegister.ready = false;
905
			setState(State::PROGRAM);
906
			scheduleProgramOperation(time);
907
		} else {
908
			// immediate completion
909
			statusRegister.sectorLock = true;
910
			status.dataPolling = !status.dataPolling || statusRegister.eraseSuspend;
911
		}
912
	}
913
	return false;
914
}
915
916
bool AmdFlash::checkCommandBufferProgram(EmuTime time)
917
{
918
	static constexpr std::array<uint8_t, 2> cmdSeq = {0xaa, 0x55};
919
	if (chip.program.bufferPageSize > 1 && partialMatch(cmdSeq) && (cmd.size() <= 2 || cmd[2].value == 0x25)) {
920
		if (cmd.size() <= 3) {
921
			if (cmd.size() == 3) clearStatus();
922
			return true;
923
		} else if (cmd.size() <= 4) {
924
			if (cmd[3].value < chip.program.bufferPageSize && getSector(cmd[3].addr) == getSector(cmd[2].addr)) {
925
				return true;
926
			}
927
		} else if (cmd.size() <= 5) {
928
			if (getSector(cmd[4].addr) == getSector(cmd[2].addr)) {
929
				status.dataPolling = ~cmd.back().value & 0x80;
930
				return true;
931
			}
932
		} else if (cmd.size() <= size_t(5 + cmd[3].value)) {
933
			const size_t pageMask = chip.program.bufferPageSize - 1;
934
			if ((cmd.back().addr & ~pageMask) == (cmd[4].addr & ~pageMask)) {
935
				status.dataPolling = ~cmd.back().value & 0x80;
936
				return true;
937
			}
938
		} else {
939
			const Sector& sector = getSector(cmd[2].addr);
940
			if (cmd.back().value == 0x29 && getSector(cmd.back().addr) == sector) {
941
				if (isWritable(sector) && !erase.isPending(sector)) {
942
					const size_t pageMask = chip.program.bufferPageSize - 1;
943
					program.address = cmd[4].addr & ~pageMask;
944
					program.buffer.assign(chip.program.bufferPageSize, 0xFFFF);
945
946
					// For buffer program commands, if you write 0x7F and then 0xF7 to the same empty
947
					// flash memory location within the same command, the final value is 0xF7.
948
					// Tested on: M29W640GB, S29GL064S.
949
					for (size_t i : xrange<size_t>(4, cmd.size() - 1)) {
950
						program.buffer[cmd[i].addr & pageMask] = cmd[i].value;
951
					}
952
953
					statusRegister.ready = false;
954
					setState(State::PROGRAM);
955
					scheduleProgramOperation(time);
956
				} else {
957
					// immediate completion
958
					statusRegister.sectorLock = true;
959
					status.dataPolling = !status.dataPolling || statusRegister.eraseSuspend;
960
				}
961
				return false;
962
			}
963
		}
964
		status.abort = true;
965
		statusRegister.programStatus = true;
966
		statusRegister.bufferAbort = true;
967
		setState(State::ERROR);
968
	}
969
	return false;
970
}
971
972
void AmdFlash::scheduleProgramOperation(EmuTime time)
973
{
974
	assert(!syncOperation.isPending());
975
	syncOperation.setSyncPoint(time + chip.program.duration);
976
}
977
978
void AmdFlash::execProgramOperation(EmuTime time)
979
{
980
	assert(state == State::PROGRAM);
981
	const Sector& sector = getSector(program.address);
982
	assert(!sector.writeProtect);
983
	size_t ramAddr = program.address - sector.address + sector.writeAddress + program.index;
984
	while (program.index < program.buffer.size()) {
985
		const uint16_t value = program.buffer[program.index];
986
		++program.index;
987
		if (value < 0x100) {
988
			const uint8_t ramValue = (*ram)[ramAddr] & value;
989
			ram->write(ramAddr, ramValue);
990
			if (chip.program.bitRaiseError && ramValue != value) {
991
				program.reset();
992
				status.error = true;
993
				setState(State::ERROR);
994
				return;
995
			}
996
			break;
997
		}
998
		++ramAddr;
999
	}
1000
1001
	if (program.index >= program.buffer.size()) {
1002
		program.reset();
1003
		statusRegister.ready = true;
1004
		status.dataPolling = !status.dataPolling || statusRegister.eraseSuspend;
1005
		setState(State::READ);
1006
	} else {
1007
		syncOperation.setSyncPoint(time + chip.program.additionalDuration);
1008
	}
1009
}
1010
1011
void AmdFlash::ProgramOperation::reset()
1012
{
1013
	address = 0;
1014
	index = 0;
1015
	buffer.clear();
1016
}
1017
1018
bool AmdFlash::partialMatch(std::span<const uint8_t> dataSeq) const
1019
{
1020
	static constexpr std::array<unsigned, 5> addrSeq = {0, 1, 0, 0, 1};
1021
	static constexpr std::array<unsigned, 2> cmdAddr = {0x555, 0x2aa};
1022
	(void)addrSeq; (void)cmdAddr; // suppress (invalid) gcc warning
1023
1024
	assert(dataSeq.size() <= 5);
1025
	return std::ranges::all_of(xrange(std::min(dataSeq.size(), cmd.size())), [&](const size_t i) {
1026
		// convert byte address to native address
1027
		const size_t addr = (chip.geometry.deviceInterface == DeviceInterface::x8x16) ? cmd[i].addr >> 1 : cmd[i].addr;
1028
		return ((addr & 0x7FF) == cmdAddr[addrSeq[i]]) &&
1029
		       (cmd[i].value == dataSeq[i]);
1030
	});
1031
}
1032
1033
void AmdFlash::execOperation(EmuTime time)
1034
{
1035
	if (state == one_of(State::ERASE_SECTOR, State::ERASE_CHIP)) {
1036
		execEraseOperation(time);
1037
	} else if (state == State::PROGRAM) {
1038
		execProgramOperation(time);
1039
	} else {
1040
		UNREACHABLE;
1041
	}
1042
}
1043
1044
static constexpr auto stateInfo = std::to_array<enum_string<AmdFlash::State>>({
1045
	{ "IDLE",         AmdFlash::State::READ },       // back compat with v3
1046
	{ "IDENT",        AmdFlash::State::AUTOSELECT }, // back compat with v3
1047
	{ "PRGERR",       AmdFlash::State::ERROR },      // back compat with v3
1048
	{ "READ",         AmdFlash::State::READ  },
1049
	{ "AUTOSELECT",   AmdFlash::State::AUTOSELECT },
1050
	{ "CFI",          AmdFlash::State::CFI },
1051
	{ "STATUS",       AmdFlash::State::STATUS },
1052
	{ "ERROR",        AmdFlash::State::ERROR },
1053
	{ "ERASE_SECTOR", AmdFlash::State::ERASE_SECTOR },
1054
	{ "ERASE_CHIP",   AmdFlash::State::ERASE_CHIP },
1055
	{ "PROGRAM",      AmdFlash::State::PROGRAM },
1056
});
1057
SERIALIZE_ENUM(AmdFlash::State, stateInfo);
1058
1059
template<typename Archive>
1060
void AmdFlash::AddressValue::serialize(Archive& ar, unsigned /*version*/)
1061
{
1062
	ar.serialize("address", addr,
1063
	             "value",   value);
1064
}
1065
1066
template<typename Archive>
1067
void AmdFlash::EraseOperation::serialize(Archive& ar, unsigned /*version*/)
1068
{
1069
	ar.serialize("index", index,
1070
	             "buffer", buffer);
1071
}
1072
1073
template<typename Archive>
1074
void AmdFlash::ProgramOperation::serialize(Archive& ar, unsigned /*version*/)
1075
{
1076
		ar.serialize("address", address,
1077
		             "index", index,
1078
		             "buffer", buffer);
1079
}
1080
1081
// version 1: Initial version.
1082
// version 2: Added vppWpPinLow.
1083
// version 3: Changed cmd to static_vector, added status.
1084
// version 4: Added status register, sectors and operation / suspend schedulables.
1085
template<typename Archive>
1086
void AmdFlash::serialize(Archive& ar, unsigned version)
1087
{
1088
	ar.serialize("ram", *ram);
1089
	if (ar.versionAtLeast(version, 3)) {
1090
		ar.serialize("cmd",    cmd,
1091
		             "status", status.raw);
1092
	} else {
1093
		std::array<AddressValue, 8> cmdArray;
1094
		unsigned cmdSize = 0;
1095
		ar.serialize("cmd",    cmdArray,
1096
		             "cmdIdx", cmdSize);
1097
		cmd.assign(cmdArray.begin(), cmdArray.begin() + cmdSize);
1098
	}
1099
	ar.serialize("state", state);
1100
	if (ar.versionAtLeast(version, 2)) {
1101
		ar.serialize("vppWpPinLow", vppWpPinLow);
1102
	}
1103
	if (ar.versionAtLeast(version, 4)) {
1104
		ar.serialize("statusRegister", statusRegister.raw,
1105
		             "erase", erase,
1106
					 "program", program,
1107
		             "syncOperation", syncOperation,
1108
		             "syncSuspend", syncSuspend);
1109
	}
1110
}
1111
INSTANTIATE_SERIALIZE_METHODS(AmdFlash);
1112
1113
} // namespace openmsx