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

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

unix.superglobalmegacorp.com

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