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

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

unix.superglobalmegacorp.com

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