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