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

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

unix.superglobalmegacorp.com

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