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