--- nono/vm/msxdos.cpp 2026/04/29 17:05:25 1.1.1.3 +++ nono/vm/msxdos.cpp 2026/04/29 17:06:00 1.1.1.7 @@ -16,6 +16,7 @@ #include "memorystream.h" #include "mpu64180.h" #include "subram.h" +#include "xpbus.h" #include #include #include @@ -27,13 +28,6 @@ static void msxdos_callback(void *arg); MSXDOSDevice::MSXDOSDevice() : inherited() { - // 大きさは適当 - uint devlen = 128 * 1024; - imagebuf.reset(new uint8 [devlen]); - memset(&imagebuf[0], 0, devlen); - mem = &imagebuf[0]; - - mask = devlen - 1; } // デストラクタ @@ -49,35 +43,57 @@ MSXDOSDevice::Init() return false; } + // ROM 領域を用意。 + if (AllocROM(128 * 1024, 0) == false) { + // エラーメッセージは表示済み。 + return false; + } + + cursor_x = 0; xp = GetMPU64180Device(); + xpbus = GetXPbusDevice(); // システムコール処理のコールバックを設定 xp->SetSYSCALLCallback(msxdos_callback, this); + // 実行ファイルオープン。 + // 失敗したらエラー終了したいので Init() で行うが、この時点ではまだ + // 書き込み先となる SubRAM の初期化が終わっていないので書き込めない。 + if (LoadBinary(gMainApp.exec_file) == false) { + return false; + } + + return true; +} + +// リセット +void +MSXDOSDevice::ResetHard(bool poweron) +{ // // ROM っぽいものを用意。 // MemoryStreamBE ms(imagebuf.get()); ms.SetOffset(0); - ms.Write32(0x00002000); // +00: リセットベクタ SP - ms.Write32(0x41000402); // +04: リセットベクタ PC + ms.Write4(0x00002000); // +00: リセットベクタ SP + ms.Write4(0x41000402); // +04: リセットベクタ PC // 全ベクタを RTE に向ける for (int i = 8; i < 0x400; i += 4) { - ms.Write32(0x41000000); + ms.Write4(0x41000000); } ms.SetOffset(0x400); //all_handler: - ms.Write16(0x4e73); // rte + ms.Write2(0x4e73); // rte // ここからリセット時に実行する命令 - ms.Write16(0x41f9); // lea.l $49000000,a0 - ms.Write32(0x49000000); - ms.Write16(0x117c); // move.b #$b6,3(a0) ; SetMode - ms.Write32(0x00b60003); - ms.Write16(0x117c); // move.b #$0f,3(a0) ; XPRESET <- %1 - ms.Write32(0x000f0003); - ms.Write16(0x117c); // move.b #$0e,3(a0) ; XPRESET <- %0 - ms.Write32(0x000e0003); - ms.Write32(0x4e722700); //@:stop #0x2700 - ms.Write16(0x60fa); // bra @b + ms.Write2(0x41f9); // lea.l $49000000,a0 + ms.Write4(0x49000000); + ms.Write2(0x117c); // move.b #$b6,3(a0) ; SetMode + ms.Write4(0x00b60003); + ms.Write2(0x117c); // move.b #$0f,3(a0) ; XPRESET <- %1 + ms.Write4(0x000f0003); + ms.Write2(0x117c); // move.b #$0e,3(a0) ; XPRESET <- %0 + ms.Write4(0x000e0003); + ms.Write4(0x4e722700); //@:stop #0x2700 + ms.Write2(0x60fa); // bra @b // // SubRAM に XP 用ファームウェアを書き込む。 @@ -87,17 +103,25 @@ MSXDOSDevice::Init() IODeviceStream xs(subram); const uint16 _doscall = 0x7000; const uint16 _term = 0x7010; - const uint16 _trap_handler = 0x7020; - const uint16 _rominit = 0x7030; - const uint16 _msg_trap = 0x7050; + const uint16 _rominit = 0x7020; + const uint16 _msg_trap = 0x7040; + const uint16 _trap_handler = 0x7060; // リセットベクタ xs.SetAddr(0x0000); - xs.Write8(0xc3); // 0000: JP _rominit - xs.Write16LE(_rominit); - xs.Write16(0x0000); // 0003: 00,00 - xs.Write8(0xc3); // 0005: JP _doscall - xs.Write16LE(_doscall); + xs.Write1(0xc3); // 0000: JP _rominit + xs.Write2LE(_rominit); + xs.Write2(0x0000); // 0003: 00,00 + xs.Write1(0xc3); // 0005: JP _doscall + xs.Write2LE(_doscall); + + // RST エントリの初期化 + for (int i = 1; i < 8; i++) { + xs.SetAddr(i * 8); + xs.Write1(0x0e); // +00: LD C,`RST xxH` + xs.Write1(0xc7 | (i << 3)); + xs.Write2(0xedff); // +02: SYSCALL + } // ここから ROM。 // CP/M は 0005H の飛び先がワークの先頭らしく、ここより前を @@ -105,72 +129,77 @@ MSXDOSDevice::Init() // DOSCALL ハンドラ。 xs.SetAddr(_doscall); - xs.Write8(0x79); // 7000: LD A,C - xs.Write8(0xb7); // 7001: OR A - xs.Write8(0xca); // 7002: JP Z,_term + 2 - xs.Write16LE(_term + 2); - xs.Write16(0xedff); // 7005: SYSCALL - xs.Write8(0xc9); // 7007: RET + xs.Write1(0x79); // 7000: LD A,C + xs.Write1(0xb7); // 7001: OR A + xs.Write1(0xca); // 7002: JP Z,_term + 2 + xs.Write2LE(_term + 2); + xs.Write2(0xedff); // 7005: SYSCALL + xs.Write1(0xc9); // 7007: RET // TERM xs.SetAddr(_term); - xs.Write16(0x0e00); // 7010: LD C,00H - xs.Write16(0xedff); // 7012: SYSCALL - xs.Write8(0x76); // 7014: HALT - xs.Write8(0x18fd); // 7015: JR 7014H ; 念のため - - // トラップハンドラ。メッセージを出して終了。 - xs.SetAddr(_trap_handler); - xs.Write8(0x11); // 7020: LD DE,msg_trap - xs.Write16LE(_msg_trap); - xs.Write16(0x0e09); // 7023: LD C,09H - xs.Write16(0xedff); // 7025: SYSCALL - xs.Write8(0xc3); // 7026: JP _term - xs.Write16LE(_term); + xs.Write2(0x0e00); // 7010: LD C,00H + xs.Write2(0xedff); // 7012: SYSCALL + xs.Write1(0x76); // 7014: HALT + xs.Write2(0x18fd); // 7015: JR 7014H ; 念のため // XP 側の初期化ルーチン。 // 0000H 番地をトラップハンドラに向け変えて、 // 0100H にジャンプ。戻ってきたら終了。 xs.SetAddr(_rominit); - xs.Write16(0x3e3c); // 7030: LD A,3CH - xs.Write16(0xed39); // 7032: OUT0 (36H),A - xs.Write8(0x36); - xs.Write8(0x21); // 7035: LD HL,_trap_handler - xs.Write16LE(_trap_handler); - xs.Write8(0x22); // 7038: LD (0001H),HL - xs.Write16LE(0x0001); - xs.Write8(0xcd); // 703b: CALL 0100H - xs.Write16LE(0x0100); - xs.Write8(0xc3); // 703e: JP _term - xs.Write16LE(_term); + xs.Write2(0x3e3c); // 7030: LD A,3CH + xs.Write2(0xed39); // 7032: OUT0 (36H),A + xs.Write1(0x36); + xs.Write1(0x21); // 7035: LD HL,_trap_handler + xs.Write2LE(_trap_handler); + xs.Write1(0x22); // 7038: LD (0001H),HL + xs.Write2LE(0x0001); + xs.Write1(0xcd); // 703b: CALL 0100H + xs.Write2LE(0x0100); + xs.Write1(0xc3); // 703e: JP _term + xs.Write2LE(_term); // msg_trap xs.SetAddr(_msg_trap); xs.WriteString("trap occured!\x0d\x0a$"); - // 実行ファイルオープン - std::vector src; - if (LoadBinary(gMainApp.exec_file, src) == false) { - return false; - } + // トラップハンドラ。 + // XXX: とりあえず未定義命令をスキップしたい。 + // まだ LD IXH,n のようなオペランドを持つ命令をうまくスキップできない。 + xs.SetAddr(_trap_handler); + xs.Write1(0xe3); // +00: EX (SP),HL + xs.Write1(0xf5); // +01: PUSH AF + xs.Write1(0x23); // +02: INC HL + xs.Write3(0xed3834); // +03: IN0 A,(34H) + xs.Write1(0xf2); // +06: JP P,_term + xs.Write2LE(_term); + xs.Write2(0xcb77); // +09: BIT 6,A + xs.Write2(0x2802); // +0b: JR Z,@f + xs.Write1(0x23); // +0d: INC HL + xs.Write1(0x23); // +0e: INC HL + // @@: + xs.Write2(0xcbbf); // +0f: RES 7,A + xs.Write3(0xed3934); // +11: OUT0 (34H),A + xs.Write1(0xf1); // +14: POP AF + xs.Write1(0xe3); // +15: EX (SP),HL + xs.Write1(0xc9); // +16: RET - // コピー + // 読んでおいたファイルをここでコピー。 xs.SetAddr(0x100); - for (auto s : src) { - xs.Write8(s); + for (auto s : filebuf) { + xs.Write1(s); } - - return true; + filebuf.clear(); } -// filename で示されるファイルをロードする。 +// filename で示されるファイルを filebuf にロードする。 // 成功すれば data に格納して true を返す。 // 失敗すればエラーメッセージを表示し、false を返す。 // サポートしているのは // o z80-asm の出力する .z80 形式 // o MSX-DOS の .COM 形式 (生バイナリ) bool -MSXDOSDevice::LoadBinary(const char *filename, std::vector& dst) +MSXDOSDevice::LoadBinary(const char *filename) { struct stat st; size_t dlsize; @@ -218,8 +247,8 @@ MSXDOSDevice::LoadBinary(const char *fil return false; } - dst.resize(dlsize); - r = read(fd, dst.data(), dst.size()); + filebuf.resize(dlsize); + r = read(fd, filebuf.data(), filebuf.size()); if (r < 0) { warn("\%s\" read failed", filename); return false; @@ -236,22 +265,36 @@ MSXDOSDevice::LoadBinary(const char *fil void msxdos_callback(void *arg) { - auto msxdosdev = (MSXDOSDevice *)arg; + auto *msxdosdev = reinterpret_cast(arg); msxdosdev->Syscall(); } +// 1 文字表示 +void +MSXDOSDevice::Putc(int ch) +{ + putchar(ch); + cursor_x++; + if (ch == '\r' || ch == '\n') { + cursor_x = 0; + } +} + // DOS コールの処理 void MSXDOSDevice::Syscall() { switch (xp->reg.c) { case 0x00: // _TERM + if (cursor_x != 0) { + Putc('\n'); + } putmsg(1, "DOS _TERM"); Human68kDevice::RequestExit(0); break; case 0x02: // _PUTC - putchar(xp->reg.e); + Putc(xp->reg.e); fflush(stdout); break; @@ -259,15 +302,28 @@ MSXDOSDevice::Syscall() { uint16 de = xp->reg.GetDE(); uint8 ch; - for (; (ch = xp->Read8(de)) != '$'; de++) { - putchar(ch); + for (; (ch = xpbus->Read1(de)) != '$'; de++) { + Putc(ch); } fflush(stdout); break; } + case 0xc7: + case 0xcf: + case 0xd7: + case 0xdf: + case 0xe7: + case 0xef: + case 0xf7: + case 0xff: + putmsg(0, "RST %2Xh default handler, aborted.", (xp->reg.c & 0x38)); + Human68kDevice::RequestExit(0); + break; + default: - putmsg(0, "Syscall: unsupported DOSCALL C=%02XH", xp->reg.c); + putmsg(0, "Syscall: unsupported DOSCALL C=%02XH at $%04x", xp->reg.c, + xp->reg.pc); break; } }