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

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

unix.superglobalmegacorp.com

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