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