--- nono/m680x0/m68030bus.cpp 2026/04/29 17:05:15 1.1.1.4 +++ nono/m680x0/m68030bus.cpp 2026/04/29 17:05:57 1.1.1.10 @@ -4,249 +4,751 @@ // Licensed under nono-license.txt // -// 論理アクセス - #include "mpu680x0.h" +#include "mainbus.h" +#include "m68030cache.h" -// バスエラー時に SSW をセットする。 -// {fetch,read,write}_NN_mmu() 関数の下請け。 -void -m68kbus::SET_SSW(bool use_mmu, uint size) +// 現在の 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 にある + +// reg.pc からワードをフェッチし reg.pc を進める。 +uint32 +MPU68030Device::fetch_2() { - // サイズを設定 - ssw &= ~M68K::SSW_SIZE_MASK; - ssw |= size; + busaddr addr = busaddr(reg.pc) | fc_inst | BusAddr::Size2; - if (use_mmu) { - // データアクセスなら再試行のため DataFault ビットをセット。 - // 命令アクセスなら RB をセット。 - if ((ssw & M68K::FC_DATA)) { - ssw |= M68K::SSW_DF; - } else { - ssw |= M68K::SSW_RB; + // まず可能ならキャッシュから探す。 + 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; } } -} -// {fetch,read,write}_NN() と {read,write}_NN_fc() の共通部分。 -// fetch には FC 指定版はないので分ける必要は本来ないけど対称性のため。 -// bus に必要な情報はセットされている。 -// _bus 接尾辞はバスアクセスというより bus 構造体を使ったアクセスという感じ。 - -inline uint32 -MPU680x0Device::fetch_16_bus() -{ - if (mmu_enable) { - return fetch_16_mmu(); + busdata bd; + if (__predict_true(cache->enable)) { + bd = read_cache(cache, addr); } else { - return fetch_16_mmu(); + bd = read_nocache(addr); } + if (__predict_false(bd.IsBusErr())) { + throw M68K::EXCEP_BUSERR; + } + reg.pc += 2; + return bd.Data(); } -inline uint32 -MPU680x0Device::fetch_32_bus() +// reg.pc からロングワード(ミスアライン可)をフェッチし reg.pc を進める。 +uint32 +MPU68030Device::fetch_4() { - if (mmu_enable) { - return fetch_32_mmu(); - } else { - return fetch_32_mmu(); + busaddr addr = busaddr(reg.pc) | fc_inst | BusAddr::Size4; + + // まず可能ならキャッシュから探す。 + 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; + } } -} -inline uint32 -MPU680x0Device::read_8_bus() -{ -if(1)putlog(2, "%s $%08x", __func__, bus.laddr); - if (mmu_enable) { - return read_8_mmu(); + fetch: + busdata bd; + if (__predict_true(cache->enable)) { + bd = read_cache(cache, addr); } else { - return read_8_mmu(); + bd = read_nocache(addr); + } + if (__predict_false(bd.IsBusErr())) { + throw M68K::EXCEP_BUSERR; } + reg.pc += 4; + return bd.Data(); } -inline uint32 -MPU680x0Device::read_16_bus() +// read_1,2,4() の共通部分。ミスアライン可。 +// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。 +uint32 +MPU68030Device::read_data(busaddr addr) { -if(1)putlog(2, "%s $%08x", __func__, bus.laddr); - if (mmu_enable) { - return read_16_mmu(); + m68030Cache *cache = dcache.get(); + busdata data; + if (__predict_true(cache->enable)) { + data = read_cache(cache, addr); } else { - return read_16_mmu(); + data = read_nocache(addr); + } + if (__predict_false(data.IsBusErr())) { + throw M68K::EXCEP_BUSERR; } + return data.Data(); } -inline uint32 -MPU680x0Device::read_32_bus() +// write_1,2,4() の共通部分。ミスアライン可。 +// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。 +void +MPU68030Device::write_data(busaddr addr, uint32 data) { -if(1)putlog(2, "%s $%08x", __func__, bus.laddr); - if (mmu_enable) { - return read_32_mmu(); - } else { - return read_32_mmu(); + busdata r = write_bus(addr, data); + if (__predict_false(r.IsBusErr())) { + throw M68K::EXCEP_BUSERR; } } -inline void -MPU680x0Device::write_8_bus() -{ -if(1)putlog(2, "%s $%08x", __func__, bus.laddr); - if (mmu_enable) { - write_8_mmu(); - } else { - write_8_mmu(); +// キャッシュ無効時の読み込み。ミスアライン可。 +// バスエラーも 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(); + + 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; + } + + addr += datasize; + reqsize -= datasize; + addr.ChangeSize(reqsize); } + return data; } -inline void -MPU680x0Device::write_16_bus() -{ -if(1)putlog(2, "%s $%08x", __func__, bus.laddr); - if (mmu_enable) { - write_16_mmu(); - } else { - write_16_mmu(); +// キャッシュ有効時の読み込み。ミスアライン可。 +// バスエラーも 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++; + + 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; + } + } + } + + // 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); } + + return data; } -inline void -MPU680x0Device::write_32_bus() -{ -if(1)putlog(2, "%s $%08x", __func__, bus.laddr); - if (mmu_enable) { - write_32_mmu(); - } else { - write_32_mmu(); +// シングル充填を行い、(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。 // -// CPU 内部からのメモリアクセスは常にこの8つ (またはこれを呼び出すヘルパー -// 関数) を呼ぶこと。 -// uint32 fetch_{16,32}() -// uint32 read_{8,16,32}(laddr) -// void write_{8,16,32}(laddr, data) -// +// 第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 { + // バイトポートアクセスの場合。 -uint32 -MPU680x0Device::fetch_16() -{ - bus.ssw = M68K::SSW_BUS_R | bus.fc2 | M68K::FC_PROG; - bus.laddr = reg.pc; - return fetch_16_bus(); -} + // 第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); + } + } -uint32 -MPU680x0Device::fetch_32() -{ - bus.ssw = M68K::SSW_BUS_R | bus.fc2 | M68K::FC_PROG; - bus.laddr = reg.pc; - return fetch_32_bus(); + done: + return data; } -uint32 -MPU680x0Device::read_8(uint32 addr) -{ - bus.ssw = M68K::SSW_BUS_R | bus.fc2 | M68K::FC_DATA; - bus.laddr = addr; - return read_8_bus(); -} +// キャッシュ/バスへの書き込み。ミスアライン可。 +// バスエラーも 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; + } -uint32 -MPU680x0Device::read_16(uint32 addr) -{ - bus.ssw = M68K::SSW_BUS_R | bus.fc2 | M68K::FC_DATA; - bus.laddr = addr; - return read_16_bus(); + // まず外部バスを駆動して 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; + } + } + + 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; + } + } + } + } + + // バスアクセスでバスエラーが出たらキャッシュ更新後に発報。 + if (__predict_false(bd.IsBusErr())) { + return bd; + } + + // ロングワード境界をまたいだ余りがあればもう一回。 + if (nextsize == 0) { + break; + } + putlog(3, " nextsize=%u", nextsize); + addr += addr.GetSize(); + addr.ChangeSize(nextsize); + data = nextdata; + } + + return 0; } -uint32 -MPU680x0Device::read_32(uint32 addr) +// mainbus の paddr から指定サイズで1回読み込む。 +// ウェイトはこちらで加算して、バスエラーも含めた bd を返す。 +busdata +MPU680x0Device::read_phys(const busaddr paddr) { - bus.ssw = M68K::SSW_BUS_R | bus.fc2 | M68K::FC_DATA; - bus.laddr = addr; - return read_32_bus(); + busdata bd = mainbus->Read(paddr); + buswait += bd.GetWait(); + return bd; } -void -MPU680x0Device::write_8(uint32 addr, uint32 data) +// mainbus の paddr に指定サイズで1回書き込む。 +// ウェイトはこちらで加算して、バスエラーも含めた bd を返す。 +busdata +MPU680x0Device::write_phys(const busaddr paddr, uint32 data) { - bus.ssw = M68K::SSW_BUS_W | bus.fc2 | M68K::FC_DATA; - bus.laddr = addr; - bus.data = data; - write_8_bus(); + busdata bd = mainbus->Write(paddr, data); + buswait += bd.GetWait(); + return bd; } -void -MPU680x0Device::write_16(uint32 addr, uint32 data) +// バースト転送。 +// paddr の下位4ビットは無視する。 +// paddr がバースト転送可能なら dst に 16 バイト転送して true を返す。 +// paddr がバースト転送不可なら dst は触らず false を返す。 +// VM (バス) がバースト転送対応している場合のみ呼ぶこと。 +bool +MPU680x0Device::read_phys_burst(busaddr paddr, uint32 *dst) { - bus.ssw = M68K::SSW_BUS_W | bus.fc2 | M68K::FC_DATA; - bus.laddr = addr; - bus.data = data; - write_16_bus(); + paddr &= 0xfffffff0U; + busdata bd = mainbus->ReadBurst16(paddr, dst); + buswait += bd.GetWait(); + + // BusErr ビットをバースト転送不可フラグとして使っている。 + return !bd.IsBusErr(); } -void -MPU680x0Device::write_32(uint32 addr, uint32 data) +// 物理メモリからのロングワード読み込み。 +// 現在の bus に影響を与えず指定のアドレスを読み出す。MMU 専用。 +busdata +MPU680x0Device::read_phys_4(uint32 addr) { - bus.ssw = M68K::SSW_BUS_W | bus.fc2 | M68K::FC_DATA; - bus.laddr = addr; - bus.data = data; - write_32_bus(); -} + m68kbus backup; + busdata bd; -// -// MOVES 用の FC 指定版。 -// 通常アクセスが現在の FC 空間に対してだが、 -// こちらは SFC/DFC で指定された FC 空間に対してアクセスする。 -// + // バス情報を一旦退避 + backup = bus; -uint32 -MPU680x0Device::read_8_fc(uint32 addr) -{ - bus.ssw = M68K::SSW_BUS_R | reg.sfc; - bus.laddr = addr; - return read_8_bus(); -} + // 物理バスアクセス + bus.laddr = busaddr(addr) | BusAddr::SRead | BusAddr::Size4; + bd = read_phys(bus.laddr); + if (__predict_false(bd.IsBusErr())) { + return bd; + } -uint32 -MPU680x0Device::read_16_fc(uint32 addr) -{ - bus.ssw = M68K::SSW_BUS_R | reg.sfc; - bus.laddr = addr; - return read_16_bus(); -} + // バス情報を復元 + bus = backup; + return bd; +} + +// 物理メモリへのロングワード書き込み。 +// 現在の bus に影響を与えず指定のアドレスに書き出す。MMU 専用。 +busdata +MPU680x0Device::write_phys_4(uint32 addr, uint32 data) +{ + m68kbus backup; + busdata bd; + + // バス情報を一旦退避 + backup = bus; + + // 物理バスアクセス + bus.laddr = busaddr(addr) | BusAddr::SWrite | BusAddr::Size4; + bd = write_phys(bus.laddr, data); + if (__predict_false(bd.IsBusErr())) { + return bd; + } -uint32 -MPU680x0Device::read_32_fc(uint32 addr) -{ - bus.ssw = M68K::SSW_BUS_R | reg.sfc; - bus.laddr = addr; - return read_32_bus(); + // バス情報を復元 + bus = backup; + return bd; } +// CACR レジスタ書き込み。 void -MPU680x0Device::write_8_fc(uint32 addr, uint32 data) +MPU68030Device::SetCACR(uint32 newval) { - bus.ssw = M68K::SSW_BUS_W | reg.dfc; - bus.laddr = addr; - bus.data = data; - write_8_bus(); + 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"); + } + } + + // 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; + } + + newval &= ~M68030::CACR_CI; + } + + // 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"); + } + } + + // 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; + } + + newval &= ~M68030::CACR_CD; + } + + // DBE (データバースト有効) + if ((chgval & M68030::CACR_DBE) != 0) { + dcache->burst_enable = + (newval & M68030::CACR_DBE) && + (dcache->freeze == false) && + has_burst_access; + } + + // WA (ライトアロケート) + if ((chgval & M68030::CACR_WA) != 0) { + dcache->wa = (newval & M68030::CACR_WA); + } + + // とりあえずレジスタイメージも保存。 + reg.cacr = newval; } +// リセット例外の CPU 固有部分。 void -MPU680x0Device::write_16_fc(uint32 addr, uint32 data) +MPU68030Device::ResetCache() { - bus.ssw = M68K::SSW_BUS_W | reg.dfc; - bus.laddr = addr; - bus.data = data; - write_16_bus(); + SetCACR(M68030::CACR_CD | M68030::CACR_CI); + icache->hist.Clear(); + dcache->hist.Clear(); } +// キャッシュの統計情報を計算。 void -MPU680x0Device::write_32_fc(uint32 addr, uint32 data) +m68030Cache::CalcStat() { - bus.ssw = M68K::SSW_BUS_W | reg.dfc; - bus.laddr = addr; - bus.data = data; - write_32_bus(); + 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); + } }