--- nono/vm/romemu_luna88k.cpp 2026/04/29 17:05:03 1.1.1.9 +++ nono/vm/romemu_luna88k.cpp 2026/04/29 17:05:51 1.1.1.17 @@ -4,40 +4,44 @@ // Licensed under nono-license.txt // -// LUNA88K の ROM エミュレーション // +// LUNA-88K の ROM エミュレーション +// + // IODevice -// | -// +-- ROMDevice (LoadROM()、ウェイト、マスク等を持つ) -// | | -// | +-- PROMDevice (LUNA* の PROM) -// | +-- IPLROM1Device (X680x0 の IPLROM 後半) -// | +-- IPLROM2Device (X680x0 の IPLROM 前半) -// | +-- CGROMDevice (X680x0 の CGROM) -// | +-- LunaPROMEmuDevice (LUNA PROM エミュレーションの共通部分) -// | | -// | +-- Luna1PROMEmuDevice (LUNA1 の PROM エミュレーション) -// | | -// | | +----------------------+ -// | +--| Luna88kPROMEmuDevice | (LUNA88K の PROM エミュレーション) -// | +----------------------+ -// | -// +-- PROM0Device (LUNA* のブートページ切り替え用プロキシ) -// +-- IPLROM0Device (X680x0 のブートページ切り替え用プロキシ) +// | +// +- ROMDevice (LoadROM()、ウェイト、マスク等を持つ) +// | +- PROMDevice (LUNA* の PROM) +// | +- IPLROM1Device (X680x0 の IPLROM 後半) +// | +- IPLROM2Device (X680x0 の IPLROM 前半) +// | +- CGROMDevice (X680x0 の CGROM) +// | | +// | +- ROMEmuDevice +// | +- LunaPROMEmuDevice (LUNA PROM エミュレーションの共通部分) +// | | +- Luna1PROMEmuDevice (LUNA-I の PROM エミュレーション) +// | | | +// | | | +----------------------+ +// | | +-| Luna88kPROMEmuDevice | (LUNA-88K の PROM エミュレーション) +// | | +----------------------+ +// | | +// | +- NewsROMEmuDevice (NEWS の ROM エミュレーション) +// | +- ROM30EmuDevice (X68030 の ROM30 エミュレーション) +// | +- Virt68kROMEmuDevice (virt-m68k の IPLROM 相当の何か) +// | +// +- PROM0Device (LUNA* のブートページ切り替え用プロキシ) +// +- IPLROM0Device (X680x0 のブートページ切り替え用プロキシ) #include "romemu_luna88k.h" #include "iodevstream.h" -#include "lunafb.h" -#include "m88100.h" #include "mainapp.h" +#include "mainram.h" #include "memorystream.h" #include "mk48t02.h" #include "mpu88xx0.h" -#include "pio.h" -#include "ram.h" +#include "sysclk.h" #include "sysctlr.h" -static bool luna88k_fline_callback(m88kcpu *cpu, void *arg); +static bool luna88k_fline_callback(MPU88xx0Device *cpu, void *arg); // m88k の命令コードを少し読みやすく書きたい。 // 必要なものは適宜足すこと。 @@ -58,64 +62,118 @@ 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, + psr = 0x01, + ssbr = 0x03, + snip = 0x05, + sfip = 0x06, + sr0 = 0x11, + sr1 = 0x12, + sr2 = 0x13, + sr3 = 0x14, }; +enum CMPbits { + CMP_EQ = 2, + CMP_NE = 3, + CMP_LT = 6, +}; + +#define USC __unused static constexpr -static constexpr uint32 OP3(uint32 base, uint32 rd, uint32 rs1, uint32 rs2) { +USC uint32 OP3(uint32 base, uint32 rd, uint32 rs1, uint32 rs2) { return (base) | (rd << 21) | (rs1 << 16) | rs2; } -static constexpr uint32 OP4(uint32 base, uint32 rd, uint32 rs1, +USC uint32 OP4(uint32 base, uint32 rd, uint32 rs1, uint32 w, uint32 o) { return (base) | (rd << 21) | (rs1 << 16) | (w << 5) | o; } -static constexpr uint32 OP_addu(OPr rd, OPr rs1, uint32 imm) { +USC uint32 OP_addu(OPr rd, OPr rs1, uint32 imm) { return OP3(0x60000000, (uint32)rd, (uint32)rs1, imm); } -static constexpr uint32 OP_bcnd(OPm5 m5, OPr rs1, uint32 d16) { - return OP3(0xe8000000, (uint32)m5, (uint32)rs1, d16); +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)); } -static constexpr uint32 OP_bsr(uint32 d26) { - return (0xc8000000 | d26); +USC uint32 OP_bsr(uint32 d26) { + return (0xc8000000 | (d26 & 0x03ffffff)); } -static constexpr uint32 OP_clr(OPr rd, OPr rs1, uint32 w, uint32 o) { +USC uint32 OP_clr(OPr rd, OPr rs1, uint32 w, uint32 o) { return OP4(0xf0008000, (uint32)rd, (uint32)rs1, w, o); } -static constexpr uint32 OP_jmp(OPr rs2) { +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); } -static constexpr uint32 OP_ld(OPr rd, OPr rs1, uint32 imm) { +USC uint32 OP_ld(OPr rd, OPr rs1, uint32 imm) { return OP3(0x14000000, (uint32)rd, (uint32)rs1, imm); } -static constexpr uint32 OP_ldcr(OPr rd, uint32 crs) { +USC uint32 OP_ldcr(OPr rd, uint32 crs) { return OP4(0x80004000, (uint32)rd, 0, crs, 0); } -static constexpr uint32 __unused OP_or(OPr rd, OPr rs1, OPr rs2) { +USC uint32 OP_mask(OPr rd, OPr rs1, uint32 imm) { + return OP3(0x48000000, (uint32)rd, (uint32)rs1, imm); +} +USC uint32 __unused OP_or(OPr rd, OPr rs1, OPr rs2) { return OP3(0xf4005800, (uint32)rd, (uint32)rs1, (uint32)rs2); } -static constexpr uint32 OP_or(OPr rd, OPr rs1, uint32 imm) { +USC uint32 OP_or(OPr rd, OPr rs1, uint32 imm) { return OP3(0x58000000, (uint32)rd, (uint32)rs1, imm); } -static constexpr uint32 OP_or_u(OPr rd, OPr rs1, uint32 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); } -static constexpr uint32 OP_rte() { +USC uint32 OP_rte() { return 0xf400fc00; } -static constexpr uint32 OP_st(OPr rd, OPr rs1, OPr rs2) { +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); } -static constexpr uint32 OP_stcr(OPr rs1, uint32 crd) { +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); } -static constexpr uint32 OP_doscall(OPr rd, OPr rs1, uint32 imm) { +USC uint32 OP_xcr(OPr rd, OPr rs1, uint32 cr) { + return OP4(0x8000c000, (uint32)rd, (uint32)rs1, cr, (uint32)rs1); +} +USC uint32 OP_xmem(OPr rd, OPr rs1, uint32 imm) { + return OP3(0x04000000, (uint32)rd, (uint32)rs1, imm); +} +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) @@ -123,16 +181,7 @@ static constexpr uint32 OP_doscall(OPr r // コンストラクタ Luna88kPROMEmuDevice::Luna88kPROMEmuDevice() { - devaddr = baseaddr; - - // ROM の特定アドレスを読み出す人がいるかも知れないので、 - // サイズは実 ROM と同じにしておく。LUNA-I からの類推。 - devlen = 256 * 1024; - imagebuf.reset(new uint8 [devlen]); - memset(&imagebuf[0], 0, devlen); - mem = &imagebuf[0]; - - mask = devlen - 1; + machine_name = "LUNA-88K"; // プロンプトを初期化。 prompt = default_prompt; @@ -143,18 +192,39 @@ Luna88kPROMEmuDevice::~Luna88kPROMEmuDev { } +// 初期化 bool Luna88kPROMEmuDevice::Init() { - // 親クラス if (inherited::Init() == false) { return false; } + // ROM の特定アドレスを読み出す人がいるかも知れないので、 + // サイズは実 ROM と同じにしておく。LUNA-I からの類推。 + if (AllocROM(256 * 1024, 0) == false) { + return false; + } + + SysClkDevice *sysclk = GetSysClkDevice(); + // DOS call エミュレーションのコールバックを登録 - gMPU88xx0->SetFLineCallback(luna88k_fline_callback, this); + auto mpu88xx0 = GetMPU88xx0Device(mpu); + mpu88xx0->SetFLineCallback(luna88k_fline_callback, this); + + MemoryStreamBE roms(imagebuf.get()); + + // m88k では ROMIO_BASE は上位ワードだけで表現できれば楽。 + static_assert((ROMIO_BASE & 0xffff) == 0, ""); - MemoryStreamBE ms(imagebuf.get()); + // ROM のメモリマップ: + // $41000000.L*3 リセットベクタから ROM _START へ飛ぶコード。 + // $4100000c ROM _START + // $41000200 割り込みハンドラ + // $41000270 sysclk + // $41000300 ROM GETC + // $41000400 ROM PUTC + // $41000500 データアクセス例外ハンドラ // リセット時のブートページには ROM (これ) が見えている。 // ROMIO_INIT へのアクセスでブートページを RAM に切り替えて、同時に RAM の @@ -167,108 +237,217 @@ Luna88kPROMEmuDevice::Init() // _START: // ここは 0番地。ここから _ROM_START へジャンプ - ms.Write32(OP_or_u (r6, r0, 0x4100)); - ms.Write32(OP_or (r6, r6, 0x000c)); - ms.Write32(OP_jmp (r6)); + roms.Write4(OP_or_u (r1, r0, 0x4100)); + roms.Write4(OP_or (r1, r1, 0x000c)); + roms.Write4(OP_jmp (r1)); // _ROM_START: (ここは ROM) + // r1: work (最後は戻りアドレスとして使う) + // r2: work (最後はジャンプ先として使う) + // r3: work + // r5: ROMIO_BASE + // RAM に切り替え - ms.Write32(OP_st (r0, r6, r0)); + roms.Write4(OP_st (r0, r1, r0)); - // 元の PSR を r10 に保存しつつ割り込み許可 - ms.Write32(OP_ldcr (r10, psr)); - ms.Write32(OP_clr (r2, r10, 2, 0)); // clear IND,SFRZ - ms.Write32(OP_stcr (r2, psr)); - - ms.Write32(OP_or_u (r3, r0, ROMIO_BASE >> 16)); - ms.Write32(OP_or (r3, r3, ROMIO_BASE & 0xfff)); - // ROMIO_INIT を読み出す - ms.Write32(OP_ld (r2, r3, (ROMIO_INIT - ROMIO_BASE))); - // 起動先があれば GO - ms.Write32(OP_bcnd (ne0, r2, 5)); // 5 = _go + // 割り込み許可 (INIT の前に必要)。 + // なお PROM が次段に処理を移す時は割り込み許可のまま進んでいるので + // ここも割り込みを許可したそのまま進む。 + roms.Write4(OP_ldcr (r1, psr)); + roms.Write4(OP_clr (r1, r1, 2, 0)); // clear IND,SFRZ + roms.Write4(OP_stcr (r1, psr)); + + // 先に ROMIO_INIT だけ実行。 + roms.Write4(OP_or_u (r5, r0, ROMIO_BASE >> 16)); + roms.Write4(OP_ld (r0, r5, (ROMIO_INIT - ROMIO_BASE))); + + // _rom_main: + uint32 rom_main = roms.GetOffset(); + // _rom_main 以降で r5 を(再)初期化する必要がある。 + roms.Write4(OP_or_u (r5, r0, ROMIO_BASE >> 16)); + roms.Write4(OP_st (r0, r5, (ROMIO_QUITSTOP - ROMIO_BASE))); + roms.Write4(OP_ld (r0, r5, (ROMIO_LOAD - ROMIO_BASE))); + roms.Write4(OP_br (5)); // _entry // _prompt: - // 疑似 STOP でキー入力を待つ - ms.Write32(OP_bsr (1)); // 1 = _stop - // この時点で r1 = _stop - - // _stop: - ms.Write32(OP_jmp (r1)); - - // _back: - ms.Write32(OP_ld (r2, r3, (ROMIO_ENTRY - ROMIO_BASE))); - // 起動先がなければループ - ms.Write32(OP_bcnd (eq0, r2, 0xfffd)); // 0xfffd = _prompt + uint32 prompt = roms.GetOffset(); + roms.Write4(OP_or_u (r1, r0, 0x4100)); + roms.Write4(OP_or (r1, r1, roms.GetOffset() + 12)); + roms.Write4(OP_st (r1, r5, (ROMIO_QUITSTOP - ROMIO_BASE))); + // STOP 相当の無限ループ。 + // 抜ける時は割り込みハンドラから (QUITSTOP) に rte で戻ってくる。 + roms.Write4(OP_br (0)); + + // _entry: + // 起動可能なら ENTRY が -1 以外を返す + roms.Write4(OP_ld (r2, r5, (ROMIO_ENTRY - ROMIO_BASE))); + roms.Write4(OP_or_c (r1, r0, r0)); // r1 <- $ffffffff + roms.Write4(OP_cmp (r1, r2, r1)); + // 起動不可なら _prompt へ戻る + roms.Write4(OP_bb1 (CMP_EQ, r1, (prompt - roms.GetOffset()) / 4)); // _go: // 起動先があれば後始末して - ms.Write32(OP_ld (r0, r3, (ROMIO_CLOSE - ROMIO_BASE))); - // 割り込み禁止に戻す - ms.Write32(OP_stcr (r10, psr)); + roms.Write4(OP_ld (r0, r5, (ROMIO_CLOSE - ROMIO_BASE))); + // 使ったレジスタを一応クリア + roms.Write4(OP_or (r3, r0, r0)); + roms.Write4(OP_or (r5, r0, r0)); + // 戻ってきたときのために r1 を用意して + roms.Write4(OP_or_u (r1, r0, 0x4100)); + roms.Write4(OP_or (r1, r1, roms.GetOffset() + 8)); // ジャンプ - ms.Write32(OP_jmp (r2)); - - // ここから割り込みハンドラ。割り込みは SIO のみ許可にしてある。 - // キーボード割り込みが上がったら KEYIN をコールして _back に戻る。 - ms.SetPtr(&imagebuf[0x200]); - ms.Write32(OP_ld (r0, r3, (ROMIO_KEYIN - ROMIO_BASE))); - // 戻ったところの jmp を抜ける - ms.Write32(OP_addu (r1, r1, 0x04)); - ms.Write32(OP_rte ()); + roms.Write4(OP_jmp (r2)); + // 戻ってきたら + roms.Write4(OP_br ((rom_main - roms.GetOffset()) / 4)); + + // 割り込みハンドラ。 + // _intr: + roms.SetOffset(0x200); + // r1: work + // r2: work + // r5: ROMIO_BASE + + // レジスタを退避。r1 は sr2 に退避済み。 + roms.Write4(OP_st (r2, r0, 0x2000)); + roms.Write4(OP_st (r5, r0, 0x2004)); + roms.Write4(OP_or_u (r5, r0, ROMIO_BASE >> 16)); + + roms.Write4(OP_or_u (r1, r0, 0x6500)); + roms.Write4(OP_ld (r1, r1, 0)); + roms.Write4(OP_extu (r1, r1, 3, 29)); + roms.Write4(OP_or (r2, r0, 6)); + roms.Write4(OP_cmp (r2, r1, r2)); + // 割り込みは sysclk, sio のみ許可しているので必ずどちらか。 + roms.Write4(OP_bb1 (CMP_EQ, r2, (0x270 - roms.GetOffset()) / 4)); + // key_intr: + roms.Write4(OP_ld (r0, r5, (ROMIO_KEYIN - ROMIO_BASE))); + + // _intr_ret: + // QUITSTOP があれば rte の戻り先をそっちに書き換える。 + uint32 intr_ret = roms.GetOffset(); + roms.Write4(OP_ld (r1, r5, (ROMIO_QUITSTOP - ROMIO_BASE))); + roms.Write4(OP_bcnd (eq0, r1, 6)); // beq @f + roms.Write4(OP_st (r0, r5, (ROMIO_QUITSTOP - ROMIO_BASE))); + roms.Write4(OP_or (r1, r1, SxIP_VALID)); + roms.Write4(OP_stcr (r1, snip)); + roms.Write4(OP_addu (r1, r1, 0x0004)); + roms.Write4(OP_stcr (r1, sfip)); + // @@: + // レジスタを復帰 + roms.Write4(OP_xcr (r1, r0, sr2)); + roms.Write4(OP_or (r2, r0, r0)); + roms.Write4(OP_or (r5, r0, r0)); + roms.Write4(OP_xmem (r2, r0, 0x2000)); + roms.Write4(OP_xmem (r5, r0, 0x2004)); + roms.Write4(OP_stcr (r0, ssbr)); + roms.Write4(OP_rte ()); + + // _sysclk_intr: + assertmsg(roms.GetOffset() < 0x270, "offset=$%x", roms.GetOffset()); + roms.SetOffset(0x270); + // ACK + roms.Write4(OP_or_u (r1, r0, 0x6300)); + roms.Write4(OP_st_b (r0, r1, r0)); + // inc + roms.Write4(OP_ld (r1, r5, (ROMIO_CLKCNT - ROMIO_BASE))); + roms.Write4(OP_addu (r1, r1, 1)); + roms.Write4(OP_st (r1, r5, (ROMIO_CLKCNT - ROMIO_BASE))); + roms.Write4(OP_br ((intr_ret - roms.GetOffset()) / 4)); // ROM GETC エミュレータ - ms.SetPtr(&imagebuf[0x300]); - ms.Write32(OP_doscall(r2, r0, DOS_GETCHAR)); - ms.Write32(OP_jmp (r1)); + assertmsg(roms.GetOffset() < 0x300, "offset=$%x", roms.GetOffset()); + roms.SetOffset(0x300); + roms.Write4(OP_doscall(r2, r0, DOS_GETCHAR)); + roms.Write4(OP_jmp (r1)); // ROM PUTC エミュレータ - // 表示する文字は r3 に置かれている。 - ms.SetPtr(&imagebuf[0x400]); - ms.Write32(OP_doscall(r0, r3, DOS_PUTCHAR)); - ms.Write32(OP_jmp (r1)); - + // 表示する文字は r2 に置かれている。 + roms.SetOffset(0x400); + roms.Write4(OP_doscall(r0, r2, DOS_PUTCHAR)); + roms.Write4(OP_jmp (r1)); + + // データアクセス例外ハンドラ。 + // 例外を起こした命令の次の命令 (現状 snip) に戻る。 + roms.SetOffset(0x500); + // PROM-1.20 互換動作にするため sr2 に退避した r1 はそのままにする。 + // boot のオレオレ machine read コマンドがこの動作を利用している。 + roms.Write4(OP_ldcr (r1, sr2)); + roms.Write4(OP_stcr (r0, ssbr)); + roms.Write4(OP_rte ()); + + // サブルーチン + roms.SetOffset(0x900); + roms.Write4(OP_or_u (r5, r0, ROMIO_BASE >> 16)); + roms.Write4(OP_ld (r0, r5, (ROMIO_AP1 - ROMIO_BASE))); + //@@: + uint32 subr1 = roms.GetOffset(); + roms.Write4(OP_or_u (r2, r0, 0x4100)); + roms.Write4(OP_or (r2, r2, roms.GetOffset() + 12)); + roms.Write4(OP_st (r2, r5, (ROMIO_QUITSTOP - ROMIO_BASE))); + roms.Write4(OP_br (0)); // STOP 相当 + + roms.Write4(OP_ld (r2, r5, (ROMIO_CLKCNT - ROMIO_BASE))); + roms.Write4(OP_mask (r2, r2, 0x01ff)); // r2 &= 0x1ff; + uint freq = sysclk->GetFreq(); + roms.Write4(OP_or (r3, r0, freq * 3));// r5 = freq*3; + roms.Write4(OP_cmp (r3, r2, r3)); + roms.Write4(OP_bb1 (CMP_LT, r3, (subr1 - roms.GetOffset()) / 4)); + // blt @b + roms.Write4(OP_ld (r0, r5, (ROMIO_AP2 - ROMIO_BASE))); + roms.Write4(OP_jmp (r1)); // 変なインデントここまで return true; } +// ROM 起動時の初期化 (機種依存部分) を行う。 void -Luna88kPROMEmuDevice::ResetHard() +Luna88kPROMEmuDevice::ROM_InitMD() { - b_entrypoint = 0; -} - -// 起動時の処理を行う。 -// 次段実行ファイルの読み込みに成功すればそのエントリポイントを返す。 -// そうでなければ 0 を返すので、プロンプトに処理を移すこと。 -uint32 -Luna88kPROMEmuDevice::ROM_Init() -{ - IODeviceStream rs(gRAM.get()); - uint32 entry; + IODeviceStream rams(mainram); - // デバイスの初期化 (親クラス) - InitDevice(); + // 割り込みマスクで Sysclk, SIO だけ許可 + // XXX とりあえず CPU#0 のみ + auto *sysctlr = dynamic_cast(GetInterruptDevice()); + sysctlr->WriteIntMask(0, 0xc0000000); - // 割り込みマスクで SIO だけ許可 - gInterrupt->Write32(0x65000000, 0x40000000); + // RAM のメモリマップ: + // $00000000 .. $00000fff 例外ベクタ + // $00001000 .. $000010ff トランポリン用 (PROM もここを使っている) + // $00001100 .. ? PROM 用が使う領域があるようだ + // $00002000 .. レジスタ保存用として勝手に使うことにする - rs.SetAddr(0); - // ベクタテーブルを rte で埋めておく + rams.SetAddr(0); + // ベクタテーブルを rte で埋めておく。 for (int i = 0; i < 512 * 2; i++) { - rs.Write32(OP_rte()); + rams.Write4(OP_rte()); + } + // 例外ベクタの2ワードとトランポリン先の4ワードで PROM まで飛ぶ + // コードを出力する。 + static const struct { + uint32 vec; + uint32 toaddr; + } vectors [] = { + { 1, 0x41000200 }, // 割り込み + { 3, 0x41000500 }, // データアクセス例外 + }; + uint32 midaddr = 0x1000; + for (int i = 0; i < countof(vectors); i++) { + uint32 vec = vectors[i].vec; + uint32 toaddr = vectors[i].toaddr; + rams.SetAddr(vec * 8); + // ベクタは定型文で書いておく。 + rams.Write4(OP_or (r0, r0, r0)); + rams.Write4(OP_br ((midaddr - rams.GetAddr()) / 4)); + // 経由地で r1 を sr2 に退避してから、toaddr にジャンプ。 + rams.SetAddr(midaddr); + rams.Write4(OP_stcr (r1, sr2)); + rams.Write4(OP_or_u (r1, r0, toaddr >> 16)); + rams.Write4(OP_or (r1, r1, toaddr & 0xffff)); + rams.Write4(OP_jmp (r1)); + midaddr = rams.GetAddr(); } - // +$08: 割り込みベクタ - rs.Write32(8, 0xc00003fe); // br $00001000 - - // 割り込みトランポリン (0x1000) - // ベクタテーブルは各2ワードしかないのに、0 番地付近にあるベクタテーブル - // から2ワードでは ROM まで飛べない。一体どういう設計してんだ…。 - rs.SetAddr(0x1000); - rs.Write32(OP_or_u (r6, r0, 0x4100)); - rs.Write32(OP_or (r6, r6, 0x0200)); - rs.Write32(OP_jmp (r6)); // ROM の機能テーブル // 0x00001100 ? @@ -279,85 +458,71 @@ Luna88kPROMEmuDevice::ROM_Init() // 0x00001114 ビットマッププレーンの有効状態が入っている。 // bit0: plane0 有効 // bit1..7: plane1..7 有効 - rs.SetAddr(0x110c); - rs.Write32(0x41000300); - rs.Write32(0x41000400); - rs.Write32(0x00000001); - - // スクリーンを初期化 - InitScreen("LUNA88K"); - - entry = 0; - - // 表示環境が整ったところで NVRAM を照合 - if (VerifyNVRAM() == false) { - // PROM 1.20 は初期化するかどうか確認してくるけど、とりあえず。 - InitNVRAM(); - text.Print("** NVRAM Initialized.\n"); - text.Print("\n"); - // NVRAM をクリアしたら DIPSW によらずプロンプトに降りる - goto done; - } - - // DIPSW#1-1 が UP なら自動起動、DOWN なら ROM モニタ起動なので、 - // これを真似する。 - if ((gPIO0->GetDIPSW1() & 0x80)) { - if (gMainApp.exec_file) { - // ホストのファイルを読み込んでエントリポイントアドレスを - // この Read アクセスの読み出し値として返す。 - // エラーがあればエラーメッセージは呼び出し先側で表示してある。 - entry = LoadHostFile(); - } else { - // NVRAM の設定に従ってブート先を探す。 - errmsg.clear(); + rams.SetAddr(0x110c); + rams.Write4(0x41000300); + rams.Write4(0x41000400); + rams.Write4(0x00000001); - bootinfo_t bootinfo; - GetNVRAM(bootinfo); - entry = LoadGuestFile(bootinfo); - if (entry == 0) { - text.Print("** %s\n", errmsg.c_str()); - } - } + uint32 ap = (nvram->PeekPort(0x7fd) << 8) | nvram->PeekPort(0x7fe); + if (__predict_false(0||(ap & 0x3f1f) == 0x0104)) { + auto *mpu88xx0 = GetMPU88xx0Device(mpu); + mpu88xx0->reg.r[1] = mpu88xx0->reg.nip; + mpu88xx0->DoBranch(0x41000900, false); } +} - // ここまでタイトルなどを Print してはいるが、実際の画面更新は - // ConsoleUpdate() を呼ぶまで行われないことを利用(悪用?)して、 - // ここでエラーなくブートプログラムに制御を渡せる場合には何も表示せずに - // ゲストに処理を移す。意図して或いは意図せずプロンプトに降りる場合だけ - // ConsoleUpdate を呼ぶことでタイトルを含めて画面を表示する。 - done: - if (entry == 0) { - ClearPrompt(); - ConsoleUpdate(); - } +void +Luna88kPROMEmuDevice::ROM_Close() +{ + inherited::ROM_Close(); - return entry; + // 割り込みをすべてマスクに戻す。 + // XXX とりあえず CPU#0 のみ + auto *sysctlr = dynamic_cast(GetInterruptDevice()); + sysctlr->WriteIntMask(0, 0x00000000); } -// ROM 処理。エントリポイントを返す。 +// ファイルをロードする。 +// -X が指定されていればそのホストファイル。 +// そうでなければゲストファイル。この場合引数 fname があれば +// NVRAM で指定されたデバイスから読み込む際のファイル名とする。 +// ロードできればエントリポイントを返す。 +// ロードできなければ errmsg にメッセージをセットし -1 を返す。 uint32 -Luna88kPROMEmuDevice::ROM_Entry() +Luna88kPROMEmuDevice::LoadFile(const std::string& fname) { - if (b_entrypoint != 0) { - return b_entrypoint; + uint32 entry; + + if (gMainApp.exec_file) { + // -X が指定されてたらホストプログラムを読み込む。 + entry = LoadHostFile(); } else { - return 0; + // NVRAM で指定されるゲストプログラムを読み込む。 + // XXX とりあえず boot を起動するだけ + bootinfo_t bootinfo; + GetNVRAM(bootinfo); + // 引数で指定されたらファイル名だけ上書き + if (fname.empty() == false) { + bootinfo.dkfile = fname; + } + entry = LoadGuestFile(bootinfo); } + + return entry; } void -Luna88kPROMEmuDevice::GetChar(char asciicode) +Luna88kPROMEmuDevice::ProcessChar(char asciicode) { if (prompt_idx == 0) { // 行入力モード (画面更新もしてある) - inherited::GetChar(asciicode); + inherited::ProcessChar(asciicode); } else { // 0 以外ならノンブロッキングモード // 入力されたキーを表示 - text.SetAttr(TA::Normal); if (' ' <= asciicode && asciicode < 0x7f) { - text.Putc(asciicode); + Putc(asciicode); } switch (prompt_idx) { @@ -377,10 +542,8 @@ Luna88kPROMEmuDevice::GetChar(char ascii // プロンプトを戻す prompt = default_prompt; prompt_idx = 0; - prompt_y = text.GetY(); - - // こっちは画面更新をする必要がある - ConsoleUpdate(); + prompt_y = cursor_y; + ClearPrompt(); } } @@ -401,7 +564,7 @@ Luna88kPROMEmuDevice::Command() std::vector arg; // 1ワードごとに分解 - strcpy(&buf[0], inputbuf.c_str()); + strlcpy(buf.data(), inputbuf.c_str(), buf.size()); char *p = &buf[0]; for (char *ap; (ap = strsep(&p, " ")) != NULL; ) { if (*ap != '\0') { @@ -410,7 +573,7 @@ Luna88kPROMEmuDevice::Command() } if (loglevel >= 1) { for (int i = 0; i < arg.size(); i++) { - putmsgn("arg[%d]=|%s|", i, arg[i].c_str()); + putmsgn("arg[%u]=|%s|", i, arg[i].c_str()); } } @@ -433,7 +596,7 @@ Luna88kPROMEmuDevice::Command() return; } - text.Print("** Unknown command: %s\n", arg[0].c_str()); + Print("** Unknown command: %s\n", arg[0].c_str()); } // "h" コマンド @@ -445,7 +608,7 @@ Luna88kPROMEmuDevice::CommandH(const std if (arg.size() < 2) { // ヘルプメッセージ mainhelp: - text.Print( + Print( " *** Help Message ***\n" " b : load and execute program\n" " h : show this message\n" @@ -456,7 +619,7 @@ Luna88kPROMEmuDevice::CommandH(const std const std::string& key = arg[1]; if (key == "nvram") { - text.Print( + Print( " *** Help Message ***\n" " nvram [] : show\n" " nvram : set\n" @@ -470,33 +633,20 @@ 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 == 0) { - text.Print("** %s\n", errmsg.c_str()); - } -} - -// "bh" コマンド (勝手に拡張) -// -X オプションで指定されたホストプログラムをロードして実行。 -void -Luna88kPROMEmuDevice::CommandBH() -{ - // 指定されてたら読み込む - if (gMainApp.exec_file == NULL) { - text.Print("** Cannot loaded; host program is not specified.\n"); - return; + 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); } - b_entrypoint = LoadHostFile(); } // "nvram" コマンド @@ -530,7 +680,7 @@ Luna88kPROMEmuDevice::CommandNvram(const } break; default: - text.Print("** Syntax error\n"); + Print("** Syntax error\n"); } } @@ -549,7 +699,7 @@ Luna88kPROMEmuDevice::CommandNvram(const // // オフセット 0x540 から始まるエントリが最後で、これ以上追加しようとすると // "nvram set error: no area" (PROM 1.20) と言われる。 -// LUNA88K にはないが LUNA-I では 0x560 にぽつんと ENADDR があったので、 +// LUNA-88K にはないが LUNA-I では 0x560 にぽつんと ENADDR があったので、 // その辺りとの互換性だろうか。 // NVRAM 全項目表示 @@ -563,7 +713,7 @@ Luna88kPROMEmuDevice::CommandNvramShowAl continue; } std::string val = nvram->PeekString(offset + 16); - text.Print("%-15s : %s\n", key.c_str(), val.c_str()); + Print("%-15s : %s\n", key.c_str(), val.c_str()); } } @@ -578,9 +728,9 @@ Luna88kPROMEmuDevice::CommandNvramShow(c uint32 offset = FindNVRAM(key); if (offset > 0) { std::string val = nvram->PeekString(offset + 16); - text.Print("%-15s : %s\n", key.c_str(), val.c_str()); + Print("%-15s : %s\n", key.c_str(), val.c_str()); } else { - text.Print("** Not found\n"); + Print("** Not found\n"); } } @@ -597,11 +747,11 @@ Luna88kPROMEmuDevice::CommandNvramUpdate putmsg(1, "nvram Update |%s|%s|", nvram_key.c_str(), nvram_val.c_str()); if (nvram_key.size() > 16) { - text.Print("** Key too long\n"); + Print("** Key too long\n"); return; } if (nvram_val.size() > 16) { - text.Print("** Val too long\n"); + Print("** Val too long\n"); return; } @@ -621,7 +771,7 @@ Luna88kPROMEmuDevice::CommandNvramUpdate // 先に空き領域をチェックしている nvram_offset = FindNVRAM(""); if (nvram_offset == 0) { - text.Print("** No area available\n"); + Print("** No area available\n"); return; } @@ -642,20 +792,20 @@ Luna88kPROMEmuDevice::CommandNvramUpdate assert(nvram_offset > 0); assert(nvram_val.size() <= 16); - text.CRLF(); + Putc('\n'); if (asciicode != 'y' && asciicode != 'Y') { - text.Print("** Not updated\n"); + Print("** Not updated\n"); return; } if (nvram->WriteString(nvram_offset + 16, nvram_val) == false) { - text.Print("** Write error\n"); + Print("** Write error\n"); return; } WriteNVRAMCsum(); - text.Print("Updated\n"); + Print("Updated\n"); } // NVRAM 項目を追加 (キー入力後) @@ -668,21 +818,21 @@ Luna88kPROMEmuDevice::CommandNvramNew2(c assert(nvram_key.size() <= 16); assert(nvram_val.size() <= 16); - text.CRLF(); + Putc('\n'); if (asciicode != 'y' && asciicode != 'Y') { - text.Print("** Not added\n"); + Print("** Not added\n"); return; } if (nvram->WriteString(nvram_offset, nvram_key) == false || nvram->WriteString(nvram_offset + 16, nvram_val) == false) { - text.Print("** Write error\n"); + Print("** Write error\n"); return; } WriteNVRAMCsum(); - text.Print("Added\n"); + Print("Added\n"); } // NVRAM 項目削除 @@ -699,13 +849,13 @@ Luna88kPROMEmuDevice::CommandNvramDelete putmsg(1, "nvram Delete |%s|", key.c_str()); if (key.size() > 16) { - text.Print("** key too long\n"); + Print("** key too long\n"); return; } uint32 offset = FindNVRAM(key); if (offset == 0) { - text.Print("** Not found\n"); + Print("** Not found\n"); return; } @@ -748,15 +898,15 @@ Luna88kPROMEmuDevice::CommandNvramInit2( { assert(prompt_idx == PROMPT_NVRAM_INIT); - text.CRLF(); + Putc('\n'); if (asciicode != 'y' && asciicode != 'Y') { - text.Print("** Not initialized\n"); + Print("** Not initialized\n"); return; } InitNVRAM(); - text.Print("** Initialized\n"); + Print("** Initialized\n"); } // NVRAM を初期化する @@ -795,25 +945,25 @@ Luna88kPROMEmuDevice::GetNVRAM(bootinfo_ // dkpart (パーティション) は boot_partition (0..) // dkfile (ファイル名) は boot_filename // sd 以外のデバイスは未対応 - bootinfo.dkunit = nvram->Peek(0x0070) - '0'; - bootinfo.dkpart = nvram->Peek(0x0090) - '0'; + bootinfo.dkunit = nvram->PeekPort(0x0070) - '0'; + bootinfo.dkpart = nvram->PeekPort(0x0090) - '0'; bootinfo.dkfile = nvram->PeekString(0x00b0); - putmsg(1, "%s unit=%d", __func__, bootinfo.dkunit); + putmsg(1, "%s unit=%u", __func__, bootinfo.dkunit); putmsg(1, "%s part=%c", __func__, bootinfo.dkpart + 'a'); putmsg(1, "%s file=|%s|", __func__, bootinfo.dkfile.c_str()); } // コールバック関数 (C のグローバル関数) bool -luna88k_fline_callback(m88kcpu *cpu, void *arg) +luna88k_fline_callback(MPU88xx0Device *cpu, void *arg) { - Luna88kPROMEmuDevice *dev = (Luna88kPROMEmuDevice *)arg; + auto *dev = reinterpret_cast(arg); return dev->FLineCallback(cpu); } // コールバック関数 (こっちが実体) bool -Luna88kPROMEmuDevice::FLineCallback(m88kcpu *cpu) +Luna88kPROMEmuDevice::FLineCallback(MPU88xx0Device *cpu) { // 111111_DDDDDSSSSS_000001_00nnnnnnnn // @@ -823,25 +973,25 @@ Luna88kPROMEmuDevice::FLineCallback(m88k // すべてこちらで責任持って処理すること。 // 戻り値がある DOS call では戻り値は rD に格納。 - uint32 D = (cpu->opX >> 21) & 0x1f; - uint32 S1 = (cpu->opX >> 16) & 0x1f; - uint32 num = cpu->opX & 0xff; + uint32 D = (cpu->reg.opX >> 21) & 0x1f; + uint32 S1 = (cpu->reg.opX >> 16) & 0x1f; + uint32 num = cpu->reg.opX & 0xff; switch (num) { case 0x00: // _EXIT break; case 0x01: // _GETCHAR - cpu->r[D] = getchar(); + cpu->reg.r[D] = Getc(); break; case 0x02: // _PUTCHAR - putchar(cpu->r[S1++] & 0xffff); + Putc(cpu->reg.r[S1++] & 0xff); break; default: - putlog(0, "未実装 DOS call #$%02x", num); - cpu->r[D] = 0xffffffff; + putlog(0, "DOS call #$%02x (NOT IMPLEMENTED)", num); + cpu->reg.r[D] = 0xffffffff; break; }