WD33C93.cc 13 KB

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