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

1.1       root        1: //
                      2: // nono
                      3: // Copyright (C) 2023 nono project
                      4: // Licensed under nono-license.txt
                      5: //
                      6: 
                      7: //
                      8: // MSX-DOS コマンドラインエミュレータ
                      9: //
                     10: 
                     11: #include "msxdos.h"
                     12: #include "autofd.h"
                     13: #include "human68k.h"
                     14: #include "iodevstream.h"
                     15: #include "mainapp.h"
                     16: #include "memorystream.h"
                     17: #include "mpu64180.h"
                     18: #include "subram.h"
1.1.1.5 ! root       19: #include "xpbus.h"
1.1       root       20: #include <fcntl.h>
                     21: #include <string.h>
                     22: #include <unistd.h>
                     23: #include <sys/stat.h>
                     24: 
                     25: static void msxdos_callback(void *arg);
                     26: 
                     27: // コンストラクタ
                     28: MSXDOSDevice::MSXDOSDevice()
                     29:        : inherited()
                     30: {
                     31:        // 大きさは適当
                     32:        uint devlen = 128 * 1024;
                     33:        imagebuf.reset(new uint8 [devlen]);
                     34:        memset(&imagebuf[0], 0, devlen);
                     35:        mem = &imagebuf[0];
                     36: 
                     37:        mask = devlen - 1;
                     38: }
                     39: 
                     40: // デストラクタ
                     41: MSXDOSDevice::~MSXDOSDevice()
                     42: {
                     43: }
                     44: 
                     45: // 初期化
                     46: bool
                     47: MSXDOSDevice::Init()
                     48: {
                     49:        if (inherited::Init() == false) {
                     50:                return false;
                     51:        }
                     52: 
1.1.1.5 ! root       53:        cursor_x = 0;
1.1       root       54:        xp = GetMPU64180Device();
1.1.1.5 ! root       55:        xpbus = GetXPbusDevice();
1.1       root       56: 
                     57:        // システムコール処理のコールバックを設定
                     58:        xp->SetSYSCALLCallback(msxdos_callback, this);
                     59: 
                     60:        //
                     61:        // ROM っぽいものを用意。
                     62:        //
                     63:        MemoryStreamBE ms(imagebuf.get());
                     64:        ms.SetOffset(0);
1.1.1.4   root       65:        ms.Write4(0x00002000);          // +00: リセットベクタ SP
                     66:        ms.Write4(0x41000402);          // +04: リセットベクタ PC
1.1       root       67:        // 全ベクタを RTE に向ける
                     68:        for (int i = 8; i < 0x400; i += 4) {
1.1.1.4   root       69:                ms.Write4(0x41000000);
1.1       root       70:        }
                     71:        ms.SetOffset(0x400);            //all_handler:
1.1.1.4   root       72:        ms.Write2(0x4e73);                      //      rte
1.1       root       73:        // ここからリセット時に実行する命令
1.1.1.4   root       74:        ms.Write2(0x41f9);                      //      lea.l   $49000000,a0
                     75:        ms.Write4(0x49000000);
                     76:        ms.Write2(0x117c);                      //      move.b  #$b6,3(a0)      ; SetMode
                     77:        ms.Write4(0x00b60003);
                     78:        ms.Write2(0x117c);                      //      move.b  #$0f,3(a0)      ; XPRESET <- %1
                     79:        ms.Write4(0x000f0003);
                     80:        ms.Write2(0x117c);                      //      move.b  #$0e,3(a0)      ; XPRESET <- %0
                     81:        ms.Write4(0x000e0003);
                     82:        ms.Write4(0x4e722700);          //@:stop        #0x2700
                     83:        ms.Write2(0x60fa);                      //      bra             @b
1.1       root       84: 
                     85:        //
                     86:        // SubRAM に XP 用ファームウェアを書き込む。
                     87:        // (このモードではこの後の電源オンで SubRAM を初期化しないようになってる)
                     88:        //
                     89:        auto subram = GetSubRAMDevice();
                     90:        IODeviceStream xs(subram);
                     91:        const uint16 _doscall           = 0x7000;
                     92:        const uint16 _term                      = 0x7010;
1.1.1.5 ! root       93:        const uint16 _rominit           = 0x7020;
        !            94:        const uint16 _msg_trap          = 0x7040;
        !            95:        const uint16 _trap_handler      = 0x7060;
1.1       root       96: 
                     97:        // リセットベクタ
1.1.1.4   root       98:        xs.SetOffset(0x0000);
                     99:        xs.Write1(0xc3);                        // 0000:        JP _rominit
                    100:        xs.Write2LE(_rominit);
                    101:        xs.Write2(0x0000);                      // 0003:        00,00
                    102:        xs.Write1(0xc3);                        // 0005:        JP _doscall
                    103:        xs.Write2LE(_doscall);
1.1       root      104: 
1.1.1.5 ! root      105:        // RST エントリの初期化
        !           106:        for (int i = 1; i < 8; i++) {
        !           107:                xs.SetOffset(i * 8);
        !           108:                xs.Write1(0x0e);                // +00: LD      C,`RST xxH`
        !           109:                xs.Write1(0xc7 | (i << 3));
        !           110:                xs.Write2(0xedff);              // +02: SYSCALL
        !           111:        }
        !           112: 
1.1       root      113:        // ここから ROM。
                    114:        // CP/M は 0005H の飛び先がワークの先頭らしく、ここより前を
                    115:        // SP として使っているらしいので、先頭に DOSCALL ハンドラを用意。
                    116: 
                    117:        // DOSCALL ハンドラ。
1.1.1.4   root      118:        xs.SetOffset(_doscall);
                    119:        xs.Write1(0x79);                        // 7000:        LD      A,C
                    120:        xs.Write1(0xb7);                        // 7001:        OR      A
                    121:        xs.Write1(0xca);                        // 7002:        JP      Z,_term + 2
                    122:        xs.Write2LE(_term + 2);
                    123:        xs.Write2(0xedff);                      // 7005:        SYSCALL
                    124:        xs.Write1(0xc9);                        // 7007:        RET
1.1       root      125: 
                    126:        // TERM
1.1.1.4   root      127:        xs.SetOffset(_term);
                    128:        xs.Write2(0x0e00);                      // 7010:        LD      C,00H
                    129:        xs.Write2(0xedff);                      // 7012:        SYSCALL
                    130:        xs.Write1(0x76);                        // 7014:        HALT
                    131:        xs.Write2(0x18fd);                      // 7015:        JR      7014H   ; 念のため
1.1       root      132: 
                    133:        // XP 側の初期化ルーチン。
                    134:        // 0000H 番地をトラップハンドラに向け変えて、
                    135:        // 0100H にジャンプ。戻ってきたら終了。
1.1.1.4   root      136:        xs.SetOffset(_rominit);
                    137:        xs.Write2(0x3e3c);                      // 7030:        LD      A,3CH
                    138:        xs.Write2(0xed39);                      // 7032:        OUT0    (36H),A
                    139:        xs.Write1(0x36);
                    140:        xs.Write1(0x21);                        // 7035:        LD      HL,_trap_handler
                    141:        xs.Write2LE(_trap_handler);
                    142:        xs.Write1(0x22);                        // 7038:        LD      (0001H),HL
                    143:        xs.Write2LE(0x0001);
                    144:        xs.Write1(0xcd);                        // 703b:        CALL 0100H
                    145:        xs.Write2LE(0x0100);
                    146:        xs.Write1(0xc3);                        // 703e:        JP      _term
                    147:        xs.Write2LE(_term);
1.1       root      148: 
                    149:        // msg_trap
1.1.1.4   root      150:        xs.SetOffset(_msg_trap);
1.1       root      151:        xs.WriteString("trap occured!\x0d\x0a$");
                    152: 
1.1.1.5 ! root      153:        // トラップハンドラ。
        !           154:        // XXX: とりあえず未定義命令をスキップしたい。
        !           155:        // まだ LD IXH,n のようなオペランドを持つ命令をうまくスキップできない。
        !           156:        xs.SetOffset(_trap_handler);
        !           157:        xs.Write1(0xe3);                        // +00: EX      (SP),HL
        !           158:        xs.Write1(0xf5);                        // +01: PUSH    AF
        !           159:        xs.Write1(0x23);                        // +02: INC     HL
        !           160:        xs.Write3(0xed3834);            // +03: IN0     A,(34H)
        !           161:        xs.Write1(0xf2);                        // +06: JP      P,_term
        !           162:        xs.Write2LE(_term);
        !           163:        xs.Write2(0xcb77);                      // +09: BIT     6,A
        !           164:        xs.Write2(0x2802);                      // +0b: JR      Z,@f
        !           165:        xs.Write1(0x23);                        // +0d: INC     HL
        !           166:        xs.Write1(0x23);                        // +0e: INC     HL
        !           167:                                                                // @@:
        !           168:        xs.Write2(0xcbbf);                      // +0f: RES     7,A
        !           169:        xs.Write3(0xed3934);            // +11: OUT0    (34H),A
        !           170:        xs.Write1(0xf1);                        // +14: POP     AF
        !           171:        xs.Write1(0xe3);                        // +15: EX      (SP),HL
        !           172:        xs.Write1(0xc9);                        // +16: RET
        !           173: 
1.1       root      174:        // 実行ファイルオープン
1.1.1.3   root      175:        std::vector<uint8> src;
                    176:        if (LoadBinary(gMainApp.exec_file, src) == false) {
1.1       root      177:                return false;
                    178:        }
                    179: 
                    180:        // コピー
1.1.1.4   root      181:        xs.SetOffset(0x100);
1.1.1.3   root      182:        for (auto s : src) {
1.1.1.4   root      183:                xs.Write1(s);
1.1       root      184:        }
                    185: 
                    186:        return true;
                    187: }
                    188: 
