openMSX
WD33C93.cc
Go to the documentation of this file.
1 /* Ported from:
2 ** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/wd33c93.c,v
3 ** Revision: 1.12
4 ** Date: 2007/03/25 17:05:07
5 **
6 ** Based on the WD33C93 emulation in MESS (www.mess.org).
7 **
8 ** More info: http://www.bluemsx.com
9 **
10 ** Copyright (C) 2003-2006 Daniel Vik, Tomas Karlsson, white cat
11 */
12 
13 #include "WD33C93.hh"
14 #include "SCSI.hh"
15 #include "SCSIDevice.hh"
16 #include "DummySCSIDevice.hh"
17 #include "SCSIHD.hh"
18 #include "SCSILS120.hh"
19 #include "DeviceConfig.hh"
20 #include "XMLElement.hh"
21 #include "MSXException.hh"
22 #include "StringOp.hh"
23 #include "serialize.hh"
24 #include "memory.hh"
25 #include <cassert>
26 #include <cstring>
27 
28 namespace openmsx {
29 
30 static const unsigned MAX_DEV = 8;
31 
32 static const byte REG_OWN_ID = 0x00;
33 static const byte REG_CONTROL = 0x01;
34 static const byte REG_TIMEO = 0x02;
35 static const byte REG_TSECS = 0x03;
36 static const byte REG_THEADS = 0x04;
37 static const byte REG_TCYL_HI = 0x05;
38 static const byte REG_TCYL_LO = 0x06;
39 static const byte REG_ADDR_HI = 0x07;
40 static const byte REG_ADDR_2 = 0x08;
41 static const byte REG_ADDR_3 = 0x09;
42 static const byte REG_ADDR_LO = 0x0a;
43 static const byte REG_SECNO = 0x0b;
44 static const byte REG_HEADNO = 0x0c;
45 static const byte REG_CYLNO_HI = 0x0d;
46 static const byte REG_CYLNO_LO = 0x0e;
47 static const byte REG_TLUN = 0x0f;
48 static const byte REG_CMD_PHASE = 0x10;
49 static const byte REG_SYN = 0x11;
50 static const byte REG_TCH = 0x12;
51 static const byte REG_TCM = 0x13;
52 static const byte REG_TCL = 0x14;
53 static const byte REG_DST_ID = 0x15;
54 static const byte REG_SRC_ID = 0x16;
55 static const byte REG_SCSI_STATUS = 0x17; // (r)
56 static const byte REG_CMD = 0x18;
57 static const byte REG_DATA = 0x19;
58 static const byte REG_QUEUE_TAG = 0x1a;
59 static const byte REG_AUX_STATUS = 0x1f; // (r)
60 
61 static const byte REG_CDBSIZE = 0x00;
62 static const byte REG_CDB1 = 0x03;
63 static const byte REG_CDB2 = 0x04;
64 static const byte REG_CDB3 = 0x05;
65 static const byte REG_CDB4 = 0x06;
66 static const byte REG_CDB5 = 0x07;
67 static const byte REG_CDB6 = 0x08;
68 static const byte REG_CDB7 = 0x09;
69 static const byte REG_CDB8 = 0x0a;
70 static const byte REG_CDB9 = 0x0b;
71 static const byte REG_CDB10 = 0x0c;
72 static const byte REG_CDB11 = 0x0d;
73 static const byte REG_CDB12 = 0x0e;
74 
75 static const byte OWN_EAF = 0x08; // ENABLE ADVANCED FEATURES
76 
77 // SCSI STATUS
78 static const byte SS_RESET = 0x00; // reset
79 static const byte SS_RESET_ADV = 0x01; // reset w/adv. features
80 static const byte SS_XFER_END = 0x16; // select and transfer complete
81 static const byte SS_SEL_TIMEOUT = 0x42; // selection timeout
82 static const byte SS_DISCONNECT = 0x85;
83 
84 // AUX STATUS
85 static const byte AS_DBR = 0x01; // data buffer ready
86 static const byte AS_CIP = 0x10; // command in progress, chip is busy
87 static const byte AS_BSY = 0x20; // Level 2 command in progress
88 static const byte AS_LCI = 0x40; // last command ignored
89 static const byte AS_INT = 0x80;
90 
91 /* command phase
92 0x00 NO_SELECT
93 0x10 SELECTED
94 0x20 IDENTIFY_SENT
95 0x30 COMMAND_OUT
96 0x41 SAVE_DATA_RECEIVED
97 0x42 DISCONNECT_RECEIVED
98 0x43 LEGAL_DISCONNECT
99 0x44 RESELECTED
100 0x45 IDENTIFY_RECEIVED
101 0x46 DATA_TRANSFER_DONE
102 0x47 STATUS_STARTED
103 0x50 STATUS_RECEIVED
104 0x60 COMPLETE_RECEIVED
105 */
106 
108 {
109  devBusy = false;
110 
111  for (auto* t : config.getXML()->getChildren("target")) {
112  unsigned id = t->getAttributeAsInt("id");
113  if (id >= MAX_DEV) {
115  "Invalid SCSI id: " << id <<
116  " (should be 0.." << MAX_DEV - 1 << ')');
117  }
118  if (dev[id]) {
120  "Duplicate SCSI id: " << id);
121  }
122  DeviceConfig conf(config, *t);
123  auto& type = t->getChild("type").getData();
124  if (type == "SCSIHD") {
125  dev[id] = make_unique<SCSIHD>(conf, buffer,
128  } else if (type == "SCSILS120") {
129  dev[id] = make_unique<SCSILS120>(conf, buffer,
132  } else {
133  throw MSXException("Unknown SCSI device: " + type);
134  }
135  }
136  // fill remaining targets with dummy SCSI devices to prevent crashes
137  for (auto& d : dev) {
138  if (!d) d = make_unique<DummySCSIDevice>();
139  }
140  reset(false);
141 
142  // avoid UMR on savestate
143  memset(buffer.data(), 0, SCSIDevice::BUFFER_SIZE);
144  counter = 0;
145  blockCounter = 0;
146  targetId = 0;
147 }
148 
149 void WD33C93::disconnect()
150 {
151  if (phase != SCSI::BUS_FREE) {
152  assert(targetId < MAX_DEV);
153  dev[targetId]->disconnect();
154  if (regs[REG_SCSI_STATUS] != SS_XFER_END) {
155  regs[REG_SCSI_STATUS] = SS_DISCONNECT;
156  }
157  regs[REG_AUX_STATUS] = AS_INT;
158  phase = SCSI::BUS_FREE;
159  }
160  tc = 0;
161 }
162 
163 void WD33C93::execCmd(byte value)
164 {
165  if (regs[REG_AUX_STATUS] & AS_CIP) {
166  // CIP error
167  return;
168  }
169  //regs[REG_AUX_STATUS] |= AS_CIP;
170  regs[REG_CMD] = value;
171 
172  bool atn = false;
173  switch (value) {
174  case 0x00: // Reset controller (software reset)
175  memset(regs + 1, 0, 0x1a);
176  disconnect();
177  latch = 0; // TODO: is this correct? Some doc says: reset to zero by masterreset-signal but not by reset command.
178  regs[REG_SCSI_STATUS] =
179  (regs[REG_OWN_ID] & OWN_EAF) ? SS_RESET_ADV : SS_RESET;
180  break;
181 
182  case 0x02: // Assert ATN
183  break;
184 
185  case 0x04: // Disconnect
186  disconnect();
187  break;
188 
189  case 0x06: // Select with ATN (Lv2)
190  atn = true;
191  // fall-through
192  case 0x07: // Select Without ATN (Lv2)
193  targetId = regs[REG_DST_ID] & 7;
194  regs[REG_SCSI_STATUS] = SS_SEL_TIMEOUT;
195  tc = 0;
196  regs[REG_AUX_STATUS] = AS_INT;
197  break;
198 
199  case 0x08: // Select with ATN and transfer (Lv2)
200  atn = true;
201  // fall-through
202  case 0x09: // Select without ATN and Transfer (Lv2)
203  targetId = regs[REG_DST_ID] & 7;
204 
205  if (!devBusy && targetId < MAX_DEV && /* targetId != myId && */
206  dev[targetId]->isSelected()) {
207  if (atn) {
208  dev[targetId]->msgOut(regs[REG_TLUN] | 0x80);
209  }
210  devBusy = true;
211  counter = dev[targetId]->executeCmd(
212  &regs[REG_CDB1], phase, blockCounter);
213 
214  switch (phase) {
215  case SCSI::STATUS:
216  devBusy = false;
217  regs[REG_TLUN] = dev[targetId]->getStatusCode();
218  dev[targetId]->msgIn();
219  regs[REG_SCSI_STATUS] = SS_XFER_END;
220  disconnect();
221  break;
222 
223  case SCSI::EXECUTE:
224  regs[REG_AUX_STATUS] = AS_CIP | AS_BSY;
225  bufIdx = 0;
226  break;
227 
228  default:
229  devBusy = false;
230  regs[REG_AUX_STATUS] = AS_CIP | AS_BSY | AS_DBR;
231  bufIdx = 0;
232  }
233  //regs[REG_SRC_ID] |= regs[REG_DST_ID] & 7;
234  } else {
235  // timeout
236  tc = 0;
237  regs[REG_SCSI_STATUS] = SS_SEL_TIMEOUT;
238  regs[REG_AUX_STATUS] = AS_INT;
239  }
240  break;
241 
242  case 0x18: // Translate Address (Lv2)
243  default:
244  // unsupport command
245  break;
246  }
247 }
248 
250 {
251  latch = value & 0x1f;
252 }
253 
254 // Latch incremented by one each time a register is accessed,
255 // except for the address-, aux.status-, data- and command registers.
257 {
258  switch (latch) {
259  case REG_OWN_ID:
260  regs[REG_OWN_ID] = value;
261  myId = value & 7;
262  break;
263 
264  case REG_TCH:
265  tc = (tc & 0x00ffff) + (value << 16);
266  break;
267 
268  case REG_TCM:
269  tc = (tc & 0xff00ff) + (value << 8);
270  break;
271 
272  case REG_TCL:
273  tc = (tc & 0xffff00) + (value << 0);
274  break;
275 
276  case REG_CMD_PHASE:
277  regs[REG_CMD_PHASE] = value;
278  break;
279 
280  case REG_CMD:
281  execCmd(value);
282  return; // no latch-inc for address-, aux.status-, data- and command regs.
283 
284  case REG_DATA:
285  regs[REG_DATA] = value;
286  if (phase == SCSI::DATA_OUT) {
287  buffer[bufIdx++] = value;
288  --tc;
289  if (--counter == 0) {
290  counter = dev[targetId]->dataOut(blockCounter);
291  if (counter) {
292  bufIdx = 0;
293  return;
294  }
295  regs[REG_TLUN] = dev[targetId]->getStatusCode();
296  dev[targetId]->msgIn();
297  regs[REG_SCSI_STATUS] = SS_XFER_END;
298  disconnect();
299  }
300  }
301  return; // no latch-inc for address-, aux.status-, data- and command regs.
302 
303  case REG_AUX_STATUS:
304  return; // no latch-inc for address-, aux.status-, data- and command regs.
305 
306  default:
307  if (latch <= REG_SRC_ID) {
308  regs[latch] = value;
309  }
310  break;
311  }
312  latch = (latch + 1) & 0x1f;
313 }
314 
316 {
317  byte rv = regs[REG_AUX_STATUS];
318 
319  if (phase == SCSI::EXECUTE) {
320  counter = dev[targetId]->executingCmd(phase, blockCounter);
321 
322  switch (phase) {
323  case SCSI::STATUS: // TODO how can this ever be the case?
324  regs[REG_TLUN] = dev[targetId]->getStatusCode();
325  dev[targetId]->msgIn();
326  regs[REG_SCSI_STATUS] = SS_XFER_END;
327  disconnect();
328  break;
329 
330  case SCSI::EXECUTE:
331  break;
332 
333  default:
334  regs[REG_AUX_STATUS] |= AS_DBR;
335  }
336  }
337  return rv;
338 }
339 
340 // Latch incremented by one each time a register is accessed,
341 // except for the address-, aux.status-, data- and command registers.
343 {
344  byte rv;
345  switch (latch) {
346  case REG_SCSI_STATUS:
347  rv = regs[REG_SCSI_STATUS];
348  if (rv != SS_XFER_END) {
349  regs[REG_AUX_STATUS] &= ~AS_INT;
350  } else {
351  regs[REG_SCSI_STATUS] = SS_DISCONNECT;
352  regs[REG_AUX_STATUS] = AS_INT;
353  }
354  break;
355 
356  case REG_CMD:
357  return regs[REG_CMD]; // no latch-inc for address-, aux.status-, data- and command regs.
358 
359  case REG_DATA:
360  if (phase == SCSI::DATA_IN) {
361  rv = buffer[bufIdx++];
362  regs[REG_DATA] = rv;
363  --tc;
364  if (--counter == 0) {
365  if (blockCounter > 0) {
366  counter = dev[targetId]->dataIn(blockCounter);
367  if (counter) {
368  bufIdx = 0;
369  return rv;
370  }
371  }
372  regs[REG_TLUN] = dev[targetId]->getStatusCode();
373  dev[targetId]->msgIn();
374  regs[REG_SCSI_STATUS] = SS_XFER_END;
375  disconnect();
376  }
377  } else {
378  rv = regs[REG_DATA];
379  }
380  return rv; // no latch-inc for address-, aux.status-, data- and command regs.
381 
382  case REG_TCH:
383  rv = (tc >> 16) & 0xff;
384  break;
385 
386  case REG_TCM:
387  rv = (tc >> 8) & 0xff;
388  break;
389 
390  case REG_TCL:
391  rv = (tc >> 0) & 0xff;
392  break;
393 
394  case REG_AUX_STATUS:
395  return readAuxStatus(); // no latch-inc for address-, aux.status-, data- and command regs.
396 
397  default:
398  rv = regs[latch];
399  break;
400  }
401 
402  latch = (latch + 1) & 0x1f;
403  return rv;
404 }
405 
407 {
408  return regs[REG_AUX_STATUS];
409 }
410 
412 {
413  switch (latch) {
414  case REG_TCH:
415  return (tc >> 16) & 0xff;
416  case REG_TCM:
417  return (tc >> 8) & 0xff;
418  case REG_TCL:
419  return (tc >> 0) & 0xff;
420  default:
421  return regs[latch];
422  }
423 }
424 
425 void WD33C93::reset(bool scsireset)
426 {
427  // initialized register
428  memset(regs, 0, 0x1b);
429  memset(regs + 0x1b, 0xff, 4);
430  regs[REG_AUX_STATUS] = AS_INT;
431  myId = 0;
432  latch = 0;
433  tc = 0;
434  phase = SCSI::BUS_FREE;
435  bufIdx = 0;
436  if (scsireset) {
437  for (auto& d : dev) {
438  d->reset();
439  }
440  }
441 }
442 
443 
444 static std::initializer_list<enum_string<SCSI::Phase>> phaseInfo = {
445  { "UNDEFINED", SCSI::UNDEFINED },
446  { "BUS_FREE", SCSI::BUS_FREE },
447  { "ARBITRATION", SCSI::ARBITRATION },
448  { "SELECTION", SCSI::SELECTION },
449  { "RESELECTION", SCSI::RESELECTION },
450  { "COMMAND", SCSI::COMMAND },
451  { "EXECUTE", SCSI::EXECUTE },
452  { "DATA_IN", SCSI::DATA_IN },
453  { "DATA_OUT", SCSI::DATA_OUT },
454  { "STATUS", SCSI::STATUS },
455  { "MSG_OUT", SCSI::MSG_OUT },
456  { "MSG_IN", SCSI::MSG_IN }
457 };
458 SERIALIZE_ENUM(SCSI::Phase, phaseInfo);
459 
460 template<typename Archive>
461 void WD33C93::serialize(Archive& ar, unsigned /*version*/)
462 {
463  ar.serialize_blob("buffer", buffer.data(), buffer.size());
464  char tag[8] = { 'd', 'e', 'v', 'i', 'c', 'e', 'X', 0 };
465  for (unsigned i = 0; i < MAX_DEV; ++i) {
466  tag[6] = char('0' + i);
467  ar.serializePolymorphic(tag, *dev[i]);
468  }
469  ar.serialize("bufIdx", bufIdx);
470  ar.serialize("counter", counter);
471  ar.serialize("blockCounter", blockCounter);
472  ar.serialize("tc", tc);
473  ar.serialize("phase", phase);
474  ar.serialize("myId", myId);
475  ar.serialize("targetId", targetId);
476  ar.serialize_blob("registers", regs, sizeof(regs));
477  ar.serialize("latch", latch);
478  ar.serialize("devBusy", devBusy);
479 }
481 
482 /* Here is some info on the parameters for SCSI devices:
483 static SCSIDevice* wd33c93ScsiDevCreate(WD33C93* wd33c93, int id)
484 {
485 #if 1
486  // CD_UPDATE: Use dynamic parameters instead of hard coded ones
487  int diskId, mode, type;
488 
489  diskId = diskGetHdDriveId(hdId, id);
490  if (diskIsCdrom(diskId)) {
491  mode = MODE_SCSI1 | MODE_UNITATTENTION | MODE_REMOVABLE | MODE_NOVAXIS;
492  type = SDT_CDROM;
493  } else {
494  mode = MODE_SCSI1 | MODE_UNITATTENTION | MODE_FDS120 | MODE_REMOVABLE | MODE_NOVAXIS;
495  type = SDT_DirectAccess;
496  }
497  return scsiDeviceCreate(id, diskId, buffer, nullptr, type, mode,
498  (CdromXferCompCb)wd33c93XferCb, wd33c93);
499 #else
500  SCSIDEVICE* dev;
501  int mode;
502  int type;
503 
504  if (id != 2) {
505  mode = MODE_SCSI1 | MODE_UNITATTENTION | MODE_FDS120 | MODE_REMOVABLE | MODE_NOVAXIS;
506  type = SDT_DirectAccess;
507  } else {
508  mode = MODE_SCSI1 | MODE_UNITATTENTION | MODE_REMOVABLE | MODE_NOVAXIS;
509  type = SDT_CDROM;
510  }
511  dev = scsiDeviceCreate(id, diskGetHdDriveId(hdId, id),
512  buffer, nullptr, type, mode, (CdromXferCompCb)wd33c93XferCb, wd33c93);
513  return dev;
514 #endif
515 }
516 */
517 
518 } // namespace openmsx
void writeCtrl(byte value)
Definition: WD33C93.cc:256
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
byte readAuxStatus()
Definition: WD33C93.cc:315
void serialize(Archive &ar, unsigned version)
Definition: WD33C93.cc:461
static const unsigned MODE_SCSI1
Definition: SCSIDevice.hh:15
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
void reset(bool scsireset)
Definition: WD33C93.cc:425
byte readCtrl()
Definition: WD33C93.cc:342
byte peekCtrl() const
Definition: WD33C93.cc:411
WD33C93(const DeviceConfig &config)
Definition: WD33C93.cc:107
static const unsigned BUFFER_SIZE
Definition: SCSIDevice.hh:23
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
void writeAdr(byte value)
Definition: WD33C93.cc:249
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:840
const XMLElement * getXML() const
Definition: DeviceConfig.hh:54
static const unsigned MODE_UNITATTENTION
Definition: SCSIDevice.hh:18
const Children & getChildren() const
Definition: XMLElement.hh:49
byte peekAuxStatus() const
Definition: WD33C93.cc:406
static const unsigned MODE_NOVAXIS
Definition: SCSIDevice.hh:21