--- nono/m680x0/m68030bus.cpp 2026/04/29 17:05:22 1.1.1.5 +++ nono/m680x0/m68030bus.cpp 2026/04/29 17:05:57 1.1.1.10 @@ -5,362 +5,750 @@ // #include "mpu680x0.h" +#include "mainbus.h" +#include "m68030cache.h" // 現在の FC での通常アクセス (MPU 内部から使用する) // ミスアライン可。 // バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。 -// uint32 fetch_{16,32}() -// uint32 read_{8,16,32}(laddr) -// void write_{8,16,32}(laddr, data) +// 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 -MPU680x0Device::fetch_16() +MPU68030Device::fetch_2() { - uint32 data; + busaddr addr = busaddr(reg.pc) | fc_inst | BusAddr::Size2; - if (__predict_true(icache_enable)) { - data = fetch_16_cache(reg.pc); + // まず可能ならキャッシュから探す。 + 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; + } + } + + busdata bd; + if (__predict_true(cache->enable)) { + bd = read_cache(cache, addr); } else { - data = fetch_16_bus(reg.pc); + bd = read_nocache(addr); + } + if (__predict_false(bd.IsBusErr())) { + throw M68K::EXCEP_BUSERR; } reg.pc += 2; - - return data; + return bd.Data(); } // reg.pc からロングワード(ミスアライン可)をフェッチし reg.pc を進める。 uint32 -MPU680x0Device::fetch_32() +MPU68030Device::fetch_4() { - uint32 data; + busaddr addr = busaddr(reg.pc) | fc_inst | BusAddr::Size4; - if (__predict_true(icache_enable)) { - data = fetch_32_cache(reg.pc); - } else { - if ((reg.pc & 2) == 0) { - data = fetch_32_bus(reg.pc); - } else { - data = fetch_16_bus(reg.pc) << 16; - data |= fetch_16_bus(reg.pc + 2); + // まず可能ならキャッシュから探す。 + 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; } } - reg.pc += 4; - return data; + 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(); } +// read_1,2,4() の共通部分。ミスアライン可。 +// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。 uint32 -MPU680x0Device::read_8(uint32 addr) +MPU68030Device::read_data(busaddr addr) { - busaddr laddr(addr, fc_data | busaddr::R); - return read_8_fc(laddr); + m68030Cache *cache = dcache.get(); + busdata data; + if (__predict_true(cache->enable)) { + data = read_cache(cache, addr); + } else { + data = read_nocache(addr); + } + if (__predict_false(data.IsBusErr())) { + throw M68K::EXCEP_BUSERR; + } + return data.Data(); } -uint32 -MPU680x0Device::read_16(uint32 addr) +// write_1,2,4() の共通部分。ミスアライン可。 +// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。 +void +MPU68030Device::write_data(busaddr addr, uint32 data) { - busaddr laddr(addr, fc_data | busaddr::R); - return read_16_fc(laddr); + busdata r = write_bus(addr, data); + if (__predict_false(r.IsBusErr())) { + throw M68K::EXCEP_BUSERR; + } } -uint32 -MPU680x0Device::read_32(uint32 addr) -{ - busaddr laddr(addr, fc_data | busaddr::R); - return read_32_fc(laddr); -} +// キャッシュ無効時の読み込み。ミスアライン可。 +// バスエラーも 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(); -void -MPU680x0Device::write_8(uint32 addr, uint32 data) -{ - busaddr laddr(addr, fc_data | busaddr::W); - write_8_fc(laddr, data); -} + busdata bd = read_phys(paddr); + if (__predict_false(bd.IsBusErr())) { + return bd; + } -void -MPU680x0Device::write_16(uint32 addr, uint32 data) -{ - busaddr laddr(addr, fc_data | busaddr::W); - write_16_fc(laddr, data); -} + 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; + } -void -MPU680x0Device::write_32(uint32 addr, uint32 data) -{ - busaddr laddr(addr, fc_data | busaddr::W); - write_32_fc(laddr, data); + addr += datasize; + reqsize -= datasize; + addr.ChangeSize(reqsize); + } + return data; } -// FC 指定アクセス (MPU コアから使用する) -// ミスアライン可。 -// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。 -// uint32 read_{8,16,32}_fc(busaddr) -// void write_{8,16,32}_fc(busaddr, data) -// (fetch は常に現在の FC でアクセスするので FC 指定版は不要) +// キャッシュ有効時の読み込み。ミスアライン可。 +// バスエラーも 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++; -uint32 -MPU680x0Device::read_8_fc(busaddr addr) -{ - uint32 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; + } + } + } - if (1) { - data = read_8_bus(addr); + // 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; } -uint32 -MPU680x0Device::read_16_fc(busaddr addr) +// シングル充填を行い、(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; - if (1) { - // バスアクセスは要整列。 - if (__predict_true((addr & 1) == 0)) { - data = read_16_bus(addr); + //assert(bd.IsOK()); + //assert(bd.GetSize() < 4); + + if (bd.GetSize() == 2) { + // ワードポートアクセスだったなら、第1アクセスが上位か下位かだけ。 + if ((paddr.Addr() & 2) == 0) { + data = bd.Data() << 16; } else { - data = read_8_bus(addr) << 8; - addr += 1; - data |= read_8_bus(addr); + 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 { + // バイトポートアクセスの場合。 + + // 第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); } } + done: return data; } -uint32 -MPU680x0Device::read_32_fc(busaddr addr) -{ - uint32 data; - - if (1) { - // バスアクセスは要整列。 - if (__predict_true((addr & 3) == 0)) { - data = read_32_bus(addr); - } else if (__predict_true((addr & 1) == 0)) { - data = read_16_bus(addr) << 16; - addr += 2; - data |= read_16_bus(addr); +// キャッシュ/バスへの書き込み。ミスアライン可。 +// バスエラーも 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 { - data = read_8_bus(addr) << 24; - addr += 1; - data |= read_16_bus(addr) << 8; - addr += 2; - data |= read_8_bus(addr); + nextsize = 0; + } + + // まず外部バスを駆動して 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 data; + return 0; } -void -MPU680x0Device::write_8_fc(busaddr addr, uint32 data) +// mainbus の paddr から指定サイズで1回読み込む。 +// ウェイトはこちらで加算して、バスエラーも含めた bd を返す。 +busdata +MPU680x0Device::read_phys(const busaddr paddr) { - if (1) { - write_8_bus(addr, data); - } + busdata bd = mainbus->Read(paddr); + buswait += bd.GetWait(); + return bd; } -void -MPU680x0Device::write_16_fc(busaddr addr, uint32 data) +// mainbus の paddr に指定サイズで1回書き込む。 +// ウェイトはこちらで加算して、バスエラーも含めた bd を返す。 +busdata +MPU680x0Device::write_phys(const busaddr paddr, uint32 data) { - if (1) { - // バスアクセスは要整列。 - if (__predict_true((addr & 1) == 0)) { - write_16_bus(addr, data); - } else { - write_8_bus(addr, data >> 8); - addr += 1; - write_8_bus(addr, data & 0xff); - } - } + busdata bd = mainbus->Write(paddr, data); + buswait += bd.GetWait(); + return bd; } -void -MPU680x0Device::write_32_fc(busaddr addr, uint32 data) +// バースト転送。 +// paddr の下位4ビットは無視する。 +// paddr がバースト転送可能なら dst に 16 バイト転送して true を返す。 +// paddr がバースト転送不可なら dst は触らず false を返す。 +// VM (バス) がバースト転送対応している場合のみ呼ぶこと。 +bool +MPU680x0Device::read_phys_burst(busaddr paddr, uint32 *dst) { - if (1) { - // バスアクセスは要整列。 - if (__predict_true((addr & 3) == 0)) { - write_32_bus(addr, data); - } else if (__predict_true((addr & 1) == 0)) { - write_16_bus(addr, data >> 16); - addr += 2; - write_16_bus(addr, data & 0xffff); - } else { - write_8_bus(addr, data >> 24); - addr += 1; - write_16_bus(addr, (data >> 8) & 0xffff); - addr += 2; - write_8_bus(addr, data & 0xff); - } - } -} + paddr &= 0xfffffff0U; + busdata bd = mainbus->ReadBurst16(paddr, dst); + buswait += bd.GetWait(); -// 外部バスアクセス (MMU を含む) -// ミスアライン不可。 -// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。 -// uint32 fetch_{16,32}_bus() -// uint32 read_{8,16,32}_bus(busaddr) -// void write_{8,16,32}_bus(busaddr, data) - -// goto を含むことに注意。gcc 拡張式(?) に注意。 -#define FETCH(size) ({ \ - bus.laddr.Set(addr, fc_inst); \ - bus.paddr = bus.laddr; \ - bus.ci = false; \ - if (__predict_true(mmu_enable)) { \ - if (__predict_false(TranslateFetch() == false)) \ - goto buserr; \ - } \ - busdata bd_ = mainbus->__CONCAT(Read,size)(bus.paddr); \ - if (bd_.IsBusErr()) \ - goto buserr; \ - bd_; /* これが戻り値 */ \ -}) -#define READ(size) ({ \ - bus.laddr = addr; \ - bus.paddr = addr; \ - bus.ci = false; \ - if (__predict_true(mmu_enable)) { \ - if (__predict_false(TranslateRead() == false)) \ - goto buserr; \ - } \ - busdata bd_ = mainbus->__CONCAT(Read,size)(bus.paddr); \ - if (bd_.IsBusErr()) \ - goto buserr; \ - bd_; /* これが戻り値 */ \ -}) -#define WRITE(size, data_) ({ \ - bus.laddr = addr; \ - bus.paddr = addr; \ - bus.ci = false; \ - if (__predict_true(mmu_enable)) { \ - if (__predict_false(TranslateWrite() == false)) \ - goto buserr; \ - } \ - busdata bd_ = mainbus->__CONCAT(Write,size)(bus.paddr, data_); \ - if (bd_.IsBusErr()) \ - goto buserr; \ - bd_; /* これが戻り値 */ \ -}) + // BusErr ビットをバースト転送不可フラグとして使っている。 + return !bd.IsBusErr(); +} -uint32 -MPU680x0Device::fetch_16_bus(uint32 addr) +// 物理メモリからのロングワード読み込み。 +// 現在の bus に影響を与えず指定のアドレスを読み出す。MMU 専用。 +busdata +MPU680x0Device::read_phys_4(uint32 addr) { + m68kbus backup; busdata bd; - bd = FETCH(16); - buswait += bd.GetWait(); - return bd.Data(); + // バス情報を一旦退避 + backup = bus; + + // 物理バスアクセス + bus.laddr = busaddr(addr) | BusAddr::SRead | BusAddr::Size4; + bd = read_phys(bus.laddr); + if (__predict_false(bd.IsBusErr())) { + return bd; + } - buserr: - bus.size = M68K::SSW_SIZE_WORD; - throw M68K::EXCEP_BUSERR; + // バス情報を復元 + bus = backup; + return bd; } -uint32 -MPU680x0Device::fetch_32_bus(uint32 addr) +// 物理メモリへのロングワード書き込み。 +// 現在の bus に影響を与えず指定のアドレスに書き出す。MMU 専用。 +busdata +MPU680x0Device::write_phys_4(uint32 addr, uint32 data) { + m68kbus backup; busdata bd; - bd = FETCH(32); - buswait += bd.GetWait(); - return bd.Data(); + // バス情報を一旦退避 + backup = bus; + + // 物理バスアクセス + bus.laddr = busaddr(addr) | BusAddr::SWrite | BusAddr::Size4; + bd = write_phys(bus.laddr, data); + if (__predict_false(bd.IsBusErr())) { + return bd; + } - buserr: - bus.size = M68K::SSW_SIZE_LONG; - throw M68K::EXCEP_BUSERR; + // バス情報を復元 + bus = backup; + return bd; } -uint32 -MPU680x0Device::read_8_bus(busaddr addr) +// CACR レジスタ書き込み。 +void +MPU68030Device::SetCACR(uint32 newval) { - busdata data; + 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"); + } + } - data = READ(8); - putlog(2, "read_8 %08x -> %02x", bus.laddr.Addr(), data.Data()); - buswait += data.GetWait(); - return data.Data(); + // FI (命令キャッシュ凍結) + if ((chgval & M68030::CACR_FI) != 0) { + icache->freeze = (newval & M68030::CACR_FI); + putlog(1, "CACR:FI <- %u", icache->freeze); + } - buserr: - bus.size = M68K::SSW_SIZE_BYTE; - throw M68K::EXCEP_BUSERR; -} + // 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; -uint32 -MPU680x0Device::read_16_bus(busaddr addr) -{ - busdata data; + newval &= ~M68030::CACR_CEI; + } - data = READ(16); - putlog(2, "read_16 %08x -> %04x", bus.laddr.Addr(), data.Data()); - buswait += data.GetWait(); - return data.Data(); + // CI (命令キャッシュクリア) + if ((chgval & M68030::CACR_CI) != 0) { + for (auto& l : icache->line) { + l.valid_all = 0; + } - buserr: - bus.size = M68K::SSW_SIZE_WORD; - throw M68K::EXCEP_BUSERR; -} + newval &= ~M68030::CACR_CI; + } -uint32 -MPU680x0Device::read_32_bus(busaddr addr) -{ - busdata data; + // IBE (命令バースト有効) + if ((chgval & M68030::CACR_IBE) != 0) { + icache->burst_enable = + (newval & M68030::CACR_IBE) && + (icache->freeze == false) && + has_burst_access; + } - data = READ(32); - putlog(2, "read_32 %08x -> %08x", bus.laddr.Addr(), data.Data()); - buswait += data.GetWait(); - return data.Data(); + // 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"); + } + } - buserr: - bus.size = M68K::SSW_SIZE_LONG; - throw M68K::EXCEP_BUSERR; -} + // FD (データキャッシュ凍結) + if ((chgval & M68030::CACR_FD) != 0) { + dcache->freeze = (newval & M68030::CACR_FD); + putlog(1, "CACR:FD <- %u", dcache->freeze); + } -void -MPU680x0Device::write_8_bus(busaddr addr, uint32 data) -{ - busdata rd; + // 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; + } - rd = WRITE(8, data); - putlog(2, "write_8 %08x <- %02x", bus.laddr.Addr(), data); - buswait += rd.GetWait(); - return; - - buserr: - bus.size = M68K::SSW_SIZE_BYTE; - throw M68K::EXCEP_BUSERR; + // 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_bus(busaddr addr, uint32 data) +MPU68030Device::ResetCache() { - busdata rd; - - rd = WRITE(16, data); - putlog(2, "write_16 %08x <- %04x", bus.laddr.Addr(), data); - buswait += rd.GetWait(); - return; - - buserr: - bus.size = M68K::SSW_SIZE_WORD; - throw M68K::EXCEP_BUSERR; + SetCACR(M68030::CACR_CD | M68030::CACR_CI); + icache->hist.Clear(); + dcache->hist.Clear(); } +// キャッシュの統計情報を計算。 void -MPU680x0Device::write_32_bus(busaddr addr, uint32 data) +m68030Cache::CalcStat() { - busdata rd; + m68030CacheStat s; - rd = WRITE(32, data); - putlog(2, "write_32 %08x <- %08x", bus.laddr.Addr(), data); - buswait += rd.GetWait(); - return; - - buserr: - bus.size = M68K::SSW_SIZE_LONG; - throw M68K::EXCEP_BUSERR; + 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); + } }