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