openMSX
MB89352.cc
Go to the documentation of this file.
1/* Ported from:
2** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/MB89352.c,v
3** Revision: 1.9
4** Date: 2007/03/28 17:35:35
5**
6** More info: http://www.bluemsx.com
7**
8** Copyright (C) 2003-2007 Daniel Vik, white cat
9*/
10/*
11 * Notes:
12 * Not support padding transfer and interrupt signal. (Not used MEGA-SCSI)
13 * Message system might be imperfect. (Not used in MEGA-SCSI usually)
14 */
15#include "MB89352.hh"
16#include "SCSIDevice.hh"
17#include "DummySCSIDevice.hh"
18#include "SCSIHD.hh"
19#include "SCSILS120.hh"
20#include "DeviceConfig.hh"
21#include "XMLElement.hh"
22#include "MSXException.hh"
23#include "enumerate.hh"
24#include "narrow.hh"
25#include "serialize.hh"
26#include "xrange.hh"
27#include <cassert>
28#include <memory>
29
30namespace openmsx {
31
32static constexpr uint8_t REG_BDID = 0; // Bus Device ID (r/w)
33static constexpr uint8_t REG_SCTL = 1; // Spc Control (r/w)
34static constexpr uint8_t REG_SCMD = 2; // Command (r/w)
35static constexpr uint8_t REG_OPEN = 3; // (open)
36static constexpr uint8_t REG_INTS = 4; // Interrupt Sense (r/w)
37static constexpr uint8_t REG_PSNS = 5; // Phase Sense (r)
38static constexpr uint8_t REG_SDGC = 5; // SPC Diag. Control (w)
39static constexpr uint8_t REG_SSTS = 6; // SPC SCSI::STATUS (r)
40static constexpr uint8_t REG_SERR = 7; // SPC Error SCSI::STATUS (r/w?)
41static constexpr uint8_t REG_PCTL = 8; // Phase Control (r/w)
42static constexpr uint8_t REG_MBC = 9; // Modified Byte Counter(r)
43static constexpr uint8_t REG_DREG = 10; // Data Register (r/w)
44static constexpr uint8_t REG_TEMP = 11; // Temporary Register (r/w)
45 // Another value is maintained respectively
46 // for writing and for reading
47static constexpr uint8_t REG_TCH = 12; // Transfer Counter High(r/w)
48static constexpr uint8_t REG_TCM = 13; // Transfer Counter Mid (r/w)
49static constexpr uint8_t REG_TCL = 14; // Transfer Counter Low (r/w)
50
51static constexpr uint8_t REG_TEMPWR = 13; // (TEMP register preservation place for writing)
52static constexpr uint8_t FIX_PCTL = 14; // (REG_PCTL & 7)
53
54static constexpr uint8_t PSNS_IO = 0x01;
55static constexpr uint8_t PSNS_CD = 0x02;
56static constexpr uint8_t PSNS_MSG = 0x04;
57static constexpr uint8_t PSNS_BSY = 0x08;
58static constexpr uint8_t PSNS_SEL = 0x10;
59static constexpr uint8_t PSNS_ATN = 0x20;
60static constexpr uint8_t PSNS_ACK = 0x40;
61static constexpr uint8_t PSNS_REQ = 0x80;
62
63static constexpr uint8_t PSNS_SELECTION = PSNS_SEL;
64static constexpr uint8_t PSNS_COMMAND = PSNS_CD;
65static constexpr uint8_t PSNS_DATAIN = PSNS_IO;
66static constexpr uint8_t PSNS_DATAOUT = 0;
67static constexpr uint8_t PSNS_STATUS = PSNS_CD | PSNS_IO;
68static constexpr uint8_t PSNS_MSGIN = PSNS_MSG | PSNS_CD | PSNS_IO;
69static constexpr uint8_t PSNS_MSGOUT = PSNS_MSG | PSNS_CD;
70
71static constexpr uint8_t INTS_ResetCondition = 0x01;
72static constexpr uint8_t INTS_SPC_HardError = 0x02;
73static constexpr uint8_t INTS_TimeOut = 0x04;
74static constexpr uint8_t INTS_ServiceRequited = 0x08;
75static constexpr uint8_t INTS_CommandComplete = 0x10;
76static constexpr uint8_t INTS_Disconnected = 0x20;
77static constexpr uint8_t INTS_ReSelected = 0x40;
78static constexpr uint8_t INTS_Selected = 0x80;
79
80static constexpr uint8_t CMD_BusRelease = 0x00;
81static constexpr uint8_t CMD_Select = 0x20;
82static constexpr uint8_t CMD_ResetATN = 0x40;
83static constexpr uint8_t CMD_SetATN = 0x60;
84static constexpr uint8_t CMD_Transfer = 0x80;
85static constexpr uint8_t CMD_TransferPause = 0xA0;
86static constexpr uint8_t CMD_Reset_ACK_REQ = 0xC0;
87static constexpr uint8_t CMD_Set_ACK_REQ = 0xE0;
88static constexpr uint8_t CMD_MASK = 0xE0;
89
91{
92 // TODO: devBusy = false;
93
94 // ALMOST COPY PASTED FROM WD33C93:
95
96 for (const auto* t : config.getXML()->getChildren("target")) {
97 unsigned id = t->getAttributeValueAsInt("id", 0);
98 if (id >= MAX_DEV) {
99 throw MSXException(
100 "Invalid SCSI id: ", id,
101 " (should be 0..", MAX_DEV - 1, ')');
102 }
103 if (dev[id]) {
104 throw MSXException("Duplicate SCSI id: ", id);
105 }
106 DeviceConfig conf(config, *t);
107 auto type = t->getChildData("type");
108 if (type == "SCSIHD") {
109 dev[id] = std::make_unique<SCSIHD>(conf, buffer,
111 } else if (type == "SCSILS120") {
112 dev[id] = std::make_unique<SCSILS120>(conf, buffer,
114 } else {
115 throw MSXException("Unknown SCSI device: ", type);
116 }
117 }
118 // fill remaining targets with dummy SCSI devices to prevent crashes
119 for (auto& d : dev) {
120 if (!d) d = std::make_unique<DummySCSIDevice>();
121 }
122 reset(false);
123
124 // avoid UMR on savestate
125 ranges::fill(buffer, 0);
126}
127
128void MB89352::disconnect()
129{
130 if (phase != SCSI::BUS_FREE) {
131 assert(targetId < MAX_DEV);
132 dev[targetId]->disconnect();
133 regs[REG_INTS] |= INTS_Disconnected;
134 phase = SCSI::BUS_FREE;
135 nextPhase = SCSI::UNDEFINED;
136 }
137
138 regs[REG_PSNS] = 0;
139 isBusy = false;
140 isTransfer = false;
141 counter = 0;
142 tc = 0;
143 atn = 0;
144}
145
146void MB89352::softReset()
147{
148 isEnabled = false;
149
150 for (auto i : xrange(2, 15)) {
151 regs[i] = 0;
152 }
153 regs[15] = 0xFF; // un mapped
154 ranges::fill(cdb, 0);
155
156 cdbIdx = 0;
157 bufIdx = 0;
158 phase = SCSI::BUS_FREE;
159 disconnect();
160}
161
162void MB89352::reset(bool scsiReset)
163{
164 regs[REG_BDID] = 0x80; // Initial value
165 regs[REG_SCTL] = 0x80;
166 rst = false;
167 atn = 0;
168 myId = 7;
169
170 softReset();
171
172 if (scsiReset) {
173 for (auto& d : dev) {
174 d->reset();
175 }
176 }
177}
178
179void MB89352::setACKREQ(uint8_t& value)
180{
181 // REQ check
182 if ((regs[REG_PSNS] & (PSNS_REQ | PSNS_BSY)) != (PSNS_REQ | PSNS_BSY)) {
183 // set ACK/REQ: REQ/BSY check error
184 if (regs[REG_PSNS] & PSNS_IO) { // SCSI -> SPC
185 value = 0xFF;
186 }
187 return;
188 }
189
190 // phase check
191 if (regs[FIX_PCTL] != (regs[REG_PSNS] & 7)) {
192 // set ACK/REQ: phase check error
193 if (regs[REG_PSNS] & PSNS_IO) { // SCSI -> SPC
194 value = 0xFF;
195 }
196 if (isTransfer) {
197 regs[REG_INTS] |= INTS_ServiceRequited;
198 }
199 return;
200 }
201
202 switch (phase) {
203 case SCSI::DATA_IN: // Transfer phase (data in)
204 value = buffer[bufIdx];
205 ++bufIdx;
206 regs[REG_PSNS] = PSNS_ACK | PSNS_BSY | PSNS_DATAIN;
207 break;
208
209 case SCSI::DATA_OUT: // Transfer phase (data out)
210 buffer[bufIdx] = value;
211 ++bufIdx;
212 regs[REG_PSNS] = PSNS_ACK | PSNS_BSY | PSNS_DATAOUT;
213 break;
214
215 case SCSI::COMMAND: // Command phase
216 if (counter < 0) {
217 // Initialize command routine
218 cdbIdx = 0;
219 counter = (value < 0x20) ? 6 : ((value < 0xA0) ? 10 : 12);
220 }
221 cdb[cdbIdx] = value;
222 ++cdbIdx;
223 regs[REG_PSNS] = PSNS_ACK | PSNS_BSY | PSNS_COMMAND;
224 break;
225
226 case SCSI::STATUS: // SCSI::STATUS phase
227 value = dev[targetId]->getStatusCode();
228 regs[REG_PSNS] = PSNS_ACK | PSNS_BSY | PSNS_STATUS;
229 break;
230
231 case SCSI::MSG_IN: // Message In phase
232 value = dev[targetId]->msgIn();
233 regs[REG_PSNS] = PSNS_ACK | PSNS_BSY | PSNS_MSGIN;
234 break;
235
236 case SCSI::MSG_OUT: // Message Out phase
237 msgin |= dev[targetId]->msgOut(value);
238 regs[REG_PSNS] = PSNS_ACK | PSNS_BSY | PSNS_MSGOUT;
239 break;
240
241 default:
242 // set ACK/REQ code error
243 break;
244 }
245}
246
247void MB89352::resetACKREQ()
248{
249 // ACK check
250 if ((regs[REG_PSNS] & (PSNS_ACK | PSNS_BSY)) != (PSNS_ACK | PSNS_BSY)) {
251 // reset ACK/REQ: ACK/BSY check error
252 return;
253 }
254
255 // phase check
256 if (regs[FIX_PCTL] != (regs[REG_PSNS] & 7)) {
257 // reset ACK/REQ: phase check error
258 if (isTransfer) {
259 regs[REG_INTS] |= INTS_ServiceRequited;
260 }
261 return;
262 }
263
264 switch (phase) {
265 case SCSI::DATA_IN: // Transfer phase (data in)
266 if (--counter > 0) {
267 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAIN;
268 } else {
269 if (blockCounter > 0) {
270 counter = narrow<int>(dev[targetId]->dataIn(blockCounter));
271 if (counter) {
272 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAIN;
273 bufIdx = 0;
274 break;
275 }
276 }
277 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_STATUS;
278 phase = SCSI::STATUS;
279 }
280 break;
281
282 case SCSI::DATA_OUT: // Transfer phase (data out)
283 if (--counter > 0) {
284 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAOUT;
285 } else {
286 counter = narrow<int>(dev[targetId]->dataOut(blockCounter));
287 if (counter) {
288 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAOUT;
289 bufIdx = 0;
290 break;
291 }
292 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_STATUS;
293 phase = SCSI::STATUS;
294 }
295 break;
296
297 case SCSI::COMMAND: // Command phase
298 if (--counter > 0) {
299 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_COMMAND;
300 } else {
301 bufIdx = 0; // reset buffer index
302 // TODO: devBusy = true;
303 counter = narrow<int>(dev[targetId]->executeCmd(cdb, phase, blockCounter));
304 switch (phase) {
305 case SCSI::DATA_IN:
306 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAIN;
307 break;
308 case SCSI::DATA_OUT:
309 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAOUT;
310 break;
311 case SCSI::STATUS:
312 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_STATUS;
313 break;
314 case SCSI::EXECUTE:
315 regs[REG_PSNS] = PSNS_BSY;
316 return; // note: return iso break
317 default:
318 // phase error
319 break;
320 }
321 // TODO: devBusy = false;
322 }
323 break;
324
325 case SCSI::STATUS: // SCSI::STATUS phase
326 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_MSGIN;
327 phase = SCSI::MSG_IN;
328 break;
329
330 case SCSI::MSG_IN: // Message In phase
331 if (msgin <= 0) {
332 disconnect();
333 break;
334 }
335 msgin = 0;
336 [[fallthrough]];
337 case SCSI::MSG_OUT: // Message Out phase
338 if (msgin == -1) {
339 disconnect();
340 return;
341 }
342
343 if (atn) {
344 if (msgin & 2) {
345 disconnect();
346 return;
347 }
348 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_MSGOUT;
349 return;
350 }
351
352 if (msgin & 1) {
353 phase = SCSI::MSG_IN;
354 } else {
355 if (msgin & 4) {
356 phase = SCSI::STATUS;
357 nextPhase = SCSI::UNDEFINED;
358 } else {
359 phase = nextPhase;
360 nextPhase = SCSI::UNDEFINED;
361 }
362 }
363
364 msgin = 0;
365
366 switch (phase) {
367 case SCSI::COMMAND:
368 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_COMMAND;
369 break;
370 case SCSI::DATA_IN:
371 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAIN;
372 break;
373 case SCSI::DATA_OUT:
374 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAOUT;
375 break;
376 case SCSI::STATUS:
377 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_STATUS;
378 break;
379 case SCSI::MSG_IN:
380 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_MSGIN;
381 break;
382 default:
383 // MsgOut code error
384 break;
385 }
386 return;
387
388 default:
389 //UNREACHABLE;
390 // reset ACK/REQ code error
391 break;
392 }
393
394 if (atn) {
395 nextPhase = phase;
396 phase = SCSI::MSG_OUT;
397 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_MSGOUT;
398 }
399}
400
402{
403 if (isTransfer && (tc > 0)) {
404 setACKREQ(regs[REG_DREG]);
405 resetACKREQ();
406
407 --tc;
408 if (tc == 0) {
409 isTransfer = false;
410 regs[REG_INTS] |= INTS_CommandComplete;
411 }
412 regs[REG_MBC] = (regs[REG_MBC] - 1) & 0x0F;
413 return regs[REG_DREG];
414 } else {
415 return 0xFF;
416 }
417}
418
419void MB89352::writeDREG(uint8_t value)
420{
421 if (isTransfer && (tc > 0)) {
422 setACKREQ(value);
423 resetACKREQ();
424
425 --tc;
426 if (tc == 0) {
427 isTransfer = false;
428 regs[REG_INTS] |= INTS_CommandComplete;
429 }
430 regs[REG_MBC] = (regs[REG_MBC] - 1) & 0x0F;
431 }
432}
433
434void MB89352::writeRegister(uint8_t reg, uint8_t value)
435{
436 switch (reg) {
437 case REG_DREG: // write data Register
438 writeDREG(value);
439 break;
440
441 case REG_SCMD: {
442 if (!isEnabled) {
443 break;
444 }
445
446 // bus reset
447 if (value & 0x10) {
448 if (((regs[REG_SCMD] & 0x10) == 0) && (regs[REG_SCTL] == 0)) {
449 rst = true;
450 regs[REG_INTS] |= INTS_ResetCondition;
451 for (auto& d : dev) {
452 d->busReset();
453 }
454 disconnect(); // alternative routine
455 }
456 } else {
457 rst = false;
458 }
459
460 regs[REG_SCMD] = value;
461
462 // execute spc command
463 switch (value & CMD_MASK) {
464 case CMD_Set_ACK_REQ:
465 switch (phase) {
466 case SCSI::DATA_IN:
467 case SCSI::STATUS:
468 case SCSI::MSG_IN:
469 setACKREQ(regs[REG_TEMP]);
470 break;
471 default:
472 setACKREQ(regs[REG_TEMPWR]);
473 }
474 break;
475
476 case CMD_Reset_ACK_REQ:
477 resetACKREQ();
478 break;
479
480 case CMD_Select: {
481 if (rst) {
482 regs[REG_INTS] |= INTS_TimeOut;
483 break;
484 }
485
486 if (regs[REG_PCTL] & 1) {
487 // reselection error
488 regs[REG_INTS] |= INTS_TimeOut;
489 disconnect();
490 break;
491 }
492 bool err = false;
493 int x = regs[REG_BDID] & regs[REG_TEMPWR];
494 if (phase == SCSI::BUS_FREE && x && x != regs[REG_TEMPWR]) {
495 x = regs[REG_TEMPWR] & ~regs[REG_BDID];
496 assert(x != 0); // because of the check 2 lines above
497
498 // the targetID is calculated.
499 // It is given priority that the number is large.
500 targetId = 0;
501 while (true) {
502 x >>= 1;
503 if (x == 0) break;
504 ++targetId;
505 assert(targetId < MAX_DEV);
506 }
507
508 if ( dev[targetId]->isSelected()) {
509 // target selection OK
510 regs[REG_INTS] |= INTS_CommandComplete;
511 isBusy = true;
512 msgin = 0;
513 counter = -1;
514 err = false;
515 if (atn) {
516 phase = SCSI::MSG_OUT;
517 nextPhase = SCSI::COMMAND;
518 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_MSGOUT;
519 } else {
520 phase = SCSI::COMMAND;
521 nextPhase = SCSI::UNDEFINED;
522 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_COMMAND;
523 }
524 } else {
525 err = true;
526 }
527 } else {
528 err = true;
529 }
530
531 if (err) {
532 // target selection error
533 regs[REG_INTS] |= INTS_TimeOut;
534 disconnect();
535 }
536 break;
537 }
538 // hardware transfer
539 case CMD_Transfer:
540 if ((regs[FIX_PCTL] == (regs[REG_PSNS] & 7)) &&
541 (regs[REG_PSNS] & (PSNS_REQ | PSNS_BSY))) {
542 isTransfer = true; // set Xfer in Progress
543 } else {
544 // phase error
545 regs[REG_INTS] |= INTS_ServiceRequited;
546 }
547 break;
548
549 case CMD_BusRelease:
550 disconnect();
551 break;
552
553 case CMD_SetATN:
554 atn = PSNS_ATN;
555 break;
556
557 case CMD_ResetATN:
558 atn = 0;
559 break;
560
561 case CMD_TransferPause:
562 // nothing is done in the initiator.
563 break;
564 }
565 break; // end of REG_SCMD
566 }
567 case REG_INTS: // Reset Interrupts
568 regs[REG_INTS] &= ~value;
569 if (rst) {
570 regs[REG_INTS] |= INTS_ResetCondition;
571 }
572 break;
573
574 case REG_TEMP:
575 regs[REG_TEMPWR] = value;
576 break;
577
578 case REG_TCL:
579 tc = (tc & 0xFFFF00) + (value << 0);
580 break;
581
582 case REG_TCM:
583 tc = (tc & 0xFF00FF) + (value << 8);
584 break;
585
586 case REG_TCH:
587 tc = (tc & 0x00FFFF) + (value << 16);
588 break;
589
590 case REG_PCTL:
591 regs[REG_PCTL] = value;
592 regs[FIX_PCTL] = value & 7;
593 break;
594
595 case REG_BDID:
596 // set Bus Device ID
597 value &= 7;
598 myId = value;
599 regs[REG_BDID] = uint8_t(1 << value);
600 break;
601
602 // Nothing
603 case REG_SDGC:
604 case REG_SSTS:
605 case REG_SERR:
606 case REG_MBC:
607 case 15:
608 break;
609
610 case REG_SCTL: {
611 if (bool flag = !(value & 0xE0); flag != isEnabled) {
612 isEnabled = flag;
613 if (!flag) {
614 softReset();
615 }
616 }
617 [[fallthrough]];
618 }
619 default:
620 regs[reg] = value;
621 }
622}
623
624uint8_t MB89352::getSSTS() const
625{
626 uint8_t result = 1; // set fifo empty
627 if (isTransfer && (regs[REG_PSNS] & PSNS_IO)) { // SCSI -> SPC transfer
628 if (tc >= 8) {
629 result = 2; // set fifo full
630 } else {
631 if (tc != 0) {
632 result = 0; // set fifo 1..7 bytes
633 }
634 }
635 }
636 if (phase != SCSI::BUS_FREE) {
637 result |= 0x80; // set indicator
638 }
639 if (isBusy) {
640 result |= 0x20; // set SPC_BSY
641 }
642 if ((phase >= SCSI::COMMAND) || isTransfer) {
643 result |= 0x10; // set Xfer in Progress
644 }
645 if (rst) {
646 result |= 0x08; // set SCSI RST
647 }
648 if (tc == 0) {
649 result |= 0x04; // set tc = 0
650 }
651 return result;
652}
653
654uint8_t MB89352::readRegister(uint8_t reg)
655{
656 switch (reg) {
657 case REG_DREG:
658 return readDREG();
659
660 case REG_PSNS:
661 if (phase == SCSI::EXECUTE) {
662 counter = narrow<int>(dev[targetId]->executingCmd(phase, blockCounter));
663 if (atn && phase != SCSI::EXECUTE) {
664 nextPhase = phase;
665 phase = SCSI::MSG_OUT;
666 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_MSGOUT;
667 } else {
668 switch (phase) {
669 case SCSI::DATA_IN:
670 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAIN;
671 break;
672 case SCSI::DATA_OUT:
673 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAOUT;
674 break;
675 case SCSI::STATUS:
676 regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_STATUS;
677 break;
678 case SCSI::EXECUTE:
679 regs[REG_PSNS] = PSNS_BSY;
680 break;
681 default:
682 // phase error
683 break;
684 }
685 }
686 }
687 return regs[REG_PSNS] | atn;
688
689 default:
690 return peekRegister(reg);
691 }
692}
693
694uint8_t MB89352::peekDREG() const
695{
696 if (isTransfer && (tc > 0)) {
697 return regs[REG_DREG];
698 } else {
699 return 0xFF;
700 }
701}
702
703uint8_t MB89352::peekRegister(uint8_t reg) const
704{
705 switch (reg) {
706 case REG_DREG:
707 return peekDREG();
708 case REG_PSNS:
709 return regs[REG_PSNS] | atn;
710 case REG_SSTS:
711 return getSSTS();
712 case REG_TCH:
713 return narrow_cast<uint8_t>((tc >> 16) & 0xFF);
714 case REG_TCM:
715 return narrow_cast<uint8_t>((tc >> 8) & 0xFF);
716 case REG_TCL:
717 return narrow_cast<uint8_t>((tc >> 0) & 0xFF);
718 default:
719 return regs[reg];
720 }
721}
722
723
724// TODO duplicated in WD33C93.cc
725static constexpr std::initializer_list<enum_string<SCSI::Phase>> phaseInfo = {
726 { "UNDEFINED", SCSI::UNDEFINED },
727 { "BUS_FREE", SCSI::BUS_FREE },
728 { "ARBITRATION", SCSI::ARBITRATION },
729 { "SELECTION", SCSI::SELECTION },
730 { "RESELECTION", SCSI::RESELECTION },
731 { "COMMAND", SCSI::COMMAND },
732 { "EXECUTE", SCSI::EXECUTE },
733 { "DATA_IN", SCSI::DATA_IN },
734 { "DATA_OUT", SCSI::DATA_OUT },
735 { "STATUS", SCSI::STATUS },
736 { "MSG_OUT", SCSI::MSG_OUT },
737 { "MSG_IN", SCSI::MSG_IN }
738};
740
741template<typename Archive>
742void MB89352::serialize(Archive& ar, unsigned /*version*/)
743{
744 ar.serialize_blob("buffer", buffer);
745 std::array<char, 8> tag = {'d', 'e', 'v', 'i', 'c', 'e', 'X', 0};
746 for (auto [i, d] : enumerate(dev)) {
747 tag[6] = char('0' + i);
748 ar.serializePolymorphic(tag.data(), *d);
749 }
750 ar.serialize("bufIdx", bufIdx,
751 "msgin", msgin,
752 "counter", counter,
753 "blockCounter", blockCounter,
754 "tc", tc,
755 "phase", phase,
756 "nextPhase", nextPhase,
757 "myId", myId,
758 "targetId", targetId);
759 ar.serialize_blob("registers", regs);
760 ar.serialize("rst", rst,
761 "atn", atn,
762 "isEnabled", isEnabled,
763 "isBusy", isBusy,
764 "isTransfer", isTransfer,
765 "cdbIdx", cdbIdx);
766 ar.serialize_blob("cdb", cdb);
767}
769
770} // namespace openmsx
uintptr_t id
TclObject t
const XMLElement * getXML() const
uint8_t peekRegister(uint8_t reg) const
Definition MB89352.cc:703
void reset(bool scsiReset)
Definition MB89352.cc:162
void writeRegister(uint8_t reg, uint8_t value)
Definition MB89352.cc:434
uint8_t readRegister(uint8_t reg)
Definition MB89352.cc:654
void serialize(Archive &ar, unsigned version)
Definition MB89352.cc:742
void writeDREG(uint8_t value)
Definition MB89352.cc:419
MB89352(const DeviceConfig &config)
Definition MB89352.cc:90
uint8_t peekDREG() const
Definition MB89352.cc:694
uint8_t readDREG()
Definition MB89352.cc:401
static constexpr unsigned MODE_MEGASCSI
Definition SCSIDevice.hh:20
static constexpr unsigned MODE_SCSI2
Definition SCSIDevice.hh:17
ChildRange getChildren() const
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
Definition enumerate.hh:28
This file implemented 3 utility functions:
Definition Autofire.cc:11
constexpr void fill(ForwardRange &&range, const T &value)
Definition ranges.hh:305
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define SERIALIZE_ENUM(TYPE, INFO)
constexpr auto xrange(T e)
Definition xrange.hh:132