--- nono/vm/ram.cpp 2026/04/29 17:04:32 1.1.1.2 +++ nono/vm/ram.cpp 2026/04/29 17:04:45 1.1.1.5 @@ -1,6 +1,7 @@ // // nono -// Copyright (C) 2018 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // #include "ram.h" @@ -16,13 +17,15 @@ // // メインメモリ // + +std::unique_ptr gRAM; + // RAM はロングワード単位でホストバイトオーダ配置なので、 // バイトアクセス、ワードアクセス時は HLB(), HLW() マクロを使用のこと。 -// - std::unique_ptr ram; + +// RAM 容量(バイト単位)であり ram[] の確保したバイト数 int ram_size; -std::unique_ptr gRAM; RAMDevice::RAMDevice() { @@ -31,6 +34,29 @@ RAMDevice::RAMDevice() devaddr = 0; ram_size = 0; + + // アクセスウェイト + switch (gMainApp.GetVMType()) { + case VMTYPE_LUNA88K: + // 取扱説明書p.21 に、CMMU からのアクセスタイムが + // リード(4byte)で 200ns、バーストリード(16byte)で 320ns + // ライト(4byte)で 160ns、バーストライト(16byte)で 280ns とある。 + // 25MHz は 40ns/clockで、この数値は CMMU からのアクセス全体の時間 + // らしいので、ここで加算するウェイトはそれから 2クロック引いたもの。 + read_wait = 3; + write_wait = 2; + break; + case VMTYPE_LUNA1: + // LUNA-I は1ウェイト。 + read_wait = 1; + write_wait = 1; + break; + + case VMTYPE_X68030: + default: + // X68030 ではシステムポートが設定するのでここでは不要。 + break; + } } RAMDevice::~RAMDevice() @@ -41,21 +67,38 @@ RAMDevice::~RAMDevice() bool RAMDevice::Init() { - // とりあえず。実際は設定ファイルから - if (gMainApp.GetVMType() == VMTYPE_LUNA || - gMainApp.GetVMType() == VMTYPE_LUNA88K) { - ram_size = 16 * 1024 * 1024; - } else { - ram_size = 12 * 1024 * 1024; + int ram_size_MB; + + 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) { + item.Err(); + return false; } + + ram_size = ram_size_MB * 1024 * 1024; ram.reset(new uint8[ram_size]); return true; } +void +RAMDevice::ResetHard() +{ + // X68030 ではリセットで戻るはず。(LUNA は固定なので関係ない) + if (gMainApp.GetVMType() == VMTYPE_X68030) { + read_wait = 0; + write_wait = 0; + } +} + uint64 RAMDevice::Read8(uint32 addr) { + gMPU->AddCycle(read_wait); uint32 data; data = ram[HLB(addr)]; return data; @@ -64,6 +107,7 @@ RAMDevice::Read8(uint32 addr) uint64 RAMDevice::Read16(uint32 addr) { + gMPU->AddCycle(read_wait); uint32 data; data = *(uint16 *)&ram[HLW(addr)]; return data; @@ -72,6 +116,7 @@ RAMDevice::Read16(uint32 addr) uint64 RAMDevice::Read32(uint32 addr) { + gMPU->AddCycle(read_wait); uint32 data; data = *(uint32 *)&ram[addr]; return data; @@ -80,6 +125,7 @@ RAMDevice::Read32(uint32 addr) uint64 RAMDevice::Write8(uint32 addr, uint32 data) { + gMPU->AddCycle(write_wait); ram[HLB(addr)] = data; return 0; } @@ -87,6 +133,7 @@ RAMDevice::Write8(uint32 addr, uint32 da uint64 RAMDevice::Write16(uint32 addr, uint32 data) { + gMPU->AddCycle(write_wait); *(uint16 *)&ram[HLW(addr)] = data; return 0; } @@ -94,6 +141,7 @@ RAMDevice::Write16(uint32 addr, uint32 d uint64 RAMDevice::Write32(uint32 addr, uint32 data) { + gMPU->AddCycle(write_wait); *(uint32 *)&ram[addr] = data; return 0; } @@ -104,6 +152,14 @@ RAMDevice::Peek8(uint32 addr) return ram[HLB(addr)]; } +// アクセスウェイトを設定 (X68030 でシステムポートから呼ばれる) +void +RAMDevice::SetWait(uint32 wait) +{ + read_wait = wait; + write_wait = wait; +} + // filepath で指定されるホストの実行ファイルをゲストにロードする。 // ファイルのフォーマットは自動で認識する。 // target_mid は machine id で、実体は a.out の値を流用してはいるが @@ -175,7 +231,7 @@ RAMDevice::LoadGzFile(const char *filepa int rv; // gzip なら(?) ファイルの末尾4バイトが (LE で?) 展開後サイズ(?)。 - decompsize = le32toh(*(uint32 *)&infile[infilesize - 4]); + decompsize = le32toh(*(const uint32 *)&infile[infilesize - 4]); std::unique_ptr dstbuf(new uint8[decompsize]); // gzip 展開 @@ -189,7 +245,7 @@ RAMDevice::LoadGzFile(const char *filepa return 0; } - z.next_in = (Bytef *)infile; + z.next_in = const_cast(infile); z.avail_in = infilesize - 4; z.next_out = dstbuf.get(); z.avail_out = decompsize; @@ -218,8 +274,8 @@ RAMDevice::LoadFile(const char *name, co uint32 entry = 0; // フォーマットだけ判定して分岐 - Elf32_Ehdr *ehdr = (Elf32_Ehdr *)file; - aout_header *aout = (aout_header *)file; + 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); } else if ((be32toh(aout->magic) & 0xffff) == AOUT_OMAGIC) { @@ -247,14 +303,14 @@ RAMDevice::Load_aout(const char *name, c // ヘッダを読み込む // BigEndian しかターゲットにしてないので最初から全部変換しとく - aout.magic = be32toh(((aout_header *)file)->magic); - aout.textsize = be32toh(((aout_header *)file)->textsize); - aout.datasize = be32toh(((aout_header *)file)->datasize); - aout.bsssize = be32toh(((aout_header *)file)->bsssize); - aout.symsize = be32toh(((aout_header *)file)->symsize); - aout.entry = be32toh(((aout_header *)file)->entry); - aout.textrelsize = be32toh(((aout_header *)file)->textrelsize); - aout.datarelsize = be32toh(((aout_header *)file)->datarelsize); + aout.magic = be32toh(((const aout_header *)file)->magic); + aout.textsize = be32toh(((const aout_header *)file)->textsize); + aout.datasize = be32toh(((const aout_header *)file)->datasize); + aout.bsssize = be32toh(((const aout_header *)file)->bsssize); + aout.symsize = be32toh(((const aout_header *)file)->symsize); + aout.entry = be32toh(((const aout_header *)file)->entry); + aout.textrelsize = be32toh(((const aout_header *)file)->textrelsize); + aout.datarelsize = be32toh(((const aout_header *)file)->datarelsize); // マジックをチェック uint32 mid = AOUT_MID(aout.magic); @@ -306,6 +362,65 @@ RAMDevice::Load_aout(const char *name, c #define elf32toh(x) ((ehdr->e_ident[EI_DATA] == ELFDATA2MSB) ? \ be32toh(x) : le32toh(x)) +// XXX このへん、そのうちきれいにする +#define GetElfStr1(array, v) GetElfStr(array, countof(array), v) +static const char * +GetElfStr(const std::pair *map, uint mapcount, uint v) +{ + for (int i = 0; i < mapcount; i++) { + if (map[i].first == v) { + return map[i].second; + } + } + return "?"; +} + +static std::pair class_str[] = { + { ELFCLASSNONE, "NONE" }, + { ELFCLASS32, "32bit" }, + { ELFCLASS64, "64bit" }, +}; +#define GetElfClassStr(x) GetElfStr1(class_str, (x)) + +static std::pair data_str[] = { + { ELFDATANONE, "NONE" }, + { ELFDATA2LSB, "LE" }, + { ELFDATA2MSB, "BE" }, +}; +#define GetElfDataStr(x) GetElfStr1(data_str, (x)) + +static std::pair etype_str[] = { + { ET_NONE, "ET_NONE" }, + { ET_REL, "ET_REL" }, + { ET_EXEC, "ET_EXEC" }, + { ET_DYN, "ET_DYN" }, + { ET_CORE, "ET_CORE" }, +}; +#define GetElfETypeStr(x) GetElfStr1(etype_str, (x)) + +static std::pair machine_str[] = { + // 多いので関係分のみ + { EM_NONE, "NONE" }, + { EM_68K, "Motorola 68000" }, + { EM_88K, "Motorola 88000" }, +}; +#define GetElfMachineStr(x) GetElfStr1(machine_str, (x)) + +static std::pair ptype_str[] = { + { PT_NULL, "PT_NULL" }, + { PT_LOAD, "PT_LOAD" }, + { PT_DYNAMIC, "PT_DYNAMIC" }, + { PT_INTERP, "PT_INTERP" }, + { PT_NOTE, "PT_NOTE" }, + { PT_SHLIB, "PT_SHLIB" }, + { PT_PHDR, "PT_PHDR" }, + { PT_GNU_RELRO, "PT_GNU_RELRO" }, // GNU specific + { PT_OPENBSD_RANDOMIZE, "PT_OPENBSD_RANDOMIZE" }, + { PT_OPENBSD_WXNEEDED, "PT_OPENBSD_WXNEEDED" }, + { PT_OPENBSD_BOOTDATA, "PT_OPENBSD_BOOTDATA" }, +}; +#define GetElfPTypeStr(x) GetElfStr1(ptype_str, (x)) + // ホストの a.out をゲストの RAM に読み込む。 // target_aout_mid は a.out での machine id (機種情報) で、こっちで ELF の // machine type に読み替える。 @@ -338,11 +453,18 @@ RAMDevice::Load_elf32(const char *name, uint16 e_type = elf16toh(ehdr->e_type); uint16 e_machine = elf16toh(ehdr->e_machine); uint32 e_entry = elf32toh(ehdr->e_entry); - putlog(1, "EI_CLASS = $%02x", ehdr->e_ident[EI_CLASS]); - putlog(1, "EI_DATA = $%02x", ehdr->e_ident[EI_DATA]); - putlog(1, "e_type = $%02x", e_type); - putlog(1, "e_machine = $%02x", e_machine); - putlog(1, "e_entry = $%08x", e_entry); + if (loglevel >= 1) { + uint n; + n = ehdr->e_ident[EI_CLASS]; + putlog("EI_CLASS = $%02x (%s)", n, GetElfClassStr(n)); + n = ehdr->e_ident[EI_DATA]; + putlog("EI_DATA = $%02x (%s)", n, GetElfDataStr(n)); + + putlog("e_type = $%02x (%s)", e_type, GetElfETypeStr(e_type)); + putlog("e_machine = $%02x (%s)", e_machine, + GetElfMachineStr(e_machine)); + putlog("e_entry = $%08x", e_entry); + } if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) { warnx("Load_elf32 \"%s\" not ELF32 format", name); @@ -366,9 +488,11 @@ RAMDevice::Load_elf32(const char *name, uint32 e_phoff = elf32toh(ehdr->e_phoff); uint16 e_phentsize = elf16toh(ehdr->e_phentsize); uint16 e_phnum = elf16toh(ehdr->e_phnum); - putlog(1, "e_phoff = $%08x", e_phoff); - putlog(1, "e_phentsize = $%04x", e_phentsize); - putlog(1, "e_phnum = %d", e_phnum); + if (loglevel >= 1) { + putlog("e_phoff = $%08x", e_phoff); + putlog("e_phentsize = $%04x", e_phentsize); + putlog("e_phnum = %d", e_phnum); + } if (e_phentsize != sizeof(Elf32_Phdr)) { warnx("Load_elf32 \"%s\" unknown program header size 0x%x", name, e_phentsize); @@ -386,11 +510,14 @@ RAMDevice::Load_elf32(const char *name, uint32 p_vaddr = elf32toh(phdr->p_vaddr); // 仮想アドレス uint32 p_filesz = elf32toh(phdr->p_filesz); // 読み込み元ファイルサイズ uint32 p_memsz = elf32toh(phdr->p_memsz); // 書き込み先サイズ - putlog(1, "[%d] p_type = $%08x", j, p_type); - putlog(1, "[%d] p_offset = $%08x", j, p_offset); - putlog(1, "[%d] p_vaddr = $%08x", j, p_vaddr); - putlog(1, "[%d] p_filesz = $%08x", j, p_filesz); - putlog(1, "[%d] p_memsz = $%08x", j, p_memsz); + if (loglevel >= 1) { + putlog("[%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); + } if (p_type == PT_LOAD) { ds.SetAddr(p_vaddr); @@ -402,8 +529,16 @@ RAMDevice::Load_elf32(const char *name, for (; i < p_memsz; i++) { ds.Write8(0); } + } else if (p_type == PT_NOTE) { + // ベンダー文字列みたいなのらしいので無視してよい + continue; + } else if (p_type == PT_OPENBSD_RANDOMIZE) { + // XXX どうする? + continue; + } else { - warnx("Load_elf32 \"%s\" p_type %d not supported", name, p_type); + warnx("Load_elf32 \"%s\" p_type 0x%x(%s) not supported", + name, p_type, GetElfPTypeStr(p_type)); return 0; } }