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