--- nono/m680x0/m68030mmu.cpp 2026/04/29 17:04:28 1.1.1.1 +++ nono/m680x0/m68030mmu.cpp 2026/04/29 17:05:08 1.1.1.6 @@ -1,10 +1,9 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "header.h" -#include "mystring.h" #include "m68030core.h" #include "m68030bus.h" #include "m68030ea.h" @@ -29,8 +28,8 @@ // ディスクリプタ #define MMU_DESC_S (0x00000100) #define MMU_DESC_CI (0x00000040) -#define MMU_DESC_M (0x00000010) -#define MMU_DESC_U (0x00000008) +#define MMU_DESC_M (0x00000010) +#define MMU_DESC_U (0x00000008) #define MMU_DESC_WP (0x00000004) #define MMU_DESC_DT (0x00000003) @@ -46,54 +45,41 @@ 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); -} +// MMU デバッグモードなら ATC エントリ a を表示 +#define FILL_PRINT(fc, a) cpulogf(3, [&] { return (a)->fill_print(fc); }) -// IF_DEBUG 中だと分かってるところではこっちを使う -static void IPRINTF(int indent, const char *fmt, ...)__printflike(2, 3); -static void -IPRINTF(int indent, const char *fmt, ...) +// インデント付き 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...) \ + cpulogf(lv, [&] { return indent_format(indent, arg); }) // テーブルサーチ操作クラス -class m68030MMU +class m68030MMU final { public: m68030MMU(m68kcpu *); - virtual ~m68030MMU() { } + ~m68030MMU() { } // サーチを実行 void search(); @@ -273,10 +259,11 @@ m68kcpu::SetTT(int n, uint32 data) mmu_enable_changed(); } -// アクセスがこの TT と一致するか調べる。 +// アクセスがこの TT とマッチするか調べる。 // ssw_laddr は上位32ビットが cpu->bus.ssw、下位32ビットが論理アドレス // (下24ビットは呼び出し側でマスクしなくてよい)。 -// 一致すれば true を返す。 +// この TT が有効でアドレスがマッチすれば true を返す。 +// (TT:E は演算に織り込んである、このすぐ上の SetTT() のコメント参照) bool m68030TT::Match(uint64 ssw_laddr) { @@ -705,8 +692,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 +709,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 +740,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; } @@ -807,7 +794,7 @@ m68030ATC::fill_peek(uint32 laddr, uint if (a->GetTag() == laddr) { // ヒットした uint32 paddr = a->GetPAddr(); - DPRINTF(1, "fill_peek: match %d:$%08x -> $%08x", + MMULOG(3, 1, "fill_peek: match %d:$%08x -> $%08x", fc, laddr, paddr); return paddr; } @@ -819,7 +806,7 @@ m68030ATC::fill_peek(uint32 laddr, uint if ((a->GetTag() == laddr) && ((a->fc & 4) == fc)) { // ヒットした uint32 paddr = a->GetPAddr(); - DPRINTF(1, "fill_peek: match %d:$%08x -> $%08x", + MMULOG(3, 1, "fill_peek: match %d:$%08x -> $%08x", a->fc, laddr, paddr); return paddr; } @@ -832,11 +819,12 @@ m68030ATC::fill_peek(uint32 laddr, uint // デバッグ表示。 // 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 +833,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 +858,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 +867,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 +881,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 +891,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; @@ -1073,10 +1063,10 @@ m68030MMU::search() } m_lv[FCL] = m_fc; - IF_DEBUG { + cpulogf(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 +1078,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,7 +1100,7 @@ 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_PAGE: // ページディスクリプタ m_type = EARLY; @@ -1127,7 +1117,7 @@ m68030MMU::search() // 必要ならファンクションコード・ルックアップ if (cpu->mmu_tc.fcl) { x = FCL; - DPRINTF(2, "search: %s tableaddr=$%08x idx=$%x", + MMULOG(3, 2, "search: %s tableaddr=$%08x idx=$%x", m_lvname[x], m_desc2, m_lv[x]); // ディスクリプタをフェッチ @@ -1169,7 +1159,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]); // ディスクリプタをフェッチ @@ -1264,7 +1254,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); // バスエラー発生フラグをクリアしておく @@ -1309,7 +1299,7 @@ m68030MMU::fetch_desc(uint32 tableaddr, // 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 +1338,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 +1371,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,7 +1392,7 @@ 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(); @@ -1466,7 +1456,7 @@ m68030MMU::phys_read_32(uint32 addr) // 物理バスアクセス cpu->bus.ssw = SSW_BUS_R | FC_SD; - data = m68030_phys_read_32(addr); + data = vm_phys_read_32(addr); if ((int64)data < 0) return data; @@ -1488,7 +1478,7 @@ m68030MMU::phys_write_32(uint32 addr, ui // 物理バスアクセス cpu->bus.ssw = SSW_BUS_W | FC_SD; - berr = m68030_phys_write_32(addr, data); + berr = vm_phys_write_32(addr, data); if ((int64)berr < 0) return berr; @@ -1505,7 +1495,7 @@ m68030MMU::phys_peek_32(uint32 addr) uint32 data; // 1バイト目はバスエラーを検査。 - peekdata = m68030_phys_peek_8(addr); + peekdata = vm_phys_peek_8(addr); if ((int64)peekdata < 0) { // バスエラー return -1; @@ -1514,9 +1504,9 @@ m68030MMU::phys_peek_32(uint32 addr) // ここはロングワード境界でアクセスしてるはずなので、 // 先頭バイト以外のバスエラーチェックは不要。 - data |= m68030_phys_peek_8(addr + 1) << 16; - data |= m68030_phys_peek_8(addr + 2) << 8; - data |= m68030_phys_peek_8(addr + 3); + 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; } @@ -1554,7 +1544,7 @@ m68030MMU::dtname(uint32 dt) static char buf[32]; switch (dt) { case DT_INVALID: return "invalid"; - case DT_PAGE: return "page"; + case DT_PAGE: return "page"; case DT_VALID4: return "valid4"; case DT_VALID8: return "valid8"; } @@ -1592,7 +1582,7 @@ m68030_check_tt(m68kcpu *cpu) // 一致した match += i + 1; ci |= tt.ci != 0; - DPRINTF(1, "check_tt: TT%d match", i); + MMULOG(3, 1, "check_tt: TT%d match", i); } } @@ -1601,7 +1591,7 @@ m68030_check_tt(m68kcpu *cpu) // 一致したら PA <- LA if (match != 0) { - DPRINTF(0, "check_tt: result %08x (match TT%s)", + MMULOG(3, 0, "check_tt: result %08x (match TT%s)", cpu->bus.laddr, (match == 3) ? "0 and TT1" : ((match == 1) ? "0" : "1")); return true; @@ -1615,7 +1605,7 @@ bool m68030_mmu_translate_fetch(m68kcpu *cpu) { // 状態表示 - DPRINTF(0, "translate_fetch: enter %c:$%08x pc=$%08x", + MMULOG(3, 0, "translate_fetch: enter %c:$%08x pc=$%08x", (cpu->bus.GetFC() & 0x04) ? 'S' : 'U', cpu->bus.laddr, RegPPC); @@ -1631,7 +1621,7 @@ m68030_mmu_translate_fetch(m68kcpu *cpu) if (cpu->mmu_tc.e) { const m68030ATCLine& a = cpu->atc.fill_fetch(); if (a.IsBusError()) { - DPRINTF(0, "translate_fetch: result BusErr (ATC:B)"); + MMULOG(3, 0, "translate_fetch: result BusErr (ATC:B)"); return false; } @@ -1640,7 +1630,7 @@ m68030_mmu_translate_fetch(m68kcpu *cpu) //cpu->bus.ci = a->ci; } - DPRINTF(0, "translate_fetch: result $%08x", cpu->bus.paddr); + MMULOG(3, 0, "translate_fetch: result $%08x", cpu->bus.paddr); return true; } @@ -1650,7 +1640,7 @@ bool m68030_mmu_translate_read(m68kcpu *cpu) { // 状態表示 - DPRINTF(0, "translate_read: enter %c:$%08x pc=%c:$%08x", + MMULOG(3, 0, "translate_read: enter %c:$%08x pc=%c:$%08x", (cpu->bus.GetFC() & 0x04) ? 'S' : 'U', cpu->bus.laddr, (cpu->reg.s) ? 'S' : 'U', @@ -1667,7 +1657,7 @@ m68030_mmu_translate_read(m68kcpu *cpu) if (cpu->mmu_tc.e) { const m68030ATCLine& a = cpu->atc.fill_read(); if (a.IsBusError()) { - DPRINTF(0, "translate_read: result BusErr (ATC:B)"); + MMULOG(3, 0, "translate_read: result BusErr (ATC:B)"); return false; } @@ -1676,7 +1666,7 @@ m68030_mmu_translate_read(m68kcpu *cpu) //cpu->bus.ci = a->ci; } - DPRINTF(0, "translate_read: result $%08x", cpu->bus.paddr); + MMULOG(3, 0, "translate_read: result $%08x", cpu->bus.paddr); return true; } @@ -1686,7 +1676,7 @@ bool m68030_mmu_translate_write(m68kcpu *cpu) { // 状態表示 - DPRINTF(0, "translate_write: enter %c:$%08x pc=%c:$%08x", + MMULOG(3, 0, "translate_write: enter %c:$%08x pc=%c:$%08x", (cpu->bus.GetFC() & 0x04) ? 'S' : 'U', cpu->bus.laddr, (cpu->reg.s) ? 'S' : 'U', @@ -1703,12 +1693,12 @@ m68030_mmu_translate_write(m68kcpu *cpu) if (cpu->mmu_tc.e) { const m68030ATCLine& a = cpu->atc.fill_write(); if (a.IsBusError()) { - DPRINTF(0, "translate_write: result BusErr (ATC:B)"); + MMULOG(3, 0, "translate_write: result BusErr (ATC:B)"); return false; } if (a.IsWProtect()) { // Write アクセスで WP が立ってたらバスエラー - DPRINTF(0, "translate_write: result Buserr (ATC:WP)"); + MMULOG(3, 0, "translate_write: result Buserr (ATC:WP)"); return false; } @@ -1717,7 +1707,7 @@ m68030_mmu_translate_write(m68kcpu *cpu) //cpu->bus.ci = a->ci; } - DPRINTF(0, "translate_write: result $%08x", cpu->bus.paddr); + MMULOG(3, 0, "translate_write: result $%08x", cpu->bus.paddr); return true; } @@ -1731,21 +1721,23 @@ m68030_mmu_translate_peek(m68kcpu *cpu, uint64 paddr; // 状態表示 - DPRINTF(0, "translate_peek: enter %c:$%08x", + MMULOG(3, 0, "translate_peek: enter %c:$%08x", (fc & 0x04) ? 'S' : 'U', laddr); // まず TT をチェック paddr = laddr; - for (int i = 0; i < 2; i++) { - m68030TT& tt = cpu->mmu_tt[i]; - uint64 ssw = (uint64)(SSW_BUS_R | fc) << 32; - if (tt.Match(ssw | laddr)) { - // 一致した。 - // CI はここでは使わないため、一致したことだけわかればよい - DPRINTF(0, "translate_peek: result $%08x (match tt)", - (uint32)paddr); - return paddr; + if (__predict_false(cpu->mmu_tt_quick[laddr >> 24] != 0)) { + uint64 ssw_laddr = ((uint64)(SSW_BUS_R | fc) << 32) | laddr; + for (int i = 0; i < 2; i++) { + m68030TT& tt = cpu->mmu_tt[i]; + if (tt.Match(ssw_laddr)) { + // 一致した。 + // CI はここでは使わないため、一致したことだけわかればよい + MMULOG(3, 0, "translate_peek: result $%08x (match tt)", + (uint32)paddr); + return paddr; + } } } @@ -1755,7 +1747,7 @@ m68030_mmu_translate_peek(m68kcpu *cpu, 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)", + MMULOG(3, 0, "translate_peek: result $%08x (match atc)", (uint32)paddr); return paddr; } @@ -1776,15 +1768,22 @@ m68030_mmu_translate_peek(m68kcpu *cpu, if ((int64)paddr >= 0) { paddr |= laddr & cpu->mmu_tc.pgmask; - DPRINTF(0, "translate_peek: result $%08x (table search)", + MMULOG(3, 0, "translate_peek: result $%08x (table search)", (uint32)paddr); return paddr; } } + + // TC が有効だがヒットしなかったらバスエラー + MMULOG(3, 0, "translate_peek: result BusErr"); + return (uint64)-1; } - DPRINTF(0, "translate_peek: result BusErr"); - return (uint64)-1; + // TT が無効かまたは有効でもヒットせず、 + // かつ TC も無効だったら (こっちは「有効でヒットせず」は含まない)、 + // 論理アドレスをそのまま返す + MMULOG(3, 0, "translate_peek: result $%08x (no translation)", laddr); + return laddr; } // @@ -1854,7 +1853,7 @@ get_fc(m68kcpu *cpu) } // MMU 不当命令 (2ワード目で確定) -void +static void mmu_op_illg2(m68kcpu *cpu) { cpulog(1, "MMU 不当命令 %04x %04x", RegIR, RegIR2);