--- nono/vm/human68k.cpp 2026/04/29 17:04:32 1.1.1.3 +++ nono/vm/human68k.cpp 2026/04/29 17:05:33 1.1.1.14 @@ -1,23 +1,12 @@ // // nono -// Copyright (C) 2017 Y.Sugahara (moveccr) -// -// human68k .r .x .z console emulator +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "human68k.h" -#include "mainapp.h" -#include "mpu.h" -#include "m68030bus.h" -#include "m68030core.h" -#include "bus.h" -#include "iodevstream.h" -#include "scheduler.h" -#include "vm.h" -#include -#include -#include -#include +// +// Human68k .r .x .z console emulator +// // メモリマップ // @@ -31,55 +20,80 @@ // 0002'0000 ロードアドレス // 00bf'ffff RAM_END -static bool human68k_fline_callback(m68kcpu *cpu, void *arg); +#include "human68k.h" +#include "autofd.h" +#include "iodevstream.h" +#include "mainapp.h" +#include "mainbus.h" +#include "mainram.h" +#include "mpu680x0.h" +#include "power.h" +#include "scheduler.h" +#include +#include +#include +#include -// コンストラクタ -Human68k::Human68k() -{ - logname = "human68k"; - devname = "Human68k"; +static bool human68k_fline_callback(MPU680x0Device *, void *arg); - assert(gMainApp.human68k_file); - human68k_file = gMainApp.human68k_file; - human68k_arg = gMainApp.human68k_arg; +#define RegD(n) mpu680x0->reg.D[n] +#define RegA(n) mpu680x0->reg.A[n] +// コンストラクタ +Human68kDevice::Human68kDevice() + : inherited(OBJ_HUMAN68K) +{ Files[0].fd = 0; - Files[0].filename = strdup("|stdin"); + Files[0].filename = "|stdin"; Files[1].fd = 1; - Files[1].filename = strdup("|stdout"); + Files[1].filename = "|stdout"; Files[2].fd = 2; - Files[2].filename = strdup("|stderr"); + Files[2].filename = "|stderr"; } // デストラクタ -Human68k::~Human68k() +Human68kDevice::~Human68kDevice() { } +// 初期化 bool -Human68k::Init() +Human68kDevice::Init() { - bool r; uint32 psp_base; uint8 *file; + uint32 last_addr; + int ram_size; + + if (inherited::Init() == false) { + return false; + } + + assert(gMainApp.exec_file); + human68k_file = gMainApp.exec_file; + human68k_arg = gMainApp.exec_arg; - // ショートカット用 - ram = gRAM.get(); - // Fライン命令をこちらで処理する - gMPU680x0->SetFLineCallback(human68k_fline_callback, this); + mainbus = GetMainbusDevice(); + mainram = GetMainRAMDevice(); + mpu680x0 = GetMPU680x0Device(mpu); - // ワークを用意 (どこでやるか) + // Fライン命令をこちらで処理する。 + mpu680x0->SetFLineCallback(human68k_fline_callback, this); + + IODeviceStream ds(mainram); + + // ワークを用意 (どこでやるか)。 // $CBC.B MPU 種別 (3:68030) - gRAM->Write8(0x0cbc, 3); - // $CBD.B FPU 有無 (0xff:あり) - if (gMPU680x0->HaveFPU()) { - gRAM->Write8(0x0cbd, 0xff); + ds.Write1(0x0cbc, 3); + // $CBD.B FPU 有無 (0xff:あり)。 + if (mpu680x0->HasFPU()) { + ds.Write1(0x0cbd, 0xff); } - putmsg(1, "arg=%s", human68k_arg); + putmsg(1, "arg=%s", human68k_arg.c_str()); - // 実行ファイルオープン - int file_fd = open(human68k_file, O_RDONLY); + // 実行ファイルオープン。 + autofd file_fd = open(human68k_file, O_RDONLY); if (file_fd == -1) { warn("Human68k executable \"%s\" open failed", human68k_file); return false; @@ -88,7 +102,6 @@ Human68k::Init() struct stat st; if (fstat(file_fd, &st) != 0) { warn("Human68k executable \"%s\" fstat failed", human68k_file); - close(file_fd); return false; } uint32 file_size; @@ -99,127 +112,144 @@ Human68k::Init() file = (uint8 *)mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, file_fd, 0); if (file == MAP_FAILED) { warn("Human68k executable \"%s\" mmap failed", human68k_file); - close(file_fd); return false; } - // 拡張子判別 + // 拡張子判別。 if (isext(human68k_file, ".r")) { - r = LoadR(file, file_size); + if (LoadR(file, file_size) == false) { + goto abort; + } } else if (isext(human68k_file, ".x")) { - r = LoadX(file, file_size); + if (LoadX(file, file_size) == false) { + goto abort; + } } else if (isext(human68k_file, ".z")) { - r = LoadZ(file, file_size); + if (LoadZ(file, file_size) == false) { + goto abort; + } } else { - if (file[0] == 0x48 && file[1] == 0x55) { - r = LoadX(file, file_size); - } else if (file[0] == 0x60 && file[1] == 0x1a) { - r = LoadZ(file, file_size); - } else { - warnx("Human68k executable \"%s\" cannot identify file type", - human68k_file); - munmap(file, file_size); - close(file_fd); - return false; + // ELF .o のロードを試行する。 + if (LoadELF(file, file_size) == false) { + if (file[0] == 0x48 && file[1] == 0x55) { + if (LoadX(file, file_size) == false) { + goto abort; + } + } else if (file[0] == 0x60 && file[1] == 0x1a) { + if (LoadZ(file, file_size) == false) { + goto abort; + } + } else { + errno = ENOEXEC; + warn("Human68k executable \"%s\"", human68k_file); + goto abort; + } } } - if (!r) { - warnx("Human68k executable \"%s\" invalid file format", human68k_file); - munmap(file, file_size); - close(file_fd); - return false; - } - munmap(file, file_size); - close(file_fd); - uint32 last_addr = load_addr + load_size + bss_size; + last_addr = load_addr + load_size + bss_size; + ram_size = mainram->GetSize(); - IODeviceStream ds(ram); boot_addr = 0x200; - ds.Write32(0, ram_size - 4); // 初期 SSP - ds.Write32(4, boot_addr); + ds.Write4(0, ram_size - 4); // 初期 SSP + ds.Write4(4, boot_addr); // TRAP #15 ベクタを書き込む。 - ds.Write32(0xbc, 0x1000); - // TRAP #15 ハンドラを書き込む。 - // エミュレータ内部命令 $f300 で処理して RTE するだけ。 - ds.Write16(0x1000, 0xf600); // .dw $f300 - ds.Write16(0x1002, 0x4e73); // rte + ds.Write4(0xbc, 0x1000); + // TRAP #15 (IOCS コール) ハンドラを書き込む。 + // オレオレ F ライン命令 (エミュレータ内部命令) で処理して RTE するだけ。 + ds.Write2(0x1000, 0xfa00); + ds.Write2(0x1002, 0x4e73); // rte + + // STOP による停止。終了時に CPU を止めておくため。 + ds.Write4(0x1010, 0x4e722700); // stop #$2700 // コマンドライン文字列を書き込む。LASCII 形式 - ds.SetAddr(0xc001); + ds.SetOffset(0xc001); for (int i = 0; i < 255; i++) { uint8 c = human68k_arg[i]; - ds.Write8(c); + ds.Write1(c); if (c == '\0') { - ds.Write8(0xc000, i); + ds.Write1(0xc000, i); break; } } + // XXX: 255文字以上のときどうなるか調べること // PSP (process entry) を書き込む。 - // instructiontest.x はコマンドラインを指定するだけで動くようだ + // instructiontest.x はコマンドラインを指定するだけで動くようだ。 // PSP はロードアドレス-256 の位置でなければならないようだ。 psp_base = 0x1ff00; - ds.SetAddr(psp_base); + ds.SetOffset(psp_base); - ds.Write32(-1); // 1つ前のメモリ管理ポインタ - ds.Write32(-1); // このメモリを確保したプロセスのメモリ管理ポインタ - ds.Write32(ram_size - 4 + 1); // このメモリブロックの終わり+1 のアドレス - ds.Write32(-1); // 次のメモリ管理ポインタ - - ds.Write32(psp_base + 0x20, 0xc000); // コマンドライン - - ds.SetAddr(boot_addr); - ds.Write16(0x2c4f); // move.l a7,a6 - ds.Write16(0x2e7c); // move.l psp_base,a7 - ds.Write32(psp_base); - ds.Write16(0x4e66); // move.l a6,usp - ds.Write16(0x207c); // move.l psp_base,a0 - ds.Write32(psp_base); - ds.Write16(0x227c); // move.l last_addr,a1 - ds.Write32(last_addr); - ds.Write16(0x247c); // move.l #$c000,a2 - ds.Write32(0xc000); - ds.Write16(0x267c); // move.l #$e000,a3 - ds.Write32(0xe000); - ds.Write16(0x287c); // move.l exec_addr,a4 - ds.Write32(exec_addr); - ds.Write16(0x2c49); // move.l a1,a6 - ds.Write16(0x46fc); // move.w #$0700,sr - ds.Write16(0x0700); - ds.Write16(0x4eb9); // jsr.l exec_addr - ds.Write32(exec_addr); - ds.Write16(0xff00); // DOS _EXIT + ds.Write4(-1); // 1つ前のメモリ管理ポインタ + ds.Write4(-1); // このメモリを確保したプロセスのメモリ管理ポインタ + ds.Write4(ram_size - 4 + 1); // このメモリブロックの終わり+1 のアドレス + ds.Write4(-1); // 次のメモリ管理ポインタ + + ds.Write4(psp_base + 0x20, 0xc000); // コマンドライン + + ds.SetOffset(boot_addr); + ds.Write2(0x2c4f); // move.l a7,a6 + ds.Write2(0x2e7c); // move.l psp_base,a7 + ds.Write4(psp_base); + ds.Write2(0x4e66); // move.l a6,usp + ds.Write2(0x207c); // move.l psp_base,a0 + ds.Write4(psp_base); + ds.Write2(0x227c); // move.l last_addr,a1 + ds.Write4(last_addr); + ds.Write2(0x247c); // move.l #$c000,a2 + ds.Write4(0xc000); + ds.Write2(0x267c); // move.l #$e000,a3 + ds.Write4(0xe000); + ds.Write2(0x287c); // move.l exec_addr,a4 + ds.Write4(exec_addr); + ds.Write2(0x2c49); // move.l a1,a6 + ds.Write2(0x46fc); // move.w #$0700,sr + ds.Write2(0x0700); + ds.Write2(0x4eb9); // jsr.l exec_addr + ds.Write4(exec_addr); + ds.Write2(0xff00); // DOS _EXIT putmsg(1, "LoadFile complete"); return true; + + abort: + munmap(file, file_size); + return false; } +// R 形式ファイルをロードする。 +// 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。 bool -Human68k::LoadR(uint8 *file, uint size) +Human68kDevice::LoadR(const uint8 *file, uint size) { - // リロケータブルなのでどこでもいい + // リロケータブルなのでどこでもいい。 load_addr = default_load_addr; load_size = size; - LoadMem(load_addr, &file[0], load_size); + if (LoadMem(load_addr, &file[0], load_size) == false) { + return false; + } - // R 形式はファイル先頭が実行開始位置 + // R 形式はファイル先頭が実行開始位置。 exec_addr = load_addr; text_size = load_size; putmsg(1, "r format"); return true; } +// X 形式ファイルをロードする。 +// 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。 bool -Human68k::LoadX(uint8 *file, uint size) +Human68kDevice::LoadX(const uint8 *file, uint size) { - XFileHeader *hdr = (XFileHeader *)file; + auto *hdr = reinterpret_cast(file); uint hdr_size = sizeof(*hdr); if (!(hdr->magic[0] == 'H' && hdr->magic[1] == 'U')) { - errx(EXIT_FAILURE, "invalid magic"); + warnx("Human68k executable \"%s\" invalid magic", human68k_file); + return false; } base_addr = be32toh(hdr->base_addr); exec_addr = be32toh(hdr->exec_addr); @@ -229,42 +259,44 @@ Human68k::LoadX(uint8 *file, uint size) uint32 reloc_size = be32toh(hdr->reloc_size); - // 最適配置位置も可能だが今回見送り + // 最適配置位置も可能だが今回見送り。 load_addr = default_load_addr; load_size = text_size + data_size; - LoadMem(load_addr, &file[hdr_size], load_size); + if (LoadMem(load_addr, &file[hdr_size], load_size) == false) { + return false; + } - // 再配置テーブルの file での位置 + // 再配置テーブルの file での位置。 uint32 reloc_pos = hdr_size + load_size; - // 再配置 - IODeviceStream ds(ram); + // 再配置。 + IODeviceStream ds(mainram); uint32 offset = load_addr - base_addr; uint32 reloc_end = reloc_pos + reloc_size; uint32 A = load_addr; uint32 B = base_addr; uint32 C = A - B; while (reloc_pos < reloc_end) { - putlog(1, "reloc_pos=%08x reloc_end=%08x", reloc_pos, reloc_end); + putmsg(3, "reloc_pos=%08x reloc_end=%08x", reloc_pos, reloc_end); uint32 D; - D = be16toh(*(uint16 *)&file[reloc_pos]); - putlog(1, "D=%x", D); + D = be16toh(*(const uint16 *)&file[reloc_pos]); + putmsg(3, "D=%x", D); reloc_pos += 2; if (D == 1) { - D = be32toh(*(uint32 *)&file[reloc_pos]); - putlog(1, " odd, D=%x", D); + D = be32toh(*(const uint32 *)&file[reloc_pos]); + putmsg(3, " odd, D=%x", D); reloc_pos += 4; } if ((D & 1) == 0) { A += D; - uint32 old = ds.Read32(A); - ds.Write32(A, old + C); - putlog(1, " Write_L A=%x, old=%x new=%x", A, old, old + C); + uint32 old = ds.Read4(A); + ds.Write4(A, old + C); + putmsg(3, " Write_L A=%x, old=%x new=%x", A, old, old + C); } else { A += D - 1; - uint32 old = ds.Read16(A); - ds.Write16(A, old + C); - putlog(1, " Write_W A=%x, old=%x new=%x", A, old, old + C); + uint32 old = ds.Read2(A); + ds.Write2(A, old + C); + putmsg(3, " Write_W A=%x, old=%x new=%x", A, old, old + C); } } @@ -275,15 +307,17 @@ Human68k::LoadX(uint8 *file, uint size) return true; } - +// Z 形式ファイルをロードする。 +// 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。 bool -Human68k::LoadZ(uint8 *file, uint size) +Human68kDevice::LoadZ(const uint8 *file, uint size) { - ZFileHeader *hdr = (ZFileHeader *)file; + auto *hdr = reinterpret_cast(file); uint32 hdr_size = sizeof(*hdr); if (be16toh(hdr->magic1) != 0x601a) { - errx(EXIT_FAILURE, "invalid magic"); + warnx("Human68k executable \"%s\" invalid magic", human68k_file); + return false; } text_size = be32toh(hdr->text_size); data_size = be32toh(hdr->data_size); @@ -292,38 +326,62 @@ Human68k::LoadZ(uint8 *file, uint size) exec_addr = base_addr; if (base_addr < 0x20000) { - errx(EXIT_FAILURE, "base, unsupported"); + warnx("Human68k executable \"%s\" unsupported base specified", + human68k_file); + return false; } load_addr = base_addr; load_size = text_size + data_size; - LoadMem(load_addr, &file[hdr_size], load_size); + if (LoadMem(load_addr, &file[hdr_size], load_size) == false) { + return false; + } putmsg(1, "z format, base_addr=%08x, exec_addr=%08x", base_addr, exec_addr); return true; } +// なぜか m68k ELF .o が読める。 +// 成功すれば true を返す。失敗すればエラーメッセージを表示して false を返す。 bool -Human68k::LoadMem(uint32 addr, uint8 *src, uint size) +Human68kDevice::LoadELF(const uint8 *file, uint size) { - if (addr + size > ram_size) { - errx(EXIT_FAILURE, "file too large"); + LoadInfo info(human68k_file, file, size); + if (mainram->LoadExec(&info) == false) { + return false; } - IODeviceStream ds(ram, addr); - for (uint32 i = 0; i < size; i++) { - ds.Write8(*src++); + // XXX load_size は esym のような気がする。 + // XXX text_size も今はやろうと思えば返せる構造になったが未対応。 + load_addr = info.entry; + load_size = size; + exec_addr = info.entry; + text_size = size; + return true; +} + +// src から size バイトを RAM の addr 以降にロードする。 +// 成功すれば true を返す。 +// ファイルが RAM に収まらない場合はエラーメッセージを表示して false を返す。 +bool +Human68kDevice::LoadMem(uint32 addr, const uint8 *src, uint size) +{ + if (addr + size >= mainram->GetSize()) { + warnx("Human68k executable \"%s\": file too large", human68k_file); + return false; } + + mainram->WriteMem(addr, src, size); return true; } // file の拡張子が ext なら true を返す。ext は '.' を含む。 bool -Human68k::isext(const char *file, const char *ext) +Human68kDevice::isext(const char *file, const char *ext) { size_t len_file = strlen(file); size_t len_ext = strlen(ext); - if (len_ext <= 0) { + if (len_ext == 0) { return false; } if (len_file < len_ext) { @@ -334,325 +392,151 @@ Human68k::isext(const char *file, const } -// fileaddr: Human68k ファイル名のゲストVA -// atr: Human68k atr -// mode: unix open mode -// return: Human68k fileno -int32 -Human68k::OpenFile(uint32 fileaddr, uint16 atr, int mode) -{ - // XXX: unix host only - - int32 fileno; - Human68k::File *f = NULL; - - // 開いているエントリを検索して Human fileno を取得 - for (int i = 0; i < FilesCount; i++) { - if (Files[i].fd == -1) { - fileno = i; - f = &Files[fileno]; - break; - } - } - if (f == NULL) { - return -1; - } - - char filename[256]; - char *p = filename; - - // ファイル名変換 - IODeviceStream ds(ram, fileaddr); - for (int i = 0; i < countof(filename) - 3; i++) { - uint8 c = ds.Read8(); - - if (c == 0) break; - if (c < 32 || c >= 127 || c == 0x5c) { - p += sprintf(p, "%02X", c); - } else { - *p++ = c; - } - } - *p = '\0'; - - putmsg(1, "OpenFile: %d %s", fileno, filename); - int fd = open(filename, mode, 0666); - if (fd == -1) { - warn("OpenFile.open"); - return -1; - } - - f->fd = fd; - f->filename = strdup(filename); - - return fileno; +// 仮想マシンの中から終了させる +// (MSXDOSDevice からも終了時に呼ぶ)。 +// XXX code は未対応。 +/*static*/ void +Human68kDevice::RequestExit(int code) +{ + // 電源を即切る + auto power = GetPowerDevice(); + power->PushPowerButton(); + power->SetSystemPowerOn(false); } -// fileno: Human68k fileno -int32 -Human68k::CloseFile(int32 fileno) -{ - Human68k::File *f; - - if (fileno <= 0 || fileno >= FilesCount) { - warnx("CloseFile: unopened fileno=%d", fileno); - return -1; - } - f = &Files[fileno]; - putmsg(1, "CloseFile: %d %s", fileno, f->filename); - if (f->fd > 2) { - // stdin/out/err は閉じない - close(f->fd); - } - f->fd = -1; - free(f->filename); - return 0; -} - -// fileno: Human68k fileno -// dataaddr: data addr (guest VA) -// size: data length -int32 -Human68k::WriteFile(int32 fileno, uint32 dataaddr, uint32 size) -{ - Human68k::File *f; - - if (fileno <= 0 || fileno >= FilesCount) { - warnx("WriteFile: unopened fileno=%d", fileno); - return -1; - } - - f = &Files[fileno]; - if (f->fd < 0) { - return -1; - } - - IODeviceStream ds(ram, dataaddr); - std::unique_ptr buf(new uint8[size]); - for (int i = 0; i < size; i++) { - buf[i] = ds.Read8(); - } - - int32 rv = write(f->fd, buf.get(), size); - return rv; -} - -// fileno: Human68k fileno -// dataaddr: data addr (guest VA) -void -Human68k::FputsFile(int32 fileno, uint32 dataaddr) +// F-Line 命令をこちらで処理する。 +bool +human68k_fline_callback(MPU680x0Device *, void *arg) { - Human68k::File *f; - - if (fileno <= 0 || fileno >= FilesCount) { - return; - } - - f = &Files[fileno]; - if (f->fd < 0) { - return; - } - - IODeviceStream ds(ram, dataaddr); - int size = 1024; - uint8 buf[size]; - bool eof = false; - - do { - int len = 0; - for (int i = 0; i < size; i++) { - buf[i] = ds.Read8(); - if (buf[i] == 0) { - eof = true; - break; - } - len++; - } - if (len > 0) { - write(f->fd, buf, len); - } - } while (!eof); + auto *human68k = reinterpret_cast(arg); + return human68k->FLineOp(); } - -// Human68k DOSCALL Host Emulation - -void -Human68k::IOCS(m68kcpu *cpu) +// F-Line 命令。 +bool +Human68kDevice::FLineOp() { - switch (RegD(0) & 0xff) { - case 0x7f: // _ONTIME - { - putmsg(1, "IOCS ONTIME"); - uint64 t = gScheduler->GetVirtTime(); - // nanosec to 10msec, in day - RegD(0) = (t / 10_msec) % 8640000; - // nanosec to day - RegD(1) = t / 86400_sec; - break; - } - case 0x82: // _B_BPEEK - { - uint32 data = m68030_read_8(cpu, RegA(1)); - RegD(0) = (RegD(0) & 0xffffff00) | data; - RegA(1)++; - break; - } - case 0xac: // _SYS_STAT (ROM1.3) - switch (RegD(1)) { - case 0: - // MPU 状態の取得 - RegD(0) = - ((250) << 16) // 25.0MHz - | ((gMPU680x0->HaveFPU() ? 1 : 0) << 15) // FPU - | ((0) << 14) // MMU - | ((3) << 0); // MPU Type - break; - case 1: - // キャッシュ状態の取得 - RegD(0) = 0; - break; - case 2: - // キャッシュを SRAM の設定値に設定 - RegD(0) = 0; - break; - case 3: - // キャッシュの消去 - RegD(0) = 0; - break; - case 4: - // キャッシュの設定 - RegD(0) = 0; - break; - default: - break; - } + switch (mpu680x0->GetIR()) { + case 0xfa00: // IOCS call emulation + IOCS(); break; - default: - printf("Unimplemented IOCS $%02x\n", RegD(0) & 0xff); - exit(1); - } -} - -// F-Line 命令をこちらで処理する -bool -human68k_fline_callback(m68kcpu *cpu, void *arg) -{ - auto *human68k = (Human68k *)arg; - return human68k->FLineOp(cpu); -} + case 0xff00: // _EXIT + putlog(1, "DOS _EXIT"); + // XXX CPU の実行を止める。方式自体を I/O 方式に見直したときには + // ちゃんとした方法を取る予定。 + mpu680x0->SetSR(mpu680x0->GetSR() | 0x2000); + mpu680x0->reg.pc = 0x1010; + RequestExit(0); + break; -// F-Line 命令 -bool -Human68k::FLineOp(m68kcpu *cpu) -{ - switch (RegIR) { - case 0xf600: // IOCS call emulation - IOCS(cpu); - break; - - case 0xff00: // EXIT - putmsg(1, "DOS EXIT"); - exit(0); - - case 0xff09: // PRINT - { - // STDOUT - uint32 dataptr = m68030_read_32(cpu, RegA(7)); - do { - int c = m68030_read_8(cpu, dataptr++); - if (c == 0) break; - printf("%c", c); - } while (1); - RegD(0) = 0; + case 0xff09: // _PRINT + { + uint32 dataptr = mainbus->HVRead4(RegA(7)); + putlog(2, "DOS _PRINT($%06x)", dataptr); + DOS_PRINT(dataptr); break; } - case 0xff1e: // FPUTS + case 0xff1e: // _FPUTS { - uint32 mesptr = m68030_read_32(cpu, RegA(7)); - uint16 fileno = m68030_read_16(cpu, RegA(7) + 4); + uint32 mesptr = mainbus->HVRead4(RegA(7)); + uint16 fileno = mainbus->HVRead2(RegA(7) + 4); + putlog(2, "DOS _FPUTS(fileno=%d, mesptr=$%06x)", fileno, mesptr); FputsFile(fileno, mesptr); break; } - case 0xff20: // SUPER + case 0xff20: // _SUPER { - uint32 data = m68030_read_32(cpu, RegA(7)); + uint32 data = mainbus->HVRead4(RegA(7)); + putlog(1, "DOS _SUPER($%x) to %s", data, + (data == 0) ? "SUPERVISOR" : "USER"); if (data == 0) { - m68030_set_sr(cpu, RegSR | 0x2000); - data = RegUSP; + mpu680x0->SetSR(mpu680x0->GetSR() | 0x2000); + data = mpu680x0->reg.usp; RegD(0) = RegA(7); RegA(7) = data; - putmsg(1, "SUPERVISOR MODE"); } else { - m68030_set_sr(cpu, RegSR | 0x2000); + mpu680x0->SetSR(mpu680x0->GetSR() | 0x2000); RegA(7) = data; - m68030_set_sr(cpu, RegSR & ~0x2000); - putmsg(1, "USER MODE"); + mpu680x0->SetSR(mpu680x0->GetSR() & ~0x2000); } break; } - case 0xff25: // INTVCS + case 0xff25: // _INTVCS { - uint16 intno = m68030_read_16(cpu, RegA(7)); - uint32 addr = m68030_read_32(cpu, RegA(7) + 2); + uint16 intno = mainbus->HVRead2(RegA(7)); + uint32 addr = mainbus->HVRead4(RegA(7) + 2); + putlog(1, "DOS _INTVCS(intno=$%02x, addr=$%06x)", intno, addr); uint32 vecaddr = (intno & 0xff) * 4; if (intno <= 0xff) { - RegD(0) = m68030_read_32(cpu, vecaddr); - m68030_write_32(cpu, vecaddr, addr); + RegD(0) = mainbus->HVRead4(vecaddr); + mainbus->HVWrite4(vecaddr, addr); } else { vecaddr += 0xd000; - RegD(0) = m68030_read_32(cpu, vecaddr); - m68030_write_32(cpu, vecaddr, addr); - } + RegD(0) = mainbus->HVRead4(vecaddr); + mainbus->HVWrite4(vecaddr, addr); + } break; } - case 0xff27: // GETTIM2 + case 0xff27: // _GETTIM2 { - // DUMMY - RegD(0) = 0; + struct tm tm; + time_t now = time(NULL); + localtime_r(&now, &tm); + RegD(0) = (tm.tm_hour << 16) + | (tm.tm_min << 8) + | tm.tm_sec; + putlog(1, "DOS _GETTIM2 -> $%08x", RegD(0)); break; } - case 0xff2a: // GETDATE + case 0xff2a: // _GETDATE { - // DUMMY - RegD(0) = 0; + struct tm tm; + time_t now = time(NULL); + localtime_r(&now, &tm); + RegD(0) = (tm.tm_wday << 16) + | ((tm.tm_year + 1900 - 1980) << 9) + | ((tm.tm_mon + 1) << 5) + | tm.tm_mday; + putlog(1, "DOS _GETDATE -> $%08x", RegD(0)); break; } - case 0xff30: // VERNUM + case 0xff30: // _VERNUM { RegD(0) = 0x36380302; // ver3.02 + putlog(1, "DOS _VERNUM -> $%08x", RegD(0)); break; } - case 0xff35: // INTVCG + case 0xff35: // _INTVCG { - uint16 intno = m68030_read_16(cpu, RegA(7)); - + uint16 intno = mainbus->HVRead2(RegA(7)); uint32 vecaddr = (intno & 0xff) * 4; if (intno <= 0xff) { - RegD(0) = m68030_read_32(cpu, vecaddr); + RegD(0) = mainbus->HVRead4(vecaddr); } else { vecaddr += 0xd000; - RegD(0) = m68030_read_32(cpu, vecaddr); + RegD(0) = mainbus->HVRead4(vecaddr); } + putlog(1, "DOS _INTVCG(intno=%d) -> $%08x", intno, RegD(0)); break; } - case 0xff3c: // CREATE + case 0xff3c: // _CREATE { - uint32 file = m68030_read_32(cpu, RegA(7)); - uint16 atr = m68030_read_32(cpu, RegA(7) + 4); + uint32 file = mainbus->HVRead4(RegA(7)); + uint16 atr = mainbus->HVRead2(RegA(7) + 4); + putlog(1, "DOS _CREATE(file=$%x, atr=$%x)", file, atr); int32 fileno = OpenFile(file, atr, O_CREAT | O_TRUNC | O_RDWR | O_SYNC); @@ -660,74 +544,127 @@ Human68k::FLineOp(m68kcpu *cpu) break; } - case 0xff3e: // CLOSE + case 0xff3d: // _OPEN { - uint16 fileno = m68030_read_16(cpu, RegA(7)); + uint32 file = mainbus->HVRead4(RegA(7)); + uint16 mode = mainbus->HVRead2(RegA(7) + 4); + int unix_mode = 0; + bool ok = true; + putlog(1, "DOS _OPEN(file=$%x, mode=$%x)", file, mode); + // mode の辞書モード、シェアリングモードは無視している。 + switch (mode & 0x03) { // read/write + case 2: + unix_mode |= O_RDWR; + break; + case 1: + unix_mode |= O_WRONLY; + break; + case 0: + unix_mode |= O_RDONLY; + break; + default: + RegD(0) = -12; // アクセスモード異常 + ok = false; + break; + } + + if (ok) { + int32 fileno = OpenFile(file, 0, unix_mode | O_SYNC); + RegD(0) = fileno; + } + break; + } + + case 0xff3e: // _CLOSE + { + uint16 fileno = mainbus->HVRead2(RegA(7)); + + putlog(1, "DOS _CLOSE(fileno=%d)", fileno); CloseFile(fileno); RegD(0) = 0; break; } - case 0xff40: // WRITE + case 0xff40: // _WRITE { -// putmsg(1, "DOS _WRITE at %08x", RegPPC); - uint16 fileno = m68030_read_16(cpu, RegA(7)); - uint32 dataptr = m68030_read_32(cpu, RegA(7) + 2); - uint32 size = m68030_read_32(cpu, RegA(7) + 6); -// putmsg(1, "WRITE(%d, 0x%08x, 0x%08x)\n", fileno, dataptr, size); + uint16 fileno = mainbus->HVRead2(RegA(7)); + uint32 dataptr = mainbus->HVRead4(RegA(7) + 2); + uint32 size = mainbus->HVRead4(RegA(7) + 6); + putlog(2, "DOS _WRITE(fileno=%u, dataptr=$%06x, size=$%08x)", + fileno, dataptr, size); RegD(0) = WriteFile(fileno, dataptr, size); break; } - case 0xff44: // IOCTRL + case 0xff44: // _IOCTRL { - uint32 mode = m68030_read_16(cpu, RegA(7)); + uint32 mode = mainbus->HVRead2(RegA(7)); uint32 fileno; switch (mode) { - case 0: - fileno = m68030_read_16(cpu, RegA(7) + 2); - putmsg(1, "DOS IOCTRL(mode=%d, fileno=%d)", mode, fileno); + case 0: // IOCTRLGT 装置情報取得 + fileno = mainbus->HVRead2(RegA(7) + 2); if (fileno == 0) { // STDIN RegD(0) = 0x8081; // 100u'uuuu'100u'0001; - break; } else if (fileno == 1) { // STDOUT RegD(0) = 0x8082; // 100u'uuuu'100u'0010; - break; } else if (fileno == 2) { // STDERR RegD(0) = 0x8081; // 100u'uuuu'100u'0001; - break; } else { RegD(0) = -1; } + putlog(1, "DOS _IOCTRL(mode=0(IOCTRLGT), fileno=%d) -> $%x", + fileno, RegD(0)); + break; + + case 7: // IOCTRLOS 出力ステータス取得 + fileno = mainbus->HVRead2(RegA(7) + 2); + if (fileno == 0) { + RegD(0) = 0; + } else if (fileno == 1 || fileno == 2) { + RegD(0) = -1; + } else { + int filemode = GetFileMode(fileno); + if (filemode < 0) { + RegD(0) = filemode; + } else if (filemode == O_WRONLY || filemode == O_RDWR) { + RegD(0) = -1; + } else { + RegD(0) = 0; + } + } + putlog(1, "DOS _IOCTRL(mode=7(IOCTRLOS), fileno=%d) -> %d", + fileno, RegD(0)); break; + default: - errx(EXIT_FAILURE, "DOS IOCTRL(mode=%d) not impl.", mode); + errx(EXIT_FAILURE, "DOS _IOCTRL(mode=%d) not implemented", mode); } break; } - case 0xff4a: // SETBLOCK + case 0xff4a: // _SETBLOCK { - uint32 memptr = m68030_read_32(cpu, RegA(7)); - uint32 len = m68030_read_32(cpu, RegA(7) + 4); - putmsg(1, "DOS SETBLOCK(memptr=$%x len=$%x)", memptr, len); + uint32 memptr = mainbus->HVRead4(RegA(7)); + uint32 len = mainbus->HVRead4(RegA(7) + 4); + putlog(1, "DOS _SETBLOCK(memptr=$%x, len=$%x)", memptr, len); // なにもせずに、できたという - RegD(0) = m68030_read_32(cpu, RegA(7) + 4); + RegD(0) = mainbus->HVRead4(RegA(7) + 4); break; } - case 0xff4c: // EXIT2 + case 0xff4c: // _EXIT2 { - uint32 data = m68030_read_16(cpu, RegA(7)); - putmsg(1, "DOS EXIT2(%d)", data); - exit(data); + uint32 data = mainbus->HVRead2(RegA(7)); + putlog(1, "DOS _EXIT2(%d)", data); + RequestExit(data); + break; } - case 0xffac: // GETFCB + case 0xffac: // _GETFCB { - uint32 fileno = m68030_read_16(cpu, RegA(7)); - putmsg(1, "DOS GETFCB(fileno=%d)", fileno); + uint32 fileno = mainbus->HVRead2(RegA(7)); + putlog(1, "DOS _GETFCB(fileno=%d)", fileno); if (fileno <= 4) { RegD(0) = 0x8000 + fileno * 0x60; } else { @@ -736,27 +673,27 @@ Human68k::FLineOp(m68kcpu *cpu) break; } - case 0xfff7: // BUS_ERR + case 0xfff7: // _BUS_ERR { - uint32 p1 = m68030_read_32(cpu, RegA(7)); - uint32 p2 = m68030_read_32(cpu, RegA(7) + 4); - uint16 size = m68030_read_16(cpu, RegA(7) + 8); - putmsg(1, "DOS BUS_ERR(size=%d p1=$%x p2=$%x)", size, p1, p2); + uint32 p1 = mainbus->HVRead4(RegA(7)); + uint32 p2 = mainbus->HVRead4(RegA(7) + 4); + uint16 size = mainbus->HVRead2(RegA(7) + 8); + putlog(1, "DOS _BUS_ERR(size=%d p1=$%x p2=$%x)", size, p1, p2); RegD(0) = -1; uint32 data; try { if (size == 1) { - data = m68030_read_8(cpu, p1); + data = mainbus->HVRead1(p1); } else if (size == 2 && ((p1 & 1) == 0)) { - data = m68030_read_16(cpu, p1); + data = mainbus->HVRead2(p1); } else if (size == 4 && ((p1 & 1) == 0)) { - data = m68030_read_32(cpu, p1); + data = mainbus->HVRead4(p1); } else { break; } } catch (int cause) { - if (cause == M68K_EXCEP_BUSERR) { + if (cause == M68K::EXCEP_BUSERR) { RegD(0) = 2; break; } else { @@ -766,16 +703,16 @@ Human68k::FLineOp(m68kcpu *cpu) try { if (size == 1) { - m68030_write_8(cpu, p2, data); + mainbus->HVWrite1(p2, data); } else if (size == 2 && ((p2 & 1) == 0)) { - m68030_write_16(cpu, p2, data); + mainbus->HVWrite2(p2, data); } else if (size == 4 && ((p2 & 1) == 0)) { - m68030_write_32(cpu, p2, data); + mainbus->HVWrite4(p2, data); } else { break; } } catch (int cause) { - if (cause == M68K_EXCEP_BUSERR) { + if (cause == M68K::EXCEP_BUSERR) { RegD(0) = 1; break; } else { @@ -786,9 +723,254 @@ Human68k::FLineOp(m68kcpu *cpu) break; } + case 0xffff: // _CHANGE_PR + { + putlog(1, "DOS _CHANGE_PR"); + // なにもしない。 + break; + } + default: - printf("Unimplemented DOSCALL $%02x at $%08X\n", RegIR, RegPPC); - exit(1); + printf("DOSCALL $%02x at $%08X (NOT IMPLEMENTED)\n", + mpu680x0->GetIR(), mpu680x0->GetPPC()); + RequestExit(1); + break; } return true; } + +void +Human68kDevice::IOCS() +{ + // XXX 本当は HVRead*() の IsBusErr() を調べないといけない。 + + // 呼び出し元っぽい PC を表示したい。 + uint32 pc = mainbus->HVRead4(RegA(7) + 2) - 2; + if ((mainbus->HVRead2(pc - 2) & 0xff00) == 0x7000) { + // moveq #imm, d0 + pc -= 2; + } + + switch (RegD(0) & 0xff) { + case 0x21: // _B_PRINT + { + uint32 dataptr = RegA(1); + putlog(2, "PC=$%06x IOCS _B_PRINT($%06x)", pc, dataptr); + DOS_PRINT(dataptr); + // 戻り値 d0 は表示後の位置だが無視。 + break; + } + case 0x7f: // _ONTIME + { + putlog(1, "PC=$%06x IOCS _ONTIME", pc); + uint64 t = scheduler->GetVirtTime(); + // nanosec to 10msec, in day + RegD(0) = (t / 10_msec) % 8640000; + // nanosec to day + RegD(1) = t / 86400_sec; + break; + } + case 0x82: // _B_BPEEK + { + uint32 data = mainbus->HVRead1(RegA(1)); + putlog(1, "PC=$%06x IOCS _B_BPEEK($%06x) -> $%02x", pc, RegA(1), data); + RegD(0) = (RegD(0) & 0xffffff00) | data; + RegA(1)++; + break; + } + case 0xac: // _SYS_STAT (ROM1.3) + switch (RegD(1)) { + case 0: + // MPU 状態の取得 + RegD(0) = + ((250) << 16) // 25.0MHz + | ((mpu680x0->HasFPU() ? 1 : 0) << 15) // FPU + | ((0) << 14) // MMU + | ((3) << 0); // MPU Type + break; + case 1: + // キャッシュ状態の取得 + RegD(0) = 0; + break; + case 2: + // キャッシュを SRAM の設定値に設定 + RegD(0) = 0; + break; + case 3: + // キャッシュの消去 + RegD(0) = 0; + break; + case 4: + // キャッシュの設定 + RegD(0) = 0; + break; + default: + break; + } + putlog(1, "PC=$%06x IOCS _SYS_STAT(%x) -> $%x", pc, RegD(1), RegD(0)); + break; + + default: + printf("IOCS $%02x at $%06x (NOT IMPLEMENTED)\n", + RegD(0) & 0xff, pc); + RequestExit(1); + break; + } +} + +void +Human68kDevice::DOS_PRINT(uint32 dataptr) +{ + int c; + while ((c = mainbus->HVRead1(dataptr++)) != 0) { + putchar(c); + } + RegD(0) = 0; +} + +// ファイルをオープンする。 +// fileaddr: Human68k ファイル名のゲストアドレス +// atr: Human68k の属性 +// mode: unix open mode +// 戻り値は Human68k fileno。 +int32 +Human68kDevice::OpenFile(uint32 fileaddr, uint16 atr, int mode) +{ + int32 fileno {}; + Human68kDevice::File *f = NULL; + + // 空いてるエントリを検索して Human fileno を取得。 + for (int i = 0; i < FilesCount; i++) { + if (Files[i].fd == -1) { + fileno = i; + f = &Files[fileno]; + break; + } + } + if (f == NULL) { + return -1; + } + + // ファイル名変換。 + std::string filename; + for (uint8 c; (c = mainbus->HVRead1(fileaddr++)) != '\0'; ) { + if (c < 32 || c >= 127 || c == 0x5c) { + char hex[16]; + snprintf(hex, sizeof(hex), "%02X", c); + filename += std::string(hex); + } else { + filename += c; + } + } + + int fd = open(filename.c_str(), mode, 0666); + if (fd == -1) { + putmsg(1, "OpenFile: %s: %s", filename.c_str(), strerror(errno)); + return -1; + } + + f->fd = fd; + f->filename = filename; + + putmsg(1, "OpenFile: name=\"%s\" mode=$%x -> fileno=%u", + filename.c_str(), mode, fileno); + return fileno; +} + +// ファイルをクローズする。 +// fileno: Human68k fileno +int32 +Human68kDevice::CloseFile(int32 fileno) +{ + Human68kDevice::File *f; + + if (fileno <= 0 || fileno >= FilesCount) { + putmsg(1, "CloseFile: fileno=%d is not opened", fileno); + return -1; + } + f = &Files[fileno]; + putmsg(1, "CloseFile: %d \"%s\"", fileno, f->filename.c_str()); + if (f->fd > 2) { + close(f->fd); + } + f->fd = -1; + f->filename.clear(); + return 0; +} + +// ファイルに書き出す。 +// fileno: Human68k fileno +// dataaddr: データのゲストアドレス +// size: データ長 +int32 +Human68kDevice::WriteFile(int32 fileno, uint32 dataaddr, uint32 size) +{ + Human68kDevice::File *f; + + if (fileno <= 0 || fileno >= FilesCount) { + putmsg(1, "WriteFile: fileno=%d is not opened", fileno); + return -1; + } + + f = &Files[fileno]; + if (f->fd < 0) { + return -1; + } + + std::vector buf(size); + mainram->ReadMem(dataaddr, buf.data(), size); + + int32 rv = write(f->fd, buf.data(), size); + return rv; +} + +// 文字列をファイルに書き出す。 +// fileno: Human68k fileno +// dataaddr: data addr (guest VA) +void +Human68kDevice::FputsFile(int32 fileno, uint32 dataaddr) +{ + Human68kDevice::File *f; + + if (fileno <= 0 || fileno >= FilesCount) { + putmsg(1, "FputsFile: fileno=%d is not opened", fileno); + return; + } + + f = &Files[fileno]; + if (f->fd < 0) { + return; + } + + std::vector buf; + for (uint8 c; (c = mainbus->HVRead1(dataaddr++)) != '\0'; ) { + buf.push_back(c); + } + if (buf.empty() == false) { + write(f->fd, buf.data(), buf.size()); + } +} + +// ファイルモードを取得する。 +// fileno: Human68k fileno +// 戻り値: <0 Humak68k error code +// : >=0 unix file mode +int +Human68kDevice::GetFileMode(int32 fileno) +{ + Human68kDevice::File *f; + + if (fileno <= 0 || fileno >= FilesCount) { + return -2 /* FILE_NOT_FOUND */; + } + + f = &Files[fileno]; + if (f->fd < 0) { + return -2 /* FILE_NOT_FOUND */; + } + + int fl = fcntl(f->fd, F_GETFL); + int accmode = fl & O_ACCMODE; + + return accmode; +}