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

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

unix.superglobalmegacorp.com

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