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