--- nono/m680x0/m68030mmu.cpp 2026/04/29 17:04:54 1.1.1.5 +++ nono/m680x0/m68030mmu.cpp 2026/04/29 17:05:15 1.1.1.8 @@ -4,12 +4,9 @@ // Licensed under nono-license.txt // -#include "m68030core.h" -#include "m68030bus.h" -#include "m68030ea.h" +#include "mpu680x0.h" #include "m68030mmu.h" -#include "bus.h" -#include "mpu.h" +#include "mainbus.h" #define TIA (0) #define TIB (1) @@ -45,57 +42,44 @@ enum ATCtype { // デバッグ表示 // -#define IF_DEBUG if (gMPU->loglevel > 0) - // デバッグ表示のため値の表示に必要な桁数を返す #define NIBBLE(x) ( (x) == 0 ? 1 : ((x) + 3) / 4 ) -// インデント付き fmt, ap を出力 -static void viprintf(int indent, const char *fmt, va_list) __printflike(2, 0); -static void -viprintf(int indent, const char *fmt, va_list ap) -{ - static const char *indentstr = " "; // 長さは適当 - char buf[1024]; - - vsnprintf(buf, sizeof(buf), fmt, ap); - cpulog(3, "%s%s", &indentstr[strlen(indentstr) - indent], buf); -} - -// IF_DEBUG 中だと分かってるところではこっちを使う -static void IPRINTF(int indent, const char *fmt, ...)__printflike(2, 3); -static void -IPRINTF(int indent, const char *fmt, ...) +// MMU デバッグモードなら ATC エントリ a を表示 +#define FILL_PRINT(fc, a) cpu->putlogf(3, [&] { return (a)->fill_print(fc); }) + +// インデント付き string_format +static std::string indent_format(int indent, const char *fmt, ...) + __printflike(2, 3); +static std::string +indent_format(int indent, const char *fmt, ...) { va_list ap; + char *buf; va_start(ap, fmt); - viprintf(indent, fmt, ap); + vasprintf(&buf, fmt, ap); va_end(ap); -} -// MMU デバッグモードならインデント付き fmt... を出力 -#define DPRINTF(indent, ...) do { \ - IF_DEBUG \ - IPRINTF(indent, __VA_ARGS__); \ -} while(0) + std::string rv(indent, ' '); + rv += buf; + free(buf); -// MMU デバッグモードなら ATC エントリ a を表示 -#define FILL_PRINT(fc, a) do { \ - IF_DEBUG \ - (a)->fill_print(fc); \ -} while (0) + return rv; +} +#define MMULOG(lv, indent, arg...) \ + gMPU->putlogf(lv, [&] { return indent_format(indent, arg); }) // テーブルサーチ操作クラス class m68030MMU final { public: - m68030MMU(m68kcpu *); - ~m68030MMU() { } + m68030MMU(MPU680x0Device *); + ~m68030MMU(); // サーチを実行 - void search(); + void search(int level); // ATC にエントリを作成 void make_atc(m68030ATCLine *); @@ -116,9 +100,6 @@ class m68030MMU final // MMU による物理メモリ書き込み uint64 phys_write_32(uint32 addr, uint32 data); - // デバッガ読み込み - uint64 phys_peek_32(uint32 addr); - // ディスクリプタを表示用に (デバッグ用) std::string sprintf_desc(); @@ -134,8 +115,9 @@ class m68030MMU final bool m_berr; // テーブルサーチ中にバスエラー bool m_ptest; // PTEST[RW] 命令中なら真 bool m_debugger; // デバッガからのアクセスなら true にする - private: int x; // 段数 TIA〜TID, FCL + uint32 m_lastaddr; // 最後にアクセスしたディスクリプタアドレス(PTEST) + private: int m_lv[MAX_LV]; uint32 m_laddr; int m_fc; @@ -147,13 +129,13 @@ class m68030MMU final // デバッグ用文字列 static const char * const m_lvname[MAX_LV]; - m68kcpu *cpu; + MPU680x0Device *cpu; }; // 現在の TC:E、TT:E の状態に応じてアドレス変換の有無を切り替える。 // mmu_enable ならアドレス変換ありのバスアクセスを使う。see m68030bus.cpp void -m68kcpu::mmu_enable_changed() +MPU680x0Device::mmu_enable_changed() { bool enable; @@ -164,11 +146,11 @@ m68kcpu::mmu_enable_changed() if (mmu_enable && !enable) { // 無効にする mmu_enable = false; - cpulog(1, "MMU アドレス変換停止"); + putlog(1, "MMU Translation Disabled"); } else if (!mmu_enable && enable) { // 有効にする mmu_enable = true; - cpulog(1, "MMU アドレス変換開始"); + putlog(1, "MMU Translation Enabled"); } } @@ -177,22 +159,30 @@ m68kcpu::mmu_enable_changed() // レジスタクラス // -void -m68kcpu::SetSRP(uint32 h, uint32 l) +bool +MPU680x0Device::SetSRP(uint32 h, uint32 l) { + if ((h & m68030RP::DT_MASK) == 0) { + return false; + } reg.srp.h = h & m68030RP::H_MASK; reg.srp.l = l & m68030RP::L_MASK; + return true; } -void -m68kcpu::SetCRP(uint32 h, uint32 l) +bool +MPU680x0Device::SetCRP(uint32 h, uint32 l) { + if ((h & m68030RP::DT_MASK) == 0) { + return false; + } reg.crp.h = h & m68030RP::H_MASK; reg.crp.l = l & m68030RP::L_MASK; + return true; } void -m68kcpu::SetTT(int n, uint32 data) +MPU680x0Device::SetTT(int n, uint32 data) { uint32 lbase; uint32 lmask; @@ -232,8 +222,7 @@ m68kcpu::SetTT(int n, uint32 data) // ため base 側にビットを立てておくと必ず不一致になることを利用して、 // E ビットの評価も一度に行う。 // ・RW ビットは SSW の RW と比較するので、SSW_RW のビット位置を使う - // (0x00000040 の位置)。 - // また TT::RW は Write が %1 だが SSW_RW は Read が %1 なので反転。 + // (0x00000040 の位置)。TT:RW も SSW_RW も Read が %1。 // ・RWM が %1 なら R/W は無視、RWM が %0 なら R/W の一致も検査するので // RWM が %0 の時は RW のマスクビットを立てておく。 // ・FCMask は %1 が無視を表しているので NOT しておく。 @@ -245,12 +234,12 @@ m68kcpu::SetTT(int n, uint32 data) // : E は ~TT:E // mask 00000000 00000000 10000000 0M000CCC : M は R/~W マスク // : C は FC マスク - sbase = ~data & m68030TT::E; - sbase |= (~data & m68030TT::RW) >> 3; - sbase |= ( data & m68030TT::FCBASE_MASK) >> 4; + sbase = ~data & m68030TT::E; + sbase |= (data & m68030TT::RW) >> 3; + sbase |= (data & m68030TT::FCBASE_MASK) >> 4; smask = m68030TT::E; if ((data & m68030TT::RWM) == 0) { - smask |= SSW_RW; + smask |= M68K::SSW_RW; } smask |= ~data & m68030TT::FCMASK_MASK; @@ -278,7 +267,7 @@ m68kcpu::SetTT(int n, uint32 data) // この TT が有効でアドレスがマッチすれば true を返す。 // (TT:E は演算に織り込んである、このすぐ上の SetTT() のコメント参照) bool -m68030TT::Match(uint64 ssw_laddr) +m68030TT::Match(uint64 ssw_laddr) const { bool rv = (((ssw_laddr ^ base) & mask) == 0); return rv; @@ -287,7 +276,7 @@ m68030TT::Match(uint64 ssw_laddr) // 成功すれば true、MMU 構成例外が起きる場合は false を返す。 // 呼び出し側で例外を起こすこと。 bool -m68kcpu::SetTC(uint32 data) +MPU680x0Device::SetTC(uint32 data) { // マスクしてレジスタイメージにセット reg.tc = data & m68030TC::MASK; @@ -391,7 +380,7 @@ m68030TC::MakeMask() } void -m68kcpu::SetMMUSR(uint16 data) +MPU680x0Device::SetMMUSR(uint16 data) { reg.mmusr = data & m68030MMUSR::MASK; } @@ -417,6 +406,11 @@ m68030ATCTable::m68030ATCTable() #endif } +// デストラクタ +m68030ATCTable::~m68030ATCTable() +{ +} + #if !defined(ATC_SINGLE) // a のエントリを先頭にする。 m68030ATCLine * @@ -439,9 +433,14 @@ m68030ATCTable::MoveHead(m68030ATCLine * // // コンストラクタ -m68030ATC::m68030ATC(m68kcpu *parent) +m68030ATC::m68030ATC(MPU680x0Device *cpu_) +{ + cpu = cpu_; +} + +// デストラクタ +m68030ATC::~m68030ATC() { - cpu = parent; } #if defined(ATC_SINGLE) @@ -608,7 +607,7 @@ m68030ATC::fill_fetch() // テーブルサーチして結果を ATC に入れる m68030MMU mmu(cpu); - mmu.search(); + mmu.search(7); mmu.make_atc(a); FILL_PRINT(fc, a); @@ -679,7 +678,7 @@ m68030ATC::fill_read() // テーブルサーチして結果を ATC に入れる m68030MMU mmu(cpu); - mmu.search(); + mmu.search(7); mmu.make_atc(a); FILL_PRINT(fc, a); @@ -705,8 +704,8 @@ m68030ATC::fill_write() // テーブルサーチを実行する。 if (!a->IsWProtect() && !a->IsModified()) { FILL_PRINT(fc, a); - DPRINTF(1, "ATC_fill: write access but M is not set, " - "so invalidate it"); + MMULOG(3, 1, "ATC_fill: write access but M is not set, " + "so invalidate it"); a->Invalidate(); goto search; } @@ -722,8 +721,8 @@ m68030ATC::fill_write() // テーブルサーチを実行する。 if (!a->IsWProtect() && !a->IsModified()) { FILL_PRINT(fc, a); - DPRINTF(1, "ATC_fill: write access but M is not set, " - "so invalidate it"); + MMULOG(3, 1, "ATC_fill: write access but M is not set, " + "so invalidate it"); a->Invalidate(); break; } @@ -753,8 +752,8 @@ m68030ATC::fill_write() // テーブルサーチを実行する。 if (!a->IsWProtect() && !a->IsModified()) { FILL_PRINT(fc, a); - DPRINTF(1, "ATC_fill: write access but M is not set, " - "so invalidate it"); + MMULOG(3, 1, "ATC_fill: write access but M is not set, " + "so invalidate it"); a->Invalidate(); break; } @@ -782,7 +781,7 @@ m68030ATC::fill_write() // テーブルサーチして結果を ATC に入れる m68030MMU mmu(cpu); - mmu.search(); + mmu.search(7); mmu.make_atc(a); FILL_PRINT(fc, a); @@ -790,14 +789,13 @@ m68030ATC::fill_write() } // ピークアクセス用に ATC を検索する。 -// ATC になければ (uint64)-1 を返す。ここでは疑似テーブルサーチは行わない。 +// ATC になければ NULL を返す。ここでは疑似テーブルサーチは行わない。 // laddr は論理ページアドレスにマスクしておくこと。 -// 戻り値はいずれの場合も物理ページアドレスか、バスエラーなら (uint64)-1。 -uint64 +const m68030ATCLine * m68030ATC::fill_peek(uint32 laddr, uint fc) { m68030ATCTable *table; - m68030ATCLine *a; + const m68030ATCLine *a; #if !defined(ATC_SINGLE) // キャッシュとマッチすればそれを返す @@ -806,10 +804,9 @@ m68030ATC::fill_peek(uint32 laddr, uint a = &table->line[i]; if (a->GetTag() == laddr) { // ヒットした - uint32 paddr = a->GetPAddr(); - DPRINTF(1, "fill_peek: match %d:$%08x -> $%08x", - fc, laddr, paddr); - return paddr; + MMULOG(3, 1, "fill_peek: match %d:$%08x -> $%08x", + fc, laddr, (uint32)a->GetPAddr()); + return a; } } #else @@ -818,25 +815,25 @@ m68030ATC::fill_peek(uint32 laddr, uint for (a = table->head; a; a = a->next) { if ((a->GetTag() == laddr) && ((a->fc & 4) == fc)) { // ヒットした - uint32 paddr = a->GetPAddr(); - DPRINTF(1, "fill_peek: match %d:$%08x -> $%08x", - a->fc, laddr, paddr); - return paddr; + MMULOG(3, 1, "fill_peek: match %d:$%08x -> $%08x", + a->fc, laddr, (uint32)a->GetPAddr()); + return a; } } #endif - return (uint64)-1; + return NULL; } // デバッグ表示。 // ATC_SINGLE なら fc は ATCLine に含まれているため、引数はダミーで // 変数名が衝突するため仮引数名も変えてある。なんだかなあ。 +// XXX print といいつつ string を返す #if defined(ATC_SINGLE) -void +std::string m68030ATCLine::fill_print(uint dummyfc) #else -void +std::string m68030ATCLine::fill_print(uint fc) #endif { @@ -845,10 +842,12 @@ m68030ATCLine::fill_print(uint fc) if (!IsBusError()) { uint32 wp = IsWProtect(); uint32 mod = IsModified(); - IPRINTF(1, "ATC_fill: match %d:$%08x -> $%08x WP=%d M=%d", + return indent_format(1, + "ATC_fill: match %d:$%08x -> $%08x WP=%d M=%d", fc, laddr, GetPAddr(), wp, mod); } else { - IPRINTF(1, "ATC_fill: match %d:$%08x -> BusErr", + return indent_format(1, + "ATC_fill: match %d:$%08x -> BusErr", fc, laddr); } } @@ -868,7 +867,7 @@ m68030ATCTable::Lookup() for (int i = 0; i < countof(line); i++) { m68030ATCLine& a = line[i]; if (a.IsInvalid()) { - DPRINTF(1, "ATC_lookup got free entry [%d]", i); + MMULOG(3, 1, "ATC_lookup got free entry [%d]", i); return &a; } if (a.age < minage) { @@ -877,7 +876,7 @@ m68030ATCTable::Lookup() } } // 空きがなければ、最古のエントリを差し出す - DPRINTF(1, "ATC_lookup got oldest entry [%d]", minidx); + MMULOG(3, 1, "ATC_lookup got oldest entry [%d]", minidx); return &line[minidx]; } #else @@ -891,7 +890,7 @@ m68030ATCTable::lookup() for (int i = 0; i < LINES; i++) { a = &line[i]; if (a->IsInvalid()) { - DPRINTF(1, "ATC_lookup got free entry [%d]", i); + MMULOG(3, 1, "ATC_lookup got free entry [%d]", i); goto found; } } @@ -901,7 +900,7 @@ m68030ATCTable::lookup() while (a->next) { a = a->next; } - DPRINTF(1, "ATC_lookup got oldest entry"); + MMULOG(3, 1, "ATC_lookup got oldest entry"); found: return a; @@ -1015,9 +1014,9 @@ m68030ATCTable::flush(uint32 fc, uint32 // // コンストラクタ -m68030MMU::m68030MMU(m68kcpu *parent) +m68030MMU::m68030MMU(MPU680x0Device *cpu_) { - cpu = parent; + cpu = cpu_; m_type = INVALID; m_s = 0; @@ -1038,11 +1037,16 @@ m68030MMU::m68030MMU(m68kcpu *parent) m_lastsize = 0; } +// デストラクタ +m68030MMU::~m68030MMU() +{ +} + // テーブルサーチのメイン部分。 // m_type および ATC 作成に必要な情報を埋めてから帰る。 // ATC は帰った先で作成する。 void -m68030MMU::search() +m68030MMU::search(int level) { const char *rpname; int dt; @@ -1073,10 +1077,10 @@ m68030MMU::search() } m_lv[FCL] = m_fc; - IF_DEBUG { + cpu->putlogf(3, [&] { std::string buf; - buf += string_format("search: laddr=$%08x ->", m_laddr); + buf = indent_format(2, "search: laddr=$%08x ->", m_laddr); if (cpu->mmu_tc.is > 0) buf += string_format(" IS=%d", cpu->mmu_tc.is); if (cpu->mmu_tc.fcl) @@ -1088,8 +1092,8 @@ m68030MMU::search() NIBBLE(cpu->mmu_tc.tix[i]), m_lv[i]); } } - IPRINTF(2, "%s", buf.c_str()); - } + return buf; + }); // サーチ時の初期化処理。図9-26 m_wp = 0; @@ -1110,8 +1114,13 @@ m68030MMU::search() // ルートポインタの DT をチェック dt = GET_DT(m_desc1); - DPRINTF(2, "search: %s dt=%s", rpname, dtname(dt)); + MMULOG(3, 2, "search: %s dt=%s", rpname, dtname(dt)); switch (dt) { + case DT_INVALID: + // 無効は設定できないはずなので、ここに来ないはず + PANIC("%s.DT==Invalid", rpname); + break; + case DT_PAGE: // ページディスクリプタ m_type = EARLY; return; @@ -1127,7 +1136,10 @@ m68030MMU::search() // 必要ならファンクションコード・ルックアップ if (cpu->mmu_tc.fcl) { x = FCL; - DPRINTF(2, "search: %s tableaddr=$%08x idx=$%x", + // たぶん検索レベルを1つ消費するはず。未確認。 + level--; + + MMULOG(3, 2, "search: %s tableaddr=$%08x idx=$%x", m_lvname[x], m_desc2, m_lv[x]); // ディスクリプタをフェッチ @@ -1156,8 +1168,7 @@ m68030MMU::search() // TIx のテーブルサーチに入る。図9-25 の下半分。 - x = TIA; - for (;;) { + for (x = TIA; x < level; x++) { // リミットチェック if (m_lastsize == 8 && limitcheck() == false) { m_type = INVALID; @@ -1169,7 +1180,7 @@ m68030MMU::search() uint32 tableaddr; tableaddr = (m_lastsize == 4) ? m_desc1 : m_desc2; tableaddr &= 0xfffffff0; - DPRINTF(2, "search: %s tableaddr=$%08x idx=$%x", + MMULOG(3, 2, "search: %s tableaddr=$%08x idx=$%x", m_lvname[x], tableaddr, m_lv[x]); // ディスクリプタをフェッチ @@ -1217,11 +1228,12 @@ m68030MMU::search() return; } // 次段のサーチを繰り返す - x++; - continue; + break; } } - __unreachable(); + + // レベル上限にひっかかった場合、x は過ぎているので一つ戻しておく。 + x--; } // リミットチェックを実行。図9-28。 @@ -1229,14 +1241,19 @@ m68030MMU::search() bool m68030MMU::limitcheck() const { - int lu = (m_desc1 & 0x80000000); - uint16 limit = (m_desc1 >> 16) & 0x7fff; + uint16 limit = (m_desc1 >> 16) & 0x7fff; - if (lu != 0 && m_lv[x] < limit) { - return false; - } - if (lu == 0 && m_lv[x] > limit) { - return false; + // 最上位ビットが L/U + if ((int32)m_desc1 < 0) { + if (m_lv[x] < limit) { + MMULOG(3, 2, "limitcheck: $%04x < $%04x", m_lv[x], limit); + return false; + } + } else { + if (m_lv[x] > limit) { + MMULOG(3, 2, "limitcheck: $%04x > $%04x", m_lv[x], limit); + return false; + } } return true; } @@ -1264,7 +1281,7 @@ m68030MMU::fetch_desc(uint32 tableaddr, // インダイレクトなら idx = 0 でコールしてあるので、 // tableaddr がそのまま PA になるという寸法。 descaddr = tableaddr + idx * m_descsize; - DPRINTF(2, "fetch_desc: descsize=%d descaddr=$%08x", + MMULOG(3, 2, "fetch_desc: descsize=%d descaddr=$%08x", m_descsize, descaddr); // バスエラー発生フラグをクリアしておく @@ -1274,14 +1291,14 @@ m68030MMU::fetch_desc(uint32 tableaddr, if (m_debugger) { // デバッガアクセス uint64 data; - data = phys_peek_32(descaddr); + data = cpu->mainbus->Peek32(descaddr); if ((int64)data < 0) { goto buserr; } m_desc1 = data; if (m_descsize == 8) { - data = phys_peek_32(descaddr + 4); + data = cpu->mainbus->Peek32(descaddr + 4); if ((int64)data < 0) { goto buserr; } @@ -1303,13 +1320,15 @@ m68030MMU::fetch_desc(uint32 tableaddr, } m_desc2 = data; } + + m_lastaddr = descaddr; } // ディスクリプタタイプをチェック // DT は必ず最初のロングワードの下位2ビット。 do_write = false; dt = GET_DT(m_desc1); - DPRINTF(2, "fetch_desc: desc=%s dt=%s", + MMULOG(3, 2, "fetch_desc: desc=%s dt=%s", sprintf_desc().c_str(), dtname(dt)); switch (dt) { case DT_INVALID: // 無効 @@ -1348,7 +1367,7 @@ m68030MMU::fetch_desc(uint32 tableaddr, goto buserr; } // バス動作正常終了 - DPRINTF(2, "fetch_desc: desc=%s updated", sprintf_desc().c_str()); + MMULOG(3, 2, "fetch_desc: desc=%s updated", sprintf_desc().c_str()); } // ステータス更新 @@ -1381,13 +1400,13 @@ m68030MMU::make_atc(m68030ATCLine *a) switch (m_type) { case INVALID: // 無効 a->data |= m68030ATCLine::BUSERROR; - DPRINTF(2, "make_atc: type=INVALID -> BusErr"); + MMULOG(3, 2, "make_atc: type=INVALID -> BusErr"); return; case NORMAL: // ノーマル case INDIRECT: // 間接 paddr = (m_descsize == 4) ? m_desc1 : m_desc2; - DPRINTF(2, "make_atc: type=NORMAL paddr=$%08x", paddr & 0xffffff00); + MMULOG(3, 2, "make_atc: type=NORMAL paddr=$%08x", paddr & 0xffffff00); break; case EARLY: // アーリーターミネーション @@ -1402,10 +1421,10 @@ m68030MMU::make_atc(m68030ATCLine *a) buf += string_format(" +%s=%08x", m_lvname[i], paddr); } } - DPRINTF(2, "%s", buf.c_str()); + MMULOG(3, 2, "%s", buf.c_str()); break; default: - __unreachable(); + PANIC("corrupted m_type=%d", (int)m_type); } // PS の部分は反映しない @@ -1448,7 +1467,7 @@ m68030MMU::make_atc_debugger() } break; default: - __unreachable(); + PANIC("corrupted m_type=%d", (int)m_type); } return paddr; } @@ -1465,8 +1484,8 @@ m68030MMU::phys_read_32(uint32 addr) backup = cpu->bus; // 物理バスアクセス - cpu->bus.ssw = SSW_BUS_R | FC_SD; - data = vm_phys_read_32(addr); + cpu->bus.ssw = M68K::SSW_BUS_R | M68K::FC_SD; + data = cpu->mainbus->Read32(addr); if ((int64)data < 0) return data; @@ -1487,8 +1506,8 @@ m68030MMU::phys_write_32(uint32 addr, ui backup = cpu->bus; // 物理バスアクセス - cpu->bus.ssw = SSW_BUS_W | FC_SD; - berr = vm_phys_write_32(addr, data); + cpu->bus.ssw = M68K::SSW_BUS_W | M68K::FC_SD; + berr = cpu->mainbus->Write32(addr, data); if ((int64)berr < 0) return berr; @@ -1497,30 +1516,6 @@ m68030MMU::phys_write_32(uint32 addr, ui return 0; } -// デバッガによるメモリ読み込み -uint64 -m68030MMU::phys_peek_32(uint32 addr) -{ - uint64 peekdata; - uint32 data; - - // 1バイト目はバスエラーを検査。 - peekdata = vm_phys_peek_8(addr); - if ((int64)peekdata < 0) { - // バスエラー - return -1; - } - data = peekdata << 24; - - // ここはロングワード境界でアクセスしてるはずなので、 - // 先頭バイト以外のバスエラーチェックは不要。 - data |= vm_phys_peek_8(addr + 1) << 16; - data |= vm_phys_peek_8(addr + 2) << 8; - data |= vm_phys_peek_8(addr + 3); - - return data; -} - // ディスクリプタ表示用の文字列を返す。 // m_descsize によって m_desc1, m_desc2 を適切に文字列にする。 std::string @@ -1578,8 +1573,8 @@ const char * const m68030MMU::m_lvname[M // 論理アドレスが TT0/TT1 どちらかと一致すれば true を返す。 // 一致しなければ false を返す。 // 高速化のため mmu_tt_quick による評価は呼び出し側で行なっている。 -static bool -m68030_check_tt(m68kcpu *cpu) +bool +MPU680x0Device::CheckTT() const { bool ci; int match; @@ -1587,12 +1582,12 @@ m68030_check_tt(m68kcpu *cpu) match = 0; ci = false; for (int i = 0; i < 2; i++) { - m68030TT& tt = cpu->mmu_tt[i]; - if (tt.Match(cpu->bus.ssw_laddr)) { + const m68030TT& tt = mmu_tt[i]; + if (tt.Match(bus.ssw_laddr)) { // 一致した match += i + 1; ci |= tt.ci != 0; - DPRINTF(1, "check_tt: TT%d match", i); + MMULOG(3, 1, "%s: TT%d match", __func__, i); } } @@ -1601,8 +1596,8 @@ m68030_check_tt(m68kcpu *cpu) // 一致したら PA <- LA if (match != 0) { - DPRINTF(0, "check_tt: result %08x (match TT%s)", - cpu->bus.laddr, + MMULOG(3, 0, "%s: result %08x (match TT%s)", __func__, + bus.laddr, (match == 3) ? "0 and TT1" : ((match == 1) ? "0" : "1")); return true; } @@ -1612,112 +1607,112 @@ m68030_check_tt(m68kcpu *cpu) // フェッチ用のアドレス変換を行う。 // 変換できれば true、バスエラーなら false を返す。 bool -m68030_mmu_translate_fetch(m68kcpu *cpu) +MPU680x0Device::TranslateFetch() { // 状態表示 - DPRINTF(0, "translate_fetch: enter %c:$%08x pc=$%08x", - (cpu->bus.GetFC() & 0x04) ? 'S' : 'U', - cpu->bus.laddr, - RegPPC); + MMULOG(3, 0, "%s: enter %c:$%08x pc=$%08x", __func__, + (bus.GetFC() & 0x04) ? 'S' : 'U', + bus.laddr, + ppc); // まず TT をチェック - if (__predict_false(cpu->mmu_tt_quick[cpu->bus.laddr >> 24] != 0)) { - if (m68030_check_tt(cpu)) { + if (__predict_false(mmu_tt_quick[bus.laddr >> 24] != 0)) { + if (CheckTT()) { return true; } } // ATC からアドレスを取得 - if (cpu->mmu_tc.e) { - const m68030ATCLine& a = cpu->atc.fill_fetch(); + if (mmu_tc.e) { + const m68030ATCLine& a = atc.fill_fetch(); if (a.IsBusError()) { - DPRINTF(0, "translate_fetch: result BusErr (ATC:B)"); + MMULOG(3, 0, "%s: result BusErr (ATC:B)", __func__); return false; } - cpu->bus.paddr = a.GetPAddr(); - cpu->bus.paddr |= cpu->bus.laddr & cpu->mmu_tc.pgmask; - //cpu->bus.ci = a->ci; + bus.paddr = a.GetPAddr(); + bus.paddr |= bus.laddr & mmu_tc.pgmask; + //bus.ci = a->ci; } - DPRINTF(0, "translate_fetch: result $%08x", cpu->bus.paddr); + MMULOG(3, 0, "%s: result $%08x", __func__, bus.paddr); return true; } // リード用のアドレス変換を行う。 // 変換できれば true、バスエラーなら false を返す。 bool -m68030_mmu_translate_read(m68kcpu *cpu) +MPU680x0Device::TranslateRead() { // 状態表示 - DPRINTF(0, "translate_read: enter %c:$%08x pc=%c:$%08x", - (cpu->bus.GetFC() & 0x04) ? 'S' : 'U', - cpu->bus.laddr, - (cpu->reg.s) ? 'S' : 'U', - RegPPC); + MMULOG(3, 0, "%s: enter %c:$%08x pc=%c:$%08x", __func__, + (bus.GetFC() & 0x04) ? 'S' : 'U', + bus.laddr, + IsSuper() ? 'S' : 'U', + ppc); // まず TT をチェック - if (__predict_false(cpu->mmu_tt_quick[cpu->bus.laddr >> 24] != 0)) { - if (m68030_check_tt(cpu)) { + if (__predict_false(mmu_tt_quick[bus.laddr >> 24] != 0)) { + if (CheckTT()) { return true; } } // ATC からアドレスを取得 - if (cpu->mmu_tc.e) { - const m68030ATCLine& a = cpu->atc.fill_read(); + if (mmu_tc.e) { + const m68030ATCLine& a = atc.fill_read(); if (a.IsBusError()) { - DPRINTF(0, "translate_read: result BusErr (ATC:B)"); + MMULOG(3, 0, "%s: result BusErr (ATC:B)", __func__); return false; } - cpu->bus.paddr = a.GetPAddr(); - cpu->bus.paddr |= cpu->bus.laddr & cpu->mmu_tc.pgmask; - //cpu->bus.ci = a->ci; + bus.paddr = a.GetPAddr(); + bus.paddr |= bus.laddr & mmu_tc.pgmask; + //bus.ci = a->ci; } - DPRINTF(0, "translate_read: result $%08x", cpu->bus.paddr); + MMULOG(3, 0, "%s: result $%08x", __func__, bus.paddr); return true; } // ライト用のアドレス変換を行う。 // 変換できれば true、バスエラーなら false を返す。 bool -m68030_mmu_translate_write(m68kcpu *cpu) +MPU680x0Device::TranslateWrite() { // 状態表示 - DPRINTF(0, "translate_write: enter %c:$%08x pc=%c:$%08x", - (cpu->bus.GetFC() & 0x04) ? 'S' : 'U', - cpu->bus.laddr, - (cpu->reg.s) ? 'S' : 'U', - RegPPC); + MMULOG(3, 0, "%s: enter %c:$%08x pc=%c:$%08x", __func__, + (bus.GetFC() & 0x04) ? 'S' : 'U', + bus.laddr, + IsSuper() ? 'S' : 'U', + ppc); // まず TT をチェック - if (__predict_false(cpu->mmu_tt_quick[cpu->bus.laddr >> 24] != 0)) { - if (m68030_check_tt(cpu)) { + if (__predict_false(mmu_tt_quick[bus.laddr >> 24] != 0)) { + if (CheckTT()) { return true; } } // ATC からアドレスを取得 - if (cpu->mmu_tc.e) { - const m68030ATCLine& a = cpu->atc.fill_write(); + if (mmu_tc.e) { + const m68030ATCLine& a = atc.fill_write(); if (a.IsBusError()) { - DPRINTF(0, "translate_write: result BusErr (ATC:B)"); + MMULOG(3, 0, "%s: result BusErr (ATC:B)", __func__); return false; } if (a.IsWProtect()) { // Write アクセスで WP が立ってたらバスエラー - DPRINTF(0, "translate_write: result Buserr (ATC:WP)"); + MMULOG(3, 0, "%s: result Buserr (ATC:WP)", __func__); return false; } - cpu->bus.paddr = a.GetPAddr(); - cpu->bus.paddr |= cpu->bus.laddr & cpu->mmu_tc.pgmask; - //cpu->bus.ci = a->ci; + bus.paddr = a.GetPAddr(); + bus.paddr |= bus.laddr & mmu_tc.pgmask; + //bus.ci = a->ci; } - DPRINTF(0, "translate_write: result $%08x", cpu->bus.paddr); + MMULOG(3, 0, "%s: result $%08x", __func__, bus.paddr); return true; } @@ -1726,73 +1721,75 @@ m68030_mmu_translate_write(m68kcpu *cpu) // サーチまで行う。false ならテーブルサーチせずに帰る。 // 変換できれば対応する物理アドレスを、バスエラーなら (uint64)-1 を返す。 uint64 -m68030_mmu_translate_peek(m68kcpu *cpu, uint32 laddr, uint fc, bool do_search) +MPU680x0Device::TranslatePeek(uint32 laddr, uint fc, bool do_search) { uint64 paddr; // 状態表示 - DPRINTF(0, "translate_peek: enter %c:$%08x", + MMULOG(3, 0, "%s: enter %c:$%08x", __func__, (fc & 0x04) ? 'S' : 'U', laddr); // まず TT をチェック paddr = laddr; - if (__predict_false(cpu->mmu_tt_quick[laddr >> 24] != 0)) { - uint64 ssw_laddr = ((uint64)(SSW_BUS_R | fc) << 32) | laddr; + if (__predict_false(mmu_tt_quick[laddr >> 24] != 0)) { + uint64 ssw_laddr = ((uint64)(M68K::SSW_BUS_R | fc) << 32) | laddr; for (int i = 0; i < 2; i++) { - m68030TT& tt = cpu->mmu_tt[i]; + m68030TT& tt = mmu_tt[i]; if (tt.Match(ssw_laddr)) { // 一致した。 // CI はここでは使わないため、一致したことだけわかればよい - DPRINTF(0, "translate_peek: result $%08x (match tt)", - (uint32)paddr); + MMULOG(3, 0, "%s: result $%08x (match tt)", + __func__, (uint32)paddr); return paddr; } } } // 次に TC (ATC と MMU テーブルサーチ)。 - if (cpu->mmu_tc.e) { + if (mmu_tc.e) { // ATC からこっそりアドレスを取得 - paddr = cpu->atc.fill_peek(laddr & cpu->mmu_tc.lmask, fc); - if ((int64)paddr >= 0) { - paddr |= laddr & cpu->mmu_tc.pgmask; - DPRINTF(0, "translate_peek: result $%08x (match atc)", - (uint32)paddr); + const m68030ATCLine *a; + a = atc.fill_peek(laddr & mmu_tc.lmask, fc); + if (a != NULL) { + paddr = a->GetPAddr(); + paddr |= laddr & mmu_tc.pgmask; + MMULOG(3, 0, "%s: result $%08x (match atc)", + __func__, (uint32)paddr); return paddr; } // 見付からなかったのでこっそりテーブルサーチ。 if (do_search) { - // mmu.search() はさすがに cpu->bus を使うので事前に退避。 - m68kbus backup = cpu->bus; + // mmu.search() はさすがに bus を使うので事前に退避。 + m68kbus backup = bus; - cpu->bus.laddr = laddr; - cpu->bus.ssw = fc; - m68030MMU mmu(cpu); + bus.laddr = laddr; + bus.ssw = fc; + m68030MMU mmu(this); mmu.m_debugger = true; - mmu.search(); + mmu.search(7); paddr = mmu.make_atc_debugger(); // リストア - cpu->bus = backup; + bus = backup; if ((int64)paddr >= 0) { - paddr |= laddr & cpu->mmu_tc.pgmask; - DPRINTF(0, "translate_peek: result $%08x (table search)", - (uint32)paddr); + paddr |= laddr & mmu_tc.pgmask; + MMULOG(3, 0, "%s: result $%08x (table search)", + __func__, (uint32)paddr); return paddr; } } // TC が有効だがヒットしなかったらバスエラー - DPRINTF(0, "translate_peek: result BusErr"); + MMULOG(3, 0, "%s: result BusErr", __func__); return (uint64)-1; } // TT が無効かまたは有効でもヒットせず、 // かつ TC も無効だったら (こっちは「有効でヒットせず」は含まない)、 // 論理アドレスをそのまま返す - DPRINTF(0, "translate_peek: result $%08x (no translation)", laddr); + MMULOG(3, 0, "%s: result $%08x (no translation)", __func__, laddr); return laddr; } @@ -1800,185 +1797,190 @@ m68030_mmu_translate_peek(m68kcpu *cpu, // 命令 // -// MMU 命令からの呼び出し用。 -// XXX 命令によってこの中のいずれかの EA に対応していないものがあれば調査。 -// XXX クロック未調査。 -static inline uint32 -cea_mmu(m68kcpu *cpu) -{ - uint mm = eamode(RegIR); - uint rr = eanum(RegIR); - - switch (mm) { - case 0: // Dn - case 1: // An - break; - - case 2: // (An) - return cea_anin(cpu, rr); - - case 3: // (An)+ - case 4: // -(An) - break; - - case 5: // d16(An) - return cea_andi(cpu, rr); - case 6: // (An,IX) - return cea_anix(cpu, rr); - case 7: - switch (rr) { - case 0: // Abs.W - return cea_absw(cpu); - case 1: // Abs.L - return cea_absl(cpu); - default: - break; - } - break; - } - // 不当命令 - throw M68K_EXCEP_ILLEGAL; -} - -// RegIR2 の下位5ビットから FC を返す。 +// ir2 の下位5ビットから FC を返す。 // 不当パターンなら C++ の例外をスローする。 -static uint32 -get_fc(m68kcpu *cpu) +uint32 +MPU680x0Device::get_fc() { - uint fcfield = RegIR2 & 0x1f; + uint fcfield = ir2 & 0x1f; if (fcfield == 0) { - return RegSFC; + return reg.sfc; } else if (fcfield == 1) { - return RegDFC; + return reg.dfc; } else if (fcfield < 0x08) { // break; } else if (fcfield < 0x10) { - return RegD(fcfield & 7) & 7; + return reg.D[fcfield & 7] & 7; } else if (fcfield < 0x18) { return fcfield & 7; } // 不当命令 - throw M68K_EXCEP_ILLEGAL; + throw M68K::EXCEP_ILLEGAL; } // MMU 不当命令 (2ワード目で確定) -static void -mmu_op_illg2(m68kcpu *cpu) +void +MPU680x0Device::mmu_op_illg2() { - cpulog(1, "MMU 不当命令 %04x %04x", RegIR, RegIR2); - m68030_exception(cpu, M68K_EXCEP_FLINE); + putlog(1, "MMU illegal instruction %04x %04x", ir, ir2); + Exception(M68K::EXCEP_FLINE); } // PFLUSH fc,#mask 命令 (EA を持たない方) void -mmu_op_pflush(m68kcpu *cpu) +MPU680x0Device::mmu_op_pflush() { uint32 mask; uint32 fc; - mask = (RegIR2 >> 5) & 7; - fc = get_fc(cpu); - cpu->atc.flush(fc, mask); + mask = (ir2 >> 5) & 7; + fc = get_fc(); + atc.flush(fc, mask); } // PFLUSH fc,#mask,ea 命令 void -mmu_op_pflush_ea(m68kcpu *cpu) +MPU680x0Device::mmu_op_pflush_ea() { uint32 ea; uint32 mask; uint32 fc; - ea = cea_mmu(cpu); - mask = (RegIR2 >> 5) & 7; - fc = get_fc(cpu); - cpu->atc.flush(fc, mask, ea); + ea = cea_copro(); + mask = (ir2 >> 5) & 7; + fc = get_fc(); + atc.flush(fc, mask, ea); } // PLOADR/PLOADW 命令 void -mmu_op_pload(m68kcpu *cpu) +MPU680x0Device::mmu_op_pload() { uint32 ea; uint32 fc; - if ((RegIR2 & 0x01e0) != 0) { - mmu_op_illg2(cpu); + if ((ir2 & 0x01e0) != 0) { + mmu_op_illg2(); return; } - ea = cea_mmu(cpu); - fc = get_fc(cpu); + ea = cea_copro(); + fc = get_fc(); // 実効アドレスを論理アドレスとする ATC を作成。 // ってこれでいいのか? - cpu->bus.laddr = ea; - if ((RegIR2 & 0x0200)) { - cpu->bus.ssw = SSW_BUS_R | fc; - cpu->atc.fill_read(); + bus.laddr = ea; + if ((ir2 & 0x0200)) { + bus.ssw = M68K::SSW_BUS_R | fc; + atc.fill_read(); } else { - cpu->bus.ssw = SSW_BUS_W | fc; - cpu->atc.fill_write(); + bus.ssw = M68K::SSW_BUS_W | fc; + atc.fill_write(); } } // PTESTR/PTESTW 命令 -// XXX てきとー void -mmu_op_ptest(m68kcpu *cpu) +MPU680x0Device::mmu_op_ptest() { - uint level = (RegIR2 >> 10) & 7; - uint rw = (RegIR2 & 0x0200) ? SSW_BUS_R : SSW_BUS_W; + uint level = (ir2 >> 10) & 7; + uint rw = (ir2 & 0x0200) ? M68K::SSW_BUS_R : M68K::SSW_BUS_W; + uint rn = (ir2 >> 5) & 0xf; uint32 ea; uint32 fc; uint16 mmusr; - ea = cea_mmu(cpu); - fc = get_fc(cpu); + ea = cea_copro(); + fc = get_fc(); + mmusr = 0; if (level == 0) { - cpulog(0, "未実装 PTEST %04x %04x", RegIR, RegIR2); - return; - } - if ((RegIR2 & 0x0100)) { - cpulog(0, "未実装 PTEST %04x %04x", RegIR, RegIR2); - return; - } + if (rn != 0) { + // レベル0で An 指定なら Fライン例外 + mmu_op_illg2(); + return; + } - // テーブルサーチを実行 - cpu->bus.laddr = ea; - cpu->bus.ssw = rw | fc; - m68030MMU table(cpu); - table.m_ptest = true; - table.search(); - // 結果を MMUSR に格納 - mmusr = 0; - if (level == 0) { - if (table.m_type == INVALID) { - mmusr |= m68030MMUSR::B; + // laddr は IS と PS のところを落としたもの。 + // ssw_laddr のアドレスは IS を落とさないもの (PSは不問)。 + uint64 ssw_laddr = ((uint64)(rw | fc) << 32) | ea; + uint32 laddr = ea & mmu_tc.lmask; + + const m68030ATCLine *a = atc.fill_peek(laddr, fc); + if (a) { + // ATC に見付かった。Modified の状態を返す。 + if (a->IsBusError()) { + mmusr |= m68030MMUSR::B; + } else if (a->IsWProtect()) { + mmusr |= m68030MMUSR::W; + } 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 = ea; + bus.ssw = rw | fc; + m68030MMU table(this); + table.m_ptest = true; + table.search(level); + if (table.m_berr) { mmusr |= m68030MMUSR::B; } + if (table.m_l) { + // XXX not tested + mmusr |= m68030MMUSR::L; + } + if (table.m_s && (fc & 4)) { + // XXX not tested + mmusr |= m68030MMUSR::S; + } + if (table.m_wp) { + mmusr |= m68030MMUSR::W; + } + if (table.m_type == INVALID) { + mmusr |= m68030MMUSR::I; + } + if (table.m_m) { + mmusr |= m68030MMUSR::M; + } + // N を求める。 + // もともとそういう風には出来ていないので逆算している。 + int n; + if (mmu_tc.fcl) { + // たぶんファンクションテーブルで1段消費する? (未確認) + if (table.x == FCL) { + n = 1; + } else { + n = table.x + 2; + } + } else { + n = table.x + 1; + } + mmusr |= n; + + // アドレスレジスタ指定があれば、最後のディスクリプタアドレスを返す。 + // rn は有効なら 8-15。 + if (rn != 0) { + // XXX 途中でバスエラーが起きるケースは未実装 + if (table.m_berr == false) { + reg.R[rn] = table.m_lastaddr; + } + } } - if (table.m_l) { - mmusr |= m68030MMUSR::L; - } - if (table.m_s && (fc & 4)) { - mmusr |= m68030MMUSR::S; - } - if (table.m_wp) { - mmusr |= m68030MMUSR::W; - } - if (table.m_type == INVALID) { - mmusr |= m68030MMUSR::I; - } - if (table.m_m) { - mmusr |= m68030MMUSR::M; - } - // XXX N 未実装 - mmusr |= 1; - cpu->SetMMUSR(mmusr); + SetMMUSR(mmusr); }