|
|
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" ! 19: #include <fcntl.h> ! 20: #include <string.h> ! 21: #include <unistd.h> ! 22: #include <sys/stat.h> ! 23: ! 24: static void msxdos_callback(void *arg); ! 25: ! 26: // コンストラクタ ! 27: MSXDOSDevice::MSXDOSDevice() ! 28: : inherited() ! 29: { ! 30: // 大きさは適当 ! 31: uint devlen = 128 * 1024; ! 32: imagebuf.reset(new uint8 [devlen]); ! 33: memset(&imagebuf[0], 0, devlen); ! 34: mem = &imagebuf[0]; ! 35: ! 36: mask = devlen - 1; ! 37: } ! 38: ! 39: // デストラクタ ! 40: MSXDOSDevice::~MSXDOSDevice() ! 41: { ! 42: } ! 43: ! 44: // 初期化 ! 45: bool ! 46: MSXDOSDevice::Init() ! 47: { ! 48: if (inherited::Init() == false) { ! 49: return false; ! 50: } ! 51: ! 52: xp = GetMPU64180Device(); ! 53: ! 54: // システムコール処理のコールバックを設定 ! 55: xp->SetSYSCALLCallback(msxdos_callback, this); ! 56: ! 57: // ! 58: // ROM っぽいものを用意。 ! 59: // ! 60: MemoryStreamBE ms(imagebuf.get()); ! 61: ms.SetOffset(0); ! 62: ms.Write32(0x00002000); // +00: リセットベクタ SP ! 63: ms.Write32(0x41000402); // +04: リセットベクタ PC ! 64: // 全ベクタを RTE に向ける ! 65: for (int i = 8; i < 0x400; i += 4) { ! 66: ms.Write32(0x41000000); ! 67: } ! 68: ms.SetOffset(0x400); //all_handler: ! 69: ms.Write16(0x4e73); // rte ! 70: // ここからリセット時に実行する命令 ! 71: ms.Write16(0x41f9); // lea.l $49000000,a0 ! 72: ms.Write32(0x49000000); ! 73: ms.Write16(0x117c); // move.b #$b6,3(a0) ; SetMode ! 74: ms.Write32(0x00b60003); ! 75: ms.Write16(0x117c); // move.b #$0f,3(a0) ; XPRESET <- %1 ! 76: ms.Write32(0x000f0003); ! 77: ms.Write16(0x117c); // move.b #$0e,3(a0) ; XPRESET <- %0 ! 78: ms.Write32(0x000e0003); ! 79: ms.Write32(0x4e722700); //@:stop #0x2700 ! 80: ms.Write16(0x60fa); // bra @b ! 81: ! 82: // ! 83: // SubRAM に XP 用ファームウェアを書き込む。 ! 84: // (このモードではこの後の電源オンで SubRAM を初期化しないようになってる) ! 85: // ! 86: auto subram = GetSubRAMDevice(); ! 87: IODeviceStream xs(subram); ! 88: const uint16 _doscall = 0x7000; ! 89: const uint16 _term = 0x7010; ! 90: const uint16 _trap_handler = 0x7020; ! 91: const uint16 _rominit = 0x7030; ! 92: const uint16 _msg_trap = 0x7050; ! 93: ! 94: // リセットベクタ ! 95: xs.SetAddr(0x0000); ! 96: xs.Write8(0xc3); // 0000: JP _rominit ! 97: xs.Write16LE(_rominit); ! 98: xs.Write16(0x0000); // 0003: 00,00 ! 99: xs.Write8(0xc3); // 0005: JP _doscall ! 100: xs.Write16LE(_doscall); ! 101: ! 102: // ここから ROM。 ! 103: // CP/M は 0005H の飛び先がワークの先頭らしく、ここより前を ! 104: // SP として使っているらしいので、先頭に DOSCALL ハンドラを用意。 ! 105: ! 106: // DOSCALL ハンドラ。 ! 107: xs.SetAddr(_doscall); ! 108: xs.Write8(0x79); // 7000: LD A,C ! 109: xs.Write8(0xb7); // 7001: OR A ! 110: xs.Write8(0xca); // 7002: JP Z,_term + 2 ! 111: xs.Write16LE(_term + 2); ! 112: xs.Write16(0xedff); // 7005: SYSCALL ! 113: xs.Write8(0xc9); // 7007: RET ! 114: ! 115: // TERM ! 116: xs.SetAddr(_term); ! 117: xs.Write16(0x0e00); // 7010: LD C,00H ! 118: xs.Write16(0xedff); // 7012: SYSCALL ! 119: xs.Write8(0x76); // 7014: HALT ! 120: xs.Write8(0x18fd); // 7015: JR 7014H ; 念のため ! 121: ! 122: // トラップハンドラ。メッセージを出して終了。 ! 123: xs.SetAddr(_trap_handler); ! 124: xs.Write8(0x11); // 7020: LD DE,msg_trap ! 125: xs.Write16LE(_msg_trap); ! 126: xs.Write16(0x0e09); // 7023: LD C,09H ! 127: xs.Write16(0xedff); // 7025: SYSCALL ! 128: xs.Write8(0xc3); // 7026: JP _term ! 129: xs.Write16LE(_term); ! 130: ! 131: // XP 側の初期化ルーチン。 ! 132: // 0000H 番地をトラップハンドラに向け変えて、 ! 133: // 0100H にジャンプ。戻ってきたら終了。 ! 134: xs.SetAddr(_rominit); ! 135: xs.Write16(0x3e3c); // 7030: LD A,3CH ! 136: xs.Write16(0xed39); // 7032: OUT0 (36H),A ! 137: xs.Write8(0x36); ! 138: xs.Write8(0x21); // 7035: LD HL,_trap_handler ! 139: xs.Write16LE(_trap_handler); ! 140: xs.Write8(0x22); // 7038: LD (0001H),HL ! 141: xs.Write16LE(0x0001); ! 142: xs.Write8(0xcd); // 703b: CALL 0100H ! 143: xs.Write16LE(0x0100); ! 144: xs.Write8(0xc3); // 703e: JP _term ! 145: xs.Write16LE(_term); ! 146: ! 147: // msg_trap ! 148: xs.SetAddr(_msg_trap); ! 149: xs.WriteString("trap occured!\x0d\x0a$"); ! 150: ! 151: // 実行ファイルオープン ! 152: const char *exec_file = gMainApp.exec_file; ! 153: data_t src = LoadBinary(exec_file); ! 154: if (src.data == NULL) { ! 155: return false; ! 156: } ! 157: ! 158: // コピー ! 159: if (src.size >= 0x7000 - 0x100) { ! 160: src.size = 0x7000 - 0x100; ! 161: } ! 162: xs.SetAddr(0x100); ! 163: for (int i = 0; i < src.size; i++) { ! 164: xs.Write8(src.data[i]); ! 165: } ! 166: ! 167: delete[] src.data; ! 168: ! 169: return true; ! 170: } ! 171: ! 172: // filename を data_t にロードする。 ! 173: // 成功すれば data_t を埋めて返す。 ! 174: // その場合 data_t.data は new[] してあるので呼び出し側で delete[] すること。 ! 175: // 失敗すればエラーメッセージを表示し、data_t.data = NULL を返す。 ! 176: // サポートしているのは ! 177: // o z80-asm の出力する .z80 形式 ! 178: // o MSX-DOS の .COM 形式 (生バイナリ) ! 179: MSXDOSDevice::data_t ! 180: MSXDOSDevice::LoadBinary(const char *filename) ! 181: { ! 182: struct stat st; ! 183: data_t dl; ! 184: char header[10]; ! 185: autofd fd; ! 186: int r; ! 187: ! 188: dl.data = NULL; ! 189: dl.size = 0; ! 190: ! 191: fd = open(filename, O_RDONLY); ! 192: if (fd < 0) { ! 193: warn("\"%s\" open failed", filename); ! 194: return dl; ! 195: } ! 196: ! 197: r = fstat(fd, &st); ! 198: if (r < 0) { ! 199: warn("\"%s\" fstat failed", filename); ! 200: return dl; ! 201: } ! 202: ! 203: r = read(fd, header, sizeof(header)); ! 204: if (r < 0) { ! 205: warn("\"%s\" read(header) failed", filename); ! 206: return dl; ! 207: } ! 208: if (r < sizeof(header)) { ! 209: warnx("\"%s\" read(header): %d: too short", filename, r); ! 210: } ! 211: ! 212: if (strncmp(header, "Z80ASM\x1a\x0a", 8) == 0) { ! 213: // .z80 形式。先頭 10 バイトがヘッダ。 ! 214: dl.size = st.st_size - 10; ! 215: } else { ! 216: // そうでなければ今の所 .COM 形式。ヘッダなしの生バイナリ。 ! 217: dl.size = st.st_size; ! 218: if (lseek(fd, 0, SEEK_SET) < 0) { ! 219: warn("\"%s\" lseek(0) failed", filename); ! 220: return dl; ! 221: } ! 222: } ! 223: ! 224: try { ! 225: dl.data = new uint8 [dl.size]; ! 226: } catch (...) { ! 227: dl.data = NULL; ! 228: warnx("Could not allocate memory %d", dl.size); ! 229: return dl; ! 230: } ! 231: ! 232: r = read(fd, dl.data, dl.size); ! 233: if (r < 0) { ! 234: warn("\%s\" read failed", filename); ! 235: goto abort; ! 236: } ! 237: if (r < dl.size) { ! 238: warn("\%s\" read: %d: too short", filename, r); ! 239: goto abort; ! 240: } ! 241: ! 242: return dl; ! 243: ! 244: abort: ! 245: delete[] dl.data; ! 246: dl.data = NULL; ! 247: dl.size = 0; ! 248: return dl; ! 249: } ! 250: ! 251: // コールバック入口のグローバル関数 ! 252: void ! 253: msxdos_callback(void *arg) ! 254: { ! 255: auto msxdosdev = (MSXDOSDevice *)arg; ! 256: msxdosdev->Syscall(); ! 257: } ! 258: ! 259: // DOS コールの処理 ! 260: void ! 261: MSXDOSDevice::Syscall() ! 262: { ! 263: switch (xp->reg.c) { ! 264: case 0x00: // _TERM ! 265: putmsg(1, "DOS _TERM"); ! 266: Human68k::RequestExit(0); ! 267: break; ! 268: ! 269: case 0x02: // _PUTC ! 270: putchar(xp->reg.e); ! 271: fflush(stdout); ! 272: break; ! 273: ! 274: case 0x09: // _PUTS ! 275: { ! 276: uint16 de = xp->reg.GetDE(); ! 277: uint8 ch; ! 278: for (; (ch = xp->Read8(de)) != '$'; de++) { ! 279: putchar(ch); ! 280: } ! 281: fflush(stdout); ! 282: break; ! 283: } ! 284: ! 285: default: ! 286: putmsg(0, "Syscall: unsupported DOSCALL C=%02XH", xp->reg.c); ! 287: break; ! 288: } ! 289: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.