--- nono/vm/ram.cpp 2026/04/29 17:04:45 1.1.1.5 +++ nono/vm/ram.cpp 2026/04/29 17:04:50 1.1.1.7 @@ -6,9 +6,11 @@ #include "ram.h" #include "aout.h" +#include "autofd.h" #include "elf.h" #include "iodevstream.h" #include "mainapp.h" +#include "mpu.h" #include #include #include @@ -24,17 +26,12 @@ std::unique_ptr gRAM; // バイトアクセス、ワードアクセス時は HLB(), HLW() マクロを使用のこと。 std::unique_ptr ram; -// RAM 容量(バイト単位)であり ram[] の確保したバイト数 -int ram_size; - RAMDevice::RAMDevice() { logname = "ram"; devname = "MainRAM"; devaddr = 0; - ram_size = 0; - // アクセスウェイト switch (gMainApp.GetVMType()) { case VMTYPE_LUNA88K: @@ -61,7 +58,6 @@ RAMDevice::RAMDevice() RAMDevice::~RAMDevice() { - ram_size = 0; } bool @@ -71,17 +67,65 @@ RAMDevice::Init() const ConfigItem& item = gConfig->Find("ram-size"); ram_size_MB = item.AsInt(); - // XXX 上限は適当。 - // LUNA は空間は 1024MB 分あるが末尾に Null と BusErr 領域が必要なはずで - // メモリの上限は 1023MB くらいのはず。 - if (ram_size_MB < 1 || ram_size_MB > 1023) { + if (ram_size_MB < 1) { item.Err(); return false; } + // 下限以外の制約は機種による + switch (gMainApp.GetVMType()) { + case VMTYPE_X68030: + // メインメモリは 12MB まで。 + // 内部は 8KB 単位で処理できるが設定が 1MB 単位。 + if (ram_size_MB > 12) { + item.Err(); + return false; + } + break; + + case VMTYPE_LUNA1: + // 16MB までは、実機に合わせて 4MB 単位とする。 + // see http://wiki.netbsd.org/ports/luna68k/luna68k_info/#hardware + // + // 16MB を超える増設 (or 魔改造) 部分は 1MB 単位で 255MB までとする。 + // 実 ROM のメモリチェックは 32KB だかそのくらいずつ行われ、 + // 最後に番兵として Null 領域 (BusError ではなく) が必要。 + // 現行の構成をあまり変えない範囲なら厳密には (256MB - 32KB) が + // 上限だが、設定ファイルからの指定が 1MB 単位なので 255MB。 + if (ram_size_MB <= 16) { + if (ram_size_MB % 4 != 0) { + item.Err(); + return false; + } + } else if (ram_size_MB > 255) { + item.Err(); + return false; + } + break; + + case VMTYPE_LUNA88K: + // 64MB までは、実機に合わせて 16MB 単位とする。 + // + // 64MB を超える増設 (or 魔改造) 部分は未確認。 + if (ram_size_MB <= 64) { + if (ram_size_MB % 16 != 0) { + item.Err(); + return false; + } + } else if (ram_size_MB > 255) { + item.Err(); + return false; + } + break; + + default: + __unreachable(); + } ram_size = ram_size_MB * 1024 * 1024; ram.reset(new uint8[ram_size]); + // devtable[] への RAM デバイスの配置はこの後 VM::Init 側で行っている + return true; } @@ -101,6 +145,7 @@ RAMDevice::Read8(uint32 addr) gMPU->AddCycle(read_wait); uint32 data; data = ram[HLB(addr)]; + putlog(4, "$%08x.B -> $%02x", addr, data); return data; } @@ -110,6 +155,7 @@ RAMDevice::Read16(uint32 addr) gMPU->AddCycle(read_wait); uint32 data; data = *(uint16 *)&ram[HLW(addr)]; + putlog(4, "$%08x.W -> $%04x", addr, data); return data; } @@ -119,6 +165,7 @@ RAMDevice::Read32(uint32 addr) gMPU->AddCycle(read_wait); uint32 data; data = *(uint32 *)&ram[addr]; + putlog(4, "$%08x.L -> $%08x", addr, data); return data; } @@ -127,6 +174,7 @@ RAMDevice::Write8(uint32 addr, uint32 da { gMPU->AddCycle(write_wait); ram[HLB(addr)] = data; + putlog(3, "$%08x.B <- $%02x", addr, data); return 0; } @@ -135,6 +183,7 @@ RAMDevice::Write16(uint32 addr, uint32 d { gMPU->AddCycle(write_wait); *(uint16 *)&ram[HLW(addr)] = data; + putlog(3, "$%08x.W <- $%04x", addr, data); return 0; } @@ -143,6 +192,7 @@ RAMDevice::Write32(uint32 addr, uint32 d { gMPU->AddCycle(write_wait); *(uint32 *)&ram[addr] = data; + putlog(3, "$%08x.L <- $%08x", addr, data); return 0; } @@ -162,17 +212,15 @@ RAMDevice::SetWait(uint32 wait) // filepath で指定されるホストの実行ファイルをゲストにロードする。 // ファイルのフォーマットは自動で認識する。 -// target_mid は machine id で、実体は a.out の値を流用してはいるが -// ELF であってもこちらで読み替える。 // 戻り値はエントリポイントアドレス。エラーなら 0 を返す。 uint32 -RAMDevice::LoadFile(const char *filepath, uint32 target_mid) +RAMDevice::LoadFile(const char *filepath) { uint8 *file; struct stat st; size_t filesize; uint32 entry; - int fd; + autofd fd; fd = open(filepath, O_RDONLY); if (fd == -1) { @@ -182,20 +230,17 @@ RAMDevice::LoadFile(const char *filepath if (fstat(fd, &st) == -1) { warn("LoadFile \"%s\" fstat failed", filepath); - close(fd); return 0; } if (!S_ISREG(st.st_mode)) { warnx("LoadFile \"%s\" not a regular file", filepath); - close(fd); return 0; } // 今の所マジック判定は最初の4バイトで出来るので if (st.st_size < 4) { warnx("LoadFile \"%s\" file too short", filepath); - close(fd); return 0; } filesize = st.st_size; @@ -203,28 +248,26 @@ RAMDevice::LoadFile(const char *filepath file = (uint8 *)mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0); if (file == MAP_FAILED) { warn("LoadFile \"%s\" mmap failed", filepath); - close(fd); return 0; } // 先頭3バイトの gzip マジックを調べる if (file[0] == 0x1f && file[1] == 0x8b && file[2] == 0x08) { // gzip - entry = LoadGzFile(filepath, file, filesize, target_mid); + entry = LoadGzFile(filepath, file, filesize); } else { // 通常ファイル - entry = LoadFile(filepath, file, filesize, target_mid); + entry = LoadFile(filepath, file, filesize); } munmap(file, filesize); - close(fd); return entry; } // gzip を展開してゲストにロードする。 uint32 -RAMDevice::LoadGzFile(const char *filepath, - const uint8 *infile, size_t infilesize, uint32 target_mid) +RAMDevice::LoadGzFile(const char *filepath, const uint8 *infile, + size_t infilesize) { z_stream z {}; uint32 decompsize; @@ -258,18 +301,15 @@ RAMDevice::LoadGzFile(const char *filepa inflateEnd(&z); // 展開できたのでロード - return LoadFile(filepath, dstbuf.get(), decompsize, target_mid); + return LoadFile(filepath, dstbuf.get(), decompsize); } // file, filesize で示されるバッファを実行ファイルとみなしてゲストにロードする。 // ファイルのフォーマットは自動で認識する。 -// target_mid は machine id で、実体は a.out の値を流用してはいるが -// ELF であってもこちらで読み替える。 // name はここではエラーメッセージとかの表示用でしかない。 // 戻り値はエントリポイントアドレス。エラーなら 0 を返す。 uint32 -RAMDevice::LoadFile(const char *name, const uint8 *file, size_t filesize, - uint32 target_mid) +RAMDevice::LoadFile(const char *name, const uint8 *file, size_t filesize) { uint32 entry = 0; @@ -277,9 +317,9 @@ RAMDevice::LoadFile(const char *name, co const Elf32_Ehdr *ehdr = (const Elf32_Ehdr *)file; const aout_header *aout = (const aout_header *)file; if (memcmp(ehdr->e_ident, "\177ELF", 4) == 0) { - entry = Load_elf32(name, file, filesize, target_mid); + entry = Load_elf32(name, file, filesize); } else if ((be32toh(aout->magic) & 0xffff) == AOUT_OMAGIC) { - entry = Load_aout(name, file, filesize, target_mid); + entry = Load_aout(name, file, filesize); } else { warnx("LoadFile \"%s\" unknown executable file format", name); } @@ -290,8 +330,7 @@ RAMDevice::LoadFile(const char *name, co // ホストの a.out をゲストの RAM に読み込む。 // 戻り値はエントリポイントアドレス。エラーなら 0 を返す。 uint32 -RAMDevice::Load_aout(const char *name, const uint8 *file, size_t filesize, - uint32 target_mid) +RAMDevice::Load_aout(const char *name, const uint8 *file, size_t filesize) { aout_header aout; int copylen; @@ -313,6 +352,14 @@ RAMDevice::Load_aout(const char *name, c aout.datarelsize = be32toh(((const aout_header *)file)->datarelsize); // マジックをチェック + uint32 target_mid; + if (gMainApp.HasVMFeature(VMF_M68K)) { + target_mid = AOUT_MID_M68K; + } else if (gMainApp.HasVMFeature(VMF_M88K)) { + target_mid = AOUT_MID_M88K; + } else { + PANIC("Unknown CPU?"); + } uint32 mid = AOUT_MID(aout.magic); if (mid != target_mid) { warnx("Load_aout \"%s\" machine id mismatch (%03x expected but %03x)", @@ -346,6 +393,11 @@ RAMDevice::Load_aout(const char *name, c /* FALLTHROUGH */ } + if (aout.entry + copylen > GetSize()) { + warnx("Load_aout \"%s\" out of memory in VM", name); + return 0; + } + // 再配置不要なので、text + data を entry に置いて entry に飛ぶだけ IODeviceStream ds(this, aout.entry); const uint8 *src = file + sizeof(aout); @@ -422,26 +474,18 @@ static std::pair pty #define GetElfPTypeStr(x) GetElfStr1(ptype_str, (x)) // ホストの a.out をゲストの RAM に読み込む。 -// target_aout_mid は a.out での machine id (機種情報) で、こっちで ELF の -// machine type に読み替える。 // 戻り値はエントリポイントアドレス。エラーなら 0 を返す。 uint32 -RAMDevice::Load_elf32(const char *name, const uint8 *file, size_t filesize, - uint32 target_aout_mid) +RAMDevice::Load_elf32(const char *name, const uint8 *file, size_t filesize) { uint16 target_elf_mid; - // a.out machine id から ELF machine type への読み替え - switch (target_aout_mid) { - case AOUT_MID_M68K: + if (gMainApp.HasVMFeature(VMF_M68K)) { target_elf_mid = EM_68K; - break; - case AOUT_MID_M88K: + } else if (gMainApp.HasVMFeature(VMF_M88K)) { target_elf_mid = EM_88K; - break; - default: - warnx("Load_elf32 unknown target_aout_mid $%x", target_aout_mid); - return 0; + } else { + PANIC("Unknown CPU?"); } // ヘッダをチェック @@ -456,14 +500,14 @@ RAMDevice::Load_elf32(const char *name, if (loglevel >= 1) { uint n; n = ehdr->e_ident[EI_CLASS]; - putlog("EI_CLASS = $%02x (%s)", n, GetElfClassStr(n)); + putlogn("EI_CLASS = $%02x (%s)", n, GetElfClassStr(n)); n = ehdr->e_ident[EI_DATA]; - putlog("EI_DATA = $%02x (%s)", n, GetElfDataStr(n)); + putlogn("EI_DATA = $%02x (%s)", n, GetElfDataStr(n)); - putlog("e_type = $%02x (%s)", e_type, GetElfETypeStr(e_type)); - putlog("e_machine = $%02x (%s)", e_machine, + putlogn("e_type = $%02x (%s)", e_type, GetElfETypeStr(e_type)); + putlogn("e_machine = $%02x (%s)", e_machine, GetElfMachineStr(e_machine)); - putlog("e_entry = $%08x", e_entry); + putlogn("e_entry = $%08x", e_entry); } if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) { @@ -489,9 +533,9 @@ RAMDevice::Load_elf32(const char *name, uint16 e_phentsize = elf16toh(ehdr->e_phentsize); uint16 e_phnum = elf16toh(ehdr->e_phnum); if (loglevel >= 1) { - putlog("e_phoff = $%08x", e_phoff); - putlog("e_phentsize = $%04x", e_phentsize); - putlog("e_phnum = %d", e_phnum); + putlogn("e_phoff = $%08x", e_phoff); + putlogn("e_phentsize = $%04x", e_phentsize); + putlogn("e_phnum = %d", e_phnum); } if (e_phentsize != sizeof(Elf32_Phdr)) { warnx("Load_elf32 \"%s\" unknown program header size 0x%x", @@ -511,15 +555,20 @@ RAMDevice::Load_elf32(const char *name, uint32 p_filesz = elf32toh(phdr->p_filesz); // 読み込み元ファイルサイズ uint32 p_memsz = elf32toh(phdr->p_memsz); // 書き込み先サイズ if (loglevel >= 1) { - putlog("[%d] p_type = $%08x (%s)", j, + putlogn("[%d] p_type = $%08x (%s)", j, p_type, GetElfPTypeStr(p_type)); - putlog(" p_offset = $%08x", p_offset); - putlog(" p_vaddr = $%08x", p_vaddr); - putlog(" p_filesz = $%08x", p_filesz); - putlog(" p_memsz = $%08x", p_memsz); + putlogn(" p_offset = $%08x", p_offset); + putlogn(" p_vaddr = $%08x", p_vaddr); + putlogn(" p_filesz = $%08x", p_filesz); + putlogn(" p_memsz = $%08x", p_memsz); } if (p_type == PT_LOAD) { + if (p_vaddr + p_memsz > GetSize()) { + warn("Load_elf32 \"%s\" out of memory in VM", name); + return 0; + } + ds.SetAddr(p_vaddr); const uint8 *src = file + p_offset; int i = 0;