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