Annotation of nono/vm/romemu_x68k.cpp, revision 1.1.1.6

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: };

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.