|
|
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.