|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2022 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // X68030 の ROM30 (SCSI ROM) エミュレーション
9: //
10:
11: #include "romemu_x68k.h"
1.1.1.2 root 12: #include "mainbus.h"
13: #include "mainram.h"
1.1 root 14: #include "memorystream.h"
15: #include "mpu680x0.h"
16: #include "romimg_x68k.h"
17: #include "scsidev.h"
18: #include "spc.h"
19: #include "sram.h"
20:
21: // コンストラクタ
22: ROM30EmuDevice::ROM30EmuDevice()
1.1.1.2 root 23: : inherited(OBJ_IPLROM2)
1.1 root 24: {
1.1.1.2 root 25: uint devlen = 0x020000;
1.1 root 26: mask = devlen - 1;
27:
28: imagebuf.reset(new uint8 [devlen]);
29: memset(&imagebuf[0], 0xff, devlen);
30: mem = &imagebuf[0];
31: }
32:
33: // デストラクタ
34: ROM30EmuDevice::~ROM30EmuDevice()
35: {
36: }
37:
1.1.1.2 root 38: // 初期化
1.1 root 39: bool
40: ROM30EmuDevice::Init()
41: {
42: if (inherited::Init() == false) {
43: return false;
44: }
45:
1.1.1.2 root 46: mainbus = GetMainbusDevice();
47: spc = GetSPCDevice();
1.1 root 48:
49: MemoryStreamBE ms(imagebuf.get());
50: // fc0000.L 〜 fc001c.L: SCSI ブートアドレス
51: for (int i = 0; i < 8; i++) {
52: ms.Write32(0x00fc0100);
53: }
54: // fc0020.L: SCSI 初期化アドレス
55: ms.Write32(0x00fc0200);
56: // fc0024.B: マジック
57: ms.Write32(('S' << 24) | ('C' << 16) | ('S' << 8) | 'I');
58: ms.Write16(('I' << 8) | 'N');
59: ms.Write8(0);
60: ms.Write8(0);
61:
62: //
63: // SCSI ブート
64: //
65: ms.SetOffset(0x100); //_SCSIBOOT:
66: ms.Write16(0x6100); // bsr $fc0200 ; SCSIIOCS 初期化
67: ms.Write16(0x0200 - ms.GetOffset());
68: ms.Write16(0x2039); // move.l (ROMIO_BOOT),d0
69: ms.Write32(ROMIO_BOOT);
70: ms.Write16(0x6602); // bne _go ; 起動先があればGO
71: //_bootfail:
72: ms.Write16(0x4e75); // rts
73: //_go:
74: ms.Write16(0x2040); // move.l d0,a0 ; ジャンプ先を a0 にセット
75: ms.Write16(0x4ed0); // jmp (a0) ; で、ジャンプ
76:
77: //
78: // SCSIIOCS 初期化
79: //
80: ms.SetOffset(0x200); //_SCSIINIT:
81: ms.Write32(0x323c01f5); // move.w #$01f5,d1
82: ms.Write16(0x43f9); // lea.l $00fc0300,a1
83: ms.Write32(0x00fc0300);
84: ms.Write32(0x70804e4f); // IOCS _B_INTVCS
85: // ; 戻り値(旧アドレス)は破棄
86: ms.Write16(0x4e75); // rts
87:
88: //
89: // SCSIIOCS エントリ
90: //
91: ms.SetOffset(0x300); //_SCSIIOCS:
92: ms.Write16(0x2039); // move.l (ROMIO_SCSI),d0
93: ms.Write32(ROMIO_SCSI);
94: ms.Write16(0x4e75); // rts
95:
96: return true;
97: }
98:
1.1.1.3 ! root 99: busdata
1.1 root 100: ROM30EmuDevice::Read32(uint32 addr)
101: {
102: // IPLROM2 領域からのロングワードアクセスでだけ謎の I/O 空間が見える。
1.1.1.2 root 103: if ((mpu->GetPaddr() & 0xfffe0000) == baseaddr) {
1.1 root 104: switch (addr) {
105: case ROMIO_SCSI:
106: return SCSIIOCS();
107:
108: case ROMIO_BOOT:
109: return SCSIBoot();
110:
111: default:
112: break;
113: }
114: }
115:
116: return inherited::Read32(addr);
117: }
118:
119: // SRAM で指定された SCSI ID のデバイスからブートブロックを読み込む。
120: // 成功すればジャンプ先アドレスを、失敗すれば 0 を返す。
121: uint32
122: ROM30EmuDevice::SCSIBoot() const
123: {
1.1.1.2 root 124: auto mainram = GetMainRAMDevice();
125: auto sram = GetSRAMDevice();
126:
1.1.1.3 ! root 127: auto mpu680x0 = GetMPU680x0Device(mpu);
1.1.1.2 root 128: m68kreg& reg = mpu680x0->reg;
129:
1.1 root 130: // SRAM の ROM 起動アドレスから SCSI ID を取得。
1.1.1.2 root 131: uint32 romaddr = sram->GetROMAddr();
1.1 root 132: if ((romaddr & 0xffff00) != 0xfc0000) {
133: return 0;
134: }
135: int id = (romaddr & 0x1f) >> 2;
136:
137: // ターゲットディスク
1.1.1.2 root 138: SCSITarget *target = spc->GetTarget(id);
1.1 root 139: if (target == NULL) {
140: putlog(1, "%s: SCSI ID %d not found", __func__, id);
141: return 0;
142: }
143: SCSIDisk *disk = dynamic_cast<SCSIDisk*>(target);
144:
145: // Human68k は 1024 byte/sector を「セクタ」と呼んでいるが、色々紛らわしい
146: // ことになるので、ここではメディアのブロックサイズのほうを「セクタ」、
147: // Human68k の 1024 byte/sector のほうを「ブロック」と勝手に呼び分ける。
148: //
149: // セクタサイズが 512 byte/sector なら1ブロックを 1024 byte、
150: // セクタサイズが 2048 byte/sector なら1ブロックを 2048 byte とするようだ。
151:
152: // セクタサイズを取得 (ただしこれが SCSI 側用語でブロックサイズ orz)
153: uint32 sectsize = disk->GetBlocksize();
154: uint32 blocksize;
155: if (sectsize == 512) {
156: blocksize = 1024;
157: } else if (sectsize == 2048) {
158: blocksize = sectsize;
159: } else {
160: putlog(1, "%s: SCSI ID %d: sectsize %d not supported", __func__,
161: id, sectsize);
162: return 0;
163: }
164: putlog(2, "%s: SCSI ID %d: sectsize=%d, blocksize=%d", __func__, id,
165: sectsize, blocksize);
166:
167: // +0 ブロック目先頭8バイトのマジックを確認
168: std::vector<uint8> block(blocksize);
169: if (disk->Peek(&block[0], 0, 8) == false) {
170: putlog(1, "%s: SCSI ID %d: Reading magic failed", __func__, id);
171: return 0;
172: }
173: if (memcmp(&block[0], "X68SCSI1", 8) != 0) {
174: putlog(1, "%s: SCSI ID %d: invalid magic", __func__, id);
175: return 0;
176: }
177:
178: // +1 ブロック目から1ブロックを読んで...
179: if (disk->Peek(&block[0], blocksize, blocksize) == false) {
180: putlog(1, "%s: SCSI ID %d: Reading boot block failed", __func__, id);
181: return 0;
182: }
183: // 先頭バイトが 0x60 (BRA.[BW] 命令の1バイト目) であること
184: if (block[0] != 0x60) {
185: putlog(1, "%s: SCSI ID %d: no branch instruction", __func__, id);
186: return 0;
187: }
188: // メモリの 0x2000 番地に置く
189: for (int i = 0; i < blocksize; i++) {
1.1.1.2 root 190: mainram->Write8(0x2000 + i, block[i]);
1.1 root 191: }
192:
193: // D4 にはこの時の SCSI ID が置いてある(のが見えている)
1.1.1.2 root 194: reg.D[4] = id;
1.1 root 195:
196: // XXX D2 は最後に _S_READ を発行した LBA を指しているようだ。
197: // なので +1 ブロックの LBA になる。
1.1.1.2 root 198: reg.D[2] = blocksize / sectsize;
1.1 root 199:
200: // 成功したのでジャンプ先アドレスを返す
201: putlog(1, "%s succeeded", __func__);
202: return 0x2000;
203: }
204:
205: // SCSIIOCS のなんちゃってエミュレーション
206: uint32
207: ROM30EmuDevice::SCSIIOCS()
208: {
1.1.1.3 ! root 209: auto mpu680x0 = GetMPU680x0Device(mpu);
1.1.1.2 root 210: m68kreg& reg = mpu680x0->reg;
1.1 root 211:
1.1.1.2 root 212: uint32 scsicall = reg.D[1];
1.1 root 213: switch (scsicall) {
214: case 0x20: // _S_INQUIRY
215: return SCSI_S_INQUIRY(scsicall);
216:
217: case 0x21: // _S_READ
218: case 0x26: // _S_READEXT
219: // XXX 今はどちらも Read(10) で処理している
220: return SCSI_S_READEXT(scsicall);
221:
222: case 0x24: // _S_TESTUNIT
223: return SCSI_S_TESTUNIT(scsicall);
224:
225: case 0x25: // _S_READCAP
226: return SCSI_S_READCAP(scsicall);
227:
228: default:
229: putlog(0, "SCSIIOCS $%02x (NOT IMPLEMENTED)", scsicall);
230: break;
231: }
232:
233: return 0xffffffff;
234: }
235:
236: #define PRE \
1.1.1.3 ! root 237: auto mpu680x0 = GetMPU680x0Device(mpu); \
1.1.1.2 root 238: m68kreg& reg = mpu680x0->reg; \
239: uint32 d4 = reg.D[4]; \
1.1 root 240: uint32 id = d4 & 0x0000ffff; \
241: uint32 lun = (d4 >> 16) & 0x0000ffff; \
242: std::string scsiname = GetSCSICallName(scsicall); \
243: /* ターゲットディスク */ \
1.1.1.2 root 244: SCSITarget *target = spc->GetTarget(id); \
1.1 root 245: if (target == NULL) { \
246: putlog(1, "%s ID%d not found", scsiname.c_str(), id); \
247: return -1; \
248: } \
249: SCSIDisk *disk __unused = dynamic_cast<SCSIDisk*>(target); \
250: /* ログ表示用 */ \
251: std::string loghdr; \
252: if (loglevel >= 1) { \
253: loghdr = string_format("%s ID%d", scsiname.c_str(), id); \
254: if (lun != 0) { \
255: loghdr + string_format(":LUN%d", lun); \
256: } \
257: } \
258: /*end*/
259:
260: uint32
261: ROM30EmuDevice::SCSI_S_INQUIRY(uint32 scsicall)
262: {
263: PRE;
264:
1.1.1.3 ! root 265: busaddr a1(reg.A[1], busaddr::S);
1.1.1.2 root 266: uint32 reqlen = reg.D[3];
1.1.1.3 ! root 267: putlog(1, "%s a1=$%06x", loghdr.c_str(), a1.Addr());
1.1 root 268: std::vector<uint8> cmdseq(6);
269: cmdseq[0] = SCSI::Command::Inquiry;
270: cmdseq[1] = lun << 5;
271: cmdseq[4] = reqlen;
272:
273: SCSICmd *cmd = disk->SelectCommand(cmdseq);
274: auto phase = cmd->Command(cmdseq);
275: if (phase != SCSI::XferPhase::DataIn) {
276: putlog(0, "%s moved to %s (NOT IMPLEMENTED)",
277: loghdr.c_str(), SCSI::GetPhaseName(phase));
278: return -1;
279: }
280:
281: const std::vector<uint8>& res = cmd->buf;
282: // アロケーションサイズ(reqlen)と結果(cmd->buf)の小さい方まで転送
283: int len = std::min((int)reqlen, (int)res.size());
284: for (int i = 0; i < len; i++) {
1.1.1.3 ! root 285: busdata bd = mainbus->Write8(a1++, res[i]);
! 286: if (bd.IsBusErr()) {
1.1 root 287: return -1;
288: }
289: }
290: return 0;
291: }
292:
293: uint32
294: ROM30EmuDevice::SCSI_S_READEXT(uint32 scsicall)
295: {
296: PRE;
297:
1.1.1.3 ! root 298: busaddr a1(reg.A[1], busaddr::S);
1.1.1.2 root 299: uint32 lba = reg.D[2];
300: uint32 blkcount = reg.D[3];
301: uint32 blkshift = reg.D[5];
1.1 root 302:
303: uint32 blkbytes = 0x100 << blkshift;
304: uint32 bytes = blkcount * blkbytes;
305:
306: putlog(1, "%s a1=$%06x lba=$%08x bytes=%d d5=%d",
1.1.1.3 ! root 307: loghdr.c_str(), a1.Addr(), lba, bytes, blkshift);
1.1 root 308: std::vector<uint8> cmdseq(10);
309: cmdseq[0] = SCSI::Command::Read10;
310: cmdseq[1] = lun << 5;
311: cmdseq[2] = lba >> 24;
312: cmdseq[3] = lba >> 16;
313: cmdseq[4] = lba >> 8;
314: cmdseq[5] = lba;
315: cmdseq[7] = blkcount >> 8;
316: cmdseq[8] = blkcount;
317:
318: SCSICmd *cmd = disk->SelectCommand(cmdseq);
319: auto phase = cmd->Command(cmdseq);
320: if (phase != SCSI::XferPhase::DataIn) {
321: // XXX 本当はステータスコードかメッセージコードを返す?
322: putlog(0, "%s moved to %s (NOT IMPLEMENTED)",
323: loghdr.c_str(), SCSI::GetPhaseName(phase));
324: return -1;
325: }
326:
327: const std::vector<uint8>& res = cmd->buf;
328: if (res.size() != bytes) {
329: putlog(1, "%s res.size=%d mismatch with bytes=%d",
330: loghdr.c_str(), (int)res.size(), bytes);
331: // SCSIIOCS ブロックサイズとディスクのセクタサイズが不整合の
332: // 場合、転送は DMAC か SPC のいずれかが打ち切る。
333: // XXX そのときも何らかのエラーが発生しているはずだが?
334: if (res.size() < bytes) {
335: bytes = res.size();
336: }
337: }
338:
339: for (int i = 0; i < bytes; i++) {
1.1.1.3 ! root 340: busdata bd = mainbus->Write8(a1++, res[i]);
! 341: if (bd.IsBusErr()) {
1.1 root 342: return -1;
343: }
344: }
345: return 0;
346: }
347:
348: uint32
349: ROM30EmuDevice::SCSI_S_TESTUNIT(uint32 scsicall)
350: {
351: PRE;
352:
353: putlog(1, "%s", loghdr.c_str());
354: return 0;
355: }
356:
357: uint32
358: ROM30EmuDevice::SCSI_S_READCAP(uint32 scsicall)
359: {
360: PRE;
361:
1.1.1.3 ! root 362: busaddr a1(reg.A[1], busaddr::S);
! 363: putlog(1, "%s a1=$%06x", loghdr.c_str(), a1.Addr());
1.1 root 364: std::vector<uint8> cmdseq(10);
365: cmdseq[0] = SCSI::Command::ReadCapacity;
366: cmdseq[1] = lun << 5;
367:
368: SCSICmd *cmd = disk->SelectCommand(cmdseq);
369: auto phase = cmd->Command(cmdseq);
370: if (phase != SCSI::XferPhase::DataIn) {
371: putlog(0, "%s moved to %s (NOT IMPLEMENTED)",
372: loghdr.c_str(), SCSI::GetPhaseName(phase));
373: return -1;
374: }
375:
376: const std::vector<uint8>& res = cmd->buf;
377: assert(res.size() == 8);
378: for (auto data : res) {
1.1.1.3 ! root 379: busdata bd = mainbus->Write8(a1++, data);
! 380: if (bd.IsBusErr()) {
1.1 root 381: return -1;
382: }
383: }
384: return 0;
385: }
386:
387: // SCSIIOCS コール名を返す
388: /*static*/ std::string
389: ROM30EmuDevice::GetSCSICallName(uint number)
390: {
391: if (number < countof(x68k_scsicall)) {
392: const char *name = x68k_scsicall[number];
393: if (name) {
394: return std::string(name);
395: }
396: }
397:
398: return string_format("undefined SCSIIOCS $%02x", number);
399: }
400:
401: /*static*/ const char * const
402: ROM30EmuDevice::x68k_scsicall[0x40] = {
403: "_S_RESET", // $00
404: "_S_SELECT", // $01
405: "_S_SELECTA", // $02
406: "_S_CMDOUT", // $03
407: "_S_DATAIN", // $04
408: "_S_DATAOUT", // $05
409: "_S_STSIN", // $06
410: "_S_MSGIN", // $07
411: "_S_MSGOUT", // $08
412: "_S_PHASE", // $09
413: "_S_LEVEL", // $0a
414: "_S_DATAINI", // $0b
415: "_S_DATAOUTI", // $0c
416: "_S_MSGOUTEXT", // $0d
417: NULL, // $0e
418: NULL, // $0f
419:
420: NULL, // $10
421: NULL, // $11
422: NULL, // $12
423: NULL, // $13
424: NULL, // $14
425: NULL, // $15
426: NULL, // $16
427: NULL, // $17
428: NULL, // $18
429: NULL, // $19
430: NULL, // $1a
431: NULL, // $1b
432: NULL, // $1c
433: NULL, // $1d
434: NULL, // $1e
435: NULL, // $1f
436:
437: "_S_INQUIRY", // $20
438: "_S_READ", // $21
439: "_S_WRITE", // $22
440: "_S_FORMAT", // $23
441: "_S_TESTUNIT", // $24
442: "_S_READCAP", // $25
443: "_S_READEXT", // $26
444: "_S_WRITEEXT", // $27
445: "_S_VERIFYEXT", // $28
446: "_S_MODESENSE", // $29
447: "_S_MODESELECT",// $2a
448: "_S_REZEROUNIT",// $2b
449: "_S_REQUEST", // $2c
450: "_S_SEEK", // $2d
451: "_S_READI", // $2e
452: "_S_STARTSTOP", // $2f
453:
454: "_S_EJECT6MO1", // $30
455: "_S_REASSIGN", // $31
456: "_S_PAMEDIUM", // $33
457: NULL, // $34
458: NULL, // $35
459: "_b_dskini", // $36
460: "_b_format", // $37
461: "_b_badfmt", // $38
462: "_b_assign", // $39
463: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.