Annotation of nono/vm/romemu_x68k.cpp, revision 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.