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