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

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

unix.superglobalmegacorp.com

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