--- nono/m680x0/m68030bus.cpp 2026/04/29 17:05:22 1.1.1.5 +++ nono/m680x0/m68030bus.cpp 2026/04/29 17:05:31 1.1.1.7 @@ -5,362 +5,843 @@ // #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 = (reg.pc & 0x000000f0) >> 4; + uint32 sel = (reg.pc & 0x0000000c) >> 2; + 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())) { + bus.size = addr.GetSize(); + 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 = (reg.pc & 0x000000f0) >> 4; + uint32 sel = (reg.pc & 0x0000000c) >> 2; + 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; -} - -uint32 -MPU680x0Device::read_8(uint32 addr) -{ - busaddr laddr(addr, fc_data | busaddr::R); - return read_8_fc(laddr); -} -uint32 -MPU680x0Device::read_16(uint32 addr) -{ - busaddr laddr(addr, fc_data | busaddr::R); - return read_16_fc(laddr); + fetch: + busdata bd; + if (__predict_true(cache->enable)) { + bd = read_cache(cache, addr); + } else { + bd = read_nocache(addr); + } + if (__predict_false(bd.IsBusErr())) { + bus.size = addr.GetSize(); + throw M68K::EXCEP_BUSERR; + } + reg.pc += 4; + return bd.Data(); } +// read_1,2,4() の共通部分。ミスアライン可。 +// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。 uint32 -MPU680x0Device::read_32(uint32 addr) -{ - busaddr laddr(addr, fc_data | busaddr::R); - return read_32_fc(laddr); -} - -void -MPU680x0Device::write_8(uint32 addr, uint32 data) -{ - busaddr laddr(addr, fc_data | busaddr::W); - write_8_fc(laddr, data); -} - -void -MPU680x0Device::write_16(uint32 addr, uint32 data) +MPU68030Device::read_data(busaddr addr) { - busaddr laddr(addr, fc_data | busaddr::W); - write_16_fc(laddr, data); + 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())) { + bus.size = addr.GetSize(); + throw M68K::EXCEP_BUSERR; + } + return data.Data(); } +// write_1,2,4() の共通部分。ミスアライン可。 +// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。 void -MPU680x0Device::write_32(uint32 addr, uint32 data) +MPU68030Device::write_data(busaddr addr, uint32 data) { - busaddr laddr(addr, fc_data | busaddr::W); - write_32_fc(laddr, data); + busdata r = write_bus(addr, data); + if (__predict_false(r.IsBusErr())) { + bus.size = addr.GetSize(); + throw M68K::EXCEP_BUSERR; + } } -// FC 指定アクセス (MPU コアから使用する) -// ミスアライン可。 -// バスエラーが起きると例外 M68K::EXCEP_BUSERR をスローする。 -// uint32 read_{8,16,32}_fc(busaddr) -// void write_{8,16,32}_fc(busaddr, data) -// (fetch は常に現在の FC でアクセスするので FC 指定版は不要) +// キャッシュ無効時の読み込み。ミスアライン可。 +// バスエラーも busdata を返す。 +busdata +MPU68030Device::read_nocache(busaddr addr) +{ + // デバイスは、addr の下位ビットによらず、自身のデバイスのポートサイズ分 + // のデータと、その応答サイズ(自分のポートサイズ) を Size フィールドに + // セットして返してくる。 + // + // MPU(68030) は、A1-A0、残りバイト数、デバイスの応答サイズの情報から + // 受け取ったデータバスのうち適切なところを切り出して使う。 + + uint32 data = 0; + for (;;) { + bus.laddr = addr; + bus.paddr = addr; + if (__predict_false(TranslateRead() == false)) { + return BusData::BusErr; + } + busdata bd = read_phys(); + if (__predict_false(bd.IsBusErr())) { + return bd; + } -uint32 -MPU680x0Device::read_8_fc(busaddr addr) -{ - uint32 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; + } - if (1) { - data = read_8_bus(addr); + addr += datasize; + reqsize -= datasize; + addr.ChangeSize(reqsize); } - return data; } -uint32 -MPU680x0Device::read_16_fc(busaddr addr) -{ - uint32 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 を返す。 +busdata +MPU68030Device::read_cache(m68030Cache *cache, busaddr addr) +{ + putlog(2, "read_cache $%u.%08x.%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; + 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); + } else { + cache->stat_miss++; + + // キャッシュになければバスアクセス。 + bus.laddr = addr; + bus.paddr = addr; + if (__predict_false(TranslateRead() == false)) { + putlog(2, " 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(); + } + } else { + // キャッシュに充填しないので、要求サイズだけ読み出す。 + bd = read_phys(); + 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(); + } + } - if (1) { - // バスアクセスは要整列。 - if (__predict_true((addr & 1) == 0)) { - data = read_16_bus(addr); + data <<= opersize * 8; + // このエントリから必要な部分を抜き出す。 + uint32 mask = 0xffffffffU >> ((4 - opersize) * 8); + if (reqsize == opersize) { + // 要求サイズがこのエントリをはみ出していない。 + entry_data >>= (4 - reqoffset - opersize) * 8; + data |= entry_data & mask; + putlog(2, " data=$%0*x (done)", opersize * 2, data); + break; } else { - data = read_8_bus(addr) << 8; - addr += 1; - data |= read_8_bus(addr); + data |= entry_data & mask; + putlog(2, " data=$%0*x", opersize * 2, data); } + addr += opersize; + addr.ChangeSize(reqsize - opersize); } 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); - } else { - data = read_8_bus(addr) << 24; - addr += 1; - data |= read_16_bus(addr) << 8; - addr += 2; - data |= read_8_bus(addr); +// シングル充填によるライン充填。 +// 要求オペランドが2エントリ目にかかっていても、バスエラーを起こさないようだ。 +busdata +MPU68030Device::read_line(m68030CacheLine *l, uint32 tag, uint32 opermask) +{ + 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); - return data; -} + 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; + } -void -MPU680x0Device::write_8_fc(busaddr addr, uint32 data) -{ - if (1) { - write_8_bus(addr, data); + l->valid[sel] = 1; + l->data[sel] = bd.Data(); } + + return data; } -void -MPU680x0Device::write_16_fc(busaddr addr, uint32 data) -{ - if (1) { - // バスアクセスは要整列。 - if (__predict_true((addr & 1) == 0)) { - write_16_bus(addr, data); +// シングル充填の残りの読み出し。 +// まず bus.paddr を1回読んでみて、ロングワードポートならそのまま使える +// (し、大抵はこのケースなので早期に抜けたい) のでここには来ない。 +// ロングワードでない時だけここへ来て残りを埋める、という分担になっている。 +// +// readmask は読み込みを行うバイト (キャッシュ充填なら %1111)。 +// opermask はバスエラーを起こすバイト。 +// readmask、opermask はともに下位の 4 ビットで、 +// ビット 3, 2, 1, 0 がそれぞれ +0, +1, +2, +3 バイト目を示す。 +// +// 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) と同じ。 +busdata +MPU68030Device::read_single(busdata bd, uint32 readmask, uint32 opermask) +{ + uint32 data = 0; + uint32 portsize; + + // この時点で bd は一回目の読み込み後で、 + // バスエラーは起きておらず、ロングワードポートでなかった。 + portsize = bd.GetSize(); + + 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 = 0x3 << (2 - off2); + // 次の読み込みにむけて更新。 + offset = 2 - off2; } else { - write_8_bus(addr, data >> 8); - addr += 1; - write_8_bus(addr, data & 0xff); + data |= bd.Data() << ((3 - offset) * 8); + set = 0x1 << (3 - offset); + // 次の読み込みにむけて更新。 + offset = (offset + 1) & 3; + } + + // readmask を全部下ろせたら終了。 + readmask &= ~set; + if (readmask == 0) { + break; + } + + // 次の読み込み。 + // 同一エントリ内なので paddr の更新だけでいい。 + bus.paddr.ChangeAddr((bus.paddr.Addr() & ~3U) | offset); + bus.paddr.ChangeSize(__builtin_popcount(readmask)); + + bd = read_phys(); + portsize = bd.GetSize(); + if (__predict_false(bd.IsBusErr())) { + if ((set & opermask) != 0) { + // ターゲットオペランドと重なっていれば即バスエラー。 + return BusData::BusErr; + } else { + // オペランド外の充填中なら CI と同じパスになる。 + bus.ci = true; + break; + } } } + + return data; } -void -MPU680x0Device::write_32_fc(busaddr addr, uint32 data) -{ - 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); +// キャッシュ/バスへの書き込み。ミスアライン可。 +// バスエラーも busdata を返す。 +busdata +MPU68030Device::write_bus(busaddr addr, uint32 data) +{ + 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); + 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 { - write_8_bus(addr, data >> 24); - addr += 1; - write_16_bus(addr, (data >> 8) & 0xffff); - addr += 2; - write_8_bus(addr, data & 0xff); + nextsize = 0; + } + + // まず外部バスを駆動して CI を得る。 + // バスエラーなら記録だけしておく。キャッシュへは書き込むため。 + busdata bd = BusData::BusErr; + bus.ci = false; + bus.laddr = addr; + bus.paddr = addr; + if (__predict_true(TranslateWrite())) { + uint32 outdata = data; + for (;;) { + bd = write_phys(outdata); + if (__predict_false(bd.IsBusErr())) { + putlog(2, " write_phys -> BusErr"); + break; + } + uint32 breqsize = bus.paddr.GetSize(); + putlog(2, " write_phys $%08x data=$%0*x", + bus.paddr.Addr(), breqsize * 2, outdata); + uint32 portsize = bd.GetSize(); + uint32 offset = bus.paddr.Addr() & (portsize - 1); + uint32 datasize = portsize - offset; + if (datasize >= breqsize) { + break; + } + // 同一ロングワード内なので bus.paddr の更新だけでいい。 + bus.paddr += datasize; + breqsize -= datasize; + bus.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(2, " nextsize=%u", nextsize); + addr += addr.GetSize(); + addr.ChangeSize(nextsize); + data = nextdata; } + + return 0; } -// 外部バスアクセス (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_; /* これが戻り値 */ \ -}) +// mainbus の bus.paddr から指定サイズで1回読み込む。 +// ウェイトはこちらで加算して、バスエラーも含めた bd を返す。 +busdata +MPU680x0Device::read_phys() +{ + busaddr paddr = bus.paddr; + busdata bd = mainbus->Read(paddr); + buswait += bd.GetWait(); + return bd; +} -uint32 -MPU680x0Device::fetch_16_bus(uint32 addr) +// mainbus の bus.paddr に指定サイズで1回書き込む。 +// ウェイトはこちらで加算して、バスエラーも含めた bd を返す。 +busdata +MPU680x0Device::write_phys(uint32 data) { - busdata bd; + busaddr paddr = bus.paddr; + busdata bd = mainbus->Write(paddr, data); + buswait += bd.GetWait(); + return bd; +} - bd = FETCH(16); +// バースト転送。 +// bus.paddr の下位4ビットは無視する。 +// addr がバースト転送可能なら dst に 16 バイト転送して true を返す。 +// addr がバースト転送不可なら dst は触らず false を返す。 +// VM (バス) がバースト転送対応している場合のみ呼ぶこと。 +bool +MPU680x0Device::read_phys_burst(uint32 *dst) +{ + busaddr paddr = bus.paddr; + paddr &= 0xfffffff0U; + busdata bd = mainbus->ReadBurst16(paddr, dst); buswait += bd.GetWait(); - return bd.Data(); - buserr: - bus.size = M68K::SSW_SIZE_WORD; - throw M68K::EXCEP_BUSERR; + // BusErr ビットをバースト転送不可フラグとして使っている。 + return !bd.IsBusErr(); } -uint32 -MPU680x0Device::fetch_32_bus(uint32 addr) +// 物理メモリからのロングワード読み込み。 +// 現在の bus に影響を与えず指定のアドレスを読み出す。MMU 専用。 +busdata +MPU680x0Device::read_phys_4(uint32 addr) { + m68kbus backup; busdata bd; - bd = FETCH(32); - buswait += bd.GetWait(); - return bd.Data(); + // バス情報を一旦退避 + backup = bus; - buserr: - bus.size = M68K::SSW_SIZE_LONG; - throw M68K::EXCEP_BUSERR; + // 物理バスアクセス + bus.laddr = busaddr(addr) | BusAddr::SRead | BusAddr::Size4; + bus.paddr = bus.laddr; + bd = read_phys(); + if (__predict_false(bd.IsBusErr())) { + bus.size = 4; + return bd; + } + + // バス情報を復元 + bus = backup; + return bd; } -uint32 -MPU680x0Device::read_8_bus(busaddr addr) +// 物理メモリへのロングワード書き込み。 +// 現在の bus に影響を与えず指定のアドレスに書き出す。MMU 専用。 +busdata +MPU680x0Device::write_phys_4(uint32 addr, uint32 data) { - busdata data; + m68kbus backup; + busdata bd; - data = READ(8); - putlog(2, "read_8 %08x -> %02x", bus.laddr.Addr(), data.Data()); - buswait += data.GetWait(); - return data.Data(); + // バス情報を一旦退避 + backup = bus; + + // 物理バスアクセス + bus.laddr = busaddr(addr) | BusAddr::SWrite | BusAddr::Size4; + bus.paddr = bus.laddr; + bd = write_phys(data); + if (__predict_false(bd.IsBusErr())) { + bus.size = 4; + return bd; + } - buserr: - bus.size = M68K::SSW_SIZE_BYTE; - throw M68K::EXCEP_BUSERR; + // バス情報を復元 + bus = backup; + return bd; } -uint32 -MPU680x0Device::read_16_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) { + // 命令キャッシュ有効 + icache->enable = true; + icache->hist.Clear(); + putlog(1, "I-Cache enabled"); + } else { + // 命令キャッシュ無効 + icache->enable = false; + putlog(1, "I-Cache disabled"); + // 命令キャッシュが有効だった期間 nocache は使われずに + // 積算され続けているが、キャッシュ無効になるとこれを加算 + // しようとするのでここでリセット。 + cycle_nocache = 0; + } - data = READ(16); - putlog(2, "read_16 %08x -> %04x", bus.laddr.Addr(), data.Data()); - buswait += data.GetWait(); - return data.Data(); + // 親クラス(自分) にもコピーを持っておく。core.cpp 参照。 + icache_enable = icache->enable; + } - buserr: - bus.size = M68K::SSW_SIZE_WORD; - throw M68K::EXCEP_BUSERR; -} + // FI (命令キャッシュ凍結) + if ((chgval & M68030::CACR_FI) != 0) { + putlog(0, "CACR:FI Not Implemented"); + icache->freeze = (newval & M68030::CACR_FI); + } -uint32 -MPU680x0Device::read_32_bus(busaddr addr) -{ - busdata data; + // 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; - data = READ(32); - putlog(2, "read_32 %08x -> %08x", bus.laddr.Addr(), data.Data()); - buswait += data.GetWait(); - return data.Data(); + newval &= ~M68030::CACR_CEI; + } - buserr: - bus.size = M68K::SSW_SIZE_LONG; - throw M68K::EXCEP_BUSERR; -} + // CI (命令キャッシュクリア) + if ((chgval & M68030::CACR_CI) != 0) { + for (auto& l : icache->line) { + l.valid_all = 0; + } -void -MPU680x0Device::write_8_bus(busaddr addr, uint32 data) -{ - busdata rd; + newval &= ~M68030::CACR_CI; + } - 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; + // IBE (命令バースト有効) + if ((chgval & M68030::CACR_IBE) != 0) { + icache->burst_enable = (newval & M68030::CACR_IBE) && 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"); + } 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) && 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::unique_lock lock(hist_mtx); + hist.EnqueueForce(s); + } }