|
|
nono 1.7.0
//
// nono
// Copyright (C) 2024 nono project
// Licensed under nono-license.txt
//
#include "mpu680x0.h"
#include "m68040mmu.h"
uint32
MPU68040Device::fetch_2()
{
busaddr addr = busaddr(reg.pc) | fc_inst | BusAddr::Size2;
busdata bd = read_data(addr);
reg.pc += 2;
return bd.Data();
}
uint32
MPU68040Device::fetch_4()
{
busaddr addr = busaddr(reg.pc) | fc_inst | BusAddr::Size4;
busdata bd = read_data(addr);
reg.pc += 4;
return bd.Data();
}
// read_1,2,4() の共通部分。ミスアライン可。
// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。
uint32
MPU68040Device::read_data(busaddr addr)
{
busdata data = read_nocache(addr);
if (__predict_false(data.IsBusErr())) {
bus.laddr = addr;
throw M68K::EXCEP_BUSERR;
}
return data.Data();
}
// キャッシュ無効時の読み込み。ミスアライン可。
// バスエラーも busdata を返す。
busdata
MPU68040Device::read_nocache(busaddr addr)
{
// デバイスは、addr の下位ビットによらず、自身のデバイスのポートサイズ分
// のデータと、その応答サイズ(自分のポートサイズ) を Size フィールドに
// セットして返してくる。
//
// MPU(68040) は、A1-A0、残りバイト数、デバイスの応答サイズの情報から
// 受け取ったデータバスのうち適切なところを切り出して使う。
bool span_page = false;
uint32 data = 0;
for (;;) {
bus.laddr = addr;
busaddr paddr = TranslateRead(addr);
if (__predict_false(paddr.IsBusErr())) {
bus.atcfault = true;
// ATC フォールトが2ページ目で起きたか。
bus.atcfault2 = span_page;
return BusData::BusErr;
}
bus.ci = paddr.IsCInhibit();
busdata bd = read_phys(paddr);
if (__predict_false(bd.IsBusErr())) {
return bd;
}
uint32 reqsize = addr.GetSize();
// デバイスのポートサイズ(応答サイズ)
uint32 portsize = bd.GetSize();
// 今回のアドレスオフセット
uint32 offset = addr.Addr() & (portsize - 1);
uint32 datasize = portsize - offset;
uint32 tmp = bd.Data();
// 下詰めされたデータに対するマスク
uint32 mask = 0xffffffffU >> ((4 - datasize) * 8);
// 今回必要なところ
tmp &= mask;
if (datasize == reqsize) {
data |= tmp;
break;
} else if (datasize > reqsize) {
tmp >>= (datasize - reqsize) * 8;
data |= tmp;
break;
} else {
tmp <<= (reqsize - datasize) * 8;
data |= tmp;
}
uint32 oldaddr = addr.Addr();
addr += datasize;
reqsize -= datasize;
addr.ChangeSize(reqsize);
if (__predict_false(((addr.Addr() ^ oldaddr) & ~mmu_tc->page_mask))) {
span_page = true;
}
}
return data;
}
// write_1,2,4() の共通部分。ミスアライン可。
// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。
void
MPU68040Device::write_data(busaddr addr, uint32 data)
{
busaddr origaddr = addr;
uint32 origdata = data;
bool span_page = false;
for (;;) {
bus.laddr = addr;
busaddr paddr = TranslateWrite(addr);
if (__predict_false(paddr.IsBusErr())) {
bus.atcfault = true;
// ATC フォールトが2ページ目で起きたか。
bus.atcfault2 = span_page;
break;
}
bus.ci = paddr.IsCInhibit();
busdata bd = write_phys(paddr, data);
if (__predict_false(bd.IsBusErr())) {
break;
}
uint32 reqsize = addr.GetSize();
uint32 portsize = bd.GetSize();
uint32 offset = addr.Addr() & (portsize - 1);
uint32 datasize = portsize - offset;
if (datasize >= reqsize) {
// ここが成功して終了。
return;
}
addr += datasize;
reqsize -= datasize;
addr.ChangeSize(reqsize);
data &= (1U << (reqsize * 8)) - 1;
if (((addr.Addr() ^ origaddr.Addr()) & ~mmu_tc->page_mask) != 0) {
span_page = true;
}
}
// 報告するのは先頭のアドレス。
bus.laddr = origaddr;
bus.data = origdata;
throw M68K::EXCEP_BUSERR;
}
// CACR レジスタ書き込み。
void
MPU68040Device::SetCACR(uint32 data)
{
// とりあえず
reg.cacr = data & M68040::CACR_MASK;
}
// リセット例外の CPU 固有部分。
void
MPU68040Device::ResetCache()
{
SetCACR(0);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.