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 "countof.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 <memory>
19 
20 using std::string;
21 using std::vector;
22 
23 namespace openmsx {
24 
25 AmdFlash::AmdFlash(const Rom& rom, vector<SectorInfo> sectorInfo_,
26  word ID_, bool use12bitAddressing_,
27  const DeviceConfig& config, bool 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  , use12bitAddressing(use12bitAddressing_)
33 {
34  init(rom.getName() + "_flash", config, load, &rom);
35 }
36 
37 AmdFlash::AmdFlash(const string& name, vector<SectorInfo> sectorInfo_,
38  word ID_, bool use12bitAddressing_,
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  , use12bitAddressing(use12bitAddressing_)
45 {
46  init(name, config, true, nullptr);
47 }
48 
49 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, bool 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) {
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  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  string{}, string{}, // 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 void AmdFlash::getSectorInfo(unsigned address, unsigned& sector,
187  unsigned& sectorSize, unsigned& offset) const
188 {
189  address &= getSize() - 1;
190  auto it = begin(sectorInfo);
191  sector = 0;
192  while (address >= it->size) {
193  address -= it->size;
194  ++sector;
195  ++it;
196  assert(it != end(sectorInfo));
197  }
198  sectorSize = it->size;
199  offset = address;
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().invalidateMemCache(0x0000, 0x10000);
213 }
214 
215 byte AmdFlash::peek(unsigned address) const
216 {
217  unsigned sector, sectorSize, offset;
218  getSectorInfo(address, sector, sectorSize, offset);
219  if (state == ST_IDLE) {
220  if (const byte* addr = readAddress[sector]) {
221  return addr[offset];
222  } else {
223  return 0xFF;
224  }
225  } else {
226  if (use12bitAddressing) {
227  // convert the address to the '11 bit case'
228  address >>= 1;
229  }
230  switch (address & 3) {
231  case 0:
232  return ID >> 8;
233  case 1:
234  return ID & 0xFF;
235  case 2:
236  // 1 -> write protected
237  return isSectorWritable(sector) ? 0 : 1;
238  case 3:
239  default:
240  // TODO what is this? According to this it reads as '1'
241  // http://www.msx.org/forumtopicl8329.html
242  return 1;
243  }
244  }
245 }
246 
247 bool AmdFlash::isSectorWritable(unsigned sector) const
248 {
249  return vppWpPinLow && (sector == 0 || sector == 1) ? false : (writeAddress[sector] != -1) ;
250 }
251 
252 byte AmdFlash::read(unsigned address)
253 {
254  // note: after a read we stay in the same mode
255  return peek(address);
256 }
257 
258 const byte* AmdFlash::getReadCacheLine(unsigned address) const
259 {
260  if (state == ST_IDLE) {
261  unsigned sector, sectorSize, offset;
262  getSectorInfo(address, sector, sectorSize, offset);
263  const byte* addr = readAddress[sector];
264  return addr ? &addr[offset] : MSXDevice::unmappedRead;
265  } else {
266  return nullptr;
267  }
268 }
269 
270 void AmdFlash::write(unsigned address, byte value)
271 {
272  assert(cmdIdx < MAX_CMD_SIZE);
273  cmd[cmdIdx].addr = address;
274  cmd[cmdIdx].value = value;
275  ++cmdIdx;
276  if (checkCommandManufacturer() ||
277  checkCommandEraseSector() ||
278  checkCommandProgram() ||
279  checkCommandDoubleByteProgram() ||
280  checkCommandQuadrupleByteProgram() ||
281  checkCommandEraseChip() ||
282  checkCommandReset()) {
283  // do nothing, we're still matching a command, but it is not complete yet
284  } else {
285  reset();
286  }
287 }
288 
289 // The checkCommandXXX() methods below return
290 // true -> if the command sequence still matches, but is not complete yet
291 // false -> if the command was fully matched or does not match with
292 // the current command sequence.
293 // If there was a full match, the command is also executed.
294 bool AmdFlash::checkCommandReset()
295 {
296  if (cmd[0].value == 0xf0) {
297  reset();
298  }
299  return false;
300 }
301 
302 bool AmdFlash::checkCommandEraseSector()
303 {
304  static const byte cmdSeq[] = { 0xaa, 0x55, 0x80, 0xaa, 0x55 };
305  if (partialMatch(5, cmdSeq)) {
306  if (cmdIdx < 6) return true;
307  if (cmd[5].value == 0x30) {
308  unsigned addr = cmd[5].addr;
309  unsigned sector, sectorSize, offset;
310  getSectorInfo(addr, sector, sectorSize, offset);
311  if (isSectorWritable(sector)) {
312  ram->memset(writeAddress[sector],
313  0xff, sectorSize);
314  }
315  }
316  }
317  return false;
318 }
319 
320 bool AmdFlash::checkCommandEraseChip()
321 {
322  static const byte cmdSeq[] = { 0xaa, 0x55, 0x80, 0xaa, 0x55 };
323  if (partialMatch(5, cmdSeq)) {
324  if (cmdIdx < 6) return true;
325  if (cmd[5].value == 0x10) {
326  if (ram) ram->memset(0, 0xff, ram->getSize());
327  }
328  }
329  return false;
330 }
331 
332 bool AmdFlash::checkCommandProgramHelper(unsigned numBytes, const byte* cmdSeq, size_t cmdLen)
333 {
334  if (partialMatch(cmdLen, cmdSeq)) {
335  if (cmdIdx < (cmdLen + numBytes)) return true;
336  for (auto i = cmdLen; i < (cmdLen + numBytes); ++i) {
337  unsigned addr = cmd[i].addr;
338  unsigned sector, sectorSize, offset;
339  getSectorInfo(addr, sector, sectorSize, offset);
340  if (isSectorWritable(sector)) {
341  unsigned ramAddr = writeAddress[sector] + offset;
342  ram->write(ramAddr, (*ram)[ramAddr] & cmd[i].value);
343  }
344  }
345  }
346  return false;
347 }
348 
349 bool AmdFlash::checkCommandProgram()
350 {
351  static const byte cmdSeq[] = { 0xaa, 0x55, 0xa0 };
352  return checkCommandProgramHelper(1, cmdSeq, countof(cmdSeq));
353 }
354 
355 bool AmdFlash::checkCommandDoubleByteProgram()
356 {
357  static const byte cmdSeq[] = { 0x50 };
358  return checkCommandProgramHelper(2, cmdSeq, countof(cmdSeq));
359 }
360 
361 bool AmdFlash::checkCommandQuadrupleByteProgram()
362 {
363  static const byte cmdSeq[] = { 0x56 };
364  return checkCommandProgramHelper(4, cmdSeq, countof(cmdSeq));
365 }
366 
367 bool AmdFlash::checkCommandManufacturer()
368 {
369  static const byte cmdSeq[] = { 0xaa, 0x55, 0x90 };
370  if (partialMatch(3, cmdSeq)) {
371  if (cmdIdx == 3) {
372  setState(ST_IDENT);
373  }
374  if (cmdIdx < 4) return true;
375  }
376  return false;
377 }
378 
379 bool AmdFlash::partialMatch(size_t len, const byte* dataSeq) const
380 {
381  static const unsigned addrSeq[] = { 0, 1, 0, 0, 1 };
382  unsigned cmdAddr[2] = { 0x555, 0x2aa };
383 
384  assert(len <= 5);
385  unsigned n = std::min(unsigned(len), cmdIdx);
386  for (unsigned i = 0; i < n; ++i) {
387  // convert the address to the '11 bit case'
388  unsigned addr = use12bitAddressing ? cmd[i].addr >> 1 : cmd[i].addr;
389  if (((addr & 0x7FF) != cmdAddr[addrSeq[i]]) ||
390  (cmd[i].value != dataSeq[i])) {
391  return false;
392  }
393  }
394  return true;
395 }
396 
397 
398 static std::initializer_list<enum_string<AmdFlash::State>> stateInfo = {
399  { "IDLE", AmdFlash::ST_IDLE },
400  { "IDENT", AmdFlash::ST_IDENT }
401 };
402 SERIALIZE_ENUM(AmdFlash::State, stateInfo);
403 
404 template<typename Archive>
405 void AmdFlash::AmdCmd::serialize(Archive& ar, unsigned /*version*/)
406 {
407  ar.serialize("address", addr,
408  "value", value);
409 }
410 
411 template<typename Archive>
412 void AmdFlash::serialize(Archive& ar, unsigned version)
413 {
414  ar.serialize("ram", *ram,
415  "cmd", cmd,
416  "cmdIdx", cmdIdx,
417  "state", state);
418  if (ar.versionAtLeast(version, 2)) {
419  ar.serialize("vppWpPinLow", vppWpPinLow);
420  }
421 }
423 
424 } // namespace openmsx
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
auto sum(InputRange &&range)
Definition: stl.hh:302
byte read(unsigned address)
Definition: AmdFlash.cc:252
const std::string & getMessage() const &
Definition: MSXException.hh:23
auto xrange(T e)
Definition: xrange.hh:170
vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:269
Definition: view.hh:10
const byte * getReadCacheLine(unsigned address) const
Definition: AmdFlash.cc:258
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
const XMLElement * findChild(string_view name) const
Definition: XMLElement.cc:94
void write(unsigned addr, byte value)
Definition: SRAM.cc:67
STL namespace.
void printInfo(string_view message)
Definition: CliComm.cc:15
void memset(unsigned addr, byte c, unsigned size)
Definition: SRAM.cc:76
byte peek(unsigned address) const
Definition: AmdFlash.cc:215
CliComm & getCliComm() const
Definition: DeviceConfig.cc:18
void invalidateMemCache(word start, unsigned size)
Invalidate the CPU its cache for the interval [start, start + size) For example MSXMemoryMapper and M...
Definition: MSXCPU.cc:147
auto begin(const string_view &x)
Definition: string_view.hh:151
const std::string & getName() const
unsigned getSize() const
Definition: AmdFlash.hh:57
void resize(size_t size)
Grow or shrink the memory block.
Definition: MemBuffer.hh:120
void serialize(Archive &ar, unsigned version)
Definition: AmdFlash.cc:412
const std::string & getLoadedFilename() const
Definition: SRAM.hh:36
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
XMLElement load(string_view filename, string_view systemID)
Definition: XMLLoader.cc:31
const HardwareConfig & getHardwareConfig() const
Definition: DeviceConfig.hh:42
unsigned getSize() const
Definition: SRAM.hh:33
#define countof(array)
Definition: countof.hh:48
unsigned getSize() const
Definition: Rom.hh:32
uint16_t word
16 bit unsigned integer
Definition: openmsx.hh:29
static byte unmappedRead[0x10000]
Definition: MSXDevice.hh:274
void write(unsigned address, byte value)
Definition: AmdFlash.cc:270
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1006
bool all_of(InputRange &&range, UnaryPredicate pred)
Definition: ranges.hh:119
const XMLElement * getXML() const
Definition: DeviceConfig.hh:47
AmdFlash(const Rom &rom, std::vector< SectorInfo > sectorInfo, word ID, bool use12bitAddressing, const DeviceConfig &config, bool load=true)
Create AmdFlash with given configuration.
Definition: AmdFlash.cc:25
auto transform(InputRange &&range, OutputIter out, UnaryOperation op)
Definition: ranges.hh:161
void serialize(Archive &ar, unsigned version)
Definition: AmdFlash.cc:405
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:56
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.hh:62
void printWarning(string_view message)
Definition: CliComm.cc:20
auto end(const string_view &x)
Definition: string_view.hh:152