1.1.1.3   root      189: // filename で示されるファイルをロードする。
                    190: // 成功すれば data に格納して true を返す。
                    191: // 失敗すればエラーメッセージを表示し、false を返す。
1.1       root      192: // サポートしているのは
                    193: // o z80-asm の出力する .z80 形式
                    194: // o MSX-DOS の .COM 形式 (生バイナリ)
1.1.1.3   root      195: bool
                    196: MSXDOSDevice::LoadBinary(const char *filename, std::vector<uint8>& dst)
1.1       root      197: {
                    198:        struct stat st;
1.1.1.3   root      199:        size_t dlsize;
1.1       root      200:        char header[10];
                    201:        autofd fd;
                    202:        int r;
                    203: 
                    204:        fd = open(filename, O_RDONLY);
                    205:        if (fd < 0) {
                    206:                warn("\"%s\" open failed", filename);
1.1.1.3   root      207:                return false;
1.1       root      208:        }
                    209: 
                    210:        r = fstat(fd, &st);
                    211:        if (r < 0) {
                    212:                warn("\"%s\" fstat failed", filename);
1.1.1.3   root      213:                return false;
1.1       root      214:        }
                    215: 
                    216:        r = read(fd, header, sizeof(header));
                    217:        if (r < 0) {
                    218:                warn("\"%s\" read(header) failed", filename);
1.1.1.3   root      219:                return false;
1.1       root      220:        }
                    221:        if (r < sizeof(header)) {
                    222:                warnx("\"%s\" read(header): %d: too short", filename, r);
                    223:        }
                    224: 
                    225:        if (strncmp(header, "Z80ASM\x1a\x0a", 8) == 0) {
                    226:                // .z80 形式。先頭 10 バイトがヘッダ。
1.1.1.3   root      227:                dlsize = st.st_size - 10;
1.1       root      228:        } else {
                    229:                // そうでなければ今の所 .COM 形式。ヘッダなしの生バイナリ。
1.1.1.3   root      230:                dlsize = st.st_size;
1.1       root      231:                if (lseek(fd, 0, SEEK_SET) < 0) {
                    232:                        warn("\"%s\" lseek(0) failed", filename);
1.1.1.3   root      233:                        return false;
1.1       root      234:                }
                    235:        }
                    236: 
1.1.1.2   root      237:        // サイズ制限
1.1.1.3   root      238:        if (dlsize >= 0x7000 - 0x100) {
1.1.1.2   root      239:                errno = EFBIG;
                    240:                warn("%s", filename);
1.1.1.3   root      241:                return false;
1.1       root      242:        }
                    243: 
1.1.1.3   root      244:        dst.resize(dlsize);
                    245:        r = read(fd, dst.data(), dst.size());
1.1       root      246:        if (r < 0) {
                    247:                warn("\%s\" read failed", filename);
1.1.1.3   root      248:                return false;
1.1       root      249:        }
1.1.1.3   root      250:        if (r < dlsize) {
1.1       root      251:                warn("\%s\" read: %d: too short", filename, r);
1.1.1.3   root      252:                return false;
1.1       root      253:        }
                    254: 
1.1.1.3   root      255:        return true;
1.1       root      256: }
                    257: 
                    258: // コールバック入口のグローバル関数
                    259: void
                    260: msxdos_callback(void *arg)
                    261: {
1.1.1.4   root      262:        auto *msxdosdev = reinterpret_cast<MSXDOSDevice *>(arg);
1.1       root      263:        msxdosdev->Syscall();
                    264: }
                    265: 
