--- nono/m680x0/m68030bus.cpp 2026/04/29 17:05:48 1.1.1.9 +++ nono/m680x0/m68030bus.cpp 2026/04/29 17:05:57 1.1.1.10 @@ -24,9 +24,9 @@ MPU68030Device::fetch_2() // まず可能ならキャッシュから探す。 m68030Cache *cache = icache.get(); if (__predict_true(cache->enable)) { - uint32 tag = (addr.Get() >> 4) & 0xfffffff0; - uint32 idx = (reg.pc & 0x000000f0) >> 4; - uint32 sel = (reg.pc & 0x0000000c) >> 2; + 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])) { @@ -49,7 +49,6 @@ MPU68030Device::fetch_2() bd = read_nocache(addr); } if (__predict_false(bd.IsBusErr())) { - bus.size = addr.GetSize(); throw M68K::EXCEP_BUSERR; } reg.pc += 2; @@ -65,9 +64,9 @@ MPU68030Device::fetch_4() // まず可能ならキャッシュから探す。 m68030Cache *cache = icache.get(); if (__predict_true(cache->enable)) { - uint32 tag = (addr.Get() >> 4) & 0xfffffff0; - uint32 idx = (reg.pc & 0x000000f0) >> 4; - uint32 sel = (reg.pc & 0x0000000c) >> 2; + 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])) { @@ -100,7 +99,6 @@ MPU68030Device::fetch_4() bd = read_nocache(addr); } if (__predict_false(bd.IsBusErr())) { - bus.size = addr.GetSize(); throw M68K::EXCEP_BUSERR; } reg.pc += 4; @@ -120,7 +118,6 @@ MPU68030Device::read_data(busaddr addr) data = read_nocache(addr); } if (__predict_false(data.IsBusErr())) { - bus.size = addr.GetSize(); throw M68K::EXCEP_BUSERR; } return data.Data(); @@ -133,7 +130,6 @@ MPU68030Device::write_data(busaddr addr, { busdata r = write_bus(addr, data); if (__predict_false(r.IsBusErr())) { - bus.size = addr.GetSize(); throw M68K::EXCEP_BUSERR; } } @@ -150,14 +146,19 @@ MPU68030Device::read_nocache(busaddr add // 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; - bus.paddr = addr; - if (__predict_false(TranslateRead() == false)) { + busaddr paddr = TranslateRead(addr); + if (__predict_false(paddr.IsBusErr())) { return BusData::BusErr; } - busdata bd = read_phys(); + bus.ci = paddr.IsCInhibit(); + + busdata bd = read_phys(paddr); if (__predict_false(bd.IsBusErr())) { return bd; } @@ -192,118 +193,96 @@ MPU68030Device::read_nocache(busaddr add return data; } -// offset [0, 4) から size [1, 4] バイトのマスクを作成する。 -// マスクは +0, +1, +2, +3 バイト目がビット 3, 2, 1, 0 に対応している。 -static inline uint32 -OPERMASK(uint32 offset, uint32 size) -{ - return ((1U << size) - 1) << (4 - offset - size); -} - // キャッシュ有効時の読み込み。ミスアライン可。 // バスエラーも busdata を返す。 +// +// 簡略化・高速化のため実機と異なる制約を設けてある。 +// 1) 実際の 68030 システムでは、「ロングワードの全バイトがキャッシュ可能なら +// !CIIN をネゲートしなければならない」「いずれかのバイトでもキャッシュ可能 +// でないなら CIIN をアサートしなければならない」(MC68030UM.pdf p6-15、 +// 日本語本 p296 ※誤訳あり) とあり、CIIN は同一ロングワード内で変化しては +// いけないが、ロングワードごとには変化してよい。 +// nono の実装ではこれを同一ライン(16バイト)単位の制約にする。 +// 現状サポートしている機種ではこれで困ることはない。 busdata MPU68030Device::read_cache(m68030Cache *cache, busaddr addr) { - putlog(2, "read_cache $%u.%08x.%c", + putlog(3, "read_cache %u.%08x sz=%c", addr.GetFC(), addr.Addr(), "0BW3L"[addr.GetSize()]); uint32 data = 0; - bus.ci = false; for (;;) { uint32 entry_data = 0; - // addr から今回対象とするロングワードエントリに対するマスクを作成。 - uint32 reqoffset = addr.Addr() & 3; - uint32 reqsize = addr.GetSize(); - uint32 opersize = std::min(reqsize, 4 - reqoffset); - // まず可能ならキャッシュから探す。 - uint32 tag = (addr.Get() >> 4) & 0xfffffff0; - uint32 idx = (addr.Addr() & 0x000000f0) >> 4; - uint32 sel = (addr.Addr() & 0x0000000c) >> 2; + 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(2, " entry_data=$%08x (match)", entry_data); + putlog(3, " entry_data=$%08x (%s)", entry_data, "match"); } else { + // タグが違うか、エントリが無効。 cache->stat_miss++; - // キャッシュになければバスアクセス。 bus.laddr = addr; - bus.paddr = addr; - if (__predict_false(TranslateRead() == false)) { - putlog(2, " TranslateRead BusErr"); + busaddr paddr = TranslateRead(addr); + if (__predict_false(paddr.IsBusErr())) { + putlog(3, " TranslateRead BusErr"); return BusData::BusErr; } - putlog(2, " paddr=$%08x.%c", - bus.paddr.Addr(), "0BW3L"[bus.paddr.GetSize()]); - - busdata bd; - if (__predict_true(cache->freeze == false && bus.ci == false)) { - // キャッシュ可。 - if (l->tag != tag) { - // タグが違う場合、出来ればバーストリードを使う。 - // XXX: タグが同じでもエントリがすべて無効ならバーストリード - // するのだが、パフォーマンス落ちるので保留。 - // TODO: 要求オペランドがラインをまたぐ場合の1エントリ目は - // バースト転送しない。 - if (__predict_true(cache->burst_enable) && - __predict_true(read_phys_burst(&l->data[0]))) - { - l->tag = tag; - l->valid_all = -1; - entry_data = l->data[sel]; - putlog(2, " entry_data=$%08x (burst)", entry_data); - } else { - // シングル充填を4回。 - uint32 opermask = OPERMASK(reqoffset, opersize); - bd = read_line(l, tag, opermask); - if (__predict_false(bd.IsBusErr())) { - return bd; - } - entry_data = bd.Data(); - putlog(2, " entry_data=$%08x (line)", entry_data); - } - } else { - // このエントリだけ充填。 - bd = read_phys(); - if (__predict_false(bd.IsBusErr())) { - return bd; - } - if (__predict_false(bus.ci)) { - goto nofill; - } - if (__predict_false(bd.GetSize() != 4)) { - uint32 opermask = OPERMASK(reqoffset, opersize); - bd = read_single(bd, 0xf, opermask); - if (__predict_false(bd.IsBusErr())) { - return bd; - } - } - l->valid[sel] = 1; - l->data[sel] = bd.Data(); - entry_data = bd.Data(); - } + 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 { - // キャッシュに充填しないので、要求サイズだけ読み出す。 - bd = read_phys(); + // シングル充填。 + busdata bd = read_single(paddr); if (__predict_false(bd.IsBusErr())) { return bd; } - nofill: - if (__predict_false(bd.GetSize() != 4)) { - uint32 opermask = OPERMASK(reqoffset, opersize); - bd = read_single(bd, opermask, opermask); - 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; } - entry_data = bd.Data(); } } + // 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); @@ -311,11 +290,11 @@ MPU68030Device::read_cache(m68030Cache * // 要求サイズがこのエントリをはみ出していない。 entry_data >>= (4 - reqoffset - opersize) * 8; data |= entry_data & mask; - putlog(2, " data=$%0*x (done)", opersize * 2, data); + putlog(3, " data=$%0*x (done)", opersize * 2, data); break; } else { data |= entry_data & mask; - putlog(2, " data=$%0*x", opersize * 2, data); + putlog(3, " data=$%0*x", opersize * 2, data); } addr += opersize; addr.ChangeSize(reqsize - opersize); @@ -324,158 +303,81 @@ MPU68030Device::read_cache(m68030Cache * return data; } -// シングル充填によるライン充填。 -// 要求オペランドが2エントリ目にかかっていても、バスエラーを起こさないようだ。 +// シングル充填を行い、(CI でなければ) 完全なロングワードを返す。 +// CI なら第1サイクルだけを行い、ロングワードの所定の位置においたものを返す。 +// バスエラーなら BusErr を返す。 busdata -MPU68030Device::read_line(m68030CacheLine *l, uint32 tag, uint32 opermask) +MPU68030Device::read_single(busaddr paddr) { - uint32 sel = (bus.paddr.Addr() & 0x0c) >> 2; - - // 1エントリ目。 - busdata bd = read_phys(); - if (__predict_false(bd.IsBusErr())) { - // 初手は必ず要求オペランドなのでバスエラーは発報。 - return bd; - } - if (__predict_false(bd.GetSize() != 4)) { - // このエントリの残りを埋める。 - bd = read_single(bd, 0xf, opermask); - if (__predict_false(bd.IsBusErr())) { - return bd; - } - } - if (__predict_false(bus.ci)) { - // CI ならキャッシュを更新せず、現状のデータを返す。 - // 要求オペランド外でのバスエラーもここに来る。 - return bd.Data(); - } - // そうでなければ、ここでタグとエントリを更新。 - l->tag = tag; - l->valid_all = 0; - l->valid[sel] = 1; - l->data[sel] = bd.Data(); - - // 2エントリ目以降は充填してもしなくても1エントリ目のこれが戻り値。 - uint32 data = bd.Data(); - - // 2エントリ目以降。 - // 要求オペランドが2エントリ目にまたがっていても、 - // 2エントリ目以降はバスエラーを起こさないらしい。 - for (int i = 1; i < 4; i++) { - // アドレスを更新。 - if (++sel >= 4) { - sel = 0; - } - bus.paddr.ChangeAddr((bus.paddr.Addr() & ~0xf) | (sel << 2)); - bus.paddr.ChangeSize(4); - - bd = read_phys(); - if (__predict_false(bd.IsBusErr())) { - break; - } - if (__predict_false(bd.GetSize() != 4)) { - // このエントリの残りを埋める。 - bd = read_single(bd, 0xf, 0); - if (__predict_false(bd.IsBusErr())) { - // バスエラーならエントリを無効にして充填をアボート。 - // エントリはまだ有効にしていないので何もしなくていい。 - break; - } - } - if (__predict_false(bus.ci)) { - // CI は1ライン中で変化しないと思うが、 - // 要求オペランド外でのバスエラーもここに来る。 - break; - } - - l->valid[sel] = 1; - l->data[sel] = bd.Data(); + // 一発目。 + busdata bd = read_phys(paddr); + if (__predict_true(bd.IsOK()) && bd.GetSize() != 4) { + // 必要なら残りのサイクル。 + bd = fill_single(paddr, bd); } - - return data; + // いずれにしても bd を返す。 + return bd; } -// シングル充填の残りの読み出し。 -// まず bus.paddr を1回読んでみて、ロングワードポートならそのまま使える -// (し、大抵はこのケースなので早期に抜けたい) のでここには来ない。 -// ロングワードでない時だけここへ来て残りを埋める、という分担になっている。 -// -// readmask は読み込みを行うバイト (キャッシュ充填なら %1111)。 -// opermask はバスエラーを起こすバイト。 -// readmask、opermask はともに下位の 4 ビットで、 -// ビット 3, 2, 1, 0 がそれぞれ +0, +1, +2, +3 バイト目を示す。 +// シングル充填の第2サイクル以降を行って、(CI でなければ) 完全なロングワードを +// 返す。 +// 引数 paddr に第1サイクルの物理アドレス (インクリメントなどする前)。 +// 引数 bd は第1サイクルの結果の busdata。 // -// bd = read_phys(); -// if (BusErr) ...; -// if (bd.GetSize() != 4) { -// bd = read_single(bd, readmask, opermask); -// : -// } -// if (bus.ci) ...; -// のように呼ぶ。 -// -// 応答は、 -// a) 成功すれば、readmask で示した読み出しバイトをロングワードで返す。 -// 下詰めではなくロングワード境界からの所定のバイト位置。 -// b) opermask で示したバイトでバスエラーが起きれば、その場で処理を中断して -// BusErr を返す (上位に発報するため)。 -// c) opermask で示したバイト以外でバスエラーが起きれば、その場で処理を中断 -// し bus.ci を立てて、現状のデータを返す (エントリを更新しないため)。 -// d) バスアクセスで CI が立てば、読み込んだデータは使うがエントリは更新しない -// ので、これは c) と同じ。 +// 第1サイクルがバスエラーになっていれば事前に弾いておくこと。 +// 第1サイクルでロングワード全部埋まったケースも事前に弾いておくこと。 busdata -MPU68030Device::read_single(busdata bd, uint32 readmask, uint32 opermask) +MPU68030Device::fill_single(busaddr paddr, busdata bd) { - uint32 data = 0; - uint32 portsize; + busaddr addr_h(paddr.Get() & ~0x3ULL); + uint32 data; - // この時点で bd は一回目の読み込み後で、 - // バスエラーは起きておらず、ロングワードポートでなかった。 - portsize = bd.GetSize(); + //assert(bd.IsOK()); + //assert(bd.GetSize() < 4); - uint32 offset = bus.paddr.Addr() & 3; - for (;;) { - // bd.Data をエントリの所定の位置に移動。 - // set には今回読めたところのビットを立てていく。 - uint32 set; - if (portsize == 2) { - uint32 off2 = offset & 2; - data |= bd.Data() << ((2 - off2) * 8); - set = 0x3U << (2 - off2); - // 次の読み込みにむけて更新。 - offset = 2 - off2; + if (bd.GetSize() == 2) { + // ワードポートアクセスだったなら、第1アクセスが上位か下位かだけ。 + if ((paddr.Addr() & 2) == 0) { + data = bd.Data() << 16; } else { - data |= bd.Data() << ((3 - offset) * 8); - set = 0x1U << (3 - offset); - // 次の読み込みにむけて更新。 - offset = (offset + 1) & 3; + data = bd.Data(); } - - // readmask を全部下ろせたら終了。 - readmask &= ~set; - if (readmask == 0) { - break; + 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 { + // バイトポートアクセスの場合。 - // 次の読み込み。 - // 同一エントリ内なので paddr の更新だけでいい。 - bus.paddr.ChangeAddr((bus.paddr.Addr() & ~3U) | offset); - bus.paddr.ChangeSize(__builtin_popcount(readmask)); + // 第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(); - portsize = bd.GetSize(); - if (__predict_false(bd.IsBusErr())) { - if ((set & opermask) != 0) { - // ターゲットオペランドと重なっていれば即バスエラー。 - return BusData::BusErr; - } else { - // オペランド外の充填中なら CI と同じパスになる。 - bus.ci = true; - break; - } + bd = read_phys(paddr); + // CI はもう起きないはず。バスエラーは? + data |= bd.Data() << ((3 - offset) * 8); } } + done: return data; } @@ -486,8 +388,9 @@ MPU68030Device::write_bus(busaddr addr, { m68030Cache *cache = dcache.get(); - putlog(2, "write_bus %u.%08x sz=%u data=$%0*x", - addr.GetFC(), addr.Addr(), addr.GetSize(), addr.GetSize() * 2, data); + 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; @@ -508,30 +411,30 @@ MPU68030Device::write_bus(busaddr addr, // まず外部バスを駆動して CI を得る。 // バスエラーなら記録だけしておく。キャッシュへは書き込むため。 busdata bd = BusData::BusErr; - bus.ci = false; bus.laddr = addr; - bus.paddr = addr; - if (__predict_true(TranslateWrite())) { + busaddr paddr = TranslateWrite(addr); + bus.ci = paddr.IsCInhibit(); + if (__predict_true(paddr.IsOK())) { uint32 outdata = data; for (;;) { - bd = write_phys(outdata); + bd = write_phys(paddr, outdata); if (__predict_false(bd.IsBusErr())) { - putlog(2, " write_phys -> BusErr"); + putlog(3, " write_phys -> BusErr"); break; } - uint32 breqsize = bus.paddr.GetSize(); - putlog(2, " write_phys $%08x data=$%0*x", - bus.paddr.Addr(), breqsize * 2, outdata); + uint32 breqsize = paddr.GetSize(); + putlog(3, " write_phys $%08x data=$%0*x", + paddr.Addr(), breqsize * 2, outdata); uint32 portsize = bd.GetSize(); - uint32 offset = bus.paddr.Addr() & (portsize - 1); + uint32 offset = paddr.Addr() & (portsize - 1); uint32 datasize = portsize - offset; if (datasize >= breqsize) { break; } - // 同一ロングワード内なので bus.paddr の更新だけでいい。 - bus.paddr += datasize; + // 同一ロングワード内なので paddr の更新だけでいい。 + paddr += datasize; breqsize -= datasize; - bus.paddr.ChangeSize(breqsize); + paddr.ChangeSize(breqsize); // データは下詰めで、不要な上位ビットはクリアする仕様。 outdata &= (1U << (breqsize * 8)) - 1; } @@ -600,7 +503,7 @@ MPU68030Device::write_bus(busaddr addr, if (nextsize == 0) { break; } - putlog(2, " nextsize=%u", nextsize); + putlog(3, " nextsize=%u", nextsize); addr += addr.GetSize(); addr.ChangeSize(nextsize); data = nextdata; @@ -609,37 +512,34 @@ MPU68030Device::write_bus(busaddr addr, return 0; } -// mainbus の bus.paddr から指定サイズで1回読み込む。 +// mainbus の paddr から指定サイズで1回読み込む。 // ウェイトはこちらで加算して、バスエラーも含めた bd を返す。 busdata -MPU680x0Device::read_phys() +MPU680x0Device::read_phys(const busaddr paddr) { - busaddr paddr = bus.paddr; busdata bd = mainbus->Read(paddr); buswait += bd.GetWait(); return bd; } -// mainbus の bus.paddr に指定サイズで1回書き込む。 +// mainbus の paddr に指定サイズで1回書き込む。 // ウェイトはこちらで加算して、バスエラーも含めた bd を返す。 busdata -MPU680x0Device::write_phys(uint32 data) +MPU680x0Device::write_phys(const busaddr paddr, uint32 data) { - busaddr paddr = bus.paddr; busdata bd = mainbus->Write(paddr, data); buswait += bd.GetWait(); return bd; } // バースト転送。 -// bus.paddr の下位4ビットは無視する。 -// addr がバースト転送可能なら dst に 16 バイト転送して true を返す。 -// addr がバースト転送不可なら dst は触らず false を返す。 +// paddr の下位4ビットは無視する。 +// paddr がバースト転送可能なら dst に 16 バイト転送して true を返す。 +// paddr がバースト転送不可なら dst は触らず false を返す。 // VM (バス) がバースト転送対応している場合のみ呼ぶこと。 bool -MPU680x0Device::read_phys_burst(uint32 *dst) +MPU680x0Device::read_phys_burst(busaddr paddr, uint32 *dst) { - busaddr paddr = bus.paddr; paddr &= 0xfffffff0U; busdata bd = mainbus->ReadBurst16(paddr, dst); buswait += bd.GetWait(); @@ -661,10 +561,8 @@ MPU680x0Device::read_phys_4(uint32 addr) // 物理バスアクセス bus.laddr = busaddr(addr) | BusAddr::SRead | BusAddr::Size4; - bus.paddr = bus.laddr; - bd = read_phys(); + bd = read_phys(bus.laddr); if (__predict_false(bd.IsBusErr())) { - bus.size = 4; return bd; } @@ -686,10 +584,8 @@ MPU680x0Device::write_phys_4(uint32 addr // 物理バスアクセス bus.laddr = busaddr(addr) | BusAddr::SWrite | BusAddr::Size4; - bus.paddr = bus.laddr; - bd = write_phys(data); + bd = write_phys(bus.laddr, data); if (__predict_false(bd.IsBusErr())) { - bus.size = 4; return bd; } @@ -709,11 +605,16 @@ MPU68030Device::SetCACR(uint32 newval) // EI (命令キャッシュ有効) if ((chgval & M68030::CACR_EI) != 0) { if ((newval & M68030::CACR_EI) != 0) { - // 命令キャッシュ有効 - icache->enable = true; - icache->hist.Clear(); - cycle_table = cycle_table_030cc; - putlog(1, "I-Cache enabled"); + 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; @@ -724,8 +625,8 @@ MPU68030Device::SetCACR(uint32 newval) // FI (命令キャッシュ凍結) if ((chgval & M68030::CACR_FI) != 0) { - putlog(0, "CACR:FI Not Implemented"); icache->freeze = (newval & M68030::CACR_FI); + putlog(1, "CACR:FI <- %u", icache->freeze); } // CEI (命令キャッシュの指定エントリをクリア) @@ -750,16 +651,24 @@ MPU68030Device::SetCACR(uint32 newval) // IBE (命令バースト有効) if ((chgval & M68030::CACR_IBE) != 0) { - icache->burst_enable = (newval & M68030::CACR_IBE) && has_burst_access; + 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) { - // データキャッシュ有効。 - dcache->enable = true; - dcache->hist.Clear(); - putlog(1, "D-Cache enabled"); + 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; @@ -770,7 +679,7 @@ MPU68030Device::SetCACR(uint32 newval) // FD (データキャッシュ凍結) if ((chgval & M68030::CACR_FD) != 0) { dcache->freeze = (newval & M68030::CACR_FD); - putlog(1, "CACR:FD -> %u", dcache->freeze); + putlog(1, "CACR:FD <- %u", dcache->freeze); } // CED (データキャッシュの指定エントリをクリア) @@ -795,7 +704,10 @@ MPU68030Device::SetCACR(uint32 newval) // DBE (データバースト有効) if ((chgval & M68030::CACR_DBE) != 0) { - dcache->burst_enable = (newval & M68030::CACR_DBE) && has_burst_access; + dcache->burst_enable = + (newval & M68030::CACR_DBE) && + (dcache->freeze == false) && + has_burst_access; } // WA (ライトアロケート)