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