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