1.1.1.5 ! root      266: // 1 文字表示
        !           267: void
        !           268: MSXDOSDevice::Putc(int ch)
        !           269: {
        !           270:        putchar(ch);
        !           271:        cursor_x++;
        !           272:        if (ch == '\r' || ch == '\n') {
        !           273:                cursor_x = 0;
        !           274:        }
        !           275: }
        !           276: 
1.1       root      277: // DOS コールの処理
                    278: void
                    279: MSXDOSDevice::Syscall()
                    280: {
                    281:        switch (xp->reg.c) {
                    282:         case 0x00:             // _TERM
1.1.1.5 ! root      283:                if (cursor_x != 0) {
        !           284:                        Putc('\n');
        !           285:                }
1.1       root      286:                putmsg(1, "DOS _TERM");
1.1.1.3   root      287:                Human68kDevice::RequestExit(0);
1.1       root      288:                break;
                    289: 
                    290:         case 0x02:             // _PUTC
1.1.1.5 ! root      291:                Putc(xp->reg.e);
1.1       root      292:                fflush(stdout);
                    293:                break;
                    294: 
                    295:         case 0x09:             // _PUTS
                    296:         {
                    297:                uint16 de = xp->reg.GetDE();
                    298:                uint8 ch;
1.1.1.5 ! root      299:                for (; (ch = xpbus->Read1(de)) != '$'; de++) {
        !           300:                        Putc(ch);
1.1       root      301:                }
                    302:                fflush(stdout);
                    303:                break;
                    304:         }
                    305: 
1.1.1.5 ! root      306:         case 0xc7:
        !           307:         case 0xcf:
        !           308:         case 0xd7:
        !           309:         case 0xdf:
        !           310:         case 0xe7:
        !           311:         case 0xef:
        !           312:         case 0xf7:
        !           313:         case 0xff:
        !           314:                putmsg(0, "RST %2Xh default handler, aborted.", (xp->reg.c & 0x38));
        !           315:                Human68kDevice::RequestExit(0);
        !           316:                break;
        !           317: 
1.1       root      318:         default:
1.1.1.5 ! root      319:                putmsg(0, "Syscall: unsupported DOSCALL C=%02XH at $%04x", xp->reg.c,
        !           320:                        xp->reg.pc);
1.1       root      321:                break;
                    322:        }
                    323: }

unix.superglobalmegacorp.com

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