openMSX
SdCard.cc
Go to the documentation of this file.
1 #include "SdCard.hh"
2 #include "DeviceConfig.hh"
3 #include "HD.hh"
4 #include "MSXException.hh"
5 #include "unreachable.hh"
6 #include "endian.hh"
7 #include "serialize.hh"
8 #include "serialize_stl.hh"
9 #include <memory>
10 
11 // TODO:
12 // - replace transferDelayCounter with 0xFF's in responseQueue?
13 // - remove duplication between WRITE and MULTI_WRITE (is it worth it?)
14 // - see TODOs in the code below
15 
16 namespace openmsx {
17 
18 // data response tokens
19 static const byte DRT_ACCEPTED = 0x05;
20 static const byte DRT_WRITE_ERROR = 0x0D;
21 
22 // start block tokens and stop tran token
23 static const byte START_BLOCK_TOKEN = 0xFE;
24 static const byte START_BLOCK_TOKEN_MBW = 0xFC;
25 static const byte STOP_TRAN_TOKEN = 0xFD;
26 
27 // data error token
28 static const byte DATA_ERROR_TOKEN_ERROR = 0x01;
29 static const byte DATA_ERROR_TOKEN_OUT_OF_RANGE = 0x08;
30 
31 // responses
32 static const byte R1_BUSY = 0x00;
33 static const byte R1_IDLE = 0x01; // TODO: why is lots of code checking for this instead of R1_BUSY?
34 static const byte R1_ILLEGAL_COMMAND = 0x04;
35 static const byte R1_PARAMETER_ERROR = 0x80;
36 
38  : hd(config.getXML() ? std::make_unique<HD>(config) : nullptr)
39  , cmdIdx(0)
40  , transferDelayCounter(0)
41  , mode(COMMAND)
42  , currentSector(0)
43  , currentByteInSector(0)
44 {
45 }
46 
47 SdCard::~SdCard() = default;
48 
49 // helper methods for 'transfer' to avoid duplication
50 byte SdCard::readCurrentByteFromCurrentSector() {
51  byte retval;
52  if (currentByteInSector == -1) {
53  retval = START_BLOCK_TOKEN;
54  try {
55  hd->readSector(currentSector, sectorBuf);
56  } catch (MSXException&) {
57  retval = DATA_ERROR_TOKEN_ERROR;
58  }
59  } else {
60  // output next byte from stream
61  retval = sectorBuf.raw[currentByteInSector];
62  }
63  currentByteInSector++;
64  if (currentByteInSector == sizeof(sectorBuf)) {
65  responseQueue.push_back({0x00, 0x00}); // 2 CRC's (dummy)
66  }
67  return retval;
68 }
69 
70 byte SdCard::transfer(byte value, bool cs)
71 {
72  if (!hd) return 0xFF; // no card inserted
73 
74  if (cs) {
75  // /CS is true: not for this chip
76  return 0xFF;
77  }
78 
79  // process output
80  byte retval = 0xFF;
81  if (transferDelayCounter > 0) {
82  --transferDelayCounter;
83  } else {
84  if (responseQueue.empty()) {
85  switch (mode) {
86  case READ:
87  retval = readCurrentByteFromCurrentSector();
88  if (currentByteInSector == sizeof(sectorBuf)) {
89  mode = COMMAND;
90  }
91  break;
92  case MULTI_READ:
93  // when there's an error, you probably have to send a CMD12
94  // to go back to the COMMAND mode. This is not
95  // clear in the spec (it's wrongly suggested
96  // for the MULTI_WRITE mode!)
97  if (currentSector >= hd->getNbSectors()) {
98  // data out of range, send data error token
99  retval = DATA_ERROR_TOKEN_OUT_OF_RANGE;
100  } else {
101  retval = readCurrentByteFromCurrentSector();
102  if (currentByteInSector == sizeof(sectorBuf)) {
103  currentSector++;
104  currentByteInSector = -1;
105  }
106  }
107  break;
108  case WRITE:
109  case MULTI_WRITE:
110  // apparently nothing is returned while writing
111  case COMMAND:
112  default:
113  break;
114  }
115  } else {
116  retval = responseQueue.pop_front();
117  }
118  }
119 
120  // process input
121  switch (mode) {
122  case WRITE:
123  // first check for data token
124  if (currentByteInSector == -1) {
125  if (value == START_BLOCK_TOKEN) {
126  currentByteInSector++;
127  }
128  break;
129  }
130  if (currentByteInSector < int(sizeof(sectorBuf))) {
131  sectorBuf.raw[currentByteInSector] = value;
132  }
133  currentByteInSector++;
134  if (currentByteInSector == (sizeof(sectorBuf) + 2)) {
135  byte response = DRT_ACCEPTED;
136  // copy buffer to SD card
137  try {
138  hd->writeSector(currentSector, sectorBuf);
139  } catch (MSXException&) {
140  response = DRT_WRITE_ERROR;
141  }
142  mode = COMMAND;
143  transferDelayCounter = 1;
144  responseQueue.push_back(response);
145  }
146  break;
147  case MULTI_WRITE:
148  // first check for stop or start token
149  if (currentByteInSector == -1) {
150  if (value == STOP_TRAN_TOKEN) {
151  mode = COMMAND;
152  }
153  if (value == START_BLOCK_TOKEN_MBW) {
154  currentByteInSector++;
155  }
156  break;
157  }
158  if (currentByteInSector < int(sizeof(sectorBuf))) {
159  sectorBuf.raw[currentByteInSector] = value;
160  }
161  currentByteInSector++;
162  if (currentByteInSector == (sizeof(sectorBuf) + 2)) {
163  // check if still in valid range
164  byte response = DRT_ACCEPTED;
165  if (currentSector >= hd->getNbSectors()) {
166  response = DRT_WRITE_ERROR;
167  // note: mode is not changed, should be done by
168  // the host with CMD12 (STOP_TRANSMISSION) -
169  // however, this makes no sense, CMD12 is only
170  // for Multiple Block Read!? Wrong in the spec?
171  } else {
172  // copy buffer to SD card
173  try {
174  hd->writeSector(currentSector, sectorBuf);
175  currentByteInSector = -1;
176  currentSector++;
177  } catch (MSXException&) {
178  response = DRT_WRITE_ERROR;
179  // note: mode is not changed, should be
180  // done by the host with CMD12
181  // (STOP_TRANSMISSION) - however, this
182  // makes no sense, CMD12 is only for
183  // Multiple Block Read!? Wrong in the
184  // spec?
185  }
186  }
187  transferDelayCounter = 1;
188  responseQueue.push_back(response);
189  }
190  break;
191  case COMMAND:
192  default:
193  if ((cmdIdx == 0 && (value >> 6) == 1) // command start
194  || cmdIdx > 0) { // command in progress
195  cmdBuf[cmdIdx] = value;
196  ++cmdIdx;
197  if (cmdIdx == 6) {
198  executeCommand();
199  cmdIdx = 0;
200  }
201  }
202  break;
203  }
204 
205  return retval;
206 }
207 
208 void SdCard::executeCommand()
209 {
210  // it takes 2 transfers (2x8 cycles) before a reply
211  // can be given to a command
212  transferDelayCounter = 2;
213  byte command = cmdBuf[0] & 0x3F;
214  switch (command) {
215  case 0: // GO_IDLE_STATE
216  responseQueue.clear();
217  mode = COMMAND;
218  responseQueue.push_back(R1_IDLE);
219  break;
220  case 8: // SEND_IF_COND
221  // conditions are always OK
222  responseQueue.push_back({
223  R1_IDLE, // R1 (OK) SDHC (checked by MegaSD and FUZIX)
224  byte(0x02), // command version
225  byte(0x00), // reserved
226  byte(0x01), // voltage accepted
227  cmdBuf[4]});// check pattern
228  break;
229  case 9:{ // SEND_CSD
230  responseQueue.push_back({
231  R1_BUSY, // OK (ignored on MegaSD code, used in FUZIX)
232  // now follows a CSD version 2.0 (for SDHC)
233  START_BLOCK_TOKEN, // data token
234  byte(0x40), // CSD_STRUCTURE [127:120]
235  byte(0x0E), // (TAAC)
236  byte(0x00), // (NSAC)
237  byte(0x32), // (TRAN_SPEED)
238  byte(0x00), // CCC
239  byte(0x00), // CCC / (READ_BL_LEN)
240  byte(0x00)}); // (RBP)/(WBM)/(RBM)/ DSR_IMP
241  // SD_CARD_SIZE = (C_SIZE + 1) * 512kByte
242  auto c_size = unsigned((hd->getNbSectors() * sizeof(sectorBuf)) / (512 * 1024) - 1);
243  responseQueue.push_back({
244  byte((c_size >> 16) & 0x3F), // C_SIZE 1
245  byte((c_size >> 8) & 0xFF), // C_SIZE 2
246  byte((c_size >> 0) & 0xFF), // C_SIZE 3
247  byte(0x00), // res/(EBE)/(SS1)
248  byte(0x00), // (SS2)/(WGS)
249  byte(0x00), // (WGE)/res/(RF)/(WBL1)
250  byte(0x00), // (WBL2)/(WBP)/res
251  byte(0x00), // (FFG)/COPY/PWP/TWP/(FF)/res
252  byte(0x01)}); // CRC / 1
253  break;}
254  case 10: // SEND_CID
255  responseQueue.push_back({
256  R1_BUSY, // OK (ignored on MegaSD, unused in FUZIX so far)
257  START_BLOCK_TOKEN, // data token
258  byte(0xAA), // CID01 // manuf ID
259  byte('o' ), // CID02 // OEM/App ID 1
260  byte('p' ), // CID03 // OEM/App ID 2
261  byte('e' ), // CID04 // Prod name 1
262  byte('n' ), // CID05 // Prod name 2
263  byte('M' ), // CID06 // Prod name 3
264  byte('S' ), // CID07 // Prod name 4
265  byte('X' ), // CID08 // Prod name 5
266  byte(0x01), // CID09 // Prod Revision
267  byte(0x12), // CID10 // Prod Serial 1
268  byte(0x34), // CID11 // Prod Serial 2
269  byte(0x56), // CID12 // Prod Serial 3
270  byte(0x78), // CID13 // Prod Serial 4
271  byte(0x00), // CID14 // reserved / Y1
272  byte(0xE6), // CID15 // Y2 / M
273  byte(0x01)}); // CID16 // CRC / not used
274  break;
275  case 12: // STOP TRANSMISSION
276  responseQueue.push_back(R1_IDLE); // R1 (OK)
277  mode = COMMAND;
278  break;
279  case 16: // SET_BLOCKLEN
280  responseQueue.push_back(R1_IDLE); // OK, we don't really care
281  break;
282  case 17: // READ_SINGLE_BLOCK
283  case 18: // READ_MULTIPLE_BLOCK
284  case 24: // WRITE_BLOCK
285  case 25: // WRITE_MULTIPLE_BLOCK
286  // SDHC so the address is the sector
287  currentSector = Endian::readB32(&cmdBuf[1]);
288  if (currentSector >= hd->getNbSectors()) {
289  responseQueue.push_back(R1_PARAMETER_ERROR);
290  } else {
291  responseQueue.push_back(R1_BUSY);
292  switch (command) {
293  case 17: mode = READ; break;
294  case 18: mode = MULTI_READ; break;
295  case 24: mode = WRITE; break;
296  case 25: mode = MULTI_WRITE; break;
297  default: UNREACHABLE;
298  }
299  currentByteInSector = -1; // wait for token
300  }
301  break;
302  case 41: // implementation of ACMD 41!!
303  // SD_SEND_OP_COND
304  responseQueue.push_back(R1_BUSY);
305  break;
306  case 55: // APP_CMD
307  // TODO: go to ACMD mode, but not necessary now
308  responseQueue.push_back(R1_IDLE);
309  break;
310  case 58: // READ_OCR
311  responseQueue.push_back({
312  R1_BUSY,// R1 (OK) (ignored on MegaSD, checked in FUZIX)
313  byte(0x40), // OCR Reg part 1 (SDHC: CCS=1)
314  byte(0x00), // OCR Reg part 2
315  byte(0x00), // OCR Reg part 3
316  byte(0x00)}); // OCR Reg part 4
317  break;
318 
319  default:
320  responseQueue.push_back(R1_ILLEGAL_COMMAND);
321  break;
322  }
323 }
324 
325 static std::initializer_list<enum_string<SdCard::Mode>> modeInfo = {
326  { "COMMAND", SdCard::COMMAND },
327  { "READ", SdCard::READ },
328  { "MULTI_READ", SdCard::MULTI_READ },
329  { "WRITE", SdCard::WRITE },
330  { "MULTI_WRITE", SdCard::MULTI_WRITE }
331 };
332 SERIALIZE_ENUM(SdCard::Mode, modeInfo);
333 
334 template<typename Archive>
335 void SdCard::serialize(Archive& ar, unsigned /*version*/)
336 {
337  ar.serialize("mode", mode,
338  "cmdBuf", cmdBuf);
339  ar.serialize_blob("sectorBuf", sectorBuf.raw, sizeof(sectorBuf));
340  if (hd) ar.serialize("hd", *hd);
341  ar.serialize("cmdIdx", cmdIdx,
342  "transferDelayCounter", transferDelayCounter,
343  "responseQueue", responseQueue,
344  "currentSector", currentSector,
345  "currentByteInSector", currentByteInSector);
346 }
348 
349 } // namespace openmsx
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
STL namespace.
byte transfer(byte value, bool cs)
Definition: SdCard.cc:70
bool empty() const
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
SdCard(const DeviceConfig &config)
Definition: SdCard.cc:37
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:1006
void serialize(Archive &ar, unsigned version)
Definition: SdCard.cc:335
void push_back(U &&u)
#define UNREACHABLE
Definition: unreachable.hh:38