|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// メインメモリ
//
// MainRAM はロングワード単位でホストバイトオーダ配置なので、
// バイトアクセス、ワードアクセス時は HLB(), HLW() マクロを使用のこと。
#include "mainram.h"
#include "config.h"
#include "mainapp.h"
#include "mpu.h"
#include "prom.h"
// コンストラクタ
MainRAMDevice::MainRAMDevice()
: inherited(OBJ_MAINRAM)
{
clear_on_boot = true;
}
// デストラクタ
MainRAMDevice::~MainRAMDevice()
{
}
// 初期化
bool
MainRAMDevice::Init()
{
const ConfigItem& item = gConfig->Find("ram-size");
int ram_size_MB = item.AsInt();
if (ram_size_MB < 1) {
item.Err();
return false;
}
uint64 clock_tsec = mpu->GetClock_tsec();
// メモリの下限以外の制約は機種による。
switch (gMainApp.GetVMType()) {
case VMType::X68030:
// メインメモリは 12MB まで。
// 内部は 8KB 単位で処理できるが設定が 1MB 単位。
if (ram_size_MB > 12) {
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 単位とする。
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 > 512) {
item.Err("Configurable maximum RAM size is 512 [MB]");
return false;
}
// 実 ROM のメモリチェックは 32KB だかそのくらいずつ行われ、
// 最後に番兵として NOP 領域 (BusError ではなく) が必要。
// 現行の構成をあまり変えない範囲なら厳密には (256MB - 32KB) が
// 上限だが、サイズ指定が 1MB 単位なので 255MB を上限にする。
auto prom = dynamic_cast<PROMDevice *>(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_tsec);
burst_wait = busdata::Wait(4 * clock_tsec);
break;
}
case VMType::LUNA88K:
{
// 64MB までは、実機に合わせて 16MB 単位とする。
//
// 64MB を超える増設 (or 魔改造) 部分は未確認。
if (ram_size_MB <= 64) {
if (ram_size_MB % 16 != 0) {
item.Err("For 64MB or less, must be specified in 16 [MB] unit");
return false;
}
} else if (ram_size_MB > 512) {
item.Err("Configurable maximum RAM size is 512 [MB]");
return false;
}
// PROM 1.20 では 240MB を上限にする。
// 0x0f00'0000 (ちょうど 240MB 目) へのアクセスでバスエラーが
// 出ることを CMMU のアドレス変換チェックに使っているため。
auto prom = dynamic_cast<PROMDevice *>(GetPROMDevice());
if (prom && prom->GetROMVer() == 120) {
if (ram_size_MB > 240) {
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_tsec);
burst_wait = busdata::Wait(5 * clock_tsec);
break;
}
case VMType::NEWS:
// 電源オン時に RAM に書き込んでいるため、クリアしない。
clear_on_boot = false;
// 知らんけど 1クロックくらい入れておく。
normal_wait = busdata::Wait(1 * clock_tsec);
// 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("vmtype=%s not configured", gMainApp.GetVMTypeStr().c_str());
}
ram_size = (size_t)ram_size_MB * 1024 * 1024;
mainram.reset(new(std::nothrow) uint8[ram_size]);
if ((bool)mainram == false) {
warnx("Could not allocate MainRAM(%zu bytes)", ram_size);
return false;
}
// Human68k モードならここで一度だけメモリをゼロクリアする。
// この時は Human68k::Init() が MainRAM にプログラムを転送するため
// 電源オン時点で RAM を初期化してはいけない…。うーんこの…。
// MainRAM::Init() が Human68k::Init() より先に呼ばれることは
// VM_X68030 で指定してある。
// 一方、通常モードなら VM リスタート(電源再投入) で前の内容を残さない
// ために ResetHard(true) でメモリを (ゼロでなくてもいいので) クリアする
// 必要がある。
if (gMainApp.human_mode) {
memset(&mainram[0], 0, ram_size);
clear_on_boot = false;
}
return true;
}
// 電源オン/リセット
void
MainRAMDevice::ResetHard(bool poweron)
{
if (poweron) {
if (clear_on_boot) {
// XXX ノイズを用意するべき
memset(&mainram[0], 0, ram_size);
}
}
// X68030 ではリセットで戻るはず。(LUNA は固定なので関係ない)
if (gMainApp.IsX68030()) {
normal_wait = busdata::Wait(0);
}
}
busdata
MainRAMDevice::Read(busaddr addr)
{
uint32 paddr = addr.Addr();
busdata data;
data = *(uint32 *)&mainram[paddr & ~3];
data |= normal_wait;
data |= BusData::Size4;
return data;
}
busdata
MainRAMDevice::Write(busaddr addr, uint32 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;
}
busdata
MainRAMDevice::ReadBurst16(busaddr addr, uint32 *dst)
{
uint32 paddr = addr.Addr();
putlog(4, "$%08x BurstRead", paddr);
memcpy(dst, &mainram[paddr], 16);
return burst_wait;
}
busdata
MainRAMDevice::WriteBurst16(busaddr addr, const uint32 *src)
{
uint32 paddr = addr.Addr();
putlog(4, "$%08x BurstWrite", paddr);
memcpy(&mainram[paddr], src, 16);
return burst_wait;
}
busdata
MainRAMDevice::Peek1(uint32 addr)
{
return mainram[HLB(addr)];
}
bool
MainRAMDevice::Poke1(uint32 addr, uint32 data)
{
if ((int32)data >= 0) {
mainram[HLB(addr)] = data;
}
return true;
}
// 領域読み出し。
// addr から totallen までが領域内なら転送して true を返す。
// 領域外を指すなら何もせず false を返す。
bool
MainRAMDevice::ReadMem(uint32 addr, void *dst_, uint32 totallen)
{
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;
}
// 領域書き込み。
// addr から totallen までが領域内なら転送して true を返す。
// 領域外を指すなら何もせず false を返す。
bool
MainRAMDevice::WriteMem(uint32 addr, const void *src_, uint32 totallen)
{
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;
}
len = totallen & 3;
for (; len > 0; len--) {
mainram[HLB(addr)] = *src++;
addr++;
}
return true;
}
// アクセスウェイト [clock] を設定。
// X68030 でシステムポートから呼ばれるため normal_wait のみ設定する。
void
MainRAMDevice::SetWait(uint32 wait_clock)
{
normal_wait = busdata::Wait(wait_clock * mpu->GetClock_tsec());
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.