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 "CliComm.hh"
8 #include "HardwareConfig.hh"
9 #include "MSXException.hh"
10 #include "Math.hh"
11 #include "one_of.hh"
12 #include "ranges.hh"
13 #include "serialize.hh"
14 #include "view.hh"
15 #include "xrange.hh"
16 #include <cstring>
17 #include <cassert>
18 #include <iterator>
19 #include <memory>
20 
21 using std::string;
22 
23 namespace openmsx {
24 
26  word ID_, Addressing addressing_,
27  const DeviceConfig& config, Load load)
28  : motherBoard(config.getMotherBoard())
29  , sectorInfo(std::move(sectorInfo_))
30  , size(sum(view::transform(sectorInfo, [](auto& i) { return i.size; })))
31  , ID(ID_)
32  , addressing(addressing_)
33 {
34  init(rom.getName() + "_flash", config, load, &rom);
35 }
36 
37 AmdFlash::AmdFlash(const string& name, span<const SectorInfo> sectorInfo_,
38  word ID_, Addressing addressing_,
39  const DeviceConfig& config)
40  : motherBoard(config.getMotherBoard())
41  , sectorInfo(std::move(sectorInfo_))
42  , size(sum(view::transform(sectorInfo, [](auto& i) { return i.size; })))
43  , ID(ID_)
44  , addressing(addressing_)
45 {
46  init(name, config, Load::NORMAL, nullptr);
47 }
48 
49 [[nodiscard]] static bool sramEmpty(const SRAM& ram)
50 {
51  return ranges::all_of(xrange(ram.getSize()),
52  [&](auto i) { return ram[i] == 0xFF; });
53 }
54 
55 void AmdFlash::init(const string& name, const DeviceConfig& config, Load load, const Rom* rom)
56 {
57  assert(Math::ispow2(getSize()));
58 
59  auto numSectors = sectorInfo.size();
60 
61  unsigned writableSize = 0;
62  unsigned readOnlySize = 0;
63  writeAddress.resize(numSectors);
64  for (auto i : xrange(numSectors)) {
65  if (sectorInfo[i].writeProtected) {
66  writeAddress[i] = -1;
67  readOnlySize += sectorInfo[i].size;
68  } else {
69  writeAddress[i] = writableSize;
70  writableSize += sectorInfo[i].size;
71  }
72  }
73  assert((writableSize + readOnlySize) == getSize());
74 
75  bool loaded = false;
76  if (writableSize) {
77  if (load == Load::NORMAL) {
78  ram = std::make_unique<SRAM>(
79  name, "flash rom",
80  writableSize, config, nullptr, &loaded);
81  } else {
82  // Hack for 'Matra INK', flash chip is wired-up so that
83  // writes are never visible to the MSX (but the flash
84  // is not made write-protected). In this case it doesn't
85  // make sense to load/save the SRAM file.
86  ram = std::make_unique<SRAM>(
87  name, "flash rom",
88  writableSize, config, SRAM::DontLoadTag{});
89  }
90  }
91  if (readOnlySize) {
92  // If some part of the flash is read-only we require a ROM
93  // constructor parameter.
94  assert(rom);
95  }
96 
97  const auto* romTag = config.getXML()->findChild("rom");
98  bool initialContentSpecified = romTag && romTag->findChild("sha1");
99 
100  // check whether the loaded SRAM is empty, whilst initial content was specified
101  if (!rom && loaded && initialContentSpecified && sramEmpty(*ram)) {
102  config.getCliComm().printInfo(
103  "This flash device (", config.getHardwareConfig().getName(),
104  ") has initial content specified, but this content "
105  "was not loaded, because there was already content found "
106  "and loaded from persistent storage. However, this "
107  "content is blank (it was probably created automatically "
108  "when the specified initial content could not be loaded "
109  "when this device was used for the first time). If you "
110  "still wish to load the specified initial content, "
111  "please remove the blank persistent storage file: ",
112  ram->getLoadedFilename());
113  }
114 
115  std::unique_ptr<Rom> rom_;
116  if (!rom && !loaded) {
117  // If we don't have a ROM constructor parameter and there was
118  // no sram content loaded (= previous persistent flash
119  // content), then try to load some initial content. This
120  // represents the original content of the flash when the device
121  // ships. This ROM is optional, if it's not found, then the
122  // initial flash content is all 0xFF.
123  try {
124  rom_ = std::make_unique<Rom>(
125  "", "", // dummy name and description
126  config);
127  rom = rom_.get();
128  config.getCliComm().printInfo(
129  "Loaded initial content for flash ROM from ",
130  rom->getFilename());
131  } catch (MSXException& e) {
132  // ignore error
133  assert(rom == nullptr); // 'rom' remains nullptr
134  // only if an actual sha1sum was given, tell the user we failed to use it
135  if (initialContentSpecified) {
136  config.getCliComm().printWarning(
137  "Could not load specified initial content "
138  "for flash ROM: ", e.getMessage());
139  }
140  }
141  }
142 
143  readAddress.resize(numSectors);
144  unsigned romSize = rom ? rom->getSize() : 0;
145  unsigned offset = 0;
146  for (auto i : xrange(numSectors)) {
147  unsigned sectorSize = sectorInfo[i].size;
148  if (isSectorWritable(unsigned(i))) {
149  readAddress[i] = &(*ram)[writeAddress[i]];
150  if (!loaded) {
151  auto* ramPtr = const_cast<byte*>(
152  &(*ram)[writeAddress[i]]);
153  if (offset >= romSize) {
154  // completely past end of rom
155  memset(ramPtr, 0xFF, sectorSize);
156  } else if (offset + sectorSize >= romSize) {
157  // partial overlap
158  unsigned last = romSize - offset;
159  unsigned missing = sectorSize - last;
160  const byte* romPtr = &(*rom)[offset];
161  memcpy(ramPtr, romPtr, last);
162  memset(ramPtr + last, 0xFF, missing);
163  } else {
164  // completely before end of rom
165  const byte* romPtr = &(*rom)[offset];
166  memcpy(ramPtr, romPtr, sectorSize);
167  }
168  }
169  } else {
170  assert(rom); // must have rom constructor parameter
171  if ((offset + sectorSize) <= romSize) {
172  readAddress[i] = &(*rom)[offset];
173  } else {
174  readAddress[i] = nullptr;
175  }
176  }
177  offset += sectorSize;
178  }
179  assert(offset == getSize());
180 
181  reset();
182 }
183 
184 AmdFlash::~AmdFlash() = default;
185 
186 AmdFlash::GetSectorInfoResult AmdFlash::getSectorInfo(unsigned address) const
187 {
188  address &= getSize() - 1;
189  auto it = sectorInfo.begin();
190  unsigned sector = 0;
191  while (address >= it->size) {
192  address -= it->size;
193  ++sector;
194  ++it;
195  assert(it != sectorInfo.end());
196  }
197  unsigned sectorSize = it->size;
198  unsigned offset = address;
199  return {sector, sectorSize, offset};
200 }
201 
203 {
204  cmdIdx = 0;
205  setState(ST_IDLE);
206 }
207 
208 void AmdFlash::setState(State newState)
209 {
210  if (state == newState) return;
211  state = newState;
212  motherBoard.getCPU().invalidateAllSlotsRWCache(0x0000, 0x10000);
213 }
214 
215 byte AmdFlash::peek(unsigned address) const
216 {
217  auto [sector, sectorSize, offset] = getSectorInfo(address);
218  if (state == ST_IDLE) {
219  if (const byte* addr = readAddress[sector]) {
220  return addr[offset];
221  } else {
222  return 0xFF;
223  }
224  } else {
225  if (addressing == Addressing::BITS_12) {
226  // convert the address to the '11 bit case'
227  address >>= 1;
228  }
229  switch (address & 3) {
230  case 0:
231  return ID >> 8;
232  case 1:
233  return ID & 0xFF;
234  case 2:
235  // 1 -> write protected
236  return isSectorWritable(sector) ? 0 : 1;
237  case 3:
238  default:
239  // TODO what is this? According to this it reads as '1'
240  // http://www.msx.org/forumtopicl8329.html
241  return 1;
242  }
243  }
244 }
245 
246 bool AmdFlash::isSectorWritable(unsigned sector) const
247 {
248  return vppWpPinLow && (sector == one_of(0u, 1u)) ? false : (writeAddress[sector] != -1) ;
249 }
250 
251 byte AmdFlash::read(unsigned address) const
252 {
253  // note: after a read we stay in the same mode
254  return peek(address);
255 }
256 
257 const byte* AmdFlash::getReadCacheLine(unsigned address) const
258 {
259  if (state == ST_IDLE) {
260  auto [sector, sectorSize, offset] = getSectorInfo(address);
261  const byte* addr = readAddress[sector];
262  return addr ? &addr[offset] : MSXDevice::unmappedRead;
263  } else {
264  return nullptr;
265  }
266 }
267 
268 void AmdFlash::write(unsigned address, byte value)
269 {
270  assert(cmdIdx < MAX_CMD_SIZE);
271  cmd[cmdIdx].addr = address;
272  cmd[cmdIdx].value = value;
273  ++cmdIdx;
274  if (checkCommandManufacturer() ||
275  checkCommandEraseSector() ||
276  checkCommandProgram() ||
277  checkCommandDoubleByteProgram() ||
278  checkCommandQuadrupleByteProgram() ||
279  checkCommandEraseChip() ||
280  checkCommandReset()) {
281  // do nothing, we're still matching a command, but it is not complete yet
282  } else {
283  reset();
284  }
285 }
286 
287 // The checkCommandXXX() methods below return
288 // true -> if the command sequence still matches, but is not complete yet
289 // false -> if the command was fully matched or does not match with
290 // the current command sequence.
291 // If there was a full match, the command is also executed.
292 bool AmdFlash::checkCommandReset()
293 {
294  if (cmd[0].value == 0xf0) {
295  reset();
296  }
297  return false;
298 }
299 
300 bool AmdFlash::checkCommandEraseSector()
301 {
302  static constexpr byte cmdSeq[] = { 0xaa, 0x55, 0x80, 0xaa, 0x55 };
303  if (partialMatch(5, cmdSeq)) {
304  if (cmdIdx < 6) return true;
305  if (cmd[5].value == 0x30) {
306  unsigned addr = cmd[5].addr;
307  auto [sector, sectorSize, offset] = getSectorInfo(addr);
308  if (isSectorWritable(sector)) {
309  ram->memset(writeAddress[sector],
310  0xff, sectorSize);
311  }
312  }
313  }
314  return false;
315 }
316 
317 bool AmdFlash::checkCommandEraseChip()
318 {
319  static constexpr byte cmdSeq[] = { 0xaa, 0x55, 0x80, 0xaa, 0x55 };
320  if (partialMatch(5, cmdSeq)) {
321  if (cmdIdx < 6) return true;
322  if (cmd[5].value == 0x10) {
323  if (ram) ram->memset(0, 0xff, ram->getSize());
324  }
325  }
326  return false;
327 }
328 
329 bool AmdFlash::checkCommandProgramHelper(unsigned numBytes, const byte* cmdSeq, size_t cmdLen)
330 {
331  if (partialMatch(cmdLen, cmdSeq)) {
332  if (cmdIdx < (cmdLen + numBytes)) return true;
333  for (auto i : xrange(cmdLen, cmdLen + numBytes)) {
334  unsigned addr = cmd[i].addr;
335  auto [sector, sectorSize, offset] = getSectorInfo(addr);
336  if (isSectorWritable(sector)) {
337  unsigned ramAddr = writeAddress[sector] + offset;
338  ram->write(ramAddr, (*ram)[ramAddr] & cmd[i].value);
339  }
340  }
341  }
342  return false;
343 }
344 
345 bool AmdFlash::checkCommandProgram()
346 {
347  static constexpr byte cmdSeq[] = { 0xaa, 0x55, 0xa0 };
348  return checkCommandProgramHelper(1, cmdSeq, std::size(cmdSeq));
349 }
350 
351 bool AmdFlash::checkCommandDoubleByteProgram()
352 {
353  static constexpr byte cmdSeq[] = { 0x50 };
354  return checkCommandProgramHelper(2, cmdSeq, std::size(cmdSeq));
355 }
356 
357 bool AmdFlash::checkCommandQuadrupleByteProgram()
358 {
359  static constexpr byte cmdSeq[] = { 0x56 };
360  return checkCommandProgramHelper(4, cmdSeq, std::size(cmdSeq));
361 }
362 
363 bool AmdFlash::checkCommandManufacturer()
364 {
365  static constexpr byte cmdSeq[] = { 0xaa, 0x55, 0x90 };
366  if (partialMatch(3, cmdSeq)) {
367  if (cmdIdx == 3) {
368  setState(ST_IDENT);
369  }
370  if (cmdIdx < 4) return true;
371  }
372  return false;
373 }
374 
375 bool AmdFlash::partialMatch(size_t len, const byte* dataSeq) const
376 {
377  static constexpr unsigned addrSeq[] = { 0, 1, 0, 0, 1 };
378  unsigned cmdAddr[2] = { 0x555, 0x2aa };
379 
380  assert(len <= 5);
381  for (auto i : xrange(std::min(unsigned(len), cmdIdx))) {
382  // convert the address to the '11 bit case'
383  unsigned addr = (addressing == Addressing::BITS_12) ? cmd[i].addr >> 1 : cmd[i].addr;
384  if (((addr & 0x7FF) != cmdAddr[addrSeq[i]]) ||
385  (cmd[i].value != dataSeq[i])) {
386  return false;
387  }
388  }
389  return true;
390 }
391 
392 
393 static constexpr std::initializer_list<enum_string<AmdFlash::State>> stateInfo = {
394  { "IDLE", AmdFlash::ST_IDLE },
395  { "IDENT", AmdFlash::ST_IDENT }
396 };
398 
399 template<typename Archive>
400 void AmdFlash::AmdCmd::serialize(Archive& ar, unsigned /*version*/)
401 {
402  ar.serialize("address", addr,
403  "value", value);
404 }
405 
406 template<typename Archive>
407 void AmdFlash::serialize(Archive& ar, unsigned version)
408 {
409  ar.serialize("ram", *ram,
410  "cmd", cmd,
411  "cmdIdx", cmdIdx,
412  "state", state);
413  if (ar.versionAtLeast(version, 2)) {
414  ar.serialize("vppWpPinLow", vppWpPinLow);
415  }
416 }
418 
419 } // namespace openmsx
Definition: one_of.hh:7
unsigned getSize() const
Definition: AmdFlash.hh:65
void write(unsigned address, byte value)
Definition: AmdFlash.cc:268
void serialize(Archive &ar, unsigned version)
Definition: AmdFlash.cc:407
byte peek(unsigned address) const
Definition: AmdFlash.cc:215
byte read(unsigned address) const
Definition: AmdFlash.cc:251
AmdFlash(const Rom &rom, span< const SectorInfo > sectorInfo, word ID, Addressing addressing, const DeviceConfig &config, Load load=Load::NORMAL)
Create AmdFlash with given configuration.
Definition: AmdFlash.cc:25
const byte * getReadCacheLine(unsigned address) const
Definition: AmdFlash.cc:257
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:183
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:301
void resize(size_t size)
Grow or shrink the memory block.
Definition: MemBuffer.hh:111
Definition: span.hh:126
constexpr bool ispow2(T x) noexcept
Is the given number an integral power of two? That is, does it have exactly one 1-bit in binary repre...
Definition: Math.hh:57
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
XMLElement load(const string &filename, string_view systemID)
Definition: XMLLoader.cc:34
This file implemented 3 utility functions:
Definition: Autofire.cc:9
constexpr byte ID
Definition: MSXKanji12.cc:8
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
SRAM
Definition: SRAM.cc:150
bool all_of(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:119
auto transform(InputRange &&range, OutputIter out, UnaryOperation op)
Definition: ranges.hh:161
size_t size(std::string_view utf8)
Definition: view.hh:10
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:983
auto sum(InputRange &&range)
Definition: stl.hh:293
void serialize(Archive &ar, unsigned version)
Definition: AmdFlash.cc:400
constexpr auto xrange(T e)
Definition: xrange.hh:155