--- nono/vm/romemu_luna88k.cpp 2026/04/29 17:05:18 1.1.1.13 +++ nono/vm/romemu_luna88k.cpp 2026/04/29 17:05:25 1.1.1.14 @@ -36,6 +36,7 @@ #include "mk48t02.h" #include "mpu88xx0.h" #include "pio.h" +#include "sysclk.h" static bool luna88k_fline_callback(MPU88xx0Device *cpu, void *arg); @@ -58,13 +59,22 @@ enum class OPr { #define r8 OPr::r8 #define r9 OPr::r9 #define r10 OPr::r10 +#define r11 OPr::r11 enum OPm5 { eq0 = 0x02, ge0 = 0x03, + lt0 = 0x0c, ne0 = 0x0d, }; enum OPcr { psr = 0x01, + snip = 0x05, + sfip = 0x06, +}; +enum CMPbits { + CMP_EQ = 2, + CMP_NE = 3, + CMP_LT = 6, }; #define USC __unused static constexpr @@ -83,15 +93,30 @@ USC uint32 OP_addu(OPr rd, OPr rs1, uint USC uint32 OP_addu(OPr rd, OPr rs1, OPr rs2) { return OP3(0xf4006000, (uint32)rd, (uint32)rs1, (uint32)rs2); } +USC uint32 OP_and(OPr rd, OPr rs1, OPr rs2) { + return OP3(0xf4004000, (uint32)rd, (uint32)rs1, (uint32)rs2); +} +USC uint32 OP_bb1(uint32 b, OPr rs1, uint32 d16) { + return OP3(0xd8000000, b, (uint32)rs1, d16 & 0xffff); +} USC uint32 OP_bcnd(OPm5 m5, OPr rs1, uint32 d16) { return OP3(0xe8000000, (uint32)m5, (uint32)rs1, d16 & 0xffff); } +USC uint32 OP_br(uint32 d26) { + return (0xc0000000 | (d26 & 0x03ffffff)); +} USC uint32 OP_bsr(uint32 d26) { return (0xc8000000 | (d26 & 0x03ffffff)); } USC uint32 OP_clr(OPr rd, OPr rs1, uint32 w, uint32 o) { return OP4(0xf0008000, (uint32)rd, (uint32)rs1, w, o); } +USC uint32 OP_cmp(OPr rd, OPr rs1, OPr rs2) { + return OP3(0xf4007c00, (uint32)rd, (uint32)rs1, (uint32)rs2); +} +USC uint32 OP_extu(OPr rd, OPr rs1, uint32 w, uint32 o) { + return OP4(0xf0009800, (uint32)rd, (uint32)rs1, w, o); +} USC uint32 OP_jmp(OPr rs2) { return OP3(0xf400c000, 0, 0, (uint32)rs2); } @@ -107,21 +132,31 @@ USC uint32 __unused OP_or(OPr rd, OPr rs USC uint32 OP_or(OPr rd, OPr rs1, uint32 imm) { return OP3(0x58000000, (uint32)rd, (uint32)rs1, imm); } +USC uint32 OP_or_c(OPr rd, OPr rs1, OPr rs2) { + return OP3(0xf4005c00, (uint32)rd, (uint32)rs1, (uint32)rs2); +} USC uint32 OP_or_u(OPr rd, OPr rs1, uint32 imm) { return OP3(0x5c000000, (uint32)rd, (uint32)rs1, imm); } USC uint32 OP_rte() { return 0xf400fc00; } +USC uint32 OP_st(OPr rd, OPr rs1, uint32 imm) { + return OP3(0x24000000, (uint32)rd, (uint32)rs1, imm); +} USC uint32 OP_st(OPr rd, OPr rs1, OPr rs2) { return OP3(0xf4002400, (uint32)rd, (uint32)rs1, (uint32)rs2); } +USC uint32 OP_st_b(OPr rd, OPr rs1, OPr rs2) { + return OP3(0xf4002c00, (uint32)rd, (uint32)rs1, (uint32)rs2); +} USC uint32 OP_stcr(OPr rs1, uint32 crd) { return OP4(0x80008000, 0, (uint32)rs1, crd, (uint32)rs1); } USC uint32 OP_doscall(OPr rd, OPr rs1, uint32 imm) { return OP3(0xfc000400, (uint32)rd, (uint32)rs1, imm); } +#define SxIP_VALID (0x0002) #define DOS_GETCHAR (1) #define DOS_PUTCHAR (2) @@ -129,6 +164,8 @@ USC uint32 OP_doscall(OPr rd, OPr rs1, u // コンストラクタ Luna88kPROMEmuDevice::Luna88kPROMEmuDevice() { + machine_name = "LUNA-88K"; + // ROM の特定アドレスを読み出す人がいるかも知れないので、 // サイズは実 ROM と同じにしておく。LUNA-I からの類推。 uint devlen = 256 * 1024; @@ -155,10 +192,10 @@ Luna88kPROMEmuDevice::Init() return false; } - interrupt = GetInterruptDevice(); + SysClkDevice *sysclk = GetSysClkDevice(); // DOS call エミュレーションのコールバックを登録 - auto mpu88xx0 = GetMPU88xx0Device(); + auto mpu88xx0 = GetMPU88xx0Device(mpu); mpu88xx0->SetFLineCallback(luna88k_fline_callback, this); MemoryStreamBE ms(imagebuf.get()); @@ -185,47 +222,73 @@ Luna88kPROMEmuDevice::Init() // 元の PSR を r10 に保存し、r9 に割り込み許可の値を作る ms.Write32(OP_ldcr (r10, psr)); ms.Write32(OP_clr (r9, r10, 2, 0)); // clear IND,SFRZ + ms.Write32(OP_stcr (r9, psr)); + ms.Write32(OP_or (r11, r0, r0)); // r11 <- 0 ms.Write32(OP_or_u (r3, r0, ROMIO_BASE >> 16)); ms.Write32(OP_or (r3, r3, ROMIO_BASE & 0xffff)); - // ROMIO_INIT を読み出す - ms.Write32(OP_ld (r2, r3, (ROMIO_INIT - ROMIO_BASE))); - // 起動先があれば GO - ms.Write32(OP_bcnd (ge0, r2, 7)); // 7 = _go - + // ROMIO_INIT を実行 + ms.Write32(OP_ld (r0, r3, (ROMIO_INIT - ROMIO_BASE))); + ms.Write32(OP_ld (r0, r3, (ROMIO_LOAD - ROMIO_BASE))); + ms.Write32(OP_br (5)); // _entry // _prompt: - // 疑似 STOP でキー入力を待つ - ms.Write32(OP_stcr (r9, psr)); // 割り込み許可 - ms.Write32(OP_bsr (1)); // 1 = _stop - // この時点で r1 = _stop - - // _stop: - ms.Write32(OP_jmp (r1)); - - // _back: - uint32 back = baseaddr + ms.GetOffset(); - // 割り込み禁止に戻す - ms.Write32(OP_stcr (r10, psr)); + uint32 prompt = ms.GetOffset(); + ms.Write32(OP_or_u (r11, r0, 0x4100)); + ms.Write32(OP_or (r11, r11, ms.GetOffset() + 8)); + ms.Write32(OP_br (0)); // STOP 相当 + ms.Write32(OP_or (r11, r0, r0)); + // _entry: ms.Write32(OP_ld (r2, r3, (ROMIO_ENTRY - ROMIO_BASE))); - // 起動先がなければループ - ms.Write32(OP_bcnd (eq0, r2, -5)); // -5 = _prompt - + ms.Write32(OP_or_c (r5, r0, r0)); // r5 <- $ffffffff + ms.Write32(OP_cmp (r5, r2, r5)); + ms.Write32(OP_bb1 (CMP_EQ, r5, -7)); // 起動先なければループ // _go: // 起動先があれば後始末して ms.Write32(OP_ld (r0, r3, (ROMIO_CLOSE - ROMIO_BASE))); + // 割り込み禁止に戻して + ms.Write32(OP_stcr (r10, psr)); + // 戻ってきたときのために r1 を用意して + ms.Write32(OP_or_u (r1, r0, 0x4100)); + ms.Write32(OP_or (r1, r1, ms.GetOffset() + 8)); // ジャンプ ms.Write32(OP_jmp (r2)); + // 戻ってきたら + ms.Write32(OP_br ((prompt - ms.GetOffset()) / 4)); - // ここから割り込みハンドラ。割り込みは SIO のみ許可にしてある。 - // キーボード割り込みが上がったら KEYIN をコールして _back に戻る。 + // 割り込みハンドラ。 + // _intr: ms.SetOffset(0x200); + ms.Write32(OP_or_u (r6, r0, 0x6500)); + ms.Write32(OP_ld (r6, r6, 0)); + ms.Write32(OP_extu (r6, r6, 3, 29)); + ms.Write32(OP_or (r7, r0, 6)); + ms.Write32(OP_cmp (r7, r6, r7)); + ms.Write32(OP_bb1 (CMP_EQ, r7, (0x240 - ms.GetOffset()) / 4)); + // key_intr: ms.Write32(OP_ld (r0, r3, (ROMIO_KEYIN - ROMIO_BASE))); - // 戻ったところの jmp を抜ける - ms.Write32(OP_or_u (r1, r0, back >> 16)); - ms.Write32(OP_or (r1, r1, back & 0xffff)); + // _intr_ret: + // r11 があればそこに戻る + uint32 intr_ret = ms.GetOffset(); + ms.Write32(OP_bcnd (eq0, r11, 5)); // beq @f + ms.Write32(OP_or (r6, r11, SxIP_VALID)); + ms.Write32(OP_stcr (r6, snip)); + ms.Write32(OP_addu (r6, r6, 0x0004)); + ms.Write32(OP_stcr (r6, sfip)); + // @@: ms.Write32(OP_rte ()); + // Sysclk 割り込みハンドラ。 + ms.SetOffset(0x240); + // ACK + ms.Write32(OP_or_u (r6, r0, 0x6300)); + ms.Write32(OP_st_b (r0, r6, r0)); + // inc + ms.Write32(OP_ld (r6, r3, (ROMIO_CLKCNT - ROMIO_BASE))); + ms.Write32(OP_addu (r6, r6, 1)); + ms.Write32(OP_st (r6, r3, (ROMIO_CLKCNT - ROMIO_BASE))); + ms.Write32(OP_br ((intr_ret - ms.GetOffset()) / 4)); + // ROM GETC エミュレータ ms.SetOffset(0x300); ms.Write32(OP_doscall(r2, r0, DOS_GETCHAR)); @@ -237,30 +300,39 @@ Luna88kPROMEmuDevice::Init() ms.Write32(OP_doscall(r0, r2, DOS_PUTCHAR)); ms.Write32(OP_jmp (r1)); + // サブルーチン + ms.SetOffset(0x900); + ms.Write32(OP_ld (r2, r3, (ROMIO_AP1 - ROMIO_BASE))); + //@@: + ms.Write32(OP_or_u (r11, r0, 0x4100)); + ms.Write32(OP_or (r11, r11, ms.GetOffset() + 8)); + ms.Write32(OP_br (0)); // STOP 相当 + ms.Write32(OP_or (r11, r0, r0)); + + ms.Write32(OP_ld (r2, r3, (ROMIO_CLKCNT - ROMIO_BASE))); + ms.Write32(OP_or (r5, r0, 0x01ff)); + ms.Write32(OP_and (r2, r2, r5)); // r2 &= 0x1ff; + int freq = sysclk->GetFreq(); + ms.Write32(OP_or (r5, r0, freq * 3)); // r5 = freq*3; + ms.Write32(OP_cmp (r5, r2, r5)); + ms.Write32(OP_bb1 (CMP_LT, r5, -9)); // blt @b + ms.Write32(OP_ld (r0, r3, (ROMIO_AP2 - ROMIO_BASE))); + ms.Write32(OP_jmp (r1)); // 変なインデントここまで return true; } -// 起動時の処理を行う。 -// 次段実行ファイルの読み込みに成功すればそのエントリポイントを返す。 -// そうでなければ 0 を返すので、プロンプトに処理を移すこと。 -uint32 -Luna88kPROMEmuDevice::ROM_Init() +// ROM 起動時の初期化 (機種依存部分) を行う。 +void +Luna88kPROMEmuDevice::ROM_InitMD() { IODeviceStream rs(mainram); - uint32 entry; - - b_entrypoint = -1; - - // デバイスの初期化 (親クラス) - InitDevice(); - - auto pio0 = GetPIO0Device(); - // 割り込みマスクで SIO だけ許可 - interrupt->Write32(0x65000000, 0x40000000); + // 割り込みマスクで Sysclk, SIO だけ許可 + auto *interrupt = GetInterruptDevice(); + interrupt->Write32(0x65000000, 0xc0000000); rs.SetAddr(0); // ベクタテーブルを rte で埋めておく @@ -268,7 +340,7 @@ Luna88kPROMEmuDevice::ROM_Init() rs.Write32(OP_rte()); } // +$08: 割り込みベクタ - rs.Write32(8, 0xc00003fe); // br $00001000 + rs.Write32(8, OP_br(0x003fe)); // br $00001000 // 割り込みトランポリン (0x1000) // ベクタテーブルは各2ワードしかないのに、0 番地付近にあるベクタテーブル @@ -292,68 +364,43 @@ Luna88kPROMEmuDevice::ROM_Init() rs.Write32(0x41000400); rs.Write32(0x00000001); - // スクリーンを初期化 - InitScreen(); + uint32 ap = (nvram->Peek(0x7fd) << 8) | nvram->Peek(0x7fe); + if (__predict_false(0||(ap & 0x3f1f) == 0x0104)) { + auto *mpu88xx0 = GetMPU88xx0Device(mpu); + mpu88xx0->reg.r[1] = mpu88xx0->reg.nip; + mpu88xx0->DoBranch(0x41000900, false); + } +} - entry = -1; +// ファイルをロードする。 +// -X が指定されていればそのホストファイル。 +// そうでなければゲストファイル。この場合引数 fname があれば +// NVRAM で指定されたデバイスから読み込む際のファイル名とする。 +// ロードできればエントリポイントを返す。 +// ロードできなければ errmsg にメッセージをセットし -1 を返す。 +uint32 +Luna88kPROMEmuDevice::LoadFile(const std::string& fname) +{ + uint32 entry; - // 表示環境が整ったところで NVRAM を照合 - if (VerifyNVRAM() == false) { - // PROM 1.20 は初期化するかどうか確認してくるけど、とりあえず。 - InitNVRAM(); - errmsg = "NVRAM Initialized."; - // NVRAM をクリアしたら DIPSW によらずプロンプトに降りる - goto done; - } - - // -X file - // DIPSW の状態に関わらず、ファイルをロードし実行する。 - // --load-only file - // DIPSW の状態に関わらず、ファイルをロードし、プロンプトで止まる。 if (gMainApp.exec_file) { - // ホストのファイルを読み込んでエントリポイントアドレスを - // この Read アクセスの読み出し値として返す。 - // エラーがあればエラーメッセージは呼び出し先側で表示してある。 - b_entrypoint = LoadHostFile(); - if (gMainApp.load_and_exec) { - entry = b_entrypoint; - } + // -X が指定されてたらホストプログラムを読み込む。 + entry = LoadHostFile(); } else { - // DIPSW#1-1 が UP なら自動起動、DOWN なら ROM モニタ起動なので、 - // これを真似する。 - if (pio0->IsDIPSW11Up()) { - // NVRAM の設定に従ってブート先を探す。 - errmsg.clear(); - - bootinfo_t bootinfo; - GetNVRAM(bootinfo); - entry = LoadGuestFile(bootinfo); + // NVRAM で指定されるゲストプログラムを読み込む。 + // XXX とりあえず boot を起動するだけ + bootinfo_t bootinfo; + GetNVRAM(bootinfo); + // 引数で指定されたらファイル名だけ上書き + if (fname.empty() == false) { + bootinfo.dkfile = fname; } - } - - done: - if (entry == -1) { - PrintTitle("LUNA88K"); - if (errmsg.empty() == false) { - Print("** %s\n\n", errmsg.c_str()); - } - ClearPrompt(); + entry = LoadGuestFile(bootinfo); } return entry; } -// ROM 処理。エントリポイントを返す。 -uint32 -Luna88kPROMEmuDevice::ROM_Entry() -{ - if (b_entrypoint != -1) { - return b_entrypoint; - } else { - return 0; - } -} - void Luna88kPROMEmuDevice::ProcessChar(char asciicode) { @@ -476,35 +523,22 @@ Luna88kPROMEmuDevice::CommandH(const std } // "b" コマンド -// 引数なしなら NVRAM の情報に従ってプログラムをロードして実行。 -// 引数ありのほうは未実装。 +// 引数なしなら NVRAM の情報 (もしくは -X オプション) に従ってプログラムを +// ロードして実行。引数ありのほうは未実装。 void Luna88kPROMEmuDevice::CommandB(const std::vector& arg) { - errmsg.clear(); - - // XXX とりあえず boot を起動するだけ - bootinfo_t bootinfo; - GetNVRAM(bootinfo); - b_entrypoint = LoadGuestFile(bootinfo); - if (b_entrypoint == -1) { + entrypoint = LoadFile(""); + if (entrypoint == -1) { Print("** %s\n", errmsg.c_str()); + } else { + execute = true; + Print("%s. Entry point = $%08x\n", + (gMainApp.exec_file ? "Host program loaded" : "Loaded"), + entrypoint); } } -// "bh" コマンド (勝手に拡張) -// -X オプションで指定されたホストプログラムをロードして実行。 -void -Luna88kPROMEmuDevice::CommandBH() -{ - // 指定されてたら読み込む - if (gMainApp.exec_file == NULL) { - Print("** Cannot loaded; host program is not specified.\n"); - return; - } - b_entrypoint = LoadHostFile(); -} - // "nvram" コマンド // nvram .. 全表示 // nvram .. 表示