--- nono/m680x0/m68030bus.cpp 2026/04/29 17:04:28 1.1.1.1 +++ nono/m680x0/m68030bus.cpp 2026/04/29 17:05:57 1.1.1.10 @@ -1,483 +1,754 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -// 論理アクセス -// - -// -// 1回の(アラインド)物理アクセスを担当するマクロ。 -// これらは続く _mmu テンプレートで使用する。 -// goto を含むことに注意。gcc 拡張式(?)に注意。 -// mmu_translate_* は fetch, read, write の3種だが、m68030_phys_ は -// read, write しかないことに注意。物理バスに FC0 と FC1、つまりプログラム -// 空間かどうかの情報は出していないため。 -// - -#define FETCH(size) ({ \ - cpu->bus.paddr = cpu->bus.laddr; \ - if (use_mmu && m68030_mmu_translate_fetch(cpu) == false) \ - goto buserr; \ - uint64 data_ = m68030_phys_read_##size(cpu->bus.paddr); \ - if ((int64)data_ < 0) \ - goto buserr; \ - data_; /* これが戻り値 */ \ -}) - -#define READ(size) ({ \ - cpu->bus.paddr = cpu->bus.laddr; \ - if (use_mmu && m68030_mmu_translate_read(cpu) == false) \ - goto buserr; \ - uint64 data_ = m68030_phys_read_##size(cpu->bus.paddr); \ - if ((int64)data_ < 0) \ - goto buserr; \ - data_; /* これが戻り値 */ \ -}) - -#define WRITE(size, data) ({ \ - cpu->bus.paddr = cpu->bus.laddr; \ - if (use_mmu && m68030_mmu_translate_write(cpu) == false) \ - goto buserr; \ - int64 berr_ = m68030_phys_write_##size(cpu->bus.paddr, (data)); \ - if (berr_ < 0) \ - goto buserr; \ - 0; /* 戻り値不要だけど一応 */ \ -}) - -// バスエラー時に SSW をセットする。 -// m68030_*_NN_mmu() 関数の下請け。 -static inline void -SET_SSW(m68kcpu *cpu, bool use_mmu, uint size) -{ - // サイズを設定 - cpu->bus.ssw &= ~SSW_SIZE_MASK; - cpu->bus.ssw |= size; - - if (use_mmu) { - // データアクセスなら再試行のため DataFault ビットをセット。 - // 命令アクセスなら RB をセット。 - if ((cpu->bus.ssw & FC_DATA)) { - cpu->bus.ssw |= SSW_DF; - } else { - cpu->bus.ssw |= SSW_RB; - } - } -} -// -// MMU を考慮した下請けテンプレート。 -// 1回分のミスアラインドありのアクセスを担当。 -// +#include "mpu680x0.h" +#include "mainbus.h" +#include "m68030cache.h" + +// 現在の FC での通常アクセス (MPU 内部から使用する) +// ミスアライン可。 +// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。 +// uint32 fetch_{2,4}() +// uint32 read_{1,2,4}(laddr) .. vm/mpu680x0.h にある +// void write_{1,2,4}(laddr, data) .. vm/mpu680x0.h にある -template uint32 -m68030_fetch_16_mmu(m68kcpu *cpu) +// reg.pc からワードをフェッチし reg.pc を進める。 +uint32 +MPU68030Device::fetch_2() { - uint64 data; - - data = FETCH(16); - // XXX バスエラー時に PC をインクリメントするタイミングは? - RegPC += 2; - - return data; + busaddr addr = busaddr(reg.pc) | fc_inst | BusAddr::Size2; - buserr: - SET_SSW(cpu, use_mmu, SSW_SIZE_WORD); - throw M68K_EXCEP_BUSERR; -} - -template uint32 -m68030_fetch_32_mmu(m68kcpu *cpu) -{ - uint64 data; + // まず可能ならキャッシュから探す。 + m68030Cache *cache = icache.get(); + if (__predict_true(cache->enable)) { + uint32 tag = (addr.Get() >> 4) & 0xfffffff0; + uint32 idx = (addr.Addr() >> 4) & 0x0000000f; + uint32 sel = (addr.Addr() >> 2) & 0x00000003; + m68030CacheLine *l = &cache->line[idx]; + + if (__predict_true(l->tag == tag && l->valid[sel])) { + cache->stat_fasthit++; + uint32 data = l->data[sel]; + if ((reg.pc & 2) == 0) { + data >>= 16; + } else { + data &= 0xffff; + } + reg.pc += 2; + return data; + } + } - if ((cpu->bus.laddr & 3) == 0) { - data = FETCH(32); + busdata bd; + if (__predict_true(cache->enable)) { + bd = read_cache(cache, addr); } else { - uint64 h, l; - - h = FETCH(16); - cpu->bus.laddr += 2; - - l = FETCH(16); - data = (h << 16) | l; + bd = read_nocache(addr); } - // XXX バスエラー時に PC をインクリメントするタイミングは? - RegPC += 4; - return data; - - buserr: - SET_SSW(cpu, use_mmu, SSW_SIZE_LONG); - throw M68K_EXCEP_BUSERR; + if (__predict_false(bd.IsBusErr())) { + throw M68K::EXCEP_BUSERR; + } + reg.pc += 2; + return bd.Data(); } -template uint32 -m68030_read_8_mmu(m68kcpu *cpu) +// reg.pc からロングワード(ミスアライン可)をフェッチし reg.pc を進める。 +uint32 +MPU68030Device::fetch_4() { - uint64 data; - - data = READ(8); + busaddr addr = busaddr(reg.pc) | fc_inst | BusAddr::Size4; - if (1) - cpulog(2, "m68030_read_8 %08X -> %02X", cpu->bus.laddr, (uint32)data); - return data; + // まず可能ならキャッシュから探す。 + m68030Cache *cache = icache.get(); + if (__predict_true(cache->enable)) { + uint32 tag = (addr.Get() >> 4) & 0xfffffff0; + uint32 idx = (addr.Addr() >> 4) & 0x0000000f; + uint32 sel = (addr.Addr() >> 2) & 0x00000003; + m68030CacheLine *l = &cache->line[idx]; + + if (__predict_true(l->tag == tag && l->valid[sel])) { + uint32 data; + if ((reg.pc & 2) == 0) { + cache->stat_fasthit++; + data = l->data[sel]; + } else { + // 次のエントリにまたがる場合。 + if (sel < 3 && l->valid[sel + 1]) { + cache->stat_fasthit += 2; + data = l->data[sel] << 16; + data |= l->data[sel + 1] >> 16; + } else { + // 片方でキャッシュミスした。 + // この場合は fast* はカウントしない。 + goto fetch; + } + } + reg.pc += 4; + return data; + } + } - buserr: - SET_SSW(cpu, use_mmu, SSW_SIZE_BYTE); - throw M68K_EXCEP_BUSERR; + fetch: + busdata bd; + if (__predict_true(cache->enable)) { + bd = read_cache(cache, addr); + } else { + bd = read_nocache(addr); + } + if (__predict_false(bd.IsBusErr())) { + throw M68K::EXCEP_BUSERR; + } + reg.pc += 4; + return bd.Data(); } -template uint32 -m68030_read_16_mmu(m68kcpu *cpu) +// read_1,2,4() の共通部分。ミスアライン可。 +// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。 +uint32 +MPU68030Device::read_data(busaddr addr) { - uint64 data; - - if ((cpu->bus.laddr & 1) == 0) { - data = READ(16); + m68030Cache *cache = dcache.get(); + busdata data; + if (__predict_true(cache->enable)) { + data = read_cache(cache, addr); } else { - uint64 h, l; - - h = READ(8); - cpu->bus.laddr += 1; - - l = READ(8); - data = (h << 8) | l; + data = read_nocache(addr); } - - if (1) - cpulog(2, "m68030_read_16 %08X -> %04X", cpu->bus.laddr, (uint32)data); - return data; - - buserr: - SET_SSW(cpu, use_mmu, SSW_SIZE_WORD); - throw M68K_EXCEP_BUSERR; + if (__predict_false(data.IsBusErr())) { + throw M68K::EXCEP_BUSERR; + } + return data.Data(); } -template uint32 -m68030_read_32_mmu(m68kcpu *cpu) +// write_1,2,4() の共通部分。ミスアライン可。 +// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。 +void +MPU68030Device::write_data(busaddr addr, uint32 data) { - uint64 data; - - if ((cpu->bus.laddr & 3) == 0) { - data = READ(32); - } else if ((cpu->bus.laddr & 1) == 0) { - uint64 h, l; - - h = READ(16); - cpu->bus.laddr += 2; + busdata r = write_bus(addr, data); + if (__predict_false(r.IsBusErr())) { + throw M68K::EXCEP_BUSERR; + } +} - l = READ(16); - data = (h << 16) | l; - } else { - uint64 h, m, l; +// キャッシュ無効時の読み込み。ミスアライン可。 +// バスエラーも busdata を返す。 +busdata +MPU68030Device::read_nocache(busaddr addr) +{ + // デバイスは、addr の下位ビットによらず、自身のデバイスのポートサイズ分 + // のデータと、その応答サイズ(自分のポートサイズ) を Size フィールドに + // セットして返してくる。 + // + // MPU(68030) は、A1-A0、残りバイト数、デバイスの応答サイズの情報から + // 受け取ったデータバスのうち適切なところを切り出して使う。 + + putlog(3, "read_nocache %u.%08x sz=%c", + addr.GetFC(), addr.Addr(), "0BW3L"[addr.GetSize()]); + + uint32 data = 0; + for (;;) { + bus.laddr = addr; + busaddr paddr = TranslateRead(addr); + if (__predict_false(paddr.IsBusErr())) { + return BusData::BusErr; + } + bus.ci = paddr.IsCInhibit(); - h = READ(8); - cpu->bus.laddr += 1; + busdata bd = read_phys(paddr); + if (__predict_false(bd.IsBusErr())) { + return bd; + } - m = READ(16); - cpu->bus.laddr += 2; + 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; + } - l = READ(8); - data = (h << 24) | (m << 8) | l; + addr += datasize; + reqsize -= datasize; + addr.ChangeSize(reqsize); } - - if (1) - cpulog(2, "m68030_read_32 %08X -> %08X", cpu->bus.laddr, (uint32)data); return data; - - buserr: - SET_SSW(cpu, use_mmu, SSW_SIZE_LONG); - throw M68K_EXCEP_BUSERR; } -template void -m68030_write_8_mmu(m68kcpu *cpu) -{ - uint32 data = cpu->bus.data; +// キャッシュ有効時の読み込み。ミスアライン可。 +// バスエラーも busdata を返す。 +// +// 簡略化・高速化のため実機と異なる制約を設けてある。 +// 1) 実際の 68030 システムでは、「ロングワードの全バイトがキャッシュ可能なら +// !CIIN をネゲートしなければならない」「いずれかのバイトでもキャッシュ可能 +// でないなら CIIN をアサートしなければならない」(MC68030UM.pdf p6-15、 +// 日本語本 p296 ※誤訳あり) とあり、CIIN は同一ロングワード内で変化しては +// いけないが、ロングワードごとには変化してよい。 +// nono の実装ではこれを同一ライン(16バイト)単位の制約にする。 +// 現状サポートしている機種ではこれで困ることはない。 +busdata +MPU68030Device::read_cache(m68030Cache *cache, busaddr addr) +{ + putlog(3, "read_cache %u.%08x sz=%c", + addr.GetFC(), addr.Addr(), "0BW3L"[addr.GetSize()]); + + uint32 data = 0; + for (;;) { + uint32 entry_data = 0; + + // まず可能ならキャッシュから探す。 + uint32 tag = (addr.Get() >> 4) & 0xfffffff0; + uint32 idx = (addr.Addr() >> 4) & 0x0000000f; + uint32 sel = (addr.Addr() >> 2) & 0x00000003; + m68030CacheLine *l = &cache->line[idx]; + if (__predict_true(l->tag == tag && l->valid[sel])) { + // タグが一致してエントリも有効 (キャッシュヒット)。 + cache->stat_hit++; + entry_data = l->data[sel]; + putlog(3, " entry_data=$%08x (%s)", entry_data, "match"); + } else { + // タグが違うか、エントリが無効。 + cache->stat_miss++; - if (1) - cpulog(2, "m68030_write_8 %08X <- %02X", cpu->bus.laddr, data); + bus.laddr = addr; + busaddr paddr = TranslateRead(addr); + if (__predict_false(paddr.IsBusErr())) { + putlog(3, " TranslateRead BusErr"); + return BusData::BusErr; + } + bus.ci = paddr.IsCInhibit(); + putlog(3, " paddr=$%08x.%c", + paddr.Addr(), "0BW3L"[paddr.GetSize()]); + + // タグが違うか、全エントリが無効ならライン充填する。 + // ただし要求がラインをまたぐ場合 (例えば +$e からのロングワード + // 要求) は除く。 + bool is_linefill = (l->tag != tag || l->valid_all == 0) && + ((addr.Addr() & 0xf) + addr.GetSize() <= 0x10); + + // 可能ならバーストリードを使う。 + if (is_linefill && + __predict_true(bus.ci == false) && + __predict_true(cache->burst_enable) && + __predict_true(read_phys_burst(paddr, &l->data[0]))) + { + // read_phys_burst() はライン充填したら true を返している。 + entry_data = l->data[sel]; + putlog(3, " entry_data=$%08x (%s)", entry_data, "burst"); + l->tag = tag; + l->valid_all = -1; + } else { + // シングル充填。 + busdata bd = read_single(paddr); + if (__predict_false(bd.IsBusErr())) { + return bd; + } + entry_data = bd.Data(); + putlog(3, " entry_data=$%08x (%s)", entry_data, "single"); + + if (__predict_false(bus.ci) || __predict_false(cache->freeze)) { + // エントリを更新しない。 + } else { + l->data[sel] = entry_data; + if (l->tag != tag) { + // タグを更新して、このラインの他のエントリは無効。 + l->tag = tag; + l->valid_all = 0; + } + l->valid[sel] = 1; + } + } + } - WRITE(8, data); - return; + // addr から今回対象とするロングワードエントリに対するマスクを作成。 + uint32 reqoffset = addr.Addr() & 3; + uint32 reqsize = addr.GetSize(); + uint32 opersize = std::min(reqsize, 4 - reqoffset); + + data <<= opersize * 8; + // このエントリから必要な部分を抜き出す。 + uint32 mask = 0xffffffffU >> ((4 - opersize) * 8); + if (reqsize == opersize) { + // 要求サイズがこのエントリをはみ出していない。 + entry_data >>= (4 - reqoffset - opersize) * 8; + data |= entry_data & mask; + putlog(3, " data=$%0*x (done)", opersize * 2, data); + break; + } else { + data |= entry_data & mask; + putlog(3, " data=$%0*x", opersize * 2, data); + } + addr += opersize; + addr.ChangeSize(reqsize - opersize); + } - buserr: - SET_SSW(cpu, use_mmu, SSW_SIZE_BYTE); - throw M68K_EXCEP_BUSERR; + return data; } -template void -m68030_write_16_mmu(m68kcpu *cpu) -{ - uint32 data = cpu->bus.data; - - if (1) - cpulog(2, "m68030_write_16 %08X <- %04X", cpu->bus.laddr, data); - - if ((cpu->bus.laddr & 1) == 0) { - WRITE(16, data); +// シングル充填を行い、(CI でなければ) 完全なロングワードを返す。 +// CI なら第1サイクルだけを行い、ロングワードの所定の位置においたものを返す。 +// バスエラーなら BusErr を返す。 +busdata +MPU68030Device::read_single(busaddr paddr) +{ + // 一発目。 + busdata bd = read_phys(paddr); + if (__predict_true(bd.IsOK()) && bd.GetSize() != 4) { + // 必要なら残りのサイクル。 + bd = fill_single(paddr, bd); + } + // いずれにしても bd を返す。 + return bd; +} + +// シングル充填の第2サイクル以降を行って、(CI でなければ) 完全なロングワードを +// 返す。 +// 引数 paddr に第1サイクルの物理アドレス (インクリメントなどする前)。 +// 引数 bd は第1サイクルの結果の busdata。 +// +// 第1サイクルがバスエラーになっていれば事前に弾いておくこと。 +// 第1サイクルでロングワード全部埋まったケースも事前に弾いておくこと。 +busdata +MPU68030Device::fill_single(busaddr paddr, busdata bd) +{ + busaddr addr_h(paddr.Get() & ~0x3ULL); + uint32 data; + + //assert(bd.IsOK()); + //assert(bd.GetSize() < 4); + + if (bd.GetSize() == 2) { + // ワードポートアクセスだったなら、第1アクセスが上位か下位かだけ。 + if ((paddr.Addr() & 2) == 0) { + data = bd.Data() << 16; + } else { + data = bd.Data(); + } + if (__predict_false(bus.ci)) { + goto done; + } + // 次のアクセスはワード境界にアラインされるので + // 0x3 じゃなく0x2 でマスク。 + uint32 offset = (paddr.Addr() + 2) & 0x2; + paddr = addr_h | offset; + + bd = read_phys(paddr); + // CI はもう起きないはず。バスエラーは? + if (offset == 0) { + data |= bd.Data() << 16; + } else { + data |= bd.Data(); + } } else { - WRITE(8, data >> 8); + // バイトポートアクセスの場合。 - cpu->bus.laddr += 1; - WRITE(8, data & 0xff); + // 第1サイクルのデータを所定のバイト位置に。 + uint32 offset = paddr.Addr() & 0x3; + data = bd.Data() << ((3 - offset) * 8); + if (__predict_false(bus.ci)) { + goto done; + } + for (uint i = 1; i < 4; i++) { + // インクリメント。 + offset = (offset + 1) & 0x3; + paddr = addr_h | offset; + + bd = read_phys(paddr); + // CI はもう起きないはず。バスエラーは? + data |= bd.Data() << ((3 - offset) * 8); + } } - return; - buserr: - SET_SSW(cpu, use_mmu, SSW_SIZE_WORD); - throw M68K_EXCEP_BUSERR; + done: + return data; } -template void -m68030_write_32_mmu(m68kcpu *cpu) -{ - uint32 data = cpu->bus.data; - - if (1) - cpulog(2, "m68030_write_32 %08X <- %08X", cpu->bus.laddr, data); +// キャッシュ/バスへの書き込み。ミスアライン可。 +// バスエラーも busdata を返す。 +busdata +MPU68030Device::write_bus(busaddr addr, uint32 data) +{ + m68030Cache *cache = dcache.get(); + + putlog(3, "write_bus %u.%08x sz=%c data=$%0*x", + addr.GetFC(), addr.Addr(), "0BW3L"[addr.GetSize()], + addr.GetSize() * 2, data); + for (;;) { + uint32 nextdata; + uint32 reqoffset = addr.Addr() & 3; + uint32 reqsize = addr.GetSize(); + + // ロングワード境界をはみ出す場合は持ち越し分を覚えておく。 + int nextsize = reqoffset + reqsize - 4; + if (nextsize > 0) { + // 後ろにはみ出すケース。 + nextdata = data & ((1U << (nextsize * 8)) - 1); + data >>= nextsize * 8; + reqsize -= nextsize; + addr.ChangeSize(reqsize); + } else { + nextsize = 0; + } - if ((cpu->bus.laddr & 3) == 0) { - WRITE(32, data); - } else if ((cpu->bus.laddr & 1) == 0) { - WRITE(16, data >> 16); + // まず外部バスを駆動して CI を得る。 + // バスエラーなら記録だけしておく。キャッシュへは書き込むため。 + busdata bd = BusData::BusErr; + bus.laddr = addr; + busaddr paddr = TranslateWrite(addr); + bus.ci = paddr.IsCInhibit(); + if (__predict_true(paddr.IsOK())) { + uint32 outdata = data; + for (;;) { + bd = write_phys(paddr, outdata); + if (__predict_false(bd.IsBusErr())) { + putlog(3, " write_phys -> BusErr"); + break; + } + uint32 breqsize = paddr.GetSize(); + putlog(3, " write_phys $%08x data=$%0*x", + paddr.Addr(), breqsize * 2, outdata); + uint32 portsize = bd.GetSize(); + uint32 offset = paddr.Addr() & (portsize - 1); + uint32 datasize = portsize - offset; + if (datasize >= breqsize) { + break; + } + // 同一ロングワード内なので paddr の更新だけでいい。 + paddr += datasize; + breqsize -= datasize; + paddr.ChangeSize(breqsize); + // データは下詰めで、不要な上位ビットはクリアする仕様。 + outdata &= (1U << (breqsize * 8)) - 1; + } + } - cpu->bus.laddr += 2; - WRITE(16, data & 0xffff); - } else { - WRITE(8, data >> 24); + bool cache_writable = (cache->enable && bus.ci == false); + if (cache_writable) { + uint32 tag = (addr.Get() >> 4) & 0xfffffff0; + uint32 idx = (addr.Addr() & 0x000000f0) >> 4; + uint32 sel = (addr.Addr() & 0x0000000c) >> 2; + auto *l = &cache->line[idx]; + + // Hit Frz WA Tag LA + // 1 0 - - - : UpdateEntry + // 1 1 - - - : UpdateEntry (ex1, ex2のb8-b9 + // 0 1 - - - : Nop + // 0 0 0 - - : Nop + // 0 0 1 1 1 : (ex3 + // 0 0 1 1 0 : Nop (ex2のb6-b7 + // 0 0 1 0 1 : (ex4 + // 0 0 1 0 0 : (ex5 + bool tag_match = (l->tag == tag); + bool cache_hit = (tag_match && l->valid[sel]); + if (cache_hit) { + // エントリの更新。 + // reqsize はロングワード境界を超えないよう調整済み。 + // data は下詰め(上はクリア)なので、所定の位置まで左シフト。 + // 整列ロングワードもここに来ることに注意。 + // Fig.6-4 の ex1, ex2のb8-b9 + int lshift = (4 - reqoffset - reqsize) * 8; + uint32 mask = (0xffffffffU >> ((4 - reqsize) * 8)) << lshift; + l->data[sel] = (data << lshift) | (l->data[sel] & ~mask); + } else if (cache->freeze == false && cache->wa) { + bool long_aligned = (reqoffset == 0 && reqsize == 4); + // Fig.6-4 + if (tag_match) { + if (long_aligned) { + // ex3 + l->valid[sel] = 1; + l->data[sel] = data; + } else { + // ex2 の b6-b7 + // nop + } + } else { + if (long_aligned) { + // ex4 + l->tag = tag; + l->valid_all = 0; + l->valid[sel] = 1; + l->data[sel] = data; + } else { + // ex5 + l->valid[sel] = 0; + } + } + } + } - cpu->bus.laddr += 1; - WRITE(16, (data >> 8) & 0xffff); + // バスアクセスでバスエラーが出たらキャッシュ更新後に発報。 + if (__predict_false(bd.IsBusErr())) { + return bd; + } - cpu->bus.laddr += 2; - WRITE(8, data & 0xff); + // ロングワード境界をまたいだ余りがあればもう一回。 + if (nextsize == 0) { + break; + } + putlog(3, " nextsize=%u", nextsize); + addr += addr.GetSize(); + addr.ChangeSize(nextsize); + data = nextdata; } - return; - - buserr: - SET_SSW(cpu, use_mmu, SSW_SIZE_LONG); - throw M68K_EXCEP_BUSERR; -} -// m68030_{fetch,read,write}_NN() と m68030_{read,write}_NN_fc() の共通部分。 -// fetch には FC 指定版はないので分ける必要は本来ないけど対称性のため。 -// cpu->bus に必要な情報はセットされている。 -// _bus 接尾辞はバスアクセスというより bus 構造体を使ったアクセスという感じ。 - -static inline uint32 -m68030_fetch_16_bus(m68kcpu *cpu) -{ - if (cpu->mmu_enable) { - return m68030_fetch_16_mmu(cpu); - } else { - return m68030_fetch_16_mmu(cpu); - } + return 0; } -static inline uint32 -m68030_fetch_32_bus(m68kcpu *cpu) +// mainbus の paddr から指定サイズで1回読み込む。 +// ウェイトはこちらで加算して、バスエラーも含めた bd を返す。 +busdata +MPU680x0Device::read_phys(const busaddr paddr) { - if (cpu->mmu_enable) { - return m68030_fetch_32_mmu(cpu); - } else { - return m68030_fetch_32_mmu(cpu); - } + busdata bd = mainbus->Read(paddr); + buswait += bd.GetWait(); + return bd; } -static uint32 -m68030_read_8_bus(m68kcpu *cpu) +// mainbus の paddr に指定サイズで1回書き込む。 +// ウェイトはこちらで加算して、バスエラーも含めた bd を返す。 +busdata +MPU680x0Device::write_phys(const busaddr paddr, uint32 data) { -if(1)cpulog(2, "%s $%08x", __func__, cpu->bus.laddr); - if (cpu->mmu_enable) { - return m68030_read_8_mmu(cpu); - } else { - return m68030_read_8_mmu(cpu); - } + busdata bd = mainbus->Write(paddr, data); + buswait += bd.GetWait(); + return bd; } -static uint32 -m68030_read_16_bus(m68kcpu *cpu) +// バースト転送。 +// paddr の下位4ビットは無視する。 +// paddr がバースト転送可能なら dst に 16 バイト転送して true を返す。 +// paddr がバースト転送不可なら dst は触らず false を返す。 +// VM (バス) がバースト転送対応している場合のみ呼ぶこと。 +bool +MPU680x0Device::read_phys_burst(busaddr paddr, uint32 *dst) { -if(1)cpulog(2, "%s $%08x", __func__, cpu->bus.laddr); - if (cpu->mmu_enable) { - return m68030_read_16_mmu(cpu); - } else { - return m68030_read_16_mmu(cpu); - } -} + paddr &= 0xfffffff0U; + busdata bd = mainbus->ReadBurst16(paddr, dst); + buswait += bd.GetWait(); -static uint32 -m68030_read_32_bus(m68kcpu *cpu) -{ -if(1)cpulog(2, "%s $%08x", __func__, cpu->bus.laddr); - if (cpu->mmu_enable) { - return m68030_read_32_mmu(cpu); - } else { - return m68030_read_32_mmu(cpu); - } + // BusErr ビットをバースト転送不可フラグとして使っている。 + return !bd.IsBusErr(); } -static void -m68030_write_8_bus(m68kcpu *cpu) +// 物理メモリからのロングワード読み込み。 +// 現在の bus に影響を与えず指定のアドレスを読み出す。MMU 専用。 +busdata +MPU680x0Device::read_phys_4(uint32 addr) { -if(1)cpulog(2, "%s $%08x", __func__, cpu->bus.laddr); - if (cpu->mmu_enable) { - m68030_write_8_mmu(cpu); - } else { - m68030_write_8_mmu(cpu); - } -} + m68kbus backup; + busdata bd; -static void -m68030_write_16_bus(m68kcpu *cpu) -{ -if(1)cpulog(2, "%s $%08x", __func__, cpu->bus.laddr); - if (cpu->mmu_enable) { - m68030_write_16_mmu(cpu); - } else { - m68030_write_16_mmu(cpu); - } -} + // バス情報を一旦退避 + backup = bus; -static void -m68030_write_32_bus(m68kcpu *cpu) -{ -if(1)cpulog(2, "%s $%08x", __func__, cpu->bus.laddr); - if (cpu->mmu_enable) { - m68030_write_32_mmu(cpu); - } else { - m68030_write_32_mmu(cpu); + // 物理バスアクセス + bus.laddr = busaddr(addr) | BusAddr::SRead | BusAddr::Size4; + bd = read_phys(bus.laddr); + if (__predict_false(bd.IsBusErr())) { + return bd; } -} - -// -// CPU 内部からのメモリアクセスは常にこの8つ (またはこれを呼び出すヘルパー -// 関数) を呼ぶこと。 -// uint32 m68030_fetch_{16,32}(cpu) -// uint32 m68030_read_{8,16,32}(cpu, laddr) -// void m68030_write_{8,16,32}(cpu, laddr, data) -// -uint32 -m68030_fetch_16(m68kcpu *cpu) -{ - cpu->bus.ssw = SSW_BUS_R | cpu->bus.fc2 | FC_PROG; - cpu->bus.laddr = RegPC; - return m68030_fetch_16_bus(cpu); + // バス情報を復元 + bus = backup; + return bd; } -uint32 -m68030_fetch_32(m68kcpu *cpu) +// 物理メモリへのロングワード書き込み。 +// 現在の bus に影響を与えず指定のアドレスに書き出す。MMU 専用。 +busdata +MPU680x0Device::write_phys_4(uint32 addr, uint32 data) { - cpu->bus.ssw = SSW_BUS_R | cpu->bus.fc2 | FC_PROG; - cpu->bus.laddr = RegPC; - return m68030_fetch_32_bus(cpu); -} + m68kbus backup; + busdata bd; -uint32 -m68030_read_8(m68kcpu *cpu, uint32 addr) -{ - cpu->bus.ssw = SSW_BUS_R | cpu->bus.fc2 | FC_DATA; - cpu->bus.laddr = addr; - return m68030_read_8_bus(cpu); -} + // バス情報を一旦退避 + backup = bus; -uint32 -m68030_read_16(m68kcpu *cpu, uint32 addr) -{ - cpu->bus.ssw = SSW_BUS_R | cpu->bus.fc2 | FC_DATA; - cpu->bus.laddr = addr; - return m68030_read_16_bus(cpu); -} + // 物理バスアクセス + bus.laddr = busaddr(addr) | BusAddr::SWrite | BusAddr::Size4; + bd = write_phys(bus.laddr, data); + if (__predict_false(bd.IsBusErr())) { + return bd; + } -uint32 -m68030_read_32(m68kcpu *cpu, uint32 addr) -{ - cpu->bus.ssw = SSW_BUS_R | cpu->bus.fc2 | FC_DATA; - cpu->bus.laddr = addr; - return m68030_read_32_bus(cpu); + // バス情報を復元 + bus = backup; + return bd; } +// CACR レジスタ書き込み。 void -m68030_write_8(m68kcpu *cpu, uint32 addr, uint32 data) +MPU68030Device::SetCACR(uint32 newval) { - cpu->bus.ssw = SSW_BUS_W | cpu->bus.fc2 | FC_DATA; - cpu->bus.laddr = addr; - cpu->bus.data = data; - m68030_write_8_bus(cpu); -} + newval &= M68030::CACR_MASK; + uint32 oldval = reg.cacr; + uint32 chgval = oldval ^ newval; + + // EI (命令キャッシュ有効) + if ((chgval & M68030::CACR_EI) != 0) { + if ((newval & M68030::CACR_EI) != 0) { + if (force_disable_icache) { + putlog(1, "I-Cache not enabled (mpu-force-disable-icache)"); + newval &= ~M68030::CACR_EI; + } else { + // 命令キャッシュ有効 + icache->enable = true; + icache->hist.Clear(); + cycle_table = cycle_table_030cc; + putlog(1, "I-Cache enabled"); + } + } else { + // 命令キャッシュ無効 + icache->enable = false; + cycle_table = cycle_table_030ncc; + putlog(1, "I-Cache disabled"); + } + } -void -m68030_write_16(m68kcpu *cpu, uint32 addr, uint32 data) -{ - cpu->bus.ssw = SSW_BUS_W | cpu->bus.fc2 | FC_DATA; - cpu->bus.laddr = addr; - cpu->bus.data = data; - m68030_write_16_bus(cpu); -} + // FI (命令キャッシュ凍結) + if ((chgval & M68030::CACR_FI) != 0) { + icache->freeze = (newval & M68030::CACR_FI); + putlog(1, "CACR:FI <- %u", icache->freeze); + } + + // CEI (命令キャッシュの指定エントリをクリア) + if ((chgval & M68030::CACR_CEI) != 0) { + // CAAR の b7-2 で示されるロングワードを無効にする。 + uint32 idx = (reg.caar & 0x000000f0) >> 4; + uint32 sel = (reg.caar & 0x0000000c) >> 2; + auto& l = icache->line[idx]; + l.valid[sel] = 0; + + newval &= ~M68030::CACR_CEI; + } + + // CI (命令キャッシュクリア) + if ((chgval & M68030::CACR_CI) != 0) { + for (auto& l : icache->line) { + l.valid_all = 0; + } -void -m68030_write_32(m68kcpu *cpu, uint32 addr, uint32 data) -{ - cpu->bus.ssw = SSW_BUS_W | cpu->bus.fc2 | FC_DATA; - cpu->bus.laddr = addr; - cpu->bus.data = data; - m68030_write_32_bus(cpu); -} + newval &= ~M68030::CACR_CI; + } -// -// MOVES 用の FC 指定版。 -// 通常アクセスが現在の FC 空間に対してだが、 -// こちらは SFC/DFC で指定された FC 空間に対してアクセスする。 -// + // IBE (命令バースト有効) + if ((chgval & M68030::CACR_IBE) != 0) { + icache->burst_enable = + (newval & M68030::CACR_IBE) && + (icache->freeze == false) && + has_burst_access; + } + + // ED (データキャッシュ有効) + if ((chgval & M68030::CACR_ED) != 0) { + if ((newval & M68030::CACR_ED) != 0) { + if (force_disable_dcache) { + putlog(1, "D-Cache not enabled (mpu-force-disable-dcache)"); + newval &= ~M68030::CACR_ED; + } else { + // データキャッシュ有効。 + dcache->enable = true; + dcache->hist.Clear(); + putlog(1, "D-Cache enabled"); + } + } else { + // データキャッシュ無効。 + dcache->enable = false; + putlog(1, "D-Cache disabled"); + } + } -uint32 -m68030_read_8_fc(m68kcpu *cpu, uint32 addr) -{ - cpu->bus.ssw = SSW_BUS_R | RegSFC; - cpu->bus.laddr = addr; - return m68030_read_8_bus(cpu); -} + // FD (データキャッシュ凍結) + if ((chgval & M68030::CACR_FD) != 0) { + dcache->freeze = (newval & M68030::CACR_FD); + putlog(1, "CACR:FD <- %u", dcache->freeze); + } + + // CED (データキャッシュの指定エントリをクリア) + if ((chgval & M68030::CACR_CED) != 0) { + // CAAR の b7-2 で示されるロングワードを無効にする。 + uint32 idx = (reg.caar & 0x000000f0) >> 4; + uint32 sel = (reg.caar & 0x0000000c) >> 2; + auto& l = dcache->line[idx]; + l.valid[sel] = 0; + + newval &= ~M68030::CACR_CED; + } + + // CD (データキャッシュクリア) + if ((chgval & M68030::CACR_CD) != 0) { + for (auto& l : dcache->line) { + l.valid_all = 0; + } -uint32 -m68030_read_16_fc(m68kcpu *cpu, uint32 addr) -{ - cpu->bus.ssw = SSW_BUS_R | RegSFC; - cpu->bus.laddr = addr; - return m68030_read_16_bus(cpu); -} + newval &= ~M68030::CACR_CD; + } -uint32 -m68030_read_32_fc(m68kcpu *cpu, uint32 addr) -{ - cpu->bus.ssw = SSW_BUS_R | RegSFC; - cpu->bus.laddr = addr; - return m68030_read_32_bus(cpu); -} + // DBE (データバースト有効) + if ((chgval & M68030::CACR_DBE) != 0) { + dcache->burst_enable = + (newval & M68030::CACR_DBE) && + (dcache->freeze == false) && + has_burst_access; + } -void -m68030_write_8_fc(m68kcpu *cpu, uint32 addr, uint32 data) -{ - cpu->bus.ssw = SSW_BUS_W | RegDFC; - cpu->bus.laddr = addr; - cpu->bus.data = data; - m68030_write_8_bus(cpu); + // WA (ライトアロケート) + if ((chgval & M68030::CACR_WA) != 0) { + dcache->wa = (newval & M68030::CACR_WA); + } + + // とりあえずレジスタイメージも保存。 + reg.cacr = newval; } +// リセット例外の CPU 固有部分。 void -m68030_write_16_fc(m68kcpu *cpu, uint32 addr, uint32 data) +MPU68030Device::ResetCache() { - cpu->bus.ssw = SSW_BUS_W | RegDFC; - cpu->bus.laddr = addr; - cpu->bus.data = data; - m68030_write_16_bus(cpu); + SetCACR(M68030::CACR_CD | M68030::CACR_CI); + icache->hist.Clear(); + dcache->hist.Clear(); } +// キャッシュの統計情報を計算。 void -m68030_write_32_fc(m68kcpu *cpu, uint32 addr, uint32 data) +m68030Cache::CalcStat() { - cpu->bus.ssw = SSW_BUS_W | RegDFC; - cpu->bus.laddr = addr; - cpu->bus.data = data; - m68030_write_32_bus(cpu); + m68030CacheStat s; + + memset(&s, 0, sizeof(s)); + + // fasthit はファストパスでヒットした回数。 + uint32 total_read = stat_fasthit + stat_hit + stat_miss; + if (__predict_true(total_read != 0)) { + s.hit = (float)(stat_fasthit + stat_hit) * 100 / total_read; + stat_fasthit = 0; + stat_hit = 0; + stat_miss = 0; + } + + // 前回から今回までの間に disable になったのなら有効な値がある。 + if (__predict_true(enable || total_read != 0)) { + // ここはロックする。 + std::lock_guard lock(hist_mtx); + hist.EnqueueForce(s); + } }