--- nono/vm/human68k.cpp 2026/04/29 17:04:28 1.1.1.1 +++ nono/vm/human68k.cpp 2026/04/29 17:05:25 1.1.1.12 @@ -1,22 +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 "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 +// // メモリマップ // @@ -30,63 +20,84 @@ // 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 -// コンストラクタ -Human68k::Human68k(const char *file, const char *arg) -{ - logname = "human68k"; - devname = "Human68k"; +static bool human68k_fline_callback(MPU680x0Device *, void *arg); - assert(file); - human68k_file = file; - human68k_arg = 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; - // ショートカット用 - ram = gRAM; + if (inherited::Init() == false) { + return false; + } + + assert(gMainApp.exec_file); + human68k_file = gMainApp.exec_file; + human68k_arg = gMainApp.exec_arg; + + mainbus = GetMainbusDevice(); + ram = GetMainRAMDevice(); + mpu680x0 = GetMPU680x0Device(mpu); + // Fライン命令をこちらで処理する - gMPU->SetFLineCallback(human68k_fline_callback, this); + mpu680x0->SetFLineCallback(human68k_fline_callback, this); // ワークを用意 (どこでやるか) // $CBC.B MPU 種別 (3:68030) - gRAM->Write8(0x0cbc, 3); + ram->Write8(0x0cbc, 3); // $CBD.B FPU 有無 (0xff:あり) - if (gMPU->HaveFPU()) { - gRAM->Write8(0x0cbd, 0xff); + if (mpu680x0->HaveFPU()) { + ram->Write8(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("open %s", human68k_file); + warn("Human68k executable \"%s\" open failed", human68k_file); return false; } struct stat st; if (fstat(file_fd, &st) != 0) { - warn("fstat %s", human68k_file); + warn("Human68k executable \"%s\" fstat failed", human68k_file); return false; } uint32 file_size; @@ -96,7 +107,7 @@ Human68k::Init() // mmap file = (uint8 *)mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, file_fd, 0); if (file == MAP_FAILED) { - warn("mmap"); + warn("Human68k executable \"%s\" mmap failed", human68k_file); return false; } @@ -108,24 +119,31 @@ Human68k::Init() } else if (isext(human68k_file, ".z")) { r = LoadZ(file, file_size); } 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("Invalid file extension"); - return false; + // ELF .o のロードを試行する + r = LoadELF(file, file_size); + if (!r) { + 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); + return false; + } } } if (!r) { - warnx("Invalid file format"); + warnx("Human68k executable \"%s\" invalid file format", human68k_file); + munmap(file, file_size); return false; } munmap(file, file_size); - close(file_fd); uint32 last_addr = load_addr + load_size + bss_size; + int ram_size = ram->GetSize(); IODeviceStream ds(ram); boot_addr = 0x200; @@ -139,6 +157,9 @@ Human68k::Init() ds.Write16(0x1000, 0xf600); // .dw $f300 ds.Write16(0x1002, 0x4e73); // rte + // STOP による停止。終了時に CPU を止めておくため。 + ds.Write32(0x1010, 0x4e722700); // stop #$2700 + // コマンドライン文字列を書き込む。LASCII 形式 ds.SetAddr(0xc001); for (int i = 0; i < 255; i++) { @@ -149,6 +170,7 @@ Human68k::Init() break; } } + // XXX: 255文字以上のときどうなるか調べること // PSP (process entry) を書き込む。 // instructiontest.x はコマンドラインを指定するだけで動くようだ @@ -190,7 +212,7 @@ Human68k::Init() } bool -Human68k::LoadR(uint8 *file, uint size) +Human68kDevice::LoadR(uint8 *file, uint size) { // リロケータブルなのでどこでもいい load_addr = default_load_addr; @@ -205,7 +227,7 @@ Human68k::LoadR(uint8 *file, uint size) } bool -Human68k::LoadX(uint8 *file, uint size) +Human68kDevice::LoadX(uint8 *file, uint size) { XFileHeader *hdr = (XFileHeader *)file; uint hdr_size = sizeof(*hdr); @@ -267,9 +289,8 @@ Human68k::LoadX(uint8 *file, uint size) return true; } - bool -Human68k::LoadZ(uint8 *file, uint size) +Human68kDevice::LoadZ(uint8 *file, uint size) { ZFileHeader *hdr = (ZFileHeader *)file; uint32 hdr_size = sizeof(*hdr); @@ -295,10 +316,28 @@ Human68k::LoadZ(uint8 *file, uint size) return true; } +// なぜか m68k ELF .o が読める bool -Human68k::LoadMem(uint32 addr, uint8 *src, uint size) +Human68kDevice::LoadELF(uint8 *file, uint size) { - if (addr + size > ram_size) { + LoadInfo info(human68k_file, file, size); + if (ram->LoadExec(&info) == false) { + return false; + } + + // XXX load_size は esym のような気がする。 + // XXX text_size も今はやろうと思えば返せる構造になったが未対応。 + load_addr = info.entry; + load_size = size; + exec_addr = info.entry; + text_size = size; + return true; +} + +bool +Human68kDevice::LoadMem(uint32 addr, uint8 *src, uint size) +{ + if (addr + size >= ram->GetSize()) { errx(EXIT_FAILURE, "file too large"); } @@ -311,7 +350,7 @@ Human68k::LoadMem(uint32 addr, uint8 *sr // 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); @@ -326,17 +365,29 @@ Human68k::isext(const char *file, const } +// 仮想マシンの中から終了させる +// (MSXDOSDevice からも終了時に呼ぶ) +// XXX code は未対応。 +/*static*/ void +Human68kDevice::RequestExit(int code) +{ + // 電源を即切る + auto power = GetPowerDevice(); + power->PushPowerButton(); + power->SetSystemPowerOn(false); +} + // fileaddr: Human68k ファイル名のゲストVA // atr: Human68k atr // mode: unix open mode // return: Human68k fileno int32 -Human68k::OpenFile(uint32 fileaddr, uint16 atr, int mode) +Human68kDevice::OpenFile(uint32 fileaddr, uint16 atr, int mode) { // XXX: unix host only - int32 fileno; - Human68k::File *f = NULL; + int32 fileno {}; + Human68kDevice::File *f = NULL; // 開いているエントリを検索して Human fileno を取得 for (int i = 0; i < FilesCount; i++) { @@ -358,7 +409,9 @@ Human68k::OpenFile(uint32 fileaddr, uint for (int i = 0; i < countof(filename) - 3; i++) { uint8 c = ds.Read8(); - if (c == 0) break; + if (c == 0) { + break; + } if (c < 32 || c >= 127 || c == 0x5c) { p += sprintf(p, "%02X", c); } else { @@ -375,29 +428,29 @@ Human68k::OpenFile(uint32 fileaddr, uint } f->fd = fd; - f->filename = strdup(filename); + f->filename = filename; return fileno; } // fileno: Human68k fileno int32 -Human68k::CloseFile(int32 fileno) +Human68kDevice::CloseFile(int32 fileno) { - Human68k::File *f; + Human68kDevice::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); + putmsg(1, "CloseFile: %d %s", fileno, f->filename.c_str()); if (f->fd > 2) { // stdin/out/err は閉じない close(f->fd); } f->fd = -1; - free(f->filename); + f->filename.clear(); return 0; } @@ -405,9 +458,9 @@ Human68k::CloseFile(int32 fileno) // dataaddr: data addr (guest VA) // size: data length int32 -Human68k::WriteFile(int32 fileno, uint32 dataaddr, uint32 size) +Human68kDevice::WriteFile(int32 fileno, uint32 dataaddr, uint32 size) { - Human68k::File *f; + Human68kDevice::File *f; if (fileno <= 0 || fileno >= FilesCount) { warnx("WriteFile: unopened fileno=%d", fileno); @@ -420,23 +473,21 @@ Human68k::WriteFile(int32 fileno, uint32 } IODeviceStream ds(ram, dataaddr); - uint8 *buf = new uint8[size]; + std::unique_ptr buf(new uint8[size]); for (int i = 0; i < size; i++) { buf[i] = ds.Read8(); } - int32 rv = write(f->fd, buf, size); - - delete[] buf; + 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) +Human68kDevice::FputsFile(int32 fileno, uint32 dataaddr) { - Human68k::File *f; + Human68kDevice::File *f; if (fileno <= 0 || fileno >= FilesCount) { return; @@ -449,7 +500,7 @@ Human68k::FputsFile(int32 fileno, uint32 IODeviceStream ds(ram, dataaddr); int size = 1024; - uint8 buf[size]; + std::vector buf(size); bool eof = false; do { @@ -463,7 +514,7 @@ Human68k::FputsFile(int32 fileno, uint32 len++; } if (len > 0) { - write(f->fd, buf, len); + write(f->fd, &buf[0], len); } } while (!eof); } @@ -472,13 +523,23 @@ Human68k::FputsFile(int32 fileno, uint32 // Human68k DOSCALL Host Emulation void -Human68k::IOCS(m68kcpu *cpu) +Human68kDevice::IOCS() { + // XXX mpu680x0->{read,write}_*() はアクセス中にバスエラーが起きると + // C++ 例外をスローするので本当は catch しなければいけない。 + switch (RegD(0) & 0xff) { + case 0x21: // _B_PRINT + { + uint32 dataptr = RegA(1); + DOS_PRINT(dataptr); + // 戻り値 d0 は表示後の位置だが無視 + break; + } case 0x7f: // _ONTIME { putmsg(1, "IOCS ONTIME"); - uint64 t = gScheduler->GetVirtTime(); + uint64 t = scheduler->GetVirtTime(); // nanosec to 10msec, in day RegD(0) = (t / 10_msec) % 8640000; // nanosec to day @@ -487,7 +548,7 @@ Human68k::IOCS(m68kcpu *cpu) } case 0x82: // _B_BPEEK { - uint32 data = m68030_read_8(cpu, RegA(1)); + uint32 data = mainbus->HVRead8(RegA(1)); RegD(0) = (RegD(0) & 0xffffff00) | data; RegA(1)++; break; @@ -498,7 +559,7 @@ Human68k::IOCS(m68kcpu *cpu) // MPU 状態の取得 RegD(0) = ((250) << 16) // 25.0MHz - | ((gMPU->HaveFPU() ? 1 : 0) << 15) // FPU + | ((mpu680x0->HaveFPU() ? 1 : 0) << 15) // FPU | ((0) << 14) // MMU | ((3) << 0); // MPU Type break; @@ -524,67 +585,67 @@ Human68k::IOCS(m68kcpu *cpu) break; default: - printf("Unimplemented IOCS $%02x\n", RegD(0) & 0xff); - exit(1); + printf("IOCS $%02x (NOT IMPLEMENTED)\n", RegD(0) & 0xff); + RequestExit(1); + break; } } // F-Line 命令をこちらで処理する bool -human68k_fline_callback(m68kcpu *cpu, void *arg) +human68k_fline_callback(MPU680x0Device *, void *arg) { - auto *human68k = (Human68k *)arg; - return human68k->FLineOp(cpu); + auto *human68k = (Human68kDevice *)arg; + return human68k->FLineOp(); } // F-Line 命令 bool -Human68k::FLineOp(m68kcpu *cpu) +Human68kDevice::FLineOp() { - switch (RegIR) { + switch (mpu680x0->GetIR()) { case 0xf600: // IOCS call emulation - IOCS(cpu); + IOCS(); break; case 0xff00: // EXIT putmsg(1, "DOS EXIT"); - exit(0); + // XXX CPU の実行を止める。方式自体を I/O 方式に見直したときには + // ちゃんとした方法を取る予定。 + mpu680x0->SetSR(mpu680x0->GetSR() | 0x2000); + mpu680x0->reg.pc = 0x1010; + RequestExit(0); + break; 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; + uint32 dataptr = mainbus->HVRead32(RegA(7)); + DOS_PRINT(dataptr); break; } case 0xff1e: // FPUTS { - uint32 mesptr = m68030_read_32(cpu, RegA(7)); - uint16 fileno = m68030_read_16(cpu, RegA(7) + 4); + uint32 mesptr = mainbus->HVRead32(RegA(7)); + uint16 fileno = mainbus->HVRead16(RegA(7) + 4); FputsFile(fileno, mesptr); break; } case 0xff20: // SUPER { - uint32 data = m68030_read_32(cpu, RegA(7)); + uint32 data = mainbus->HVRead32(RegA(7)); 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); + mpu680x0->SetSR(mpu680x0->GetSR() & ~0x2000); putmsg(1, "USER MODE"); } break; @@ -592,19 +653,19 @@ Human68k::FLineOp(m68kcpu *cpu) case 0xff25: // INTVCS { - uint16 intno = m68030_read_16(cpu, RegA(7)); - uint32 addr = m68030_read_32(cpu, RegA(7) + 2); + uint16 intno = mainbus->HVRead16(RegA(7)); + uint32 addr = mainbus->HVRead32(RegA(7) + 2); uint32 vecaddr = (intno & 0xff) * 4; if (intno <= 0xff) { - RegD(0) = m68030_read_32(cpu, vecaddr); - m68030_write_32(cpu, vecaddr, addr); + RegD(0) = mainbus->HVRead32(vecaddr); + mainbus->HVWrite32(vecaddr, addr); } else { vecaddr += 0xd000; - RegD(0) = m68030_read_32(cpu, vecaddr); - m68030_write_32(cpu, vecaddr, addr); - } + RegD(0) = mainbus->HVRead32(vecaddr); + mainbus->HVWrite32(vecaddr, addr); + } break; } @@ -630,23 +691,23 @@ Human68k::FLineOp(m68kcpu *cpu) case 0xff35: // INTVCG { - uint16 intno = m68030_read_16(cpu, RegA(7)); + uint16 intno = mainbus->HVRead16(RegA(7)); uint32 vecaddr = (intno & 0xff) * 4; if (intno <= 0xff) { - RegD(0) = m68030_read_32(cpu, vecaddr); + RegD(0) = mainbus->HVRead32(vecaddr); } else { vecaddr += 0xd000; - RegD(0) = m68030_read_32(cpu, vecaddr); + RegD(0) = mainbus->HVRead32(vecaddr); } break; } case 0xff3c: // CREATE { - uint32 file = m68030_read_32(cpu, RegA(7)); - uint16 atr = m68030_read_32(cpu, RegA(7) + 4); + uint32 file = mainbus->HVRead32(RegA(7)); + uint16 atr = mainbus->HVRead32(RegA(7) + 4); int32 fileno = OpenFile(file, atr, O_CREAT | O_TRUNC | O_RDWR | O_SYNC); @@ -656,7 +717,7 @@ Human68k::FLineOp(m68kcpu *cpu) case 0xff3e: // CLOSE { - uint16 fileno = m68030_read_16(cpu, RegA(7)); + uint16 fileno = mainbus->HVRead16(RegA(7)); CloseFile(fileno); RegD(0) = 0; @@ -665,10 +726,10 @@ Human68k::FLineOp(m68kcpu *cpu) 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, "DOS _WRITE at %08x", mpu680x0->GetPPC()); + uint16 fileno = mainbus->HVRead16(RegA(7)); + uint32 dataptr = mainbus->HVRead32(RegA(7) + 2); + uint32 size = mainbus->HVRead32(RegA(7) + 6); // putmsg(1, "WRITE(%d, 0x%08x, 0x%08x)\n", fileno, dataptr, size); RegD(0) = WriteFile(fileno, dataptr, size); break; @@ -676,11 +737,11 @@ Human68k::FLineOp(m68kcpu *cpu) case 0xff44: // IOCTRL { - uint32 mode = m68030_read_16(cpu, RegA(7)); + uint32 mode = mainbus->HVRead16(RegA(7)); uint32 fileno; switch (mode) { case 0: - fileno = m68030_read_16(cpu, RegA(7) + 2); + fileno = mainbus->HVRead16(RegA(7) + 2); putmsg(1, "DOS IOCTRL(mode=%d, fileno=%d)", mode, fileno); if (fileno == 0) { // STDIN RegD(0) = 0x8081; // 100u'uuuu'100u'0001; @@ -703,24 +764,25 @@ Human68k::FLineOp(m68kcpu *cpu) case 0xff4a: // SETBLOCK { - uint32 memptr = m68030_read_32(cpu, RegA(7)); - uint32 len = m68030_read_32(cpu, RegA(7) + 4); + uint32 memptr = mainbus->HVRead32(RegA(7)); + uint32 len = mainbus->HVRead32(RegA(7) + 4); putmsg(1, "DOS SETBLOCK(memptr=$%x len=$%x)", memptr, len); // なにもせずに、できたという - RegD(0) = m68030_read_32(cpu, RegA(7) + 4); + RegD(0) = mainbus->HVRead32(RegA(7) + 4); break; } case 0xff4c: // EXIT2 { - uint32 data = m68030_read_16(cpu, RegA(7)); + uint32 data = mainbus->HVRead16(RegA(7)); putmsg(1, "DOS EXIT2(%d)", data); - exit(data); + RequestExit(data); + break; } case 0xffac: // GETFCB { - uint32 fileno = m68030_read_16(cpu, RegA(7)); + uint32 fileno = mainbus->HVRead16(RegA(7)); putmsg(1, "DOS GETFCB(fileno=%d)", fileno); if (fileno <= 4) { RegD(0) = 0x8000 + fileno * 0x60; @@ -732,25 +794,25 @@ Human68k::FLineOp(m68kcpu *cpu) 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); + uint32 p1 = mainbus->HVRead32(RegA(7)); + uint32 p2 = mainbus->HVRead32(RegA(7) + 4); + uint16 size = mainbus->HVRead16(RegA(7) + 8); putmsg(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->HVRead8(p1); } else if (size == 2 && ((p1 & 1) == 0)) { - data = m68030_read_16(cpu, p1); + data = mainbus->HVRead16(p1); } else if (size == 4 && ((p1 & 1) == 0)) { - data = m68030_read_32(cpu, p1); + data = mainbus->HVRead32(p1); } else { break; } } catch (int cause) { - if (cause == M68K_EXCEP_BUSERR) { + if (cause == M68K::EXCEP_BUSERR) { RegD(0) = 2; break; } else { @@ -760,16 +822,16 @@ Human68k::FLineOp(m68kcpu *cpu) try { if (size == 1) { - m68030_write_8(cpu, p2, data); + mainbus->HVWrite8(p2, data); } else if (size == 2 && ((p2 & 1) == 0)) { - m68030_write_16(cpu, p2, data); + mainbus->HVWrite16(p2, data); } else if (size == 4 && ((p2 & 1) == 0)) { - m68030_write_32(cpu, p2, data); + mainbus->HVWrite32(p2, data); } else { break; } } catch (int cause) { - if (cause == M68K_EXCEP_BUSERR) { + if (cause == M68K::EXCEP_BUSERR) { RegD(0) = 1; break; } else { @@ -781,8 +843,20 @@ Human68k::FLineOp(m68kcpu *cpu) } 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::DOS_PRINT(uint32 dataptr) +{ + int c; + while ((c = mainbus->HVRead8(dataptr++)) != 0) { + printf("%c", c); + } + RegD(0) = 0; +}