--- nono/vm/romemu_luna1.cpp 2026/04/29 17:05:09 1.1.1.6 +++ nono/vm/romemu_luna1.cpp 2026/04/29 17:05:28 1.1.1.10 @@ -9,23 +9,28 @@ // // 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 のブートページ切り替え用プロキシ) // ROM マップ // @@ -50,21 +55,24 @@ // "0011223344AA",0 #include "romemu_luna1.h" +#include "bt45x.h" #include "ethernet.h" -#include "lunafb.h" +#include "iodevstream.h" #include "mainapp.h" +#include "mainram.h" #include "memorystream.h" #include "mk48t02.h" #include "mpu680x0.h" -#include "pio.h" -#include "ram.h" +#include "sysclk.h" // コンストラクタ Luna1PROMEmuDevice::Luna1PROMEmuDevice() { + machine_name = "LUNA-I"; + // LUNA-I の ROM の特定アドレスを読み出す人がいるので、 // サイズは実 ROM と同じにしておく必要がある。 - devlen = 128 * 1024; + uint devlen = 128 * 1024; imagebuf.reset(new uint8 [devlen]); memset(&imagebuf[0], 0, devlen); mem = &imagebuf[0]; @@ -77,168 +85,148 @@ Luna1PROMEmuDevice::~Luna1PROMEmuDevice( { } +// 初期化 bool Luna1PROMEmuDevice::Init() { - // 親クラス if (inherited::Init() == false) { return false; } - MemoryStreamBE ms(imagebuf.get()); - ms.Write32(0x00002000); // +00: リセットベクタ SP - ms.Write32(0x41000400); // +04: リセットベクタ PC - ms.Write32(0); // +08 - ms.Write32(0x41000010); // +0c: メモリサイズ格納先 + SysClkDevice *sysclk = GetSysClkDevice(); + + MemoryStreamBE roms(imagebuf.get()); + roms.Write4(0x00002000); // +00: リセットベクタ SP + roms.Write4(0x41000400); // +04: リセットベクタ PC + roms.Write4(0); // +08 + roms.Write4(0x41000010); // +0c: メモリサイズ格納先 // +10: 値は ROM_Init() でセット - ms.SetOffset(0xb8); - ms.Write32(0x410000bc); // +b8: プレーンマスク格納先 + roms.SetOffset(0xb8); + roms.Write4(0x410000bc); // +b8: プレーンマスク格納先 // +bc: 値は ROM_Init() でセット // リセット時に実行する命令 - ms.SetOffset(0x400); - ms.Write16(0x13c0); // move.b d0,#0x41000000 ; RAMに切り替え - ms.Write32(0x41000000); - ms.Write16(0x2039); // move.l (ROMIO_INIT),d0 - ms.Write32(ROMIO_INIT); - ms.Write16(0x660c); // bne _go ; 起動先あればGO + roms.SetOffset(0x400); + roms.Write2(0x13c0); // move.b d0,#0x41000000 ; RAMに切り替え + roms.Write4(0x41000000); + roms.Write2(0x2039); // move.l (ROMIO_INIT),d0 + roms.Write4(ROMIO_INIT); + roms.Write2(0x2039); // move.l (ROMIO_LOAD),d0 + roms.Write4(ROMIO_LOAD); + roms.Write2(0x72ff); // moveq.l #0xffffffff,d1 + roms.Write2(0x6004); // bra _entry //_prompt: - uint32 _prompt = ms.GetOffset(); - ms.Write32(0x4e722500); // stop #0x2500 ; キー入力を待つ - ms.Write16(0x2039); // move.l (ROMIO_ENTRY),d0 - ms.Write32(ROMIO_ENTRY); // ; キー入力処理 - ms.Write16(0x67f4); // beq _prompt ; 起動先なければループ + uint32 _prompt = roms.GetOffset(); + roms.Write4(0x4e722500); // stop #0x2500 ; キー入力を待つ + //_entry: + roms.Write2(0x2039); // move.l (ROMIO_ENTRY),d0 + roms.Write4(ROMIO_ENTRY); // ; エントリポイント取得 + roms.Write2(0xb081); // cmp.l d1,d0 + roms.Write2(0x67f2); // beq _prompt ; 起動先なければループ //_go: - ms.Write16(0x2040); // move.l d0,a0 ; ジャンプ先を a0 にセット - ms.Write16(0x2039); // move.l (ROMIO_CLOSE),d0 - ms.Write32(ROMIO_CLOSE); // ; 後始末 - ms.Write16(0x4ed0); // jmp (a0) ; ジャンプ + roms.Write2(0x2040); // move.l d0,a0 ; ジャンプ先を a0 にセット + roms.Write2(0x2039); // move.l (ROMIO_CLOSE),d0 + roms.Write4(ROMIO_CLOSE); // ; 後始末 + roms.Write2(0x4ed0); // jmp (a0) ; ジャンプ + + // Lv5 (Sysclk) 割り込みハンドラ。 + roms.SetOffset(0x500); + roms.Write2(0x13c0); // move.b d0,#0x63000000 ; ACK + roms.Write4(0x63000000); + roms.Write2(0x52b9); // addq.l #1,(ROMIO_CLKCNT) + roms.Write4(ROMIO_CLKCNT); + roms.Write2(0x4e73); // rte // Lv6 (SIO) 割り込みハンドラ // キーボード割り込みが上がったら KEYIN をコールする - ms.SetOffset(0x600); - ms.Write16(0x4ab9); // tst.l (ROMIO_KEYIN) - ms.Write32(ROMIO_KEYIN); // ; キー入力処理 - ms.Write16(0x4e73); // rte + roms.SetOffset(0x600); + roms.Write2(0x4ab9); // tst.l (ROMIO_KEYIN) + roms.Write4(ROMIO_KEYIN); // ; キー入力処理 + roms.Write2(0x4e73); // rte // NMI のハンドラ - ms.SetOffset(0x700); - ms.Write32(0x46fc2700); // move.w #0x2700,sr - ms.Write16(0x91c8); // suba.l a0,a0 - ms.Write32(0x4e7b8801); // movec a0,vbr - ms.Write16(0x2e50); // movea.l (a0),sp - ms.Write16(0x4ef9); // jmp _prompt - ms.Write32(0x41000000 + _prompt); + roms.SetOffset(0x700); + roms.Write4(0x46fc2700); // move.w #0x2700,sr + roms.Write2(0x91c8); // suba.l a0,a0 + roms.Write4(0x4e7b8801); // movec a0,vbr + roms.Write2(0x2e50); // movea.l (a0),sp + roms.Write2(0x4ef9); // jmp _prompt + roms.Write4(0x41000000 + _prompt); // デフォルトのハンドラ - ms.SetOffset(0x800); - ms.Write16(0x4e73); // rte + roms.SetOffset(0x800); + roms.Write2(0x4e73); // rte + + // サブルーチン + roms.SetOffset(0x900); //_ap1: + roms.Write2(0x2039); // move.l (ROMIO_AP1),d0 + roms.Write4(ROMIO_AP1); // + uint freq = sysclk->GetFreq(); + roms.Write2(0x7200 + freq * 3); + // moveq.l #(CLK_FREQ*3),d1 + //@@: + roms.Write4(0x4e722400); // stop #0x2400 ; クロック入力を待つ + roms.Write2(0x2039); // move.l (ROMIO_CLKCNT),d0 + roms.Write4(ROMIO_CLKCNT); + roms.Write2(0xb001); // cmp.b d1,d0 + roms.Write2(0x66f2); // bne @b + roms.Write2(0x2039); // move.l (ROMIO_AP2),d0 + roms.Write4(ROMIO_AP2); // + roms.Write2(0x4e75); // rts // MAC アドレス - const macaddr_t macaddr = gEthernet->GetMacAddr(); + macaddr_t macaddr; + if (EthernetDevice::GetConfigMacAddr(0, &macaddr, false) == false) { + // エラーメッセージは表示済み + return false; + } + putmsg(1, "macaddr=%s", macaddr.ToString(':').c_str()); strcpy((char *)&imagebuf[0x1ffd8], "ENADDR"); memcpy(&imagebuf[0x1ffe0], macaddr.ToString().c_str(), 12); return true; } -// 起動時の処理を行う。 -// 次段実行ファイルの読み込みに成功すればそのエントリポイントを返す。 -// そうでなければ 0 を返すので、プロンプトに処理を移すこと。 -uint32 -Luna1PROMEmuDevice::ROM_Init() +// ROM 起動時の初期化 (機種依存部分) を行う。 +void +Luna1PROMEmuDevice::ROM_InitMD() { - MemoryStreamBE ms(imagebuf.get()); - uint32 entry; - - // デバイスの初期化 (親クラス) - InitDevice(); + IODeviceStream rams(mainram); + MemoryStreamBE roms(imagebuf.get()); // +0010: メモリサイズ - ms.SetOffset(0x10); - ms.Write32(gRAM->GetSize()); + roms.SetOffset(0x10); + roms.Write4(mainram->GetSize()); - // プレーン数(1..4)をプレーンマスク(0x01..0x0f)に - int nplane = gPlaneVRAM->GetPlaneCount(); + // プレーン数(1,4,8)をプレーンマスク(0x01, 0x0f, 0xff)に + int nplane = bt45x->GetPlaneCount(); int planemask = (1 << nplane) - 1; // プレーンマスク。実際は RAM に書き込まれるがとりあえず - ms.SetOffset(0xbc); - ms.Write32(planemask); + roms.SetOffset(0xbc); + roms.Write4(planemask); // RAM にベクタを書き込む - ms.SetOffset(0); - gRAM->Write32(0, ms.Read32()); // ROM のリセットベクタをコピー - gRAM->Write32(4, ms.Read32()); - - for (int i = 2; i < 256; i++) { // デフォルトベクタで埋める - gRAM->Write32(i * 4, 0x41000800); - } - gRAM->Write32(0x78, 0x41000600); // +78: Lv6 割り込み - gRAM->Write32(0x7c, 0x41000700); // +7c: Lv7 割り込み = RESET + roms.SetOffset(0); + rams.SetOffset(0); + rams.Write4(roms.Read4()); // ROM のリセットベクタをコピー + rams.Write4(roms.Read4()); + for (uint i = 2; i < 256; i++) { // デフォルトベクタで埋める + rams.Write4(0x41000800); + } + rams.Write4(0x74, 0x41000500); // +74: Lv5 割り込み + rams.Write4(0x78, 0x41000600); // +78: Lv6 割り込み + rams.Write4(0x7c, 0x41000700); // +7c: Lv7 割り込み = RESET prompt_idx = 0; prompt = default_prompt; - // スクリーンを初期化 - InitScreen(); - - entry = 0; - x_executed = false; - x_entrypoint = 0; - - // 表示環境が整ったところで NVRAM を照合 - if (VerifyNVRAM() == false) { - InitNVRAM(); - errmsg = "NVRAM Initialized."; - // NVRAM をクリアしたら DIPSW によらずプロンプトに降りる - goto done; - } - - // -X file - // DIPSW の状態に関わらず、ファイルをロードし実行する。 - // --load-only file - // DIPSW の状態に関わらず、ファイルをロードし、プロンプトで止まる。 - if (gMainApp.exec_file) { - // ホストのファイルを読み込んでエントリポイントアドレスを - // この Read アクセスの読み出し値として返す。 - // エラーがあればエラーメッセージは呼び出し先側で表示してある。 - x_entrypoint = LoadHostFile(); - if (gMainApp.load_and_exec) { - entry = x_entrypoint; - } - } else { - // DIPSW#1-1 が UP なら自動起動、DOWN なら ROM モニタ起動なので、 - // これを真似する。 - if (gPIO0->IsDIPSW11Up()) { - // NVRAM の設定に従ってブート先を探す。 - errmsg.clear(); - - bootinfo_t bootinfo; - GetNVRAM(bootinfo); - entry = LoadGuestFile(bootinfo); - } - } - - done: - if (entry == 0) { - PrintTitle("LUNA-I"); - if (errmsg.empty() == false) { - Print("** %s\n\n", errmsg.c_str()); - } - ClearPrompt(); - } - - return entry; -} - -// ROM 処理。エントリポイントを返す。 -uint32 -Luna1PROMEmuDevice::ROM_Entry() -{ - if (x_executed && x_entrypoint != 0) { - return x_entrypoint; - } else { - return 0; + uint32 ap = (nvram->PeekPort(0x7fd) << 8) | nvram->PeekPort(0x7fe); + if (__predict_false(0||(ap & 0x3f1f) == 0x0104)) { + auto mpu680x0 = GetMPU680x0Device(mpu); + m68kreg& reg = mpu680x0->reg; + reg.A[7] -= 4; + rams.Write4(reg.A[7], reg.pc); + reg.pc = 0x41000900; } } @@ -267,12 +255,50 @@ Luna1PROMEmuDevice::ROM_Close() // %d7 に howto (0 でよい) // (と %a0 にジャンプ先を入れるがこれは Init() が書き出した ROM // アセンブラコード側で行っている) - m68kcpu *cpu = gMPU680x0->GetCPU(); - RegD(6) = 0xa0060000; - RegD(7) = 0; + m68kreg& reg = GetMPU680x0Device(mpu)->reg; + reg.D[6] = 0xa0060000; + reg.D[7] = 0; +} + +// ファイルをロードする。 +// -X が指定されていればそのホストファイル。 +// そうでなければゲストファイル。この場合引数 fname があれば +// NVRAM で指定されたデバイスから読み込む際のファイル名とする。 +// 空なら NVRAM で指定されたファイル名。 +// ロードできればエントリポイントを返す。 +// ロードできなければ errmsg にメッセージをセットし -1 を返す。 +uint32 +Luna1PROMEmuDevice::LoadFile(const std::string& fname) +{ + uint32 entry = -1; + + if (gMainApp.exec_file) { + // -X が指定されてたらホストプログラムを読み込む。 + entry = LoadHostFile(); + } else { + // NVRAM で指定されるゲストプログラムを読み込む。 + std::string ctlr = nvram->PeekString(0x0030); + if (ctlr == "dk") { // harddisk + bootinfo_t bootinfo; + GetNVRAM(bootinfo); + // 引数で指定されたらファイル名だけ上書き + if (fname.empty() == false) { + bootinfo.dkfile = fname; + } + entry = LoadGuestFile(bootinfo); + + } else if (ctlr == "et" || // netboot + ctlr == "sd" || // cassette tape + ctlr == "fl" ) // floppy + { + errmsg = ctlr + " boot not supported."; + } else { + // 起きないはず + errmsg = ctlr + ": Invalid boot controller name."; + } + } - x_entrypoint = 0; - x_executed = false; + return entry; } // 入力コマンド処理 (入力行が揃ったところで呼ばれる)。 @@ -283,7 +309,7 @@ Luna1PROMEmuDevice::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') { @@ -292,7 +318,7 @@ Luna1PROMEmuDevice::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()); } } @@ -313,7 +339,6 @@ Luna1PROMEmuDevice::Command() Print("Commands\n"); Print( " g [] : load program\n" -" gh : load host program (specified by --load-only option)\n" " h : show this message\n" " k : show/set boot constants\n" " x : execute loaded program\n" @@ -324,10 +349,6 @@ Luna1PROMEmuDevice::Command() CommandG(arg); return; } - if (arg[0] == "gh") { - CommandGH(); - return; - } if (arg[0] == "k") { CommandK(arg); return; @@ -341,70 +362,35 @@ Luna1PROMEmuDevice::Command() } // "g" コマンド -// g なら NVRAM の情報に従ってプログラムをロード。 +// g なら NVRAM の情報 (もしくは -X オプション) に従ってプログラムをロード。 // g なら NVRAM の fname だけ指定の名前を使ってロード。NVRAM には // 書き込まない。 void Luna1PROMEmuDevice::CommandG(const std::vector& arg) { - std::string ctlr = nvram->PeekString(0x0030); - if (ctlr == "dk") { // harddisk - std::string fname {}; - - switch (arg.size()) { - case 1: - break; - case 2: - fname = arg[1]; - break; - default: - Print("** Invalid argument.\n"); - return; - } - errmsg.clear(); - - bootinfo_t bootinfo; - GetNVRAM(bootinfo); - // 引数で指定されたらファイル名だけ上書き - if (!fname.empty()) { - bootinfo.dkfile = fname; - } - x_entrypoint = LoadGuestFile(bootinfo); - if (x_entrypoint == 0) { - Print("** %s\n", errmsg.c_str()); - } - - } else if (ctlr == "et" || // netboot - ctlr == "sd" || // cassette tape - ctlr == "fl" ) // floppy - { - Print("** %s boot not supported.\n", ctlr.c_str()); - return; - } else { - // 起きないはず - Print("** Invalid boot controller name.\n"); - return; - } + std::string fname {}; - if (x_entrypoint) { - Print("Loaded. Entry point = $%08x\n", x_entrypoint); + switch (arg.size()) { + case 1: + break; + case 2: + fname = arg[1]; + break; + default: + Print("** Invalid argument.\n"); + return; } -} -// "gh" コマンド (勝手に拡張) -// -X オプションで指定されたホストプログラムをロードする。 -void -Luna1PROMEmuDevice::CommandGH() -{ - // 指定されてたら読み込む - if (gMainApp.exec_file == NULL) { - Print("** Cannot loaded; host program is not specified.\n"); - return; + entrypoint = LoadFile(fname); + if (entrypoint == -1) { + Print("** %s\n", errmsg.c_str()); + } else { + Print("%s. Entry point = $%08x\n", + (gMainApp.exec_file ? "Host program loaded" : "Loaded"), + entrypoint); } - x_entrypoint = LoadHostFile(); } - // k コマンドのパラメータ表 struct kinfo { uint32 offset; // NVRAM 上のオフセット @@ -578,11 +564,11 @@ Luna1PROMEmuDevice::CommandKInput(const void Luna1PROMEmuDevice::CommandX() { - if (x_entrypoint == 0) { + if (entrypoint == -1) { Print("** Program is not loaded.\n"); - } else { - x_executed = true; + return; } + execute = true; } // NVRAM を初期化する @@ -636,10 +622,10 @@ Luna1PROMEmuDevice::GetNVRAM(bootinfo_t& // DKUNIT が装置番号(今はまだ SCSI ID=6 固定)。 // DKPART が 'a'..'h' でパーティション。 // DKFILE が読み込む次段プログラム (NetBSD では通常 /boot のこと)。 - bootinfo.dkunit = nvram->Peek(0x0090) - '0'; - bootinfo.dkpart = nvram->Peek(0x00b0) - 'a'; + bootinfo.dkunit = nvram->PeekPort(0x0090) - '0'; + bootinfo.dkpart = nvram->PeekPort(0x00b0) - 'a'; bootinfo.dkfile = nvram->PeekString(0x00d0); - putmsg(1, "%s DKUNIT=%d", __func__, bootinfo.dkunit); + putmsg(1, "%s DKUNIT=%u", __func__, bootinfo.dkunit); putmsg(1, "%s DKPART=%c", __func__, bootinfo.dkpart + 'a'); putmsg(1, "%s DKFILE=|%s|", __func__, bootinfo.dkfile.c_str()); }