openMSX
AmdFlash.cc
Go to the documentation of this file.
1#include "AmdFlash.hh"
2#include "Rom.hh"
3#include "SRAM.hh"
4#include "MSXMotherBoard.hh"
5#include "MSXCPU.hh"
6#include "MSXDevice.hh"
7#include "MSXCliComm.hh"
8#include "HardwareConfig.hh"
9#include "MSXException.hh"
10#include "narrow.hh"
11#include "one_of.hh"
12#include "ranges.hh"
13#include "serialize.hh"
14#include "serialize_stl.hh"
15#include "stl.hh"
16#include "xrange.hh"
17#include <bit>
18#include <cassert>
19#include <iterator>
20#include <memory>
21
22namespace openmsx {
23
24AmdFlash::AmdFlash(const Rom& rom, const ValidatedChip& validatedChip,
25 std::span<const bool> writeProtectSectors,
26 const DeviceConfig& config, Load load)
27 : motherBoard(config.getMotherBoard())
28 , chip(validatedChip.chip)
29{
30 init(rom.getName() + "_flash", config, load, &rom, writeProtectSectors);
31}
32
33AmdFlash::AmdFlash(const std::string& name, const ValidatedChip& validatedChip,
34 std::span<const bool> writeProtectSectors,
35 const DeviceConfig& config)
36 : motherBoard(config.getMotherBoard())
37 , chip(validatedChip.chip)
38{
39 init(name, config, Load::NORMAL, nullptr, writeProtectSectors);
40}
41
42[[nodiscard]] static bool sramEmpty(const SRAM& ram)
43{
44 return ranges::all_of(xrange(ram.size()),
45 [&](auto i) { return ram[i] == 0xFF; });
46}
47
48void AmdFlash::init(const std::string& name, const DeviceConfig& config, Load load,
49 const Rom* rom, std::span<const bool> writeProtectSectors)
50{
51 auto numSectors = chip.geometry.sectorCount;
52 assert(writeProtectSectors.size() <= numSectors);
53
54 size_t writableSize = 0;
55 size_t readOnlySize = 0;
56 writeAddress.resize(numSectors);
57 for (size_t sector = 0; const AmdFlash::Region& region : chip.geometry.regions) {
58 for (size_t regionSector = 0; regionSector < region.count; regionSector++, sector++) {
59 if (sector < writeProtectSectors.size() && writeProtectSectors[sector]) {
60 writeAddress[sector] = -1;
61 readOnlySize += region.size;
62 } else {
63 writeAddress[sector] = narrow<ptrdiff_t>(writableSize);
64 writableSize += region.size;
65 }
66 }
67 }
68 assert((writableSize + readOnlySize) == size());
69
70 bool loaded = false;
71 if (writableSize) {
72 if (load == Load::NORMAL) {
73 ram = std::make_unique<SRAM>(
74 name, "flash rom",
75 writableSize, config, nullptr, &loaded);
76 } else {
77 // Hack for 'Matra INK', flash chip is wired-up so that
78 // writes are never visible to the MSX (but the flash
79 // is not made write-protected). In this case it doesn't
80 // make sense to load/save the SRAM file.
81 ram = std::make_unique<SRAM>(
82 name, "flash rom",
83 writableSize, config, SRAM::DontLoadTag{});
84 }
85 }
86 if (readOnlySize) {
87 // If some part of the flash is read-only we require a ROM
88 // constructor parameter.
89 assert(rom);
90 }
91
92 const auto* romTag = config.getXML()->findChild("rom");
93 bool initialContentSpecified = romTag && romTag->findChild("sha1");
94
95 // check whether the loaded SRAM is empty, whilst initial content was specified
96 if (!rom && loaded && initialContentSpecified && sramEmpty(*ram)) {
97 config.getCliComm().printInfo(
98 "This flash device (", config.getHardwareConfig().getName(),
99 ") has initial content specified, but this content "
100 "was not loaded, because there was already content found "
101 "and loaded from persistent storage. However, this "
102 "content is blank (it was probably created automatically "
103 "when the specified initial content could not be loaded "
104 "when this device was used for the first time). If you "
105 "still wish to load the specified initial content, "
106 "please remove the blank persistent storage file: ",
107 ram->getLoadedFilename());
108 }
109
110 std::unique_ptr<Rom> rom_;
111 if (!rom && !loaded) {
112 // If we don't have a ROM constructor parameter and there was
113 // no sram content loaded (= previous persistent flash
114 // content), then try to load some initial content. This
115 // represents the original content of the flash when the device
116 // ships. This ROM is optional, if it's not found, then the
117 // initial flash content is all 0xFF.
118 try {
119 rom_ = std::make_unique<Rom>(
120 "", "", // dummy name and description
121 config);
122 rom = rom_.get();
123 config.getCliComm().printInfo(
124 "Loaded initial content for flash ROM from ",
125 rom->getFilename());
126 } catch (MSXException& e) {
127 // ignore error
128 assert(rom == nullptr); // 'rom' remains nullptr
129 // only if an actual sha1sum was given, tell the user we failed to use it
130 if (initialContentSpecified) {
131 config.getCliComm().printWarning(
132 "Could not load specified initial content "
133 "for flash ROM: ", e.getMessage());
134 }
135 }
136 }
137
138 readAddress.resize(numSectors);
139 auto romSize = rom ? rom->size() : 0;
140 size_t offset = 0;
141 for (size_t sector = 0; const AmdFlash::Region& region : chip.geometry.regions) {
142 for (size_t regionSector = 0; regionSector < region.count; regionSector++, sector++) {
143 auto sectorSize = region.size;
144 if (isSectorWritable(sector)) {
145 readAddress[sector] = &(*ram)[writeAddress[sector]];
146 if (!loaded) {
147 auto* ramPtr = const_cast<uint8_t*>(
148 &(*ram)[writeAddress[sector]]);
149 if (offset >= romSize) {
150 // completely past end of rom
151 ranges::fill(std::span{ramPtr, sectorSize}, 0xFF);
152 } else if (offset + sectorSize >= romSize) {
153 // partial overlap
154 auto last = romSize - offset;
155 auto missing = sectorSize - last;
156 const uint8_t* romPtr = &(*rom)[offset];
157 ranges::copy(std::span{romPtr, last}, ramPtr);
158 ranges::fill(std::span{&ramPtr[last], missing}, 0xFF);
159 } else {
160 // completely before end of rom
161 const uint8_t* romPtr = &(*rom)[offset];
162 ranges::copy(std::span{romPtr, sectorSize}, ramPtr);
163 }
164 }
165 } else {
166 assert(rom); // must have rom constructor parameter
167 if ((offset + sectorSize) <= romSize) {
168 readAddress[sector] = &(*rom)[offset];
169 } else {
170 readAddress[sector] = nullptr;
171 }
172 }
173 offset += sectorSize;
174 }
175 }
176 assert(offset == size());
177
178 reset();
179}
180
181AmdFlash::~AmdFlash() = default;
182
183AmdFlash::GetSectorInfoResult AmdFlash::getSectorInfo(size_t address) const
184{
185 address &= size() - 1;
186 auto it = chip.geometry.regions.begin();
187 size_t sector = 0;
188 while (address >= it->count * it->size) {
189 address -= it->count * it->size;
190 sector += it->count;
191 ++it;
192 assert(it != chip.geometry.regions.end());
193 }
194 return {sector + address / it->size, it->size, address % it->size};
195}
196
198{
199 status = 0x80;
200 softReset();
201}
202
203void AmdFlash::softReset()
204{
205 cmd.clear();
206 setState(State::IDLE);
207 status &= 0xC4; // clear status
208}
209
210void AmdFlash::setState(State newState)
211{
212 if (state == newState) return;
213 state = newState;
214 motherBoard.getCPU().invalidateAllSlotsRWCache(0x0000, 0x10000);
215}
216
217uint8_t AmdFlash::peek(size_t address) const
218{
219 if (state == State::IDLE) {
220 auto [sector, sectorSize, offset] = getSectorInfo(address);
221 if (const uint8_t* addr = readAddress[sector]) {
222 return addr[offset];
223 } else {
224 return 0xFF;
225 }
226 } else if (state == State::IDENT) {
228 if (chip.autoSelect.oddZero && (address & 1)) {
229 // some devices return zero for odd bits instead of mirroring
230 return 0x00;
231 }
232 // convert byte address to native address
233 address >>= 1;
234 }
235 return narrow_cast<uint8_t>(peekAutoSelect(address, chip.autoSelect.undefined));
236 } else if (state == State::CFI) {
238 // convert byte address to native address
239 const uint16_t result = peekCFI(address >> 1);
240 return narrow_cast<uint8_t>(address & 1 ? result >> 8 : result);
241 } else {
242 return narrow_cast<uint8_t>(peekCFI(address));
243 }
244 } else if (state == one_of(State::STATUS, State::PRGERR)) {
245 return status;
246 } else {
248 }
249}
250
251uint16_t AmdFlash::peekAutoSelect(size_t address, uint16_t undefined) const
252{
253 switch (address & chip.autoSelect.readMask) {
254 case 0x0:
256 case 0x1:
257 return chip.autoSelect.device.size() == 1 ? chip.autoSelect.device[0] | 0x2200 : 0x227E;
258 case 0x2:
260 // convert native address to byte address
261 address <<= 1;
262 }
263 return isSectorWritable(getSectorInfo(address).sector) ? 0 : 1;
264 case 0x3:
265 // On AM29F040 it indicates "Autoselect Device Unprotect Code".
266 // Datasheet does not elaborate. Value is 0x01 according to
267 // https://www.msx.org/forum/semi-msx-talk/emulation/matra-ink-emulated
268 //
269 // On M29W640G it indicates "Extended Block Verify Code".
270 // Bit 7: 0: Extended memory block not factory locked
271 // 1: Extended memory block factory locked
272 // Other bits are 0x18 on GL, GT and GB models, and 0x01 on GH model.
273 // Datasheet does not explain their meaning.
274 // Actual value is 0x08 on GB model (verified on hardware).
275 //
276 // On S29GL064S it indicates "Secure Silicon Region Factory Protect":
277 // Bit 4: 0: WP# protects lowest address (bottom boot)
278 // 1: WP# protects highest address (top boot)
279 // Bit 7: 0: Secure silicon region not factory locked
280 // 1: Secure silicon region factory locked
281 // Other bits are 0xFF0A. Datasheet does not explain their meaning.
282 //
283 // On Alliance Memory AS29CF160B it indicates Continuation ID.
284 // Datasheet does not elaborate. Value is 0x7F.
285 return chip.autoSelect.extraCode;
286 case 0xE:
287 return chip.autoSelect.device.size() == 2 ? chip.autoSelect.device[0] | 0x2200 : undefined;
288 case 0xF:
289 return chip.autoSelect.device.size() == 2 ? chip.autoSelect.device[1] | 0x2200 : undefined;
290 default:
291 // some devices return zero instead of 0xFFFF (typical)
292 return undefined;
293 }
294}
295
296uint16_t AmdFlash::peekCFI(size_t address) const
297{
298 const size_t maskedAddress = address & chip.cfi.readMask;
299
300 if (maskedAddress < 0x10) {
301 if (chip.cfi.withManufacturerDevice) {
302 // M29W640GB exposes manufacturer & device ID below 0x10 (as 16-bit values)
303 switch (maskedAddress) {
304 case 0x0:
306 case 0x1:
307 return chip.autoSelect.device.size() == 1 ? chip.autoSelect.device[0] | 0x2200 : 0x227E;
308 case 0x2:
309 return chip.autoSelect.device.size() == 2 ? chip.autoSelect.device[0] | 0x2200 : 0xFFFF;
310 case 0x3:
311 return chip.autoSelect.device.size() == 2 ? chip.autoSelect.device[1] | 0x2200 : 0xFFFF;
312 default:
313 return 0xFFFF;
314 }
315 } else if (chip.cfi.withAutoSelect) {
316 // S29GL064S exposes auto select data below 0x10 (as 16-bit values)
317 return peekAutoSelect(address);
318 }
319 }
320
321 switch (maskedAddress) {
322 case 0x10:
323 return 'Q';
324 case 0x11:
325 return 'R';
326 case 0x12:
327 return 'Y';
328 case 0x13:
329 return 0x02; // AMD compatible
330 case 0x14:
331 return 0x00;
332 case 0x15:
333 return 0x40;
334 case 0x16:
335 case 0x17:
336 case 0x18:
337 case 0x19:
338 case 0x1A:
339 return 0x00;
340 case 0x1B:
341 return chip.cfi.systemInterface.supply.minVcc;
342 case 0x1C:
343 return chip.cfi.systemInterface.supply.maxVcc;
344 case 0x1D:
345 return chip.cfi.systemInterface.supply.minVpp;
346 case 0x1E:
347 return chip.cfi.systemInterface.supply.maxVpp;
348 case 0x1F:
350 case 0x20:
352 case 0x21:
354 case 0x22:
356 case 0x23:
358 case 0x24:
360 case 0x25:
362 case 0x26:
364 case 0x27:
365 return chip.geometry.size.exponent;
366 case 0x28:
367 return narrow<uint8_t>(to_underlying(chip.geometry.deviceInterface) & 0xFF);
368 case 0x29:
369 return narrow<uint8_t>(to_underlying(chip.geometry.deviceInterface) >> 8);
370 case 0x2A:
371 return chip.program.pageSize.exponent;
372 case 0x2B:
373 return 0x00;
374 case 0x2C:
375 return narrow<uint8_t>(chip.geometry.regions.size());
376 case 0x2D:
377 case 0x31:
378 case 0x35:
379 case 0x39:
380 if (const size_t index = (maskedAddress - 0x2D) >> 2; index < chip.geometry.regions.size()) {
381 return narrow<uint8_t>((chip.geometry.regions[index].count - 1) & 0xFF);
382 }
383 return 0x00;
384 case 0x2E:
385 case 0x32:
386 case 0x36:
387 case 0x3A:
388 if (const size_t index = (maskedAddress - 0x2E) >> 2; index < chip.geometry.regions.size()) {
389 return narrow<uint8_t>((chip.geometry.regions[index].count - 1) >> 8);
390 }
391 return 0x00;
392 case 0x2F:
393 case 0x33:
394 case 0x37:
395 case 0x3B:
396 if (const size_t index = (maskedAddress - 0x2F) >> 2; index < chip.geometry.regions.size()) {
397 return narrow<uint8_t>((chip.geometry.regions[index].size >> 8) & 0xFF);
398 }
399 return 0x00;
400 case 0x30:
401 case 0x34:
402 case 0x38:
403 case 0x3C:
404 if (const size_t index = (maskedAddress - 0x30) >> 2; index < chip.geometry.regions.size()) {
405 return narrow<uint8_t>((chip.geometry.regions[index].size >> 8) >> 8);
406 }
407 return 0x00;
408 case 0x40:
409 return 'P';
410 case 0x41:
411 return 'R';
412 case 0x42:
413 return 'I';
414 case 0x43:
415 return '0' + chip.cfi.primaryAlgorithm.version.major;
416 case 0x44:
417 return '0' + chip.cfi.primaryAlgorithm.version.minor;
418 case 0x45:
419 return narrow<uint8_t>(chip.cfi.primaryAlgorithm.addressSensitiveUnlock | (chip.cfi.primaryAlgorithm.siliconRevision << 2));
420 case 0x46:
422 case 0x47:
424 case 0x48:
426 case 0x49:
428 case 0x4A:
430 case 0x4B:
431 return chip.cfi.primaryAlgorithm.burstMode;
432 case 0x4C:
433 return chip.cfi.primaryAlgorithm.pageMode;
434 case 0x4D:
435 return chip.cfi.primaryAlgorithm.version.minor >= 1 ? chip.cfi.primaryAlgorithm.supply.minAcc : 0xFFFF;
436 case 0x4E:
437 return chip.cfi.primaryAlgorithm.version.minor >= 1 ? chip.cfi.primaryAlgorithm.supply.maxAcc : 0xFFFF;
438 case 0x4F:
439 return chip.cfi.primaryAlgorithm.version.minor >= 1 ? chip.cfi.primaryAlgorithm.bootBlockFlag : 0xFFFF;
440 case 0x50:
441 return chip.cfi.primaryAlgorithm.version.minor >= 2 ? chip.cfi.primaryAlgorithm.programSuspend : 0xFFFF;
442 default:
443 // TODO
444 // 0x61-0x64 Unique 64-bit device number / security code for M29W800DB and M29W640GB.
445 // M29W640GB also has unknown data in 0x65-0x6A, as well as some in the 0x80-0xFF range.
446 return 0xFFFF;
447 }
448}
449
450bool AmdFlash::isSectorWritable(size_t sector) const
451{
452 return vppWpPinLow && (sector == one_of(0u, 1u)) ? false : (writeAddress[sector] != -1) ;
453}
454
455uint8_t AmdFlash::read(size_t address)
456{
457 const uint8_t value = peek(address);
458 if (state == State::STATUS) {
459 setState(State::IDLE);
460 } else if (state == State::PRGERR) {
461 status ^= 0x40;
462 }
463 return value;
464}
465
466const uint8_t* AmdFlash::getReadCacheLine(size_t address) const
467{
468 if (state == State::IDLE) {
469 auto [sector, sectorSize, offset] = getSectorInfo(address);
470 const uint8_t* addr = readAddress[sector];
471 return addr ? &addr[offset] : MSXDevice::unmappedRead.data();
472 } else {
473 return nullptr;
474 }
475}
476
477void AmdFlash::write(size_t address, uint8_t value)
478{
479 cmd.push_back({address, value});
480
481 if (state == State::IDLE) {
482 if (checkCommandAutoSelect() ||
483 checkCommandEraseSector() ||
484 checkCommandProgram() ||
485 checkCommandDoubleByteProgram() ||
486 checkCommandQuadrupleByteProgram() ||
487 checkCommandBufferProgram() ||
488 checkCommandEraseChip() ||
489 checkCommandCFIQuery() ||
490 checkCommandContinuityCheck() ||
491 checkCommandStatusRead() ||
492 checkCommandStatusClear() ||
493 checkCommandLongReset() ||
494 checkCommandReset()) {
495 // do nothing, we're still matching a command, but it is not complete yet
496 } else {
497 cmd.clear();
498 }
499 } else if (state == State::IDENT) {
500 if (checkCommandCFIQuery() ||
501 checkCommandLongReset() ||
502 checkCommandReset()) {
503 // do nothing, we're still matching a command, but it is not complete yet
504 } else {
505 cmd.clear();
506 }
507 } else if (state == State::CFI) {
508 if (checkCommandLongReset() ||
509 checkCommandCFIExit() ||
510 checkCommandReset()) {
511 // do nothing, we're still matching a command, but it is not complete yet
512 } else {
513 cmd.clear();
514 }
515 } else if (state == State::STATUS) {
516 // TODO confirm. S29GL064S datasheet: "it is not recommended".
517 if (checkCommandLongReset() ||
518 checkCommandReset()) {
519 // do nothing, we're still matching a command, but it is not complete yet
520 } else {
521 cmd.clear();
522 }
523 } else if (state == State::PRGERR) {
524 if (checkCommandLongReset() ||
525 (chip.program.shortAbortReset && checkCommandReset())) {
526 // do nothing, we're still matching a command, but it is not complete yet
527 } else {
528 cmd.clear();
529 }
530 } else {
532 }
533}
534
535// The checkCommandXXX() methods below return
536// true -> if the command sequence still matches, but is not complete yet
537// false -> if the command was fully matched or does not match with
538// the current command sequence.
539// If there was a full match, the command is also executed.
540bool AmdFlash::checkCommandReset()
541{
542 if (cmd[0].value == 0xf0) {
543 softReset();
544 }
545 return false;
546}
547
548bool AmdFlash::checkCommandLongReset()
549{
550 static constexpr std::array<uint8_t, 3> cmdSeq = {0xaa, 0x55, 0xf0};
551 if (partialMatch(cmdSeq)) {
552 if (cmd.size() < 3) return true;
553 softReset();
554 }
555 return false;
556}
557
558bool AmdFlash::checkCommandStatusRead()
559{
560 static constexpr std::array<uint8_t, 1> cmdSeq = {0x70};
561 if (chip.misc.statusCommand && partialMatch(cmdSeq)) {
562 setState(State::STATUS);
563 }
564 return false;
565}
566
567bool AmdFlash::checkCommandStatusClear()
568{
569 static constexpr std::array<uint8_t, 1> cmdSeq = {0x71};
570 if (chip.misc.statusCommand && partialMatch(cmdSeq)) {
571 softReset();
572 }
573 return false;
574}
575
576bool AmdFlash::checkCommandContinuityCheck()
577{
578 if (chip.misc.continuityCommand && cmd[0] == AddressValue{0x5554AB, 0xFF}) {
579 if (cmd.size() < 2) return true;
580 if (cmd.size() == 2 && cmd[1] == AddressValue{0x2AAB54, 0x00}) {
581 status |= 0x01;
582 }
583 }
584 return false;
585}
586
587bool AmdFlash::checkCommandCFIQuery()
588{
589 // convert byte address to native address
590 const size_t addr = (chip.geometry.deviceInterface == DeviceInterface::x8x16) ? cmd[0].addr >> 1 : cmd[0].addr;
591 if (chip.cfi.command && (addr & chip.cfi.commandMask) == 0x55 && cmd[0].value == 0x98) {
592 setState(State::CFI);
593 }
594 return false;
595}
596
597bool AmdFlash::checkCommandCFIExit()
598{
599 if (chip.cfi.exitCommand && cmd[0].value == 0xff) {
600 softReset();
601 }
602 return false;
603}
604
605bool AmdFlash::checkCommandEraseSector()
606{
607 static constexpr std::array<uint8_t, 5> cmdSeq = {0xaa, 0x55, 0x80, 0xaa, 0x55};
608 if (partialMatch(cmdSeq)) {
609 if (cmd.size() < 6) return true;
610 if (cmd[5].value == 0x30) {
611 auto addr = cmd[5].addr;
612 auto [sector, sectorSize, offset] = getSectorInfo(addr);
613 if (isSectorWritable(sector)) {
614 ram->memset(writeAddress[sector], 0xff, sectorSize);
615 }
616
617 status |= 0x80; // immediate completion
618 }
619 }
620 return false;
621}
622
623bool AmdFlash::checkCommandEraseChip()
624{
625 static constexpr std::array<uint8_t, 5> cmdSeq = {0xaa, 0x55, 0x80, 0xaa, 0x55};
626 if (partialMatch(cmdSeq)) {
627 if (cmd.size() < 6) return true;
628 if (cmd[5].value == 0x10) {
629 if (ram) ram->memset(0, 0xff, ram->size());
630
631 status |= 0x80; // immediate completion
632 }
633 }
634 return false;
635}
636
637bool AmdFlash::checkCommandProgramHelper(size_t numBytes, std::span<const uint8_t> cmdSeq)
638{
639 if (numBytes <= chip.program.pageSize && partialMatch(cmdSeq)) {
640 if (cmd.size() < (cmdSeq.size() + numBytes)) return true;
641 for (auto i : xrange(cmdSeq.size(), cmdSeq.size() + numBytes)) {
642 auto addr = cmd[i].addr;
643 auto [sector, sectorSize, offset] = getSectorInfo(addr);
644 if (isSectorWritable(sector)) {
645 auto ramAddr = writeAddress[sector] + offset;
646 uint8_t ramValue = (*ram)[ramAddr] & cmd[i].value;
647 ram->write(ramAddr, ramValue);
648
649 status = (status & 0x7F) | (ramValue & 0x80); // immediate completion
650 }
651 }
652 }
653 return false;
654}
655
656bool AmdFlash::checkCommandProgram()
657{
658 static constexpr std::array<uint8_t, 3> cmdSeq = {0xaa, 0x55, 0xa0};
659 return checkCommandProgramHelper(1, cmdSeq);
660}
661
662bool AmdFlash::checkCommandDoubleByteProgram()
663{
664 static constexpr std::array<uint8_t, 1> cmdSeq = {0x50};
665 return chip.program.fastCommand && checkCommandProgramHelper(2, cmdSeq);
666}
667
668bool AmdFlash::checkCommandQuadrupleByteProgram()
669{
670 static constexpr std::array<uint8_t, 1> cmdSeq = {0x56};
671 return chip.program.fastCommand && checkCommandProgramHelper(4, cmdSeq);
672}
673
674bool AmdFlash::checkCommandBufferProgram()
675{
676 static constexpr std::array<uint8_t, 2> cmdSeq = {0xaa, 0x55};
677 if (chip.program.bufferCommand && partialMatch(cmdSeq)) {
678 if (cmd.size() <= 2) return true;
679 if (cmd.size() >= 3 && cmd[2].value == 0x25) {
680 if (cmd.size() <= 3) return true;
681 size_t sector = getSectorInfo(cmd[2].addr).sector;
682 if (cmd.size() >= 4 && cmd[3].value < chip.program.pageSize && getSectorInfo(cmd[3].addr).sector == sector) {
683 if (cmd.size() <= 4) return true;
684 const size_t pageMask = ~(chip.program.pageSize - 1);
685 const unsigned confirmIndex = 4 + cmd[3].value + 1;
686 if (cmd.size() >= 5 && ((cmd.back().addr & pageMask) == (cmd[4].addr & pageMask) || cmd.size() > confirmIndex)) {
687 if (cmd.size() >= 5 && cmd.size() <= confirmIndex) {
688 status = (status & 0x7F) | (~cmd.back().value & 0x80);
689 }
690 if (cmd.size() <= confirmIndex) return true;
691 if (cmd.size() == confirmIndex + 1 && cmd[confirmIndex].value == 0x29 && getSectorInfo(cmd[confirmIndex].addr).sector == sector) {
692 if (isSectorWritable(sector)) {
693 // TODO de-duplicate same-address writes to the last one
694 for (auto i : xrange(size_t(4), confirmIndex)) {
695 auto ramAddr = writeAddress[sector] + getSectorInfo(cmd[i].addr).offset;
696 uint8_t ramValue = (*ram)[ramAddr] & cmd[i].value;
697 ram->write(ramAddr, ramValue);
698
699 status = (status & 0x7F) | (ramValue & 0x80); // immediate completion
700 }
701 }
702 return false;
703 }
704 }
705 }
706
707 status = (status & 0xDF) | 0x02;
708 setState(State::PRGERR);
709 }
710 }
711 return false;
712}
713
714bool AmdFlash::checkCommandAutoSelect()
715{
716 static constexpr std::array<uint8_t, 3> cmdSeq = {0xaa, 0x55, 0x90};
717 if (partialMatch(cmdSeq)) {
718 if (cmd.size() < 3) return true;
719 setState(State::IDENT);
720 }
721 return false;
722}
723
724bool AmdFlash::partialMatch(std::span<const uint8_t> dataSeq) const
725{
726 static constexpr std::array<unsigned, 5> addrSeq = {0, 1, 0, 0, 1};
727 static constexpr std::array<unsigned, 2> cmdAddr = {0x555, 0x2aa};
728 (void)addrSeq; (void)cmdAddr; // suppress (invalid) gcc warning
729
730 assert(dataSeq.size() <= 5);
731 return ranges::all_of(xrange(std::min(dataSeq.size(), cmd.size())), [&](auto i) {
732 // convert byte address to native address
733 auto addr = (chip.geometry.deviceInterface == DeviceInterface::x8x16) ? cmd[i].addr >> 1 : cmd[i].addr;
734 return ((addr & 0x7FF) == cmdAddr[addrSeq[i]]) &&
735 (cmd[i].value == dataSeq[i]);
736 });
737}
738
739
740static constexpr std::initializer_list<enum_string<AmdFlash::State>> stateInfo = {
741 { "IDLE", AmdFlash::State::IDLE },
742 { "IDENT", AmdFlash::State::IDENT },
743 { "CFI", AmdFlash::State::CFI },
744 { "STATUS", AmdFlash::State::STATUS },
745 { "PRGERR", AmdFlash::State::PRGERR }
746};
748
749template<typename Archive>
750void AmdFlash::AddressValue::serialize(Archive& ar, unsigned /*version*/)
751{
752 ar.serialize("address", addr,
753 "value", value);
754}
755
756// version 1: Initial version.
757// version 2: Added vppWpPinLow.
758// version 3: Changed cmd to static_vector, added status.
759template<typename Archive>
760void AmdFlash::serialize(Archive& ar, unsigned version)
761{
762 ar.serialize("ram", *ram);
763 if (ar.versionAtLeast(version, 3)) {
764 ar.serialize("cmd", cmd,
765 "status", status);
766 } else {
767 std::array<AddressValue, 8> cmdArray;
768 unsigned cmdSize = 0;
769 ar.serialize("cmd", cmdArray,
770 "cmdIdx", cmdSize);
771 cmd = {from_range, subspan(cmdArray, 0, cmdSize)};
772 }
773 ar.serialize("state", state);
774 if (ar.versionAtLeast(version, 2)) {
775 ar.serialize("vppWpPinLow", vppWpPinLow);
776 }
777}
779
780} // namespace openmsx
void write(size_t address, uint8_t value)
Definition AmdFlash.cc:477
void serialize(Archive &ar, unsigned version)
Definition AmdFlash.cc:760
const uint8_t * getReadCacheLine(size_t address) const
Definition AmdFlash.cc:466
uint8_t read(size_t address)
Definition AmdFlash.cc:455
AmdFlash(const Rom &rom, const ValidatedChip &chip, std::span< const bool > writeProtectSectors, const DeviceConfig &config, Load load=Load::NORMAL)
Create AmdFlash with given configuration.
Definition AmdFlash.cc:24
size_t size() const
Definition AmdFlash.hh:208
uint8_t peek(size_t address) const
Definition AmdFlash.cc:217
void invalidateAllSlotsRWCache(word start, unsigned size)
Invalidate the CPU its cache for the interval [start, start + size) For example MSXMemoryMapper and M...
Definition MSXCPU.cc:181
static std::array< byte, 0x10000 > unmappedRead
Definition MSXDevice.hh:306
void resize(size_t size)
Grow or shrink the memory block.
Definition MemBuffer.hh:111
const T * data() const
Returns pointer to the start of the memory buffer.
Definition MemBuffer.hh:81
const std::string & getName() const
Definition Rom.hh:41
const std::string & getLoadedFilename() const
Definition SRAM.hh:38
void write(size_t addr, byte value)
Definition SRAM.cc:64
size_t size() const
Definition SRAM.hh:35
void memset(size_t addr, byte c, size_t size)
Definition SRAM.cc:73
constexpr size_t size() const noexcept
constexpr double e
Definition Math.hh:21
This file implemented 3 utility functions:
Definition Autofire.cc:11
bool all_of(InputRange &&range, UnaryPredicate pred)
Definition ranges.hh:188
constexpr void fill(ForwardRange &&range, const T &value)
Definition ranges.hh:307
constexpr auto copy(InputRange &&range, OutputIter out)
Definition ranges.hh:252
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
Definition ranges.hh:473
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define SERIALIZE_ENUM(TYPE, INFO)
constexpr from_range_t from_range
constexpr auto to_underlying(E e) noexcept
Definition stl.hh:465
void serialize(Archive &ar, unsigned version)
Definition AmdFlash.cc:750
ManufacturerID manufacturer
Definition AmdFlash.hh:31
static_vector< uint8_t, 2 > device
Definition AmdFlash.hh:32
struct openmsx::AmdFlash::CFI::PrimaryAlgorithm::Supply supply
struct openmsx::AmdFlash::CFI::PrimaryAlgorithm::Version version
struct openmsx::AmdFlash::CFI::SystemInterface::Supply supply
struct openmsx::AmdFlash::CFI::SystemInterface::MaxTimeoutMultiplier maxTimeoutMult
struct openmsx::AmdFlash::CFI::SystemInterface::TypicalTimeout typTimeout
struct openmsx::AmdFlash::CFI::SystemInterface systemInterface
struct openmsx::AmdFlash::CFI::PrimaryAlgorithm primaryAlgorithm
static_vector< Region, 4 > regions
Definition AmdFlash.hh:63
DeviceInterface deviceInterface
Definition AmdFlash.hh:62
power_of_two< size_t > size
Definition AmdFlash.hh:64
power_of_two< size_t > pageSize
Definition AmdFlash.hh:76
#define UNREACHABLE
constexpr auto xrange(T e)
Definition xrange.hh:132