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