--- nono/m680x0/m68030mmu.cpp 2026/04/29 17:05:22 1.1.1.9 +++ nono/m680x0/m68030mmu.cpp 2026/04/29 17:05:31 1.1.1.11 @@ -45,13 +45,6 @@ enum ATCtype { // デバッグ表示のため値の表示に必要な桁数を返す #define NIBBLE(x) ( (x) == 0 ? 1 : ((x) + 3) / 4 ) -// MMU デバッグモードなら ATC エントリ a を表示 -#define FILL_PRINT(fc, a) do { \ - if (__predict_false(loglevel >= 3)) { \ - cpu->putlogf(3, [&] { return (a)->fill_print(fc); }); \ - } \ -} while (0) - // インデント付き string_format static std::string indent_format(int indent, const char *fmt, ...) __printflike(2, 3); @@ -85,11 +78,11 @@ indent_format(int indent, const char *fm class m68030MMU final { public: - m68030MMU(MPU680x0Device *, int loglevel_); + m68030MMU(MPU68030Device *, int loglevel_); ~m68030MMU(); // サーチを実行 - void search(int level); + void search(int level, busaddr laddr); // ATC にエントリを作成 void make_atc(m68030ATCLine *); @@ -104,12 +97,6 @@ class m68030MMU final // ディスクリプタを取得 bool fetch_desc(uint32, int); - // MMU による物理メモリ読み込み - busdata phys_read_32(uint32 addr); - - // MMU による物理メモリ書き込み - busdata phys_write_32(uint32 addr, uint32 data); - // ディスクリプタを表示用に (デバッグ用) std::string sprintf_desc(); @@ -130,8 +117,6 @@ class m68030MMU final uint32 m_lastaddr {}; // 最後にアクセスしたディスクリプタアドレス(PTEST) private: int m_lv[MAX_LV] {}; - uint32 m_laddr {}; - int m_fc {}; uint32 m_desc1 {}; // ディスクリプタ uint32 m_desc2 {}; // ロングディスクリプタの後半部 int m_descsize {}; // ディスクリプタサイズ (4 or 8) @@ -142,13 +127,13 @@ class m68030MMU final int loglevel {}; - MPU680x0Device *cpu; + MPU68030Device *cpu; }; // 現在の TC:E、TT:E の状態に応じてアドレス変換の有無を切り替える。 // mmu_enable ならアドレス変換ありのバスアクセスを使う。see m68030bus.cpp void -MPU680x0Device::mmu_enable_changed() +MPU68030Device::mmu_enable_changed() { bool enable; @@ -173,7 +158,7 @@ MPU680x0Device::mmu_enable_changed() // bool -MPU680x0Device::SetSRP(uint32 h, uint32 l) +MPU68030Device::SetSRP(uint32 h, uint32 l) { if ((h & m68030RP::DT_MASK) == 0) { return false; @@ -184,7 +169,7 @@ MPU680x0Device::SetSRP(uint32 h, uint32 } bool -MPU680x0Device::SetCRP(uint32 h, uint32 l) +MPU68030Device::SetCRP(uint32 h, uint32 l) { if ((h & m68030RP::DT_MASK) == 0) { return false; @@ -195,13 +180,36 @@ MPU680x0Device::SetCRP(uint32 h, uint32 } void -MPU680x0Device::SetTT(int n, uint32 data) +MPU68030Device::SetTT(int n, uint32 data) { uint32 lbase; uint32 lmask; uint32 sbase; uint32 smask; + // ハッシュから TT#n の成分を一旦クリアする。 + if (mmu_tt[n].e) { + // 変更前の TT#n にマッチして、現在の TT#(1-n) にマッチしない + // アドレスブロックに対応する 4096 エントリをクリア。 + for (int fc = 0; fc < 8; fc++) { + auto *table = atc.fctables[fc]; + if (table) { + busaddr laddr = busaddr(0) | busaddr::FC(fc); + for (int i = 0; i < 256; i++) { + // XXX R/W は? + if (mmu_tt[n].Match(laddr) && + mmu_tt[1 - n].Match(laddr) == false) + { + uint32 baseidx = i << (24 - 12); + memset(&table->hash[baseidx], + m68030ATCTable::HASH_NONE, 4096); + } + laddr += 0x0100'0000; + } + } + } + } + // マスクしてレジスタイメージにセット reg.tt[n] = data & m68030TT::MASK; @@ -254,22 +262,31 @@ MPU680x0Device::SetTT(int n, uint32 data sbase |= (data & m68030TT::FCBASE_MASK) >> 3; smask = m68030TT::E; if ((data & m68030TT::RWM) == 0) { - smask |= M68K::SSW_RW; + smask |= M68030::SSW_RW; } smask |= (~data & m68030TT::FCMASK_MASK) << 1; mmu_tt[n].base = lbase | (((uint64)sbase) << 32); mmu_tt[n].mask = lmask | (((uint64)smask) << 32); - // 論理アドレスとEnableビットだけの早見表を作成。 - // TT0 が一致するなら bit0 をセット、TT1 が一致するなら bit1 をセット - // しておく。 - for (int i = 0; i < countof(mmu_tt_quick); i++) { - uint32 laddr = ((uint32)i) << 24; - if (mmu_tt[n].e && ((laddr ^ lbase) & lmask) == 0) { - mmu_tt_quick[i] |= (1 << n); - } else { - mmu_tt_quick[i] &= ~(1 << n); + // 更新後にこの TT が有効ならハッシュも更新。 + if (mmu_tt[n].e) { + // この TT#n にマッチするアドレスブロックに対応する 4096 エントリを + // TT 印にする。 + for (uint fc = 0; fc < 8; fc++) { + auto *table = atc.fctables[fc]; + if (table) { + busaddr laddr = busaddr(0) | busaddr::FC(fc); + for (uint i = 0; i < 256; i++) { + // XXX R/W は? + if (mmu_tt[n].Match(laddr)) { + uint32 baseidx = i << (24 - 12); + memset(&table->hash[baseidx], + m68030ATCTable::HASH_TT, 4096); + } + laddr += 0x0100'0000; + } + } } } @@ -291,7 +308,7 @@ m68030TT::Match(busaddr laddr) const // 成功すれば true、MMU 構成例外が起きる場合は false を返す。 // 呼び出し側で例外を起こすこと。 bool -MPU680x0Device::SetTC(uint32 data) +MPU68030Device::SetTC(uint32 data) { // マスクしてレジスタイメージにセット reg.tc = data & m68030TC::MASK; @@ -313,6 +330,12 @@ MPU680x0Device::SetTC(uint32 data) // マスクをあらかじめ用意しておく mmu_tc.MakeMask(); + // ATC でも PS を覚えておく必要がある。 + for (int i = 0; i < countof(atc.tables); i++) { + auto *table = atc.tables[i].get(); + table->ps4k = mmu_tc.ps - 12; + } + // TC:E が変更されていればスイッチ更新 mmu_enable_changed(); return true; @@ -384,7 +407,7 @@ m68030TC::MakeMask() // +----------------+--------------+--------------+ // shift[TIA]=20 shift[TIB]=10 // mask[TIA]=0xfff mask[TIB]=0x3ff - int sh = ps; + uint sh = ps; for (int i = TID; i >= TIA; i--) { if (tix[i] > 0) { shift[i] = sh; @@ -395,66 +418,42 @@ m68030TC::MakeMask() } void -MPU680x0Device::SetMMUSR(uint16 data) +MPU68030Device::SetMMUSR(uint16 data) { reg.mmusr = data & m68030MMUSR::MASK; } - -// -// ATC テーブル -// - -// コンストラクタ -m68030ATCTable::m68030ATCTable() -{ -#if !defined(ATC_SINGLE) - for (int i = 0; i < countof(line); i++) { - line[i].Invalidate(); - } - head = &line[0]; -#else - for (int i = 0; i < countof(line); i++) { - line[i].Invalidate(); - insert_tail(&line[i]); - } -#endif -} - -// デストラクタ -m68030ATCTable::~m68030ATCTable() -{ -} - -#if !defined(ATC_SINGLE) -// a のエントリを先頭にする。 -m68030ATCLine * -m68030ATCTable::MoveHead(m68030ATCLine *a) +// リセット例外の CPU 固有部分。 +void +MPU68030Device::ResetMMU() { - // head を向け直す - head = a; - - // 更新 - head->age = counter++; - - // そのエントリを返す - return head; + SetTC(GetTC() & ~m68030TC::TC_E); + SetTT(0, GetTT(0) & ~m68030TT::E); + SetTT(1, GetTT(1) & ~m68030TT::E); } -#endif // // ATC // +// 今となっては独立したクラスである必要はなくなっているが、これを +// MPU68030Device にマージするとパフォーマンスが下がるので維持してある。 +// // コンストラクタ -m68030ATC::m68030ATC(MPU680x0Device *cpu_) +m68030ATC::m68030ATC(MPU68030Device *cpu_) { cpu = cpu_; - for (auto& table : tables) { - table.cpu = cpu; - } + tables[0].reset(new m68030ATCTable(cpu, 1)); + tables[1].reset(new m68030ATCTable(cpu, 2)); + tables[2].reset(new m68030ATCTable(cpu, 5)); + tables[3].reset(new m68030ATCTable(cpu, 6)); + + fctables[1] = tables[0].get(); + fctables[2] = tables[1].get(); + fctables[5] = tables[2].get(); + fctables[6] = tables[3].get(); } // デストラクタ @@ -468,574 +467,295 @@ void m68030ATC::SetLogLevel(int loglevel_) { loglevel = loglevel_; - for (auto& table : tables) { - table.loglevel = loglevel_; + for (int i = 0; i < countof(tables); i++) { + auto *table = tables[i].get(); + table->loglevel = loglevel_; } } -#if defined(ATC_SINGLE) -// item をリストの先頭に挿入。 +// ATC をすべてフラッシュする。命令からコールされる void -m68030ATCTable::insert_head(m68030ATCLine *item) +m68030ATC::flush() { - item->prev = NULL; - item->next = head; - if (head) { - head->prev = item; + for (int i = 0; i < countof(tables); i++) { + auto *table = tables[i].get(); + table->Flush(); } - head = item; } -// item をリストの先頭から2番目に挿入。 +// ATC の fc, mask が一致するエントリをフラッシュする。 +// 命令からコールされる。 void -m68030ATCTable::insert_second(m68030ATCLine *item) +m68030ATC::flush(uint32 fc, uint32 mask) { - item->prev = head; - if (head) { - item->next = head->next; - if (head->next) { - head->next->prev = item; + for (uint i = 0; i < 8; i++) { + if (((i ^ fc) & mask) == 0) { + // FC が一致したテーブルを全フラッシュ + auto *table = fctables[i]; + if (table) { + table->Flush(); + } } - head->next = item; - } else { - item->next = NULL; } } -// item をリストの末尾に追加。 +// ATC の fc, mask, addr が一致するエントリをフラッシュする。 +// 命令からコールされる。 void -m68030ATCTable::insert_tail(m68030ATCLine *item) +m68030ATC::flush(uint32 fc, uint32 mask, uint32 addr) { - item->next = NULL; - if (head == NULL) { - item->prev = NULL; - head = item; - } else { - m68030ATCLine *a = head; - while (a->next) - a = a->next; - a->next = item; - item->prev = a; + for (uint i = 0; i < 8; i++) { + if (((i ^ fc) & mask) == 0) { + // FC が一致したテーブルから addr が一致したエントリをフラッシュ + auto *table = fctables[i]; + if (table) { + table->Flush(addr); + } + } } } -// item をリストから外す -void -m68030ATCTable::remove(m68030ATCLine *item) -{ - if (item->prev) - item->prev->next = item->next; - if (item->next) - item->next->prev = item->prev; - if (item == head) - head = item->next; -} - -// item をリストの先頭に移す。 -// fill_fetch() から呼ばれるもので、リストは空でなく、item は先頭から -// 3番目以内ではない、という前提で高速化。 -void -m68030ATCTable::move_head(m68030ATCLine *item) -{ - // 先頭でない前提の remove - item->prev->next = item->next; - if (item->next) - item->next->prev = item->prev; - - // 先頭がある前提の insert_head - item->prev = NULL; - item->next = head; - head->prev = item; - head = item; -} - -// item を先頭から2番目に移す。 -// fill_{read,write} から呼ばれるもので、リストは空でなく、item は先頭から -// 3番目以内ではない、という前提で高速化。 -void -m68030ATCTable::move_second(m68030ATCLine *item) -{ - // 先頭でない前提の remove - item->prev->next = item->next; - if (item->next) - item->next->prev = item->prev; - // 先頭2つがある前提の insert_second - item->prev = head; - item->next = head->next; - head->next->prev = item; - head->next = item; -} -#endif // ATC_SINGLE +// +// ATC テーブル +// -// fill_{fetch,read,write}() が各アクセス用の ATC サーチ。 -// あらかじめ論理ページにマスクされたアドレス fc:addr が ATC にあれば -// そのエントリを返す。なければ ATC を更新してそのエントリを返す。 -// fill_peek() はデバッグ用なので一切状態を更新せず結果だけ返す。 +// アドレス変換の本来のフローはこんな感じだが、 +// +// if (TT*.E || TC.E) { +// if (TT*.E が有効) { +// laddr,RW が TT[01] とマッチしたら、paddr = laddr,CI +// } +// if (TC.E が有効) { +// ATC とマッチしたら、paddr = atc[n].paddr,CI +// そうでなければ、 paddr = テーブルサーチ +// } +// } else { +// paddr = laddr (アドレス変換オフ) +// } +// +// ハッシュで一度に引く。 +// switch (hash[laddr>>12]) { +// case HASH_TT: +// laddr,RW が TT[01] とマッチしたら、paddr = laddr,CI +// case HASH_ATC: +// paddr = atc[n].paddr,CI +// case HASH_NONE: +// TC.E なら paddr = テーブルサーチ +// そうでなければ、paddr = laddr (アドレス変換オフ) +// case HASH_SUB: +// PS が 4KB 未満だとハッシュの1エントリに2つ以上が混在するので、 +// この場合は諦めて ATC を線形探索。通常起きないので性能無視。 +// } + +// ATC テーブルは本来は 1つ(FC混合) x 22 ラインだが、 +// 高速化のため FC別(4テーブル) x 22 ラインのモードを用意する。 +// FC 混合版ではテーブルは tables[0] のみで、FC(1,0,2) は busaddr の +// 34-32 ビット部分として laddr と一緒に検索キーにする。 +// FC 分離版ではテーブルは fctables[fc] で検索キーは laddr(下位32ビット部分)。 +// +// ハッシュなのでライン間の順序関係はないが、LRU で古いのを捨てる時や +// PS<4K の時に前から探索する時のために、age を使った順序管理をする。 +// ラインを使用するたびに age に ++latest_age を代入しておくことで、 +// 順序が必要な時には age の大きい順にすれば最近使った順になる。 +// 32 ビット符号なし整数が折り返すと事故るがたぶんそれより高い頻度で +// フラッシュが起きるんじゃないかな… -const m68030ATCLine& -m68030ATC::fill_fetch(busaddr addr_) +// コンストラクタ +m68030ATCTable::m68030ATCTable(MPU68030Device *cpu_, uint fc_) { - uint32 laddr = addr_.Addr(); - uint fc = addr_.GetFC(); - m68030ATCTable *table; - m68030ATCLine *a; - int i; - -#if !defined(ATC_SINGLE) - table = &tables[fc]; - // キャッシュとマッチすればそれを返す - a = table->head; - if (a->GetTag() == laddr) { - FILL_PRINT(fc, a); - table->hit_head++; - return *a; - } - for (i = 0; i < countof(table->line); i++) { - a = &table->line[i]; - if (a->GetTag() == laddr) { - // ヒットしたので先頭に移動。fetch は常に入れ替え - a = table->MoveHead(a); - - FILL_PRINT(fc, a); - table->hit[i]++; - return *a; - } - } - - // 見付からなかったので、古いエントリを1つ更新。 - a = table->MoveHead(table->Lookup()); - table->hit[m68030ATCTable::LINES]++; - // タグはこっちで初期化 - a->tag = laddr; -#else - table = &tables[0]; - // キャッシュとマッチすればそれを返す - a = table->head; - for (i = 0; a; a = a->next, i++) { - if (a->GetTag() == laddr && a->fc == fc) { - // ヒットしたので先頭に移動。 - if (i > 2) { - table->move_head(a); - } - - FILL_PRINT(fc, a); - table->hit[i]++; - return *a; - } - } + cpu = cpu_; + fc = fc_; - // 見付からなかったので、古いエントリを1つ更新。 - a = table->lookup(); - table->remove(a); - table->insert_head(a); - table->hit[m68030ATCTable::LINES]++; - // タグはこっちで初期化 - a->tag = laddr; - a->fc = fc; -#endif + std::fill(hash.begin(), hash.end(), HASH_NONE); - // テーブルサーチして結果を ATC に入れる - m68030MMU mmu(cpu, loglevel); - mmu.search(7); - mmu.make_atc(a); - - FILL_PRINT(fc, a); - return *a; + latest_age = 0; + Flush(); } -const m68030ATCLine& -m68030ATC::fill_read(busaddr addr_) +// デストラクタ +m68030ATCTable::~m68030ATCTable() { - uint32 laddr = addr_.Addr(); - uint fc = addr_.GetFC(); - m68030ATCTable *table; - m68030ATCLine *a; - int i; - -#if !defined(ATC_SINGLE) - table = &tables[fc]; - // キャッシュとマッチすればそれを返す - a = table->head; - if (a->GetTag() == laddr) { - FILL_PRINT(fc, a); - table->hit_head++; - return *a; - } - for (i = 0; i < countof(table->line); i++) { - a = &table->line[i]; - if (a->GetTag() == laddr) { - // ヒットしたので先頭に移動。 - a = table->MoveHead(a); - - FILL_PRINT(fc, a); - table->hit[i]++; - return *a; - } - } - - // 見付からなかったので、古いエントリを1つ更新 - a = table->MoveHead(table->Lookup()); - table->hit[m68030ATCTable::LINES]++; - // タグはこっちで初期化 - a->tag = laddr; -#else - table = &tables[0]; - // キャッシュとマッチすればそれを返す - a = table->head; - for (i = 0; a; a = a->next, i++) { - if ((a->GetTag() == laddr) && (a->fc == fc)) { - // ヒットしたので先頭から2番目に移動 - if (i > 2) { - table->move_second(a); - } - - FILL_PRINT(fc, a); - table->hit[i]++; - return *a; - } - } - - // 見付からなかったので、古いエントリを1つ更新 - a = table->lookup(); - table->remove(a); - table->insert_head(a); - table->hit[m68030ATCTable::LINES]++; - // タグはこっちで初期化 - a->tag = laddr; - a->fc = fc; -#endif - - // テーブルサーチして結果を ATC に入れる - m68030MMU mmu(cpu, loglevel); - mmu.search(7); - mmu.make_atc(a); - - FILL_PRINT(fc, a); - return *a; } -const m68030ATCLine& -m68030ATC::fill_write(busaddr addr_) +// この ATC テーブルのエントリをすべてフラッシュする。 +void +m68030ATCTable::Flush() { - uint32 laddr = addr_.Addr(); - uint fc = addr_.GetFC(); - m68030ATCTable *table; - m68030ATCLine *a; - int i; - -#if !defined(ATC_SINGLE) - table = &tables[fc]; - // キャッシュとマッチすればそれを返す - a = table->head; - if (a->GetTag() == laddr) { - // ライトアクセスなので、WP でなく Modified == 0 なら - // Modified を立てるため、このエントリを破棄して改めて - // テーブルサーチを実行する。 - if (!a->IsWProtect() && !a->IsModified()) { - FILL_PRINT(fc, a); - MMULOG(3, 1, "%s: write access but M is not set, " - "so invalidate it", lam_func); - a->Invalidate(); - goto search; - } - FILL_PRINT(fc, a); - table->hit_head++; - return *a; - } - for (i = 0; i < countof(table->line); i++) { - a = &table->line[i]; - if (a->GetTag() == laddr) { - // ライトアクセスなので、WP でなく Modified == 0 なら - // Modified を立てるため、このエントリを破棄して改めて - // テーブルサーチを実行する。 - if (!a->IsWProtect() && !a->IsModified()) { - FILL_PRINT(fc, a); - MMULOG(3, 1, "%s: write access but M is not set, " - "so invalidate it", lam_func); - a->Invalidate(); - break; - } - // ヒットしたので先頭に移動。 - a = table->MoveHead(a); - - FILL_PRINT(fc, a); - table->hit[i]++; - return *a; - } - } - search:; - - // 見付からなかったので、古いエントリを1つ更新 - a = table->MoveHead(table->Lookup()); - table->hit[m68030ATCTable::LINES]++; - // タグはこっちで初期化 - a->tag = laddr; -#else - table = &tables[0]; - // キャッシュとマッチすればそれを返す - a = table->head; - for (i = 0; a; a = a->next, i++) { - if ((a->GetTag() == laddr) && (a->fc == fc)) { - // ライトアクセスなので、WP でなく Modified == 0 なら - // Modified を立てるため、このエントリを破棄して改めて - // テーブルサーチを実行する。 - if (!a->IsWProtect() && !a->IsModified()) { - FILL_PRINT(fc, a); - MMULOG(3, 1, "%s: write access but M is not set, " - "so invalidate it", lam_func); - a->Invalidate(); - break; - } - - // ヒットしたので、先頭から2番目に移動 - if (i > 2) { - table->move_second(a); - } - - FILL_PRINT(fc, a); - table->hit[i]++; - return *a; - } - } - - // 見付からなかったので、古いエントリを1つ更新 - a = table->lookup(); - table->remove(a); - table->insert_head(a); - // 論理部分(タグ部分)を初期化 - a->tag = laddr; - a->fc = fc; - table->hit[m68030ATCTable::LINES]++; -#endif - - // テーブルサーチして結果を ATC に入れる - m68030MMU mmu(cpu, loglevel); - mmu.search(7); - mmu.make_atc(a); - - FILL_PRINT(fc, a); - return *a; + for (int i = 0; i < LINES; i++) { + Invalidate(&line[i]); + } } -// ピークアクセス用に ATC を検索する。 -// ATC になければ NULL を返す。ここでは疑似テーブルサーチは行わない。 -// laddr は論理ページアドレスにマスクしておくこと。 -const m68030ATCLine * -m68030ATC::fill_peek(uint32 laddr, uint fc) +// この ATC テーブルから addr が一致するエントリをすべてフラッシュする。 +void +m68030ATCTable::Flush(uint32 addr) { - m68030ATCTable *table; - const m68030ATCLine *a; - -#if !defined(ATC_SINGLE) - // キャッシュとマッチすればそれを返す - table = &tables[fc]; - for (int i = 0; i < countof(table->line); i++) { - a = &table->line[i]; - if (a->GetTag() == laddr) { - // ヒットした - MMULOG(3, 1, "%s: match %d:$%08x -> $%08x", lam_func, - fc, laddr, (uint32)a->GetPAddr()); - return a; - } - } -#else - // キャッシュとマッチすればそれを返す - table = &tables[0]; - for (a = table->head; a; a = a->next) { - if ((a->GetTag() == laddr) && ((a->fc & 4) == fc)) { - // ヒットした - MMULOG(3, 1, "%s: match %d:$%08x -> $%08x", lam_func, - a->fc, laddr, (uint32)a->GetPAddr()); - return a; + for (int i = 0; i < LINES; i++) { + m68030ATCLine *a = &line[i]; + if (a->GetTag() == addr) { + Invalidate(a); } } -#endif - - return NULL; } -// デバッグ表示。 -// ATC_SINGLE なら fc は this に含まれている。 -// !ATC_SINGLE なら引数で渡される。 -// XXX print といいつつ string を返す -std::string -m68030ATCLine::fill_print(uint fc_) +// この要素を先頭に移動。 +inline void +m68030ATCTable::MoveHead(m68030ATCLine *a) { -#if !defined(ATC_SINGLE) - uint fc = fc_; -#endif - uint32 laddr = GetLAddr(); - - if (!IsBusError()) { - uint32 ci = IsCInhibit(); - uint32 wp = IsWProtect(); - uint32 mod = IsModified(); - return indent_format(1, - "ATC_fill: match %d:$%08x -> $%08x CI=%d WP=%d M=%d", - fc, laddr, GetPAddr(), ci, wp, mod); - } else { - return indent_format(1, - "ATC_fill: match %d:$%08x -> BusErr", - fc, laddr); - } + a->age = ++latest_age; } -#if !defined(ATC_SINGLE) -// 更新用にエントリを1つ差し出す。 +// このテーブルのうち最も古い要素を差し出す。 m68030ATCLine * -m68030ATCTable::Lookup() +m68030ATCTable::RemoveTail() { - uint32 minage; - int minidx; + m68030ATCLine *a = NULL; + uint32 min = (uint32)~0; + uint32 minidx = 0; - // 空きエントリを探しながら、最古エントリを探しておく。 - // 最古エントリは age が最も小さいもの。 - minage = (uint32)-1; - minidx = 0; - for (int i = 0; i < countof(line); i++) { - m68030ATCLine& a = line[i]; - if (a.IsInvalid()) { - MMULOG(3, 1, "ATC.Lookup got free entry [%d]", i); - return &a; + // 最後に使われたの (あるいは空き) を探す。 + for (int i = 0; i < LINES; i++) { + if (line[i].IsInvalid()) { + a = &line[i]; + break; } - if (a.age < minage) { - minage = a.age; + if (line[i].age < min) { + min = line[i].age; minidx = i; } } - // 空きがなければ、最古のエントリを差し出す - MMULOG(3, 1, "ATC.Lookup got oldest entry [%d]", minidx); - return &line[minidx]; -} -#else -// 更新用にエントリを1つ差し出す。 -m68030ATCLine * -m68030ATCTable::lookup() -{ - m68030ATCLine *a; - - // 空きエントリを探す - for (int i = 0; i < LINES; i++) { - a = &line[i]; - if (a->IsInvalid()) { - MMULOG(3, 1, "ATC.Lookup got free entry [%d]", i); - goto found; - } + if (a == NULL) { + a = &line[minidx]; } - // 空きがなければ、リストの末尾が最古なのでこれを差し出す - a = head; - while (a->next) { - a = a->next; - } - MMULOG(3, 1, "ATC.Lookup got oldest entry"); + // ハッシュから削除。 + Invalidate(a); - found: return a; } -#endif -// ATC をすべてフラッシュする。命令からコールされる +// a からハッシュを作成する。 void -m68030ATC::flush() +m68030ATCTable::MakeHash(m68030ATCLine *a) { - for (int i = 0; i < countof(tables); i++) { - tables[i].flush(); + uint32 addr4k = a->GetLAddr() >> 12; + int idx = a - &line[0]; + + if (__predict_true(ps4k == 0)) { + hash[addr4k] = idx; + } else if (ps4k > 0) { + for (int i = 0; i < (1 << ps4k); i++) { + hash[addr4k + i] = idx; + } + } else { + hash[addr4k] = HASH_SUB; } } -// ATC の fc, mask が一致するエントリをフラッシュする。 -// 命令からコールされる。 +// ATC a を無効にする。すでに無効なら何もしない。 void -m68030ATC::flush(uint32 fc, uint32 mask) +m68030ATCTable::Invalidate(m68030ATCLine *a) { -#if !defined(ATC_SINGLE) - for (int i = 0; i < countof(tables); i++) { - if (((i ^ fc) & mask) == 0) { - // FC が一致したテーブルを全フラッシュ - tables[i].flush(); - } - } -#else - for (int i = 0; i < 8; i++) { - if (((i ^ fc) & mask) == 0) { - // この FC をテーブルからフラッシュ - tables[0].flush_fc(i); + if (a->IsValid()) { + // 有効ならハッシュから削除。 + if (__predict_true(ps4k == 0)) { + uint32 addr4k = a->GetLAddr() >> 12; + hash[addr4k] = HASH_NONE; + } else if (ps4k > 0) { + // PS>4KB + uint32 addr4k = a->GetLAddr() >> 12; + for (int i = 0; i < (1U << ps4k); i++) { + hash[addr4k + i] = HASH_NONE; + } + } else { + // PS<4KB、自分が抜けることでこの枠が空くかどうか + InvalidateSub(a); } + + a->Invalidate(); } -#endif } -// ATC の fc, mask, addr が一致するエントリをフラッシュする。 -// 命令からコールされる。 +// PS<4KB の場合に aa が抜けることでこの addr4k の枠が空けばハッシュから削除。 void -m68030ATC::flush(uint32 fc, uint32 mask, uint32 addr) +m68030ATCTable::InvalidateSub(const m68030ATCLine *aa) { -#if !defined(ATC_SINGLE) - for (int i = 0; i < countof(tables); i++) { - if (((i ^ fc) & mask) == 0) { - // FC が一致したテーブルから addr が一致したエントリをフラッシュ - tables[i].flush(addr); - } - } -#else - for (int i = 0; i < 8; i++) { - if (((i ^ fc) & mask) == 0) { - // FC と addr が一致したエントリをフラッシュ - tables[0].flush(i, addr); + const uint32 MASK_4K = 0xfffff000 | m68030ATCLine::INVALID; + uint32 target_laddr = aa->GetTag() & MASK_4K; + bool in_use = false; + + for (int i = 0; i < LINES; i++) { + auto *a = &line[i]; + // 自分自身は今から削除予定なので除く。 + if (a == aa) { + continue; + } + // それ以外で有効なものがあるか。 + if ((a->GetTag() & MASK_4K) == target_laddr) { + in_use = true; + break; } } -#endif -} -// この ATC テーブルのエントリをすべてフラッシュする。 -void -m68030ATCTable::flush() -{ - for (int i = 0; i < countof(line); i++) { - m68030ATCLine& a = line[i]; - a.Invalidate(); + // 誰もいなくなれば削除。 + if (in_use == false) { + hash[target_laddr >> 12] = HASH_NONE; } } -#if !defined(ATC_SINGLE) -// addr が一致するエントリをすべてフラッシュ -void -m68030ATCTable::flush(uint32 addr) +// laddr を ATC から探す。見つからなければ NULL を返す。 +m68030ATCLine * +m68030ATCTable::FindSub(const busaddr laddr) { - for (int i = 0; i < countof(line); i++) { - m68030ATCLine& a = line[i]; - if (a.GetLAddr() == addr) { - a.Invalidate(); + uint32 addr = laddr.Addr() & cpu->mmu_tc.lmask; + + // 同じエントリはないはずなので、順序通りに探さなくてもいいはず。 + for (int i = 0; i < LINES; i++) { + auto *a = &line[i]; + if (a->GetTag() == addr) { + return a; } } + return NULL; } -#else -// FC が一致するエントリをすべてフラッシュする -void -m68030ATCTable::flush_fc(uint32 fc) + +// テーブルサーチを行う。 +// テーブルという用語がぶつかっているので MMU Search にしてある。 +m68030ATCLine * +m68030ATCTable::MMUSearch(busaddr laddr) { - for (int i = 0; i < countof(line); i++) { - m68030ATCLine& a = line[i]; - if (a.fc == fc) { - a.Invalidate(); - } - } + m68030ATCLine *a = RemoveTail(); + a->tag = laddr.Addr() & cpu->mmu_tc.lmask; + + m68030MMU mmu(cpu, loglevel); + mmu.search(7, laddr); + mmu.make_atc(a); + + MakeHash(a); + return a; } -// FC と addr が一致するエントリをすべてフラッシュ -void -m68030ATCTable::flush(uint32 fc, uint32 addr) +// デバッグ表示。 +std::string +m68030ATCTable::LineToStr(const m68030ATCLine *a) const { - for (int i = 0; i < countof(line); i++) { - m68030ATCLine& a = line[i]; - if (a.fc == fc && a.GetLAddr() == addr) { - a.Invalidate(); - } + uint32 laddr = a->GetLAddr(); + + if (!a->IsBusError()) { + uint32 paddr = a->GetPAddr(); + uint32 ci = a->IsCInhibit(); + uint32 wp = a->IsWProtect(); + uint32 mod = a->IsModified(); + return indent_format(1, + "ATC: match %u:$%08x -> $%08x CI=%u WP=%u M=%u", + fc, laddr, paddr, ci, wp, mod); + } else { + return indent_format(1, + "ATC: match %u:$%08x -> BusErr", + fc, laddr); } } -#endif // @@ -1043,14 +763,13 @@ m68030ATCTable::flush(uint32 fc, uint32 // // コンストラクタ -m68030MMU::m68030MMU(MPU680x0Device *cpu_, int loglevel_) +m68030MMU::m68030MMU(MPU68030Device *cpu_, int loglevel_) { cpu = cpu_; loglevel = loglevel_; m_type = INVALID; x = -1; - m_fc = -1; } // デストラクタ @@ -1062,16 +781,18 @@ m68030MMU::~m68030MMU() // m_type および ATC 作成に必要な情報を埋めてから帰る。 // ATC は帰った先で作成する。 void -m68030MMU::search(int level) +m68030MMU::search(int level, busaddr laddr) { const char *rpname; + uint32 addr; + uint32 fc; int dt; bool is_super; - m_laddr = cpu->bus.laddr.Addr(); - m_fc = cpu->bus.GetFC(); + addr = laddr.Addr(); + fc = laddr.GetFC(); - is_super = (m_fc & 0x04); + is_super = (fc & 0x04); // アドレスを (IS,) TIA .. TID に分解。 // @@ -1088,22 +809,22 @@ m68030MMU::search(int level) for (int i = TID; i >= TIA; i--) { if (cpu->mmu_tc.tix[i] > 0) { uint32 mask = cpu->mmu_tc.mask[i]; - m_lv[i] = (m_laddr >> cpu->mmu_tc.shift[i]) & mask; + m_lv[i] = (addr >> cpu->mmu_tc.shift[i]) & mask; } } - m_lv[FCL] = m_fc; + m_lv[FCL] = fc; cpu->putlogf(3, [&] { std::string buf; - buf = indent_format(2, "search: laddr=$%08x ->", m_laddr); + buf = indent_format(2, "search: laddr=$%08x ->", addr); if (cpu->mmu_tc.is > 0) - buf += string_format(" IS=%d", cpu->mmu_tc.is); + buf += string_format(" IS=%u", cpu->mmu_tc.is); if (cpu->mmu_tc.fcl) - buf += string_format(" FC=%d", m_lv[FCL]); + buf += string_format(" FC=%u", m_lv[FCL]); for (int i = TIA; i <= TID; i++) { if (cpu->mmu_tc.tix[i] > 0) { - buf += string_format(" %s=%d($%0*x)", + buf += string_format(" %s=%u($%0*x)", m_lvname[i], cpu->mmu_tc.tix[i], NIBBLE(cpu->mmu_tc.tix[i]), m_lv[i]); } @@ -1295,7 +1016,7 @@ m68030MMU::fetch_desc(uint32 tableaddr, // インダイレクトなら idx = 0 でコールしてあるので、 // tableaddr がそのまま PA になるという寸法。 descaddr = tableaddr + idx * m_descsize; - MMULOG(3, 2, "%s: descsize=%d descaddr=$%08x", lam_func, + MMULOG(3, 2, "%s: descsize=%u descaddr=$%08x", lam_func, m_descsize, descaddr); // バスエラー発生フラグをクリアしておく @@ -1305,14 +1026,14 @@ m68030MMU::fetch_desc(uint32 tableaddr, if (m_debugger) { // デバッガアクセス uint64 data; - data = cpu->mainbus->Peek32(descaddr); + data = cpu->mainbus->Peek4(descaddr); if ((int64)data < 0) { goto buserr; } m_desc1 = data; if (m_descsize == 8) { - data = cpu->mainbus->Peek32(descaddr + 4); + data = cpu->mainbus->Peek4(descaddr + 4); if ((int64)data < 0) { goto buserr; } @@ -1321,14 +1042,14 @@ m68030MMU::fetch_desc(uint32 tableaddr, } else { busdata bd; - bd = phys_read_32(descaddr); + bd = cpu->read_phys_4(descaddr); if (bd.IsBusErr()) { goto buserr; } m_desc1 = bd.Data(); if (m_descsize == 8) { - bd = phys_read_32(descaddr + 4); + bd = cpu->read_phys_4(descaddr + 4); if (bd.IsBusErr()) { goto buserr; } @@ -1376,7 +1097,7 @@ m68030MMU::fetch_desc(uint32 tableaddr, // ライトライクルの実行 if (do_write) { - busdata bd = phys_write_32(descaddr, m_desc1); + busdata bd = cpu->write_phys_4(descaddr, m_desc1); if (bd.IsBusErr()) { goto buserr; } @@ -1437,7 +1158,7 @@ m68030MMU::make_atc(m68030ATCLine *a) MMULOG(3, 2, "%s: %s", lam_func, buf.c_str()); break; default: - PANIC("corrupted m_type=%d", (int)m_type); + PANIC("corrupted m_type=%u", (uint)m_type); } // PS の部分は反映しない @@ -1457,88 +1178,46 @@ m68030MMU::make_atc(m68030ATCLine *a) busaddr m68030MMU::make_atc_debugger() const { - uint64 paddr; + busaddr paddr; switch (m_type) { case INVALID: // 無効 - paddr = busaddr::BusErr; + paddr = BusAddr::BusErr; break; case NORMAL: // ノーマル case INDIRECT: // 間接 - paddr = (m_descsize == 4) ? m_desc1 : m_desc2; - paddr &= 0xffffff00; + { + uint32 descaddr; + descaddr = (m_descsize == 4) ? m_desc1 : m_desc2; + descaddr &= 0xffffff00; + paddr = busaddr(descaddr); break; + } case EARLY: // アーリーターミネーション - paddr = (m_descsize == 4) ? m_desc1 : m_desc2; - paddr &= 0xffffff00; + { + uint32 descaddr; + descaddr = (m_descsize == 4) ? m_desc1 : m_desc2; + descaddr &= 0xffffff00; // 残ってるビットも足して返す for (int i = x + 1; i <= TID; i++) { if (cpu->mmu_tc.tix[i] > 0) { - paddr |= m_lv[i] << cpu->mmu_tc.shift[i]; + descaddr |= m_lv[i] << cpu->mmu_tc.shift[i]; } } + paddr = busaddr(descaddr); break; + } default: - PANIC("corrupted m_type=%d", (int)m_type); + PANIC("corrupted m_type=%u", (uint)m_type); } - paddr |= busaddr::TableSearched; + paddr |= BusAddr::TableSearched; return paddr; } -// 物理メモリからの読み込み。 -// 正常に読み込めれば 32bit の値。バスエラーなら (uint64)-1 を返す。 -busdata -m68030MMU::phys_read_32(uint32 addr) -{ - m68kbus backup; - busdata bd; - - // バス情報を一旦退避 - backup = cpu->bus; - - // 物理バスアクセス - cpu->bus.laddr.Set(addr, busaddr::Read | busaddr::S); - cpu->bus.paddr = cpu->bus.laddr; - bd = cpu->mainbus->Read32(cpu->bus.paddr); - if (__predict_false(bd.IsBusErr())) { - cpu->bus.size = M68K::SSW_SIZE_LONG; - return bd; - } - - // バス情報を復元 - cpu->bus = backup; - return bd; -} - -// 物理メモリへの書き込み。 -// 正常に書き込めれば 0、バスエラーなら (uint64)-1 を返す。 -busdata -m68030MMU::phys_write_32(uint32 addr, uint32 data) -{ - m68kbus backup; - busdata bd; - - // バス情報を一旦退避 - backup = cpu->bus; - - // 物理バスアクセス - cpu->bus.laddr.Set(addr, busaddr::Write | busaddr::S); - cpu->bus.paddr = cpu->bus.laddr; - bd = cpu->mainbus->Write32(cpu->bus.paddr, data); - if (__predict_false(bd.IsBusErr())) { - cpu->bus.size = M68K::SSW_SIZE_LONG; - return bd; - } - - // バス情報を復元 - cpu->bus = backup; - return bd; -} - // ディスクリプタ表示用の文字列を返す。 // m_descsize によって m_desc1, m_desc2 を適切に文字列にする。 std::string @@ -1576,7 +1255,7 @@ m68030MMU::dtname(uint32 dt) case DT_VALID4: return "valid4"; case DT_VALID8: return "valid8"; } - sprintf(buf, "(invalid DT %d)", dt); + snprintf(buf, sizeof(buf), "(invalid DT %u)", dt); return buf; } @@ -1595,9 +1274,8 @@ const char * const m68030MMU::m_lvname[M // 論理アドレスが TT0/TT1 どちらかと一致すれば (bus.ci を更新して) true を返す。 // 一致しなければ false を返す。 -// 高速化のため mmu_tt_quick による評価は呼び出し側で行なっている。 bool -MPU680x0Device::CheckTT() +MPU68030Device::CheckTT() { bool ci; int match; @@ -1610,14 +1288,14 @@ MPU680x0Device::CheckTT() // 一致した match += i + 1; ci |= tt.ci != 0; - putlog(3, " %s: TT%d match", __func__, i); + putlog(3, " %s: TT%u match", __func__, i); } } // 一致したら PA <- LA if (match != 0) { putlog(3, "%s: result %08x (match TT%s)", __func__, - (uint32)bus.laddr, + bus.laddr.Addr(), (match == 3) ? "0 and TT1" : ((match == 1) ? "0" : "1")); bus.ci = ci; return true; @@ -1625,124 +1303,141 @@ MPU680x0Device::CheckTT() return false; } -// フェッチ用のアドレス変換を行う。 +// リード/フェッチ用のアドレス変換を行う。 // 変換できれば true、バスエラーなら false を返す。 bool -MPU680x0Device::TranslateFetch() +MPU68030Device::TranslateRead() { - busaddr baddr = bus.laddr; - uint32 laddr = baddr.Addr(); - - // 状態表示 - putlog(3, "%s: enter %c:$%08x pc=$%08x", __func__, - (baddr.IsSuper() ? 'S' : 'U'), - laddr, - ppc); - - // まず TT をチェック - if (__predict_false(mmu_tt_quick[laddr >> 24] != 0)) { - if (CheckTT()) { - return true; - } - } - - // ATC からアドレスを取得 - if (__predict_true(mmu_tc.e)) { - baddr &= mmu_tc.lmask; - const m68030ATCLine& a = atc.fill_fetch(baddr); - if (__predict_false(a.IsBusError())) { - putlog(3, "%s: result BusErr (ATC:B)", __func__); - return false; - } - - bus.paddr.SetAddr(a.GetPAddr() | (laddr & mmu_tc.pgmask)); - bus.ci = a.data & m68030ATCLine::CINHIBIT; - } - - putlog(3, "%s: result $%08x", __func__, bus.paddr.Addr()); - return true; -} - -// リード用のアドレス変換を行う。 -// 変換できれば true、バスエラーなら false を返す。 -bool -MPU680x0Device::TranslateRead() -{ - busaddr baddr = bus.laddr; - uint32 laddr = baddr.Addr(); + m68030ATCLine *a; + busaddr& laddr = bus.laddr; + uint32 addr = laddr.Addr(); + uint32 fc = laddr.GetFC(); // 状態表示 putlog(3, "%s: enter %c:$%08x pc=%c:$%08x", __func__, - (baddr.IsSuper() ? 'S' : 'U'), - laddr, + (laddr.IsSuper() ? 'S' : 'U'), + addr, IsSuper() ? 'S' : 'U', ppc); - // まず TT をチェック - if (__predict_false(mmu_tt_quick[laddr >> 24] != 0)) { - if (CheckTT()) { - return true; - } - } - // ATC からアドレスを取得 - if (__predict_true(mmu_tc.e)) { - baddr &= mmu_tc.lmask; - const m68030ATCLine& a = atc.fill_read(baddr); - if (__predict_false(a.IsBusError())) { - putlog(3, "%s: result BusErr (ATC:B)", __func__); - return false; + m68030ATCTable *table = atc.fctables[fc]; + uint8 n = table->hash[addr >> 12]; + if (__predict_true((int8)n >= 0)) { + // ATC #(n) + table->atcstat.total++; + a = &table->line[n]; + } else { + if (n == m68030ATCTable::HASH_NONE) { + if (__predict_true(mmu_tc.e)) { + // TC.E 有効で対応なしなら、テーブルサーチ。 + table->atcstat.total++; + table->atcstat.miss++; + a = table->MMUSearch(laddr); + } else { + // TC.E 無効。(total もカウントしない) + return true; + } + } else if (n == m68030ATCTable::HASH_TT) { + // TT。CheckTT で外れたかどうかはカウントしない。ほぼ 0 なので。 + table->atcstat.total++; + table->atcstat.tthit++; + if (__predict_true(CheckTT())) { + return true; + } else { + return false; + } + } else { + // PS<4KB でマッチしたので再検査。 + table->atcstat.total++; + a = table->FindSub(laddr); + if (a == NULL) { + table->atcstat.miss++; + a = table->MMUSearch(laddr); + } } - - bus.paddr.SetAddr(a.GetPAddr() | (laddr & mmu_tc.pgmask)); - bus.ci = a.data & m68030ATCLine::CINHIBIT; } - putlog(3, "%s: result $%08x", __func__, bus.paddr.Addr()); + // ATC a でマッチした + table->MoveHead(a); + putlog(3, "%s", table->LineToStr(a).c_str()); + if (__predict_false(a->IsBusError())) { + return false; + } + bus.paddr.ChangeAddr(a->GetPAddr() | (addr & mmu_tc.pgmask)); + bus.ci = a->IsCInhibit(); return true; } // ライト用のアドレス変換を行う。 // 変換できれば true、バスエラーなら false を返す。 bool -MPU680x0Device::TranslateWrite() +MPU68030Device::TranslateWrite() { - busaddr baddr = bus.laddr; - uint32 laddr = baddr.Addr(); + m68030ATCLine *a; + busaddr& laddr = bus.laddr; + uint32 addr = laddr.Addr(); + uint32 fc = laddr.GetFC(); // 状態表示 putlog(3, "%s: enter %c:$%08x pc=%c:$%08x", __func__, - (baddr.IsSuper() ? 'S' : 'U'), - laddr, + (laddr.IsSuper() ? 'S' : 'U'), + addr, IsSuper() ? 'S' : 'U', ppc); - // まず TT をチェック - if (__predict_false(mmu_tt_quick[laddr >> 24] != 0)) { - if (CheckTT()) { - return true; + m68030ATCTable *table = atc.fctables[fc]; + uint8 n = table->hash[addr >> 12]; + if (__predict_true((int8)n >= 0)) { + // ATC #(n) + table->atcstat.total++; + a = &table->line[n]; + } else { + if (n == m68030ATCTable::HASH_NONE) { + if (__predict_true(mmu_tc.e)) { + // TC.E 有効で対応なしなら、テーブルサーチ。 + table->atcstat.total++; + table->atcstat.miss++; + a = table->MMUSearch(laddr); + } else { + // TC.E 無効。(total もカウントしない) + return true; + } + } else if (n == m68030ATCTable::HASH_TT) { + // TT。CheckTT で外れたかどうかはカウントしない。ほぼ 0 なので。 + table->atcstat.total++; + table->atcstat.tthit++; + if (__predict_true(CheckTT())) { + return true; + } else { + return false; + } + } else { + // PS<4KB でマッチしたので再検査。 + table->atcstat.total++; + a = table->FindSub(laddr); + if (a == NULL) { + table->atcstat.miss++; + a = table->MMUSearch(laddr); + } } } - // ATC からアドレスを取得 - if (__predict_true(mmu_tc.e)) { - baddr &= mmu_tc.lmask; - const m68030ATCLine& a = atc.fill_write(baddr); - if (__predict_false(a.IsBusError())) { - putlog(3, "%s: result BusErr (ATC:B)", __func__); - return false; - } - if (__predict_false(a.IsWProtect())) { - // Write アクセスで WP が立ってたらバスエラー - putlog(3, "%s: result Buserr (ATC:WP)", __func__); - return false; - } + // ATC a でマッチした + table->MoveHead(a); - bus.paddr.SetAddr(a.GetPAddr() | (laddr & mmu_tc.pgmask)); - bus.ci = a.data & m68030ATCLine::CINHIBIT; + if (__predict_false(a->IsBusError())) { + return false; + } + if (__predict_false(a->IsWProtect())) { + // Write アクセスで WP が立ってたらバスエラー + putlog(3, "%s: result Buserr (ATC:WP)", __func__); + return false; } - putlog(3, "%s: result $%08x", __func__, bus.paddr.Addr()); + putlog(3, "%s", table->LineToStr(a).c_str()); + bus.paddr.ChangeAddr(a->GetPAddr() | (addr & mmu_tc.pgmask)); + bus.ci = a->IsCInhibit(); return true; } @@ -1750,73 +1445,83 @@ MPU680x0Device::TranslateWrite() // 変換できれば対応する物理アドレスを、バスエラーなら BusErr を返す。 // テーブルサーチを行ったら TableSearched ビットを立てる。 busaddr -MPU680x0Device::TranslatePeek(busaddr baddr) +MPU68030Device::TranslatePeek(busaddr laddr) { + const m68030ATCLine *a; busaddr paddr; - uint32 laddr = baddr.Addr(); + uint32 addr = laddr.Addr(); + uint32 fc = laddr.GetFC(); // 状態表示 putlog(3, "%s: enter %c:$%08x", __func__, - (baddr.IsSuper() ? 'S' : 'U'), - laddr); + (laddr.IsSuper() ? 'S' : 'U'), + addr); - // まず TT をチェック - paddr = laddr; - if (__predict_false(mmu_tt_quick[laddr >> 24] != 0)) { - for (int i = 0; i < 2; i++) { - m68030TT& tt = mmu_tt[i]; - if (tt.Match(baddr)) { - // 一致した。 - // CI はここでは使わないため、一致したことだけわかればよい - putlog(3, "%s: result $%08x (match tt)", - __func__, (uint32)paddr); + m68030ATCTable *table = atc.fctables[fc]; + uint8 n = table->hash[addr >> 12]; + if (__predict_true((int8)n >= 0)) { + // ATC #(n) + a = &table->line[n]; + atc_found: + paddr = busaddr(a->GetPAddr()); + paddr |= addr & mmu_tc.pgmask; + putlog(3, "%s: result $%08x (match atc)", __func__, paddr.Addr()); + return paddr; + } else { + if (++n == 0) { + if (__predict_true(mmu_tc.e)) { + table_search: + // TC.E 有効で対応なしなら、こっそりテーブルサーチ。 + + // mmu.search() はさすがに bus を使うので事前に退避。 + m68kbus backup = bus; + + m68030MMU mmu(this, loglevel); + mmu.m_debugger = true; + mmu.search(7, laddr); + paddr = mmu.make_atc_debugger(); + // リストア + bus = backup; + + if (paddr.IsBusErr()) { + // TC が有効だがヒットしなかったらバスエラー + putlog(3, "%s: result BusErr", __func__); + } else { + paddr |= addr & mmu_tc.pgmask; + putlog(3, "%s: result $%08x (table search)", + __func__, paddr.Addr()); + } + return paddr; + } else { + // TC.E 無効 + paddr = laddr; + putlog(3, "%s: result $%08x (no translation)", + __func__, paddr.Addr()); return paddr; } - } - } - - // 次に TC (ATC と MMU テーブルサーチ)。 - if (mmu_tc.e) { - // ATC からこっそりアドレスを取得 - const m68030ATCLine *a; - a = atc.fill_peek(laddr & mmu_tc.lmask, baddr.GetFC()); - if (a != NULL) { - paddr = a->GetPAddr(); - paddr |= laddr & mmu_tc.pgmask; - putlog(3, "%s: result $%08x (match atc)", - __func__, (uint32)paddr); - return paddr; - } - - // 見付からなかったのでこっそりテーブルサーチ。 - - // mmu.search() はさすがに bus を使うので事前に退避。 - m68kbus backup = bus; - - bus.laddr = baddr; - m68030MMU mmu(this, loglevel); - mmu.m_debugger = true; - mmu.search(7); - paddr = mmu.make_atc_debugger(); - // リストア - bus = backup; - - if (paddr.IsBusErr()) { - // TC が有効だがヒットしなかったらバスエラー - putlog(3, "%s: result BusErr", __func__); + } else if (++n == 0) { + // TT をこっそりチェック。 + for (int i = 0; i < 2; i++) { + m68030TT& tt = mmu_tt[i]; + if (tt.Match(laddr)) { + // 一致した。 + // CI はここでは使わないため、一致したことだけわかればよい + putlog(3, "%s: result $%08x (match tt)", + __func__, paddr.Addr()); + return paddr; + } + } + return BusAddr::BusErr; } else { - paddr |= laddr & mmu_tc.pgmask; - putlog(3, "%s: result $%08x (table search)", - __func__, paddr.Addr()); + // PS<4KB でマッチしたので再検査。 + a = table->FindSub(laddr); + if (a) { + goto atc_found; + } else { + goto table_search; + } } - return paddr; } - - // TT が無効かまたは有効でもヒットせず、 - // かつ TC も無効だったら (こっちは「有効でヒットせず」は含まない)、 - // 論理アドレスをそのまま返す - putlog(3, "%s: result $%08x (no translation)", __func__, laddr); - return laddr; } // @@ -1826,7 +1531,7 @@ MPU680x0Device::TranslatePeek(busaddr ba // ir2 の下位5ビットから FC を返す。 // 不当パターンなら C++ の例外をスローする。 uint32 -MPU680x0Device::get_fc() +MPU68030Device::get_fc() { uint fcfield = ir2 & 0x1f; @@ -1847,7 +1552,7 @@ MPU680x0Device::get_fc() // MMU 不当命令 (2ワード目で確定) void -MPU680x0Device::mmu_op_illg2() +MPU68030Device::mmu_op_illg2() { putlog(1, "MMU illegal instruction %04x %04x", ir, ir2); Exception(M68K::EXCEP_FLINE); @@ -1855,7 +1560,7 @@ MPU680x0Device::mmu_op_illg2() // PFLUSH fc,#mask 命令 (EA を持たない方) void -MPU680x0Device::mmu_op_pflush() +MPU68030Device::mmu_op_pflush() { uint32 mask; uint32 fc; @@ -1867,7 +1572,7 @@ MPU680x0Device::mmu_op_pflush() // PFLUSH fc,#mask,ea 命令 void -MPU680x0Device::mmu_op_pflush_ea() +MPU68030Device::mmu_op_pflush_ea() { uint32 ea; uint32 mask; @@ -1881,10 +1586,11 @@ MPU680x0Device::mmu_op_pflush_ea() // PLOADR/PLOADW 命令 void -MPU680x0Device::mmu_op_pload() +MPU68030Device::mmu_op_pload() { uint32 ea; uint32 fc; + busaddr laddr; if ((ir2 & 0x01e0) != 0) { mmu_op_illg2(); @@ -1896,20 +1602,51 @@ MPU680x0Device::mmu_op_pload() // 実効アドレスを論理アドレスとする ATC を作成。 // ってこれでいいのか? if ((ir2 & 0x0200)) { - busaddr addr(ea, busaddr::FC(fc) | busaddr::R); - atc.fill_read(addr); + laddr = busaddr(ea) | busaddr::FC(fc) | BusAddr::R; } else { - busaddr addr(ea, busaddr::FC(fc) | busaddr::W); - atc.fill_write(addr); + laddr = busaddr(ea) | busaddr::FC(fc) | BusAddr::W; + } + + m68030ATCTable *table = atc.fctables[fc]; + uint8 n = table->hash[laddr.Addr() >> 12]; + if ((int8)n >= 0) { + // すでにエントリがある場合 + if (laddr.IsWrite() == false) { + return; + } + // 命令が ploadw でエントリが WProtect==0 && Modified==0 なら、 + // 一回破棄して書き込み用に作り直すため下に合流。 + m68030ATCLine *a = &table->line[n]; + if (a->IsWProtect() == false && a->IsModified() == false) { + table->Invalidate(a); + n = m68030ATCTable::HASH_NONE; + // FALLTHORUGH + } else { + return; + } + } + if (n == m68030ATCTable::HASH_NONE || n == m68030ATCTable::HASH_SUB) { + if (__predict_true(mmu_tc.e)) { + // TC.E 有効で対応なしなら、テーブルサーチを行って + // ATC を作成。 + m68030ATCLine *a __unused; + a = table->MMUSearch(laddr); + } else { + // TC.E 無効なら何もしない? + return; + } + } else if (n == m68030ATCTable::HASH_TT) { + // TT の領域なら何もしてはいけない。 + return; } } // PTESTR/PTESTW 命令 void -MPU680x0Device::mmu_op_ptest() +MPU68030Device::mmu_op_ptest() { uint level = (ir2 >> 10) & 7; - busaddr rw = (ir2 & 0x0200) ? busaddr::R : busaddr::W; + busaddr rw = (ir2 & 0x0200) ? BusAddr::R : BusAddr::W; uint rn = (ir2 >> 5) & 0xf; uint32 ea; uint32 fc; @@ -1928,12 +1665,37 @@ MPU680x0Device::mmu_op_ptest() // laddr は IS と PS のところを落としたもの。 // ssw_laddr のアドレスは IS を落とさないもの (PSは不問)。 - busaddr ssw_laddr(ea, busaddr::FC(fc) | rw); - uint32 laddr = ea & mmu_tc.lmask; + busaddr laddr = busaddr(ea) | busaddr::FC(fc) | rw; + uint32 addr = ea & mmu_tc.lmask; + + m68030ATCTable *table = atc.fctables[fc]; + m68030ATCLine *a = NULL; + uint8 n = table->hash[addr >> 12]; + if (__predict_true((int8)n >= 0)) { + // ATC #(n) で見付かった。 + a = &table->line[n]; + } else { + if (n == m68030ATCTable::HASH_NONE) { + // ATC に見付からなかった。Invalid を立てる。 + mmusr = m68030MMUSR::I; + } else if (n == m68030ATCTable::HASH_TT) { + for (int i = 0; i < 2; i++) { + m68030TT& tt = mmu_tt[i]; + if (tt.Match(laddr)) { + mmusr |= m68030MMUSR::T; + } + } + } else { + // PS<4KB + a = table->FindSub(laddr); + if (a == NULL) { + mmusr = m68030MMUSR::I; + } + } + } - const m68030ATCLine *a = atc.fill_peek(laddr, fc); if (a) { - // ATC に見付かった。Modified の状態を返す。 + // 見付かれば Modified 等の状態を返す。 if (a->IsBusError()) { mmusr |= m68030MMUSR::B; } else if (a->IsWProtect()) { @@ -1941,44 +1703,32 @@ MPU680x0Device::mmu_op_ptest() } else if (a->IsModified()) { mmusr |= m68030MMUSR::M; } - } else { - // ATC に見付からなかった。Invalid を立てる。 - mmusr = m68030MMUSR::I; - } - - // どちらにしても TT を評価。 - for (int i = 0; i < 2; i++) { - m68030TT& tt = mmu_tt[i]; - if (tt.Match(ssw_laddr)) { - mmusr |= m68030MMUSR::T; - } } } else { // レベル 1-7 ならテーブルサーチを実行。 - bus.laddr.Set(ea, busaddr::FC(fc) | rw); - m68030MMU table(this, loglevel); - table.m_ptest = true; - table.search(level); + m68030MMU mmu(this, loglevel); + mmu.m_ptest = true; + mmu.search(level, busaddr(ea) | busaddr::FC(fc) | rw); - if (table.m_berr) { + if (mmu.m_berr) { mmusr |= m68030MMUSR::B; } - if (table.m_lim) { + if (mmu.m_lim) { // XXX not tested mmusr |= m68030MMUSR::L; } - if (table.m_s && (fc & 4)) { + if (mmu.m_s && (fc & 4)) { // XXX not tested mmusr |= m68030MMUSR::S; } - if (table.m_wp) { + if (mmu.m_wp) { mmusr |= m68030MMUSR::W; } - if (table.m_type == INVALID) { + if (mmu.m_type == INVALID) { mmusr |= m68030MMUSR::I; } - if (table.m_m) { + if (mmu.m_m) { mmusr |= m68030MMUSR::M; } // N を求める。 @@ -1986,13 +1736,13 @@ MPU680x0Device::mmu_op_ptest() int n; if (mmu_tc.fcl) { // たぶんファンクションテーブルで1段消費する? (未確認) - if (table.x == FCL) { + if (mmu.x == FCL) { n = 1; } else { - n = table.x + 2; + n = mmu.x + 2; } } else { - n = table.x + 1; + n = mmu.x + 1; } mmusr |= n; @@ -2000,8 +1750,8 @@ MPU680x0Device::mmu_op_ptest() // rn は有効なら 8-15。 if (rn != 0) { // XXX 途中でバスエラーが起きるケースは未実装 - if (table.m_berr == false) { - reg.R[rn] = table.m_lastaddr; + if (mmu.m_berr == false) { + reg.R[rn] = mmu.m_lastaddr; } } }