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 
150 {
151 }
152 
153 void WD33C93::disconnect()
154 {
155  if (phase != SCSI::BUS_FREE) {
156  assert(targetId < MAX_DEV);
157  dev[targetId]->disconnect();
158  if (regs[REG_SCSI_STATUS] != SS_XFER_END) {
159  regs[REG_SCSI_STATUS] = SS_DISCONNECT;
160  }
161  regs[REG_AUX_STATUS] = AS_INT;
162  phase = SCSI::BUS_FREE;
163  }
164  tc = 0;
165 
166  PRT_DEBUG("busfree()");
167 }
168 
169 void WD33C93::execCmd(byte value)
170 {
171  if (regs[REG_AUX_STATUS] & AS_CIP) {
172  PRT_DEBUG("wd33c93ExecCmd() CIP error");
173  return;
174  }
175  //regs[REG_AUX_STATUS] |= AS_CIP;
176  regs[REG_CMD] = value;
177 
178  bool atn = false;
179  switch (value) {
180  case 0x00: // Reset controller (software reset)
181  PRT_DEBUG("wd33c93 [CMD] Reset controller");
182  memset(regs + 1, 0, 0x1a);
183  disconnect();
184  latch = 0; // TODO: is this correct? Some doc says: reset to zero by masterreset-signal but not by reset command.
185  regs[REG_SCSI_STATUS] =
186  (regs[REG_OWN_ID] & OWN_EAF) ? SS_RESET_ADV : SS_RESET;
187  break;
188 
189  case 0x02: // Assert ATN
190  PRT_DEBUG("wd33c93 [CMD] Assert ATN");
191  break;
192 
193  case 0x04: // Disconnect
194  PRT_DEBUG("wd33c93 [CMD] Disconnect");
195  disconnect();
196  break;
197 
198  case 0x06: // Select with ATN (Lv2)
199  atn = true;
200  // fall-through
201  case 0x07: // Select Without ATN (Lv2)
202  PRT_DEBUG("wd33c93 [CMD] Select (ATN " << atn << ')');
203  targetId = regs[REG_DST_ID] & 7;
204  regs[REG_SCSI_STATUS] = SS_SEL_TIMEOUT;
205  tc = 0;
206  regs[REG_AUX_STATUS] = AS_INT;
207  break;
208 
209  case 0x08: // Select with ATN and transfer (Lv2)
210  atn = true;
211  // fall-through
212  case 0x09: // Select without ATN and Transfer (Lv2)
213  PRT_DEBUG("wd33c93 [CMD] Select and transfer (ATN " << atn << ')');
214  targetId = regs[REG_DST_ID] & 7;
215 
216  if (!devBusy && targetId < MAX_DEV && /* targetId != myId && */
217  dev[targetId]->isSelected()) {
218  if (atn) {
219  dev[targetId]->msgOut(regs[REG_TLUN] | 0x80);
220  }
221  devBusy = true;
222  counter = dev[targetId]->executeCmd(
223  &regs[REG_CDB1], phase, blockCounter);
224 
225  switch (phase) {
226  case SCSI::STATUS:
227  devBusy = false;
228  regs[REG_TLUN] = dev[targetId]->getStatusCode();
229  dev[targetId]->msgIn();
230  regs[REG_SCSI_STATUS] = SS_XFER_END;
231  disconnect();
232  break;
233 
234  case SCSI::EXECUTE:
235  regs[REG_AUX_STATUS] = AS_CIP | AS_BSY;
236  bufIdx = 0;
237  break;
238 
239  default:
240  devBusy = false;
241  regs[REG_AUX_STATUS] = AS_CIP | AS_BSY | AS_DBR;
242  bufIdx = 0;
243  }
244  //regs[REG_SRC_ID] |= regs[REG_DST_ID] & 7;
245  } else {
246  PRT_DEBUG("wd33c93 timeout on target " << int(targetId));
247  tc = 0;
248  regs[REG_SCSI_STATUS] = SS_SEL_TIMEOUT;
249  regs[REG_AUX_STATUS] = AS_INT;
250  }
251  break;
252 
253  case 0x18: // Translate Address (Lv2)
254  default:
255  PRT_DEBUG("wd33c93 [CMD] unsupport command " << int(value));
256  break;
257  }
258 }
259 
261 {
262  //PRT_DEBUG("WriteAdr value " << std::hex << (int)value);
263  latch = value & 0x1f;
264 }
265 
266 // Latch incremented by one each time a register is accessed,
267 // except for the address-, aux.status-, data- and command registers.
269 {
270  //PRT_DEBUG("wd33c93 write #" << std::hex << (int)latch << ", " << (int)value);
271  switch (latch) {
272  case REG_OWN_ID:
273  regs[REG_OWN_ID] = value;
274  myId = value & 7;
275  PRT_DEBUG("wd33c93 myid = " << int(myId));
276  break;
277 
278  case REG_TCH:
279  tc = (tc & 0x00ffff) + (value << 16);
280  break;
281 
282  case REG_TCM:
283  tc = (tc & 0xff00ff) + (value << 8);
284  break;
285 
286  case REG_TCL:
287  tc = (tc & 0xffff00) + (value << 0);
288  break;
289 
290  case REG_CMD_PHASE:
291  PRT_DEBUG("wd33c93 CMD_PHASE = " << int(value));
292  regs[REG_CMD_PHASE] = value;
293  break;
294 
295  case REG_CMD:
296  execCmd(value);
297  return; // no latch-inc for address-, aux.status-, data- and command regs.
298 
299  case REG_DATA:
300  regs[REG_DATA] = value;
301  if (phase == SCSI::DATA_OUT) {
302  buffer[bufIdx++] = value;
303  --tc;
304  if (--counter == 0) {
305  counter = dev[targetId]->dataOut(blockCounter);
306  if (counter) {
307  bufIdx = 0;
308  return;
309  }
310  regs[REG_TLUN] = dev[targetId]->getStatusCode();
311  dev[targetId]->msgIn();
312  regs[REG_SCSI_STATUS] = SS_XFER_END;
313  disconnect();
314  }
315  }
316  return; // no latch-inc for address-, aux.status-, data- and command regs.
317 
318  case REG_AUX_STATUS:
319  return; // no latch-inc for address-, aux.status-, data- and command regs.
320 
321  default:
322  if (latch <= REG_SRC_ID) {
323  regs[latch] = value;
324  }
325  break;
326  }
327  latch = (latch + 1) & 0x1f;
328 }
329 
331 {
332  byte rv = regs[REG_AUX_STATUS];
333 
334  if (phase == SCSI::EXECUTE) {
335  counter = dev[targetId]->executingCmd(phase, blockCounter);
336 
337  switch (phase) {
338  case SCSI::STATUS: // TODO how can this ever be the case?
339  regs[REG_TLUN] = dev[targetId]->getStatusCode();
340  dev[targetId]->msgIn();
341  regs[REG_SCSI_STATUS] = SS_XFER_END;
342  disconnect();
343  break;
344 
345  case SCSI::EXECUTE:
346  break;
347 
348  default:
349  regs[REG_AUX_STATUS] |= AS_DBR;
350  }
351  }
352  //PRT_DEBUG("readAuxStatus returning " << std::hex << (int)rv);
353  return rv;
354 }
355 
356 // Latch incremented by one each time a register is accessed,
357 // except for the address-, aux.status-, data- and command registers.
359 {
360  //PRT_DEBUG("ReadCtrl");
361  byte rv;
362 
363  switch (latch) {
364  case REG_SCSI_STATUS:
365  rv = regs[REG_SCSI_STATUS];
366  //PRT_DEBUG1("wd33c93 SCSI_STATUS = %X\n", rv);
367  if (rv != SS_XFER_END) {
368  regs[REG_AUX_STATUS] &= ~AS_INT;
369  } else {
370  regs[REG_SCSI_STATUS] = SS_DISCONNECT;
371  regs[REG_AUX_STATUS] = AS_INT;
372  }
373  break;
374 
375  case REG_CMD:
376  return regs[REG_CMD]; // no latch-inc for address-, aux.status-, data- and command regs.
377 
378  case REG_DATA:
379  if (phase == SCSI::DATA_IN) {
380  rv = buffer[bufIdx++];
381  regs[REG_DATA] = rv;
382  --tc;
383  if (--counter == 0) {
384  if (blockCounter > 0) {
385  counter = dev[targetId]->dataIn(blockCounter);
386  if (counter) {
387  bufIdx = 0;
388  return rv;
389  }
390  }
391  regs[REG_TLUN] = dev[targetId]->getStatusCode();
392  dev[targetId]->msgIn();
393  regs[REG_SCSI_STATUS] = SS_XFER_END;
394  disconnect();
395  }
396  } else {
397  rv = regs[REG_DATA];
398  }
399  return rv; // no latch-inc for address-, aux.status-, data- and command regs.
400 
401  case REG_TCH:
402  rv = (tc >> 16) & 0xff;
403  break;
404 
405  case REG_TCM:
406  rv = (tc >> 8) & 0xff;
407  break;
408 
409  case REG_TCL:
410  rv = (tc >> 0) & 0xff;
411  break;
412 
413  case REG_AUX_STATUS:
414  return readAuxStatus(); // no latch-inc for address-, aux.status-, data- and command regs.
415 
416  default:
417  rv = regs[latch];
418  break;
419  }
420  //PRT_DEBUG2("wd33c93 read #%X, %X\n", latch, rv);
421 
422  latch = (latch + 1) & 0x1f;
423  return rv;
424 }
425 
427 {
428  return regs[REG_AUX_STATUS];
429 }
430 
432 {
433  switch (latch) {
434  case REG_TCH:
435  return (tc >> 16) & 0xff;
436  case REG_TCM:
437  return (tc >> 8) & 0xff;
438  case REG_TCL:
439  return (tc >> 0) & 0xff;
440  default:
441  return regs[latch];
442  }
443 }
444 
445 void WD33C93::reset(bool scsireset)
446 {
447  PRT_DEBUG("wd33c93 reset");
448 
449  // initialized register
450  memset(regs, 0, 0x1b);
451  memset(regs + 0x1b, 0xff, 4);
452  regs[REG_AUX_STATUS] = AS_INT;
453  myId = 0;
454  latch = 0;
455  tc = 0;
456  phase = SCSI::BUS_FREE;
457  bufIdx = 0;
458  if (scsireset) {
459  for (auto& d : dev) {
460  d->reset();
461  }
462  }
463 }
464 
465 
466 static enum_string<SCSI::Phase> phaseInfo[] = {
467  { "UNDEFINED", SCSI::UNDEFINED },
468  { "BUS_FREE", SCSI::BUS_FREE },
469  { "ARBITRATION", SCSI::ARBITRATION },
470  { "SELECTION", SCSI::SELECTION },
471  { "RESELECTION", SCSI::RESELECTION },
472  { "COMMAND", SCSI::COMMAND },
473  { "EXECUTE", SCSI::EXECUTE },
474  { "DATA_IN", SCSI::DATA_IN },
475  { "DATA_OUT", SCSI::DATA_OUT },
476  { "STATUS", SCSI::STATUS },
477  { "MSG_OUT", SCSI::MSG_OUT },
478  { "MSG_IN", SCSI::MSG_IN }
479 };
480 SERIALIZE_ENUM(SCSI::Phase, phaseInfo);
481 
482 template<typename Archive>
483 void WD33C93::serialize(Archive& ar, unsigned /*version*/)
484 {
485  ar.serialize_blob("buffer", buffer.data(), buffer.size());
486  char tag[8] = { 'd', 'e', 'v', 'i', 'c', 'e', 'X', 0 };
487  for (unsigned i = 0; i < MAX_DEV; ++i) {
488  tag[6] = char('0' + i);
489  ar.serializePolymorphic(tag, *dev[i]);
490  }
491  ar.serialize("bufIdx", bufIdx);
492  ar.serialize("counter", counter);
493  ar.serialize("blockCounter", blockCounter);
494  ar.serialize("tc", tc);
495  ar.serialize("phase", phase);
496  ar.serialize("myId", myId);
497  ar.serialize("targetId", targetId);
498  ar.serialize_blob("registers", regs, sizeof(regs));
499  ar.serialize("latch", latch);
500  ar.serialize("devBusy", devBusy);
501 }
503 
504 /* Here is some info on the parameters for SCSI devices:
505 static SCSIDevice* wd33c93ScsiDevCreate(WD33C93* wd33c93, int id)
506 {
507 #if 1
508  // CD_UPDATE: Use dynamic parameters instead of hard coded ones
509  int diskId, mode, type;
510 
511  diskId = diskGetHdDriveId(hdId, id);
512  if (diskIsCdrom(diskId)) {
513  mode = MODE_SCSI1 | MODE_UNITATTENTION | MODE_REMOVABLE | MODE_NOVAXIS;
514  type = SDT_CDROM;
515  } else {
516  mode = MODE_SCSI1 | MODE_UNITATTENTION | MODE_FDS120 | MODE_REMOVABLE | MODE_NOVAXIS;
517  type = SDT_DirectAccess;
518  }
519  return scsiDeviceCreate(id, diskId, buffer, nullptr, type, mode,
520  (CdromXferCompCb)wd33c93XferCb, wd33c93);
521 #else
522  SCSIDEVICE* dev;
523  int mode;
524  int type;
525 
526  if (id != 2) {
527  mode = MODE_SCSI1 | MODE_UNITATTENTION | MODE_FDS120 | MODE_REMOVABLE | MODE_NOVAXIS;
528  type = SDT_DirectAccess;
529  } else {
530  mode = MODE_SCSI1 | MODE_UNITATTENTION | MODE_REMOVABLE | MODE_NOVAXIS;
531  type = SDT_CDROM;
532  }
533  dev = scsiDeviceCreate(id, diskGetHdDriveId(hdId, id),
534  buffer, nullptr, type, mode, (CdromXferCompCb)wd33c93XferCb, wd33c93);
535  return dev;
536 #endif
537 }
538 */
539 
540 } // namespace openmsx
void writeCtrl(byte value)
Definition: WD33C93.cc:268
SERIALIZE_ENUM(CassettePlayer::State, stateInfo)
byte readAuxStatus()
Definition: WD33C93.cc:330
void serialize(Archive &ar, unsigned version)
Definition: WD33C93.cc:483
static const unsigned MODE_SCSI1
Definition: SCSIDevice.hh:15
unsigned char byte
8 bit unsigned integer
Definition: openmsx.hh:33
void reset(bool scsireset)
Definition: WD33C93.cc:445
byte readCtrl()
Definition: WD33C93.cc:358
WD33C93(const DeviceConfig &config)
Definition: WD33C93.cc:107
byte peekAuxStatus() const
Definition: WD33C93.cc:426
static const unsigned BUFFER_SIZE
Definition: SCSIDevice.hh:23
byte peekCtrl() const
Definition: WD33C93.cc:431
const XMLElement * getXML() const
Definition: DeviceConfig.hh:54
void writeAdr(byte value)
Definition: WD33C93.cc:260
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:802
const Children & getChildren() const
Definition: XMLElement.hh:49
static const unsigned MODE_UNITATTENTION
Definition: SCSIDevice.hh:18
#define PRT_DEBUG(mes)
Definition: openmsx.hh:69
static const unsigned MODE_NOVAXIS
Definition: SCSIDevice.hh:21