--- nono/vm/mainram.cpp 2026/04/29 17:05:17 1.1 +++ nono/vm/mainram.cpp 2026/04/29 17:05:37 1.1.1.4 @@ -22,29 +22,6 @@ MainRAMDevice::MainRAMDevice() : inherited(OBJ_MAINRAM) { - // アクセスウェイト - switch (gMainApp.GetVMType()) { - case VMType::LUNA1: - // LUNA-I は1ウェイト。 - read_wait = 1; - write_wait = 1; - break; - - 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::X68030: - default: - // X68030 ではシステムポートが設定するのでここでは不要。 - break; - } } // デストラクタ @@ -56,17 +33,16 @@ MainRAMDevice::~MainRAMDevice() bool MainRAMDevice::Init() { - if (inherited::Init() == false) { - return false; - } - const ConfigItem& item = gConfig->Find("ram-size"); int ram_size_MB = item.AsInt(); if (ram_size_MB < 1) { item.Err(); return false; } - // 下限以外の制約は機種による + + uint32 clock_nsec = mpu->GetClock_nsec(); + + // メモリの下限以外の制約は機種による。 switch (gMainApp.GetVMType()) { case VMType::X68030: // メインメモリは 12MB まで。 @@ -75,27 +51,46 @@ MainRAMDevice::Init() item.Err("Configurable maximum main memory size is 12 [MB]"); 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。 + // 16MB を超える増設 (or 魔改造) 部分は 1MB 単位とする。 if (ram_size_MB <= 16) { if (ram_size_MB % 4 != 0) { item.Err("For 16MB or less, must be specified in 4 [MB] unit"); return false; } - } else if (ram_size_MB > 255) { - item.Err("Configurable maximum RAM size is 255 [MB]"); + } else if (ram_size_MB > 512) { + item.Err("Configurable maximum RAM size is 512 [MB]"); return false; } + + // 実 ROM のメモリチェックは 32KB だかそのくらいずつ行われ、 + // 最後に番兵として NOP 領域 (BusError ではなく) が必要。 + // 現行の構成をあまり変えない範囲なら厳密には (256MB - 32KB) が + // 上限だが、サイズ指定が 1MB 単位なので 255MB を上限にする。 + auto prom = dynamic_cast(GetPROMDevice()); + if (prom) { + if (ram_size_MB > 255) { + item.Err("(Warning) Maximum RAM size with PROM is 255[MB]; " + "Clip RAM size to 255[MB]."); + ram_size_MB = 255; + } + } + + // 通常サイクルは1ウェイトらしい? + // バーストウェイトは LUNA-88K の取説からの推論。 + normal_wait = busdata::Wait(1 * clock_nsec); + burst_wait = busdata::Wait(4 * clock_nsec); break; + } case VMType::LUNA88K: { @@ -107,8 +102,8 @@ MainRAMDevice::Init() item.Err("For 64MB or less, must be specified in 16 [MB] unit"); return false; } - } else if (ram_size_MB > 255) { - item.Err("Configurable maximum RAM size is 255 [MB]"); + } else if (ram_size_MB > 512) { + item.Err("Configurable maximum RAM size is 512 [MB]"); return false; } @@ -118,25 +113,60 @@ MainRAMDevice::Init() auto prom = dynamic_cast(GetPROMDevice()); if (prom && prom->GetROMVer() == 120) { if (ram_size_MB > 240) { - item.Err("Maximum RAM size with PROM 1.20 is 240 [MB]"); - return false; + item.Err("(warning) Maximum RAM size with PROM 1.20 is 240[MB];" + " Clip RAM size to 240[MB]."); + ram_size_MB = 240; } } + // 取扱説明書p.21 に、CMMU からのアクセスタイムが + // リード(4byte)で 200ns、バーストリード(16byte)で 320ns + // ライト(4byte)で 160ns、バーストライト(16byte)で 280ns とある。 + // 25MHz は 40ns/clock なのでそれぞれ、 + // リード(4byte)で 5 clock、バーストリード(16byte)で 8 clock + // ライト(4byte)で 4 clock、バーストライト(16byte)で 7 clock となる。 + // これは CMMU 以降のアクセスタイムらしいので、 + // リード3クロック、ライト2クロック引いたものをウェイトとしてみる? + // XXX 要確認。 + normal_wait = busdata::Wait(2 * clock_nsec); + burst_wait = busdata::Wait(5 * clock_nsec); break; } case VMType::NEWS: - // XXX とりあえず - memset(&mainram[0], 0, ram_size); + // 知らんけど 1クロックくらい入れておく。 + normal_wait = busdata::Wait(1 * clock_nsec); + // NWS-1750 はバースト転送をサポートしていないようだ。 + // http://www.ceres.dti.ne.jp/tsutsui/netbsd/port-news68k.html#19991203 + break; + + case VMType::VIRT68K: + // 1MB 単位で指定できる必要もないだろうから 16MB 単位に切り上げる。 + ram_size_MB = roundup(ram_size_MB, 16); + + // 最後の 16MB ブロックが I/O 空間なので、 + // メモリは最大で 4096 - 16 = 4080MB まで積める。 + if (ram_size_MB > 4080) { + item.Err("Configurable maximum RAM size is 4080 [MB]"); + return false; + } + + // ウェイト不要。 + normal_wait = busdata::Wait(0); + burst_wait = busdata::Wait(0); break; default: - PANIC("corrupted vmtype=%s", gMainApp.GetVMName().c_str()); + PANIC("vmtype=%s not configured", gMainApp.GetVMTypeStr().c_str()); } ram_size = ram_size_MB * 1024 * 1024; - mainram.reset(new uint8[ram_size]); + mainram.reset(new(std::nothrow) uint8[ram_size]); + if ((bool)mainram == false) { + errno = ENOMEM; + warn("MainRAM(%u bytes)", ram_size); + return false; + } // Human68k モードならここで一度だけメモリをゼロクリアする。 // この時は Human68k::Init() が MainRAM にプログラムを転送するため @@ -150,8 +180,6 @@ MainRAMDevice::Init() memset(&mainram[0], 0, ram_size); } - // devtable[] への RAM デバイスの配置はこの後 VM::Init 側で行っている - return true; } @@ -172,106 +200,190 @@ MainRAMDevice::ResetHard(bool poweron) // X68030 ではリセットで戻るはず。(LUNA は固定なので関係ない) if (gMainApp.IsX68030()) { - read_wait = 0; - write_wait = 0; + normal_wait = busdata::Wait(0); } } -uint64 -MainRAMDevice::Read8(uint32 addr) +busdata +MainRAMDevice::Read(busaddr addr) { - mpu->AddCycle(read_wait); - uint32 data; - data = mainram[HLB(addr)]; - putlog(4, "$%08x.B -> $%02x", addr, data); + uint32 paddr = addr.Addr(); + busdata data; + + data = *(uint32 *)&mainram[paddr & ~3]; + data |= normal_wait; + data |= BusData::Size4; return data; } -uint64 -MainRAMDevice::Read16(uint32 addr) +busdata +MainRAMDevice::Write(busaddr addr, uint32 data) { - mpu->AddCycle(read_wait); - uint32 data; - data = *(uint16 *)&mainram[HLW(addr)]; - putlog(4, "$%08x.W -> $%04x", addr, data); - return data; + uint32 paddr = addr.Addr(); + uint32 reqsize = addr.GetSize(); + uint32 datasize = std::min(4 - (paddr & 3U), reqsize); + + if (datasize == 4) { + *(uint32 *)&mainram[paddr] = data; + } else if (datasize == 1) { + data >>= (reqsize - datasize) * 8; + mainram[HLB(paddr)] = data; + } else { + data >>= (reqsize - datasize) * 8; + for (int i = datasize - 1; i >= 0; i--) { + mainram[HLB(paddr + i)] = data; + data >>= 8; + } + } + + busdata r = normal_wait; + r |= BusData::Size4; + return r; } -uint64 -MainRAMDevice::Read32(uint32 addr) +busdata +MainRAMDevice::ReadBurst16(busaddr addr, uint32 *dst) { - mpu->AddCycle(read_wait); - uint32 data; - data = *(uint32 *)&mainram[addr]; - putlog(4, "$%08x.L -> $%08x", addr, data); - return data; + uint32 paddr = addr.Addr(); + putlog(4, "$%08x BurstRead", paddr); + memcpy(dst, &mainram[paddr], 16); + return burst_wait; } -uint64 -MainRAMDevice::Write8(uint32 addr, uint32 data) +busdata +MainRAMDevice::WriteBurst16(busaddr addr, const uint32 *src) { - mpu->AddCycle(write_wait); - mainram[HLB(addr)] = data; - putlog(3, "$%08x.B <- $%02x", addr, data); - return 0; + uint32 paddr = addr.Addr(); + putlog(4, "$%08x BurstWrite", paddr); + memcpy(&mainram[paddr], src, 16); + return burst_wait; } -uint64 -MainRAMDevice::Write16(uint32 addr, uint32 data) +busdata +MainRAMDevice::Peek1(uint32 addr) { - mpu->AddCycle(write_wait); - *(uint16 *)&mainram[HLW(addr)] = data; - putlog(3, "$%08x.W <- $%04x", addr, data); - return 0; + return mainram[HLB(addr)]; } -uint64 -MainRAMDevice::Write32(uint32 addr, uint32 data) +bool +MainRAMDevice::Poke1(uint32 addr, uint32 data) { - mpu->AddCycle(write_wait); - *(uint32 *)&mainram[addr] = data; - putlog(3, "$%08x.L <- $%08x", addr, data); - return 0; + if ((int32)data >= 0) { + mainram[HLB(addr)] = data; + } + return true; } -uint64 -MainRAMDevice::Peek8(uint32 addr) +// 領域読み出し。 +// addr から totallen までが領域内なら転送して true を返す。 +// 領域外を指すなら何もせず false を返す。 +bool +MainRAMDevice::ReadMem(uint32 addr, void *dst_, uint32 totallen) { - return mainram[HLB(addr)]; + uint8 *dst = (uint8 *)dst_; + uintptr_t u = (uintptr_t)addr; + uint32 len; + + if (__predict_false(addr + totallen > GetSize())) { + return false; + } + + if (__predict_false(((u | (uintptr_t)dst) & 3) != 0)) { + if (((u ^ (uintptr_t)dst) & 3) || totallen < 4) { + len = totallen; + } else { + len = 4 - (uint32)(u & 3); + } + totallen -= len; + for (; len > 0; len--) { + *dst++ = mainram[HLB(addr)]; + addr++; + } + } + + len = totallen / 4; + for (; len > 0; len--) { + *(uint32 *)dst = be32toh(*(uint32 *)&mainram[addr]); + dst += 4; + addr += 4; + } + + len = totallen & 3; + for (; len > 0; len--) { + *dst++ = mainram[HLB(addr)]; + addr++; + } + + return true; } -uint64 -MainRAMDevice::Poke8(uint32 addr, uint32 data) +// 領域書き込み。 +// addr から totallen までが領域内なら転送して true を返す。 +// 領域外を指すなら何もせず false を返す。 +bool +MainRAMDevice::WriteMem(uint32 addr, const void *src_, uint32 totallen) { - if ((int32)data >= 0) { - mainram[HLB(addr)] = data; + const uint8 *src = (const uint8 *)src_; + uintptr_t u = (uintptr_t)addr; + uint32 len; + + if (__predict_false(addr + totallen > GetSize())) { + return false; + } + + if (__predict_false(((u | (uintptr_t)src) & 3) != 0)) { + if (((u ^ (uintptr_t)src) & 3) || totallen < 4) { + len = totallen; + } else { + len = 4 - (uint32)(u & 3); + } + totallen -= len; + for (; len > 0; len--) { + mainram[HLB(addr)] = *src++; + addr++; + } + } + + len = totallen / 4; + for (; len > 0; len--) { + *(uint32 *)&mainram[addr] = be32toh(*(const uint32 *)src); + addr += 4; + src += 4; } - return 0; + + len = totallen & 3; + for (; len > 0; len--) { + mainram[HLB(addr)] = *src++; + addr++; + } + + return true; } -// アクセスウェイトを設定 (X68030 でシステムポートから呼ばれる) +// アクセスウェイト [clock] を設定。 +// X68030 でシステムポートから呼ばれるため normal_wait のみ設定する。 void -MainRAMDevice::SetWait(uint32 wait) +MainRAMDevice::SetWait(uint32 wait_clock) { - read_wait = wait; - write_wait = wait; + uint32 clock_nsec = mpu->GetClock_nsec(); + + normal_wait = busdata::Wait(wait_clock * clock_nsec); } -// info->path で指定されたホストの実行ファイルをゲストにロードする。 +// info で指定された実行ファイルをゲストにロードする。 bool -MainRAMDevice::LoadFromFile(LoadInfo *info) +MainRAMDevice::LoadExec(LoadInfo *info) { BootLoader loader(this); - return loader.LoadFromFile(info); + return loader.LoadExec(info); } -// info->data, size で指定されたバッファを実行ファイルとみなしてゲストに -// ロードする。 +// info で指定されたデータファイルをゲストにロードする。 bool -MainRAMDevice::LoadFromData(LoadInfo *info) +MainRAMDevice::LoadData(LoadInfo *info) { BootLoader loader(this); - return loader.LoadFromData(info); + return loader.LoadData(info); }