|
|
1.1 root 1: //
2: // nono
1.1.1.3 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.8 root 7: #include "mpu680x0.h"
1.1 root 8: #include "m68030mmu.h"
1.1.1.8 root 9: #include "mainbus.h"
1.1 root 10:
11: // ディスクリプタタイプ
1.1.1.15! root 12: #define GET_DT(x) ((x) & 0x03)
1.1 root 13: #define DT_INVALID (0)
14: #define DT_PAGE (1)
15: #define DT_VALID4 (2)
16: #define DT_VALID8 (3)
17:
18: //
19: // デバッグ表示
20: //
21:
1.1.1.6 root 22: // インデント付き string_format
23: static std::string indent_format(int indent, const char *fmt, ...)
24: __printflike(2, 3);
25: static std::string
26: indent_format(int indent, const char *fmt, ...)
1.1 root 27: {
28: va_list ap;
1.1.1.6 root 29: char *buf;
1.1 root 30:
31: va_start(ap, fmt);
1.1.1.6 root 32: vasprintf(&buf, fmt, ap);
1.1 root 33: va_end(ap);
34:
1.1.1.6 root 35: std::string rv(indent, ' ');
36: rv += buf;
37: free(buf);
1.1 root 38:
1.1.1.6 root 39: return rv;
40: }
1.1 root 41:
1.1.1.9 root 42: #define MMULOG(lv, indent, arg...) do { \
43: if (__predict_false(loglevel >= (lv))) { \
44: cpu->putlogf(lv, [&, lam_func = __func__] { \
45: (void)lam_func; \
46: return indent_format(indent, arg); \
47: }); \
48: } \
49: } while (0)
1.1 root 50:
1.1.1.15! root 51:
! 52: //
! 53: // ページテーブル操作クラスはヘッダにいらないのでここで定義する。
! 54: //
! 55: class m68030PageTableOp
1.1 root 56: {
1.1.1.15! root 57: static constexpr uint32 PAGEDESC_S = 0x00000100;
! 58: static constexpr uint32 PAGEDESC_CI = 0x00000040;
! 59: static constexpr uint32 PAGEDESC_M = 0x00000010;
! 60: static constexpr uint32 PAGEDESC_U = 0x00000008;
! 61: static constexpr uint32 PAGEDESC_WP = 0x00000004;
! 62: static constexpr uint32 PAGEDESC_DT = 0x00000003;
! 63:
1.1 root 64: public:
1.1.1.15! root 65: // TIA 〜 TID で最大4階層。
! 66: // LV_FCL を引くのはデバッグ表示だけなので番兵より後ろ。
! 67: static constexpr int LV_TIA = 0;
! 68: static constexpr int LV_TIB = 1;
! 69: static constexpr int LV_TIC = 2;
! 70: static constexpr int LV_TID = 3;
! 71: static constexpr int MAXLV = 4;
! 72: static constexpr int LV_FCL = 5;
! 73:
! 74: // サーチ結果。
! 75: enum Result {
! 76: Normal, // 正常に解決できた。
! 77: Early, // アーリーターミネーション。
! 78: Indirect, // 間接ディスクリプタ。
! 79: Invalid, // 無効エントリに当たった。
! 80: };
! 81:
! 82: // 動作モード。整数比較もする。
! 83: enum From {
! 84: ByTranslation = 0, // アドレス変換中のテーブルサーチ。(通常)
! 85: ByPTEST = 1, // PTEST* 命令によるテーブルサーチ。
! 86: ByDebugger = -1, // デバッガによるテーブルサーチ。
! 87: };
1.1 root 88:
1.1.1.15! root 89: public:
! 90: m68030PageTableOp(MPU68030Device *, int loglevel_, m68030PageTableOp::From);
1.1 root 91:
1.1.1.15! root 92: // ページテーブルから laddr を検索する。
! 93: Result Search(int maxlv, busaddr laddr);
1.1 root 94:
1.1.1.15! root 95: // ページテーブルから laddr に対応する物理アドレスを調べる。デバッガ専用。
! 96: busaddr SearchByDebugger(busaddr laddr);
1.1 root 97:
1.1.1.15! root 98: // 検索結果から a を埋める。
! 99: void MakeATC(m68030ATC&, m68030ATCLine *a) const;
1.1 root 100:
1.1.1.15! root 101: private:
! 102: // モード判定。
! 103: bool IsDebugger() const noexcept { return (from < 0); }
! 104: bool IsPTEST() const noexcept { return (from > 0); }
! 105:
! 106: Result DoSearch(int maxlv, const busaddr laddr);
! 107:
! 108: // ディスクリプタをフェッチする。
! 109: inline bool FETCH_DESC(uint32 addr, uint idx)
! 110: {
! 111: if (__predict_false(IsDebugger())) {
! 112: return PeekDesc(addr, idx);
! 113: } else {
! 114: return FetchDesc(addr, idx);
! 115: }
! 116: }
! 117: bool FetchDesc(uint32 addr, uint idx);
! 118: bool PeekDesc(uint32 addr, uint idx);
! 119: bool CheckLimit() const;
! 120: uint32 AssembleEarlyRemains(uint32 addr) const;
1.1 root 121:
122: // ディスクリプタを表示用に (デバッグ用)
1.1.1.15! root 123: std::string sprintf_desc() const;
1.1 root 124:
125: // ディスクリプタタイプ文字列 (デバッグ用)
1.1.1.15! root 126: static const char *dtname(uint32 dt);
1.1 root 127:
128: public:
1.1.1.15! root 129: Result result {};
! 130: busaddr m_laddr {}; // 論理アドレス (入力)
! 131: busaddr m_paddr {}; // 物理アドレス
! 132: bool m_berr {}; // B: ディスクリプタフェッチでバスエラー (PTEST)
! 133: bool m_lim {}; // L: リミット
! 134: bool m_s {};
! 135: bool m_ci {};
! 136: bool m_wp {};
! 137: bool m_m {};
! 138: int m_n {}; // N: 検索回数
! 139: // 最後にアクセスしたディスクリプタアドレス (PTEST)
! 140: uint32 m_lastaddr {};
! 141:
1.1.1.8 root 142: private:
1.1.1.15! root 143: // laddr の各階層ごとの値。これがテーブルを引く際のインデックス。
! 144: std::array<int, MAXLV> m_idx {};
1.1 root 145:
1.1.1.15! root 146: int lv {}; // 現在位置(LV_*)
1.1 root 147:
1.1.1.15! root 148: // ディスクリプタにはショート(4バイト)とロング(8バイト)があり、
! 149: // どちらを使うかは前段の DT で決まる (それが m_descsize)。
! 150: // ロングディスクリプタは 1 ロングワード目がフラグ、2ロングワード目が
! 151: // アドレスとなっている。ショートディスクリプタは S ビットを除いて
! 152: // このフラグ部とアドレス部を OR しただけの形をしている。
! 153: // そのため、ロングディスクリプタなら m_desc1 に1語目(フラグ部)、
! 154: // m_desc2 に2語目(アドレス部) を置くが、ショートディスクリプタなら
! 155: // m_desc1, m_desc2 のどちらにも同じものを置けば (どうせ使う時に
! 156: // 必要なビットだけにマスクするので) そのまま使える。
! 157: uint32 m_desc1 {};
! 158: uint32 m_desc2 {};
! 159: uint m_descsize {}; // ディスクリプタサイズ (4 or 8)
! 160:
! 161: From from {}; // 動作モード
! 162:
! 163: // M ビットを更新するかどうか。
! 164: // PTEST でない(=通常サーチの) 書き込みアクセスなら true になる。
! 165: bool writeback_m {};
1.1.1.9 root 166:
1.1.1.15! root 167: int loglevel {};
1.1 root 168:
1.1.1.15! root 169: MPU68030Device *cpu {};
1.1 root 170:
1.1.1.15! root 171: static const char * const m_lvname[LV_FCL + 1];
! 172: };
1.1 root 173:
174:
175: //
1.1.1.15! root 176: // アドレス変換。
1.1 root 177: //
178:
1.1.1.15! root 179: // リード/フェッチ用のアドレス変換を行う。
! 180: // 変換後の物理アドレス、もしくは BusErr を返す。
! 181: busaddr
! 182: MPU68030Device::TranslateRead(const busaddr laddr)
1.1 root 183: {
1.1.1.15! root 184: m68030ATCLine *a;
1.1 root 185:
1.1.1.15! root 186: uint32 fc = laddr.GetFC();
! 187: putlog(3, " %s: enter %u(%s).%08x pc=%c.%08x", __func__,
! 188: fc, FCstr(fc),
! 189: laddr.Addr(),
! 190: (IsSuper() ? 'S' : 'U'),
! 191: ppc);
1.1 root 192:
1.1.1.15! root 193: #if defined(M68030_CUSTOM_ATC)
! 194: auto table = atc.GetTable(fc);
! 195: uint8 n = table->hash[laddr.Addr() >> 12];
! 196: if (__predict_true((int8)n >= 0)) {
! 197: // ATC #(n)
! 198: table->stat.total++;
! 199: a = &table->line[n];
! 200: } else if (n == m68030ATCTable_Custom::HASH_NONE) {
! 201: if (__predict_true(mmu_tc.e)) {
! 202: // TC.E 有効で対応なしなら、テーブルサーチ。
! 203: table->stat.total++;
! 204: table->stat.miss++;
! 205: a = table->Pop();
! 206: PageTableSearch(laddr, a);
! 207: } else {
! 208: // TC.E 無効。(total もカウントしない)
! 209: return laddr;
! 210: }
! 211: } else if (n == m68030ATCTable_Custom::HASH_TT) {
! 212: // TT。TTMatch() で外れたかどうかはカウントしない。ほぼ 0 なので。
! 213: table->stat.total++;
! 214: table->stat.tthit++;
! 215: return TTMatch(laddr);
! 216: } else {
! 217: // PS<4KB でマッチしたので再検査。
! 218: table->stat.total++;
! 219: a = table->FindSub(laddr);
! 220: if (a == NULL) {
! 221: table->stat.miss++;
! 222: a = table->Pop();
! 223: PageTableSearch(laddr, a);
1.1.1.10 root 224: }
225: }
1.1.1.15! root 226: table->MakeHash(a);
! 227: table->MoveHead(a);
! 228: #else
! 229: // TT は TC.E とは独立しているので先に調べる。
! 230: busaddr ttaddr = TTMatch(laddr);
! 231: if (__predict_false(ttaddr.IsOK())) {
! 232: putlog(3, " %s: result %08x CI=%u (TT)", __func__,
! 233: ttaddr.Addr(), ttaddr.IsCInhibit() ? 1 : 0);
! 234: return ttaddr;
! 235: }
! 236: // TC.E 無効なら PA=VA。
! 237: if (__predict_false(mmu_tc.e == false)) {
! 238: putlog(3, " %s: result %08x (No translation)", __func__, laddr.Addr());
! 239: return laddr;
! 240: }
! 241: // ATC を検索。なければテーブルサーチ。
! 242: auto table = atc.GetTable();
! 243: a = table->LookupAndMove(laddr);
! 244: if (__predict_false(a == NULL)) {
! 245: putlog(4, " ATC not match");
! 246: a = table->Pop();
! 247: PageTableSearch(laddr, a);
1.1 root 248: }
1.1.1.15! root 249: #endif
1.1 root 250:
1.1.1.15! root 251: putlog(4, " ATC match %s", table->LineToStr(a).c_str());
1.1 root 252:
1.1.1.15! root 253: if (__predict_false(a->IsBusError())) {
! 254: putlog(3, " %s: result BusErr", __func__);
! 255: return BusAddr::BusErr;
1.1 root 256: }
1.1.1.15! root 257: busaddr paddr = laddr;
! 258: paddr.ChangeAddr(a->GetPAddr() | (laddr.Addr() & mmu_tc.pgmask));
! 259: if (__predict_false(a->IsCInhibit())) {
! 260: paddr |= BusAddr::CInhibit;
! 261: }
! 262: putlog(3, " %s: result %08x CI=%u", __func__,
! 263: paddr.Addr(), paddr.IsCInhibit() ? 1 : 0);
! 264: return paddr;
1.1 root 265: }
266:
1.1.1.15! root 267: // ライト用のアドレス変換を行う。
! 268: // 変換後の物理アドレス、もしくは BusErr を返す。
! 269: busaddr
! 270: MPU68030Device::TranslateWrite(const busaddr laddr)
1.1 root 271: {
1.1.1.15! root 272: m68030ATCLine *a;
1.1 root 273:
1.1.1.15! root 274: uint32 fc = laddr.GetFC();
! 275: putlog(3, " %s: enter %u(%s).%08x pc=%c.%08x", __func__,
! 276: fc, FCstr(fc),
! 277: laddr.Addr(),
! 278: (IsSuper() ? 'S' : 'U'),
! 279: ppc);
1.1 root 280:
1.1.1.15! root 281: #if defined(M68030_CUSTOM_ATC)
! 282: auto table = atc.GetTable(fc);
! 283: uint8 n = table->hash[laddr.Addr() >> 12];
! 284: if (__predict_true((int8)n >= 0)) {
! 285: // ATC #(n)
! 286: table->stat.total++;
! 287: a = &table->line[n];
! 288: } else if (n == m68030ATCTable_Custom::HASH_NONE) {
! 289: if (__predict_true(mmu_tc.e)) {
! 290: // TC.E 有効で対応なしなら、テーブルサーチ。
! 291: table->stat.total++;
! 292: a = table->Pop();
! 293: goto tablesearch;
! 294: } else {
! 295: // TC.E 無効。(total もカウントしない)
! 296: return laddr;
! 297: }
! 298: } else if (n == m68030ATCTable_Custom::HASH_TT) {
! 299: // TT。TTMatch() で外れたかどうかはカウントしない。ほぼ 0 なので。
! 300: table->stat.total++;
! 301: table->stat.tthit++;
! 302: return TTMatch(laddr);
! 303: } else {
! 304: // PS<4KB でマッチしたので再検査。
! 305: table->stat.total++;
! 306: a = table->FindSub(laddr);
! 307: if (a == NULL) {
! 308: a = table->Pop();
! 309: goto tablesearch;
1.1 root 310: }
311: }
312:
1.1.1.15! root 313: // 書き込み可なのにまだ Modified == 0 なら、Modified を立てるために
! 314: // このエントリを破棄して改めてテーブルサーチを行う。
! 315: if (a->IsWProtect() == false && a->IsModified() == false) {
! 316: table->Invalidate(a);
! 317: tablesearch:
! 318: table->stat.miss++;
! 319: PageTableSearch(laddr, a);
1.1.1.10 root 320: }
1.1.1.15! root 321: table->MoveHead(a);
! 322: table->MakeHash(a);
! 323: #else
! 324: // TT は TC.E とは独立しているので先に調べる。
! 325: busaddr ttaddr = TTMatch(laddr);
! 326: if (__predict_false(ttaddr.IsOK())) {
! 327: putlog(3, " %s: result %08x CI=%u (TT)", __func__,
! 328: ttaddr.Addr(), ttaddr.IsCInhibit() ? 1 : 0);
! 329: return ttaddr;
! 330: }
! 331: // TC.E 無効なら PA=VA。
! 332: if (__predict_false(mmu_tc.e == false)) {
! 333: putlog(3, " %s: result %08x (No translation)", __func__, laddr.Addr());
! 334: return laddr;
! 335: }
! 336: // ATC を検索。
! 337: auto table = atc.GetTable();
! 338: a = table->LookupAndMove(laddr);
! 339: if (a) {
! 340: // 書き込み可なのにまだ Modified == 0 なら、Modified を立てるために
! 341: // このエントリを破棄して改めてテーブルサーチを行う。
! 342: if (a->IsWProtect() == false && a->IsModified() == false) {
! 343: putlog(4, " ATC match but !Modified");
! 344: PageTableSearch(laddr, a);
1.1 root 345: }
1.1.1.15! root 346: } else {
! 347: // なければテーブルサーチ。
! 348: putlog(4, " ATC not match");
! 349: a = table->Pop();
! 350: PageTableSearch(laddr, a);
1.1 root 351: }
1.1.1.15! root 352: #endif
1.1 root 353:
1.1.1.15! root 354: putlog(4, " ATC match %s", table->LineToStr(a).c_str());
1.1 root 355:
1.1.1.15! root 356: if (__predict_false(a->IsBusError())) {
! 357: putlog(3, " %s: result BusErr", __func__);
! 358: return BusAddr::BusErr;
! 359: }
! 360: if (__predict_false(a->IsWProtect())) {
! 361: // Write アクセスで WP が立ってたらバスエラー。
! 362: putlog(3, " %s: result Buserr (WProtect)", __func__);
! 363: return BusAddr::BusErr;
! 364: }
1.1.1.9 root 365:
1.1.1.15! root 366: busaddr paddr = laddr;
! 367: paddr.ChangeAddr(a->GetPAddr() | (laddr.Addr() & mmu_tc.pgmask));
! 368: if (__predict_false(a->IsCInhibit())) {
! 369: paddr |= BusAddr::CInhibit;
! 370: }
! 371: putlog(3, " %s: result %08x CI=%u", __func__,
! 372: paddr.Addr(), paddr.IsCInhibit() ? 1 : 0);
! 373: return paddr;
1.1.1.8 root 374: }
375:
1.1.1.15! root 376: // ピーク用のアドレス変換を行う。
! 377: // 変換できれば対応する物理アドレスを、バスエラーなら BusErr を返す。
! 378: // テーブルサーチを行ったら TableSearched ビットを立てる。
! 379: busaddr
! 380: MPU68030Device::TranslatePeek(const busaddr laddr)
1.1 root 381: {
1.1.1.15! root 382: const m68030ATCLine *a;
! 383: busaddr paddr;
1.1 root 384:
1.1.1.15! root 385: uint32 fc = laddr.GetFC();
! 386: putlog(5, " %s: enter %u(%s).%08x", __func__,
! 387: fc, FCstr(fc),
! 388: laddr.Addr());
! 389:
! 390: #if defined(M68030_CUSTOM_ATC)
! 391: auto table = atc.GetTable(fc);
! 392: uint8 n = table->hash[laddr.Addr() >> 12];
! 393: // PS<4KB のケースを先に解決する。
! 394: if (__predict_false(n == m68030ATCTable_Custom::HASH_SUB)) {
! 395: a = table->FindSub(laddr);
! 396: if (a) {
! 397: n = a - &table->line[0];
! 398: } else {
! 399: n = m68030ATCTable_Custom::HASH_NONE;
! 400: }
1.1.1.9 root 401: }
1.1.1.15! root 402: if (__predict_true((int8)n >= 0)) {
! 403: // ATC #(n)
! 404: a = &table->line[n];
! 405: paddr = busaddr(a->GetPAddr() | (laddr.Addr() & mmu_tc.pgmask));
! 406: putlog(5, "%s: result %08x (match atc)", __func__, paddr.Addr());
! 407: return paddr;
! 408: } else if (n == m68030ATCTable_Custom::HASH_TT) {
! 409: // TT をこっそりチェック。
! 410: if (TTMatch(laddr).IsOK()) {
! 411: // 一致した。
! 412: // CI はここでは使わないため、一致したことだけわかればよい
! 413: putlog(5, "%s: result %08x (match tt)", __func__, laddr.Addr());
! 414: return laddr;
! 415: }
! 416: return BusAddr::BusErr;
! 417: } else {
! 418: if (__predict_false(mmu_tc.e == false)) {
! 419: // TC.E 無効
! 420: putlog(5, "%s: result %08x (no translation)", __func__,
! 421: laddr.Addr());
! 422: return laddr;
! 423: }
! 424: // TC.E 有効で対応なしなら、こっそりテーブルサーチ。
! 425: // FALLTHROUGH
! 426: }
! 427: #else
! 428: // まず TT をこっそりチェック。
! 429: if (TTMatch(laddr).IsOK()) {
! 430: // 一致した。
! 431: // CI はここでは使わないため、一致したことだけわかればよい
! 432: putlog(5, " %s: result %08x (match tt)", __func__, laddr.Addr());
! 433: return laddr;
! 434: }
! 435:
! 436: // TC.E が無効ならここまで。
! 437: if (__predict_false(mmu_tc.e == false)) {
! 438: putlog(5, " %s: result %08x (no translation)", __func__, laddr.Addr());
! 439: return laddr;
! 440: }
! 441:
! 442: // こっそり ATC を検索。
! 443: auto table = atc.GetTable();
! 444: a = table->LookupByDebugger(laddr);
! 445: if (a) {
! 446: paddr = busaddr(a->GetPAddr() | (laddr.Addr() & mmu_tc.pgmask));
! 447: putlog(5, "%s: result %08x (match atc)", __func__, paddr.Addr());
! 448: return paddr;
! 449: }
! 450: #endif
1.1.1.9 root 451:
1.1.1.15! root 452: // こっそりテーブルサーチ。
! 453: m68030PageTableOp pt(this, loglevel, m68030PageTableOp::ByDebugger);
! 454: paddr = pt.SearchByDebugger(laddr);
! 455: paddr |= BusAddr::TableSearched;
! 456:
! 457: if (paddr.IsBusErr()) {
! 458: putlog(5, " %s: result BusErr (table search)", __func__);
! 459: } else {
! 460: paddr |= laddr.Addr() & mmu_tc.pgmask;
! 461: putlog(5, " %s: result %08x (table search)", __func__, paddr.Addr());
1.1 root 462: }
1.1.1.15! root 463: return paddr;
1.1 root 464: }
465:
1.1.1.15! root 466: // TT から laddr を検索する。
! 467: // laddr が TT0/TT1 どちらかと一致すれば CI ビットを反映したアドレスを返す。
! 468: // 一致しなければ BusErr を返す。バスエラーではないが単に無効値の意。
! 469: busaddr
! 470: MPU68030Device::TTMatch(const busaddr laddr) const
1.1 root 471: {
1.1.1.15! root 472: int match = 0;
! 473: bool ci = false;
! 474:
! 475: for (int i = 0; i < 2; i++) {
! 476: const m68030TT& tt = mmu_tt[i];
! 477: if (tt.Match(laddr)) {
! 478: match += i + 1;
! 479: if (__predict_true(tt.ci)) {
! 480: ci = true;
1.1.1.10 root 481: }
1.1 root 482: }
483: }
1.1.1.15! root 484:
! 485: if (match != 0) {
! 486: busaddr paddr = laddr;
! 487: if (ci) {
! 488: paddr |= BusAddr::CInhibit;
! 489: }
! 490: putlog(4, " %s: result %08x (match TT%s)", __func__,
! 491: paddr.Addr(),
! 492: (match == 3) ? "0 and TT1" : ((match == 1) ? "0" : "1"));
! 493: return paddr;
! 494: }
! 495: // 一致しなかった。
! 496: return BusAddr::BusErr;
1.1 root 497: }
498:
1.1.1.15! root 499: // ページテーブルから laddr を検索する。
! 500: // 見付かっても見付からなくても、a を更新して返す。
! 501: // ここでは指定の a を埋めるだけで a の移動は呼び出し側の責任。
1.1 root 502: void
1.1.1.15! root 503: MPU68030Device::PageTableSearch(busaddr laddr, m68030ATCLine *a)
1.1 root 504: {
1.1.1.15! root 505: m68030PageTableOp pt(this, loglevel, m68030PageTableOp::ByTranslation);
! 506: pt.Search(7, laddr);
! 507: pt.MakeATC(atc, a);
1.1 root 508: }
509:
510:
1.1.1.15! root 511: #if defined(M68030_CUSTOM_ATC)
1.1.1.10 root 512: //
1.1.1.15! root 513: // ATC テーブル (Custom)
1.1.1.10 root 514: //
1.1 root 515:
1.1.1.10 root 516: // アドレス変換の本来のフローはこんな感じだが、
517: //
518: // if (TT*.E || TC.E) {
519: // if (TT*.E が有効) {
520: // laddr,RW が TT[01] とマッチしたら、paddr = laddr,CI
521: // }
522: // if (TC.E が有効) {
523: // ATC とマッチしたら、paddr = atc[n].paddr,CI
524: // そうでなければ、 paddr = テーブルサーチ
525: // }
526: // } else {
527: // paddr = laddr (アドレス変換オフ)
528: // }
529: //
530: // ハッシュで一度に引く。
531: // switch (hash[laddr>>12]) {
532: // case HASH_TT:
533: // laddr,RW が TT[01] とマッチしたら、paddr = laddr,CI
534: // case HASH_ATC:
535: // paddr = atc[n].paddr,CI
536: // case HASH_NONE:
537: // TC.E なら paddr = テーブルサーチ
538: // そうでなければ、paddr = laddr (アドレス変換オフ)
539: // case HASH_SUB:
540: // PS が 4KB 未満だとハッシュの1エントリに2つ以上が混在するので、
541: // この場合は諦めて ATC を線形探索。通常起きないので性能無視。
542: // }
543:
544: // ATC テーブルは本来は 1つ(FC混合) x 22 ラインだが、
545: // 高速化のため FC別(4テーブル) x 22 ラインのモードを用意する。
546: // FC 混合版ではテーブルは tables[0] のみで、FC(1,0,2) は busaddr の
547: // 34-32 ビット部分として laddr と一緒に検索キーにする。
548: // FC 分離版ではテーブルは fctables[fc] で検索キーは laddr(下位32ビット部分)。
549: //
550: // ハッシュなのでライン間の順序関係はないが、LRU で古いのを捨てる時や
551: // PS<4K の時に前から探索する時のために、age を使った順序管理をする。
552: // ラインを使用するたびに age に ++latest_age を代入しておくことで、
553: // 順序が必要な時には age の大きい順にすれば最近使った順になる。
554: // 32 ビット符号なし整数が折り返すと事故るがたぶんそれより高い頻度で
555: // フラッシュが起きるんじゃないかな…
1.1 root 556:
1.1.1.10 root 557: // コンストラクタ
1.1.1.15! root 558: m68030ATCTable_Custom::m68030ATCTable_Custom(MPU68030Device *cpu_, uint fc_)
1.1 root 559: {
1.1.1.10 root 560: cpu = cpu_;
561: fc = fc_;
1.1 root 562:
1.1.1.15! root 563: ClearHash();
! 564: Flush();
! 565: }
1.1 root 566:
1.1.1.15! root 567: // ハッシュをすべてクリアする。
! 568: void
! 569: m68030ATCTable_Custom::ClearHash()
! 570: {
! 571: std::fill(hash.begin(), hash.end(), HASH_NONE);
1.1.1.10 root 572: latest_age = 0;
1.1 root 573: }
574:
1.1.1.15! root 575: // ハッシュを完全に作り直す。
! 576: void
! 577: m68030ATCTable_Custom::Rehash()
1.1 root 578: {
1.1.1.15! root 579: ClearHash();
! 580:
! 581: // ページサイズも再調達。
! 582: ps4k = cpu->mmu_tc.ps - 12;
! 583:
! 584: // ATCLine を適用。
! 585: if (cpu->mmu_tc.e) {
! 586: for (int i = 0; i < LINES; i++) {
! 587: const m68030ATCLine *a = &line[i];
! 588: if (a->IsValid()) {
! 589: MakeHash(a);
! 590: }
! 591: }
! 592: }
! 593:
! 594: // 続いて TT を適用。検索時は TT が優先なので TT は後から適用。
! 595: for (int n = 0; n < 2; n++) {
! 596: const m68030TT& tt = cpu->mmu_tt[n];
! 597: if (tt.e) {
! 598: busaddr laddr = busaddr(0) | busaddr::FC(fc);
! 599: for (uint i = 0; i < 256; i++) {
! 600: // XXX R/W は?
! 601: if (tt.Match(laddr)) {
! 602: uint32 baseidx = i << (24 - 12);
! 603: memset(&hash[baseidx], HASH_TT, 4096);
! 604: }
! 605: laddr += 0x0100'0000;
! 606: }
! 607: }
! 608: }
1.1 root 609: }
610:
1.1.1.10 root 611: // この ATC テーブルのエントリをすべてフラッシュする。
612: void
1.1.1.15! root 613: m68030ATCTable_Custom::Flush()
1.1 root 614: {
1.1.1.10 root 615: for (int i = 0; i < LINES; i++) {
616: Invalidate(&line[i]);
1.1.1.12 root 617: line[i].age = 0;
1.1.1.10 root 618: }
1.1.1.12 root 619: latest_age = 0;
1.1 root 620: }
621:
1.1.1.10 root 622: // この ATC テーブルから addr が一致するエントリをすべてフラッシュする。
623: void
1.1.1.15! root 624: m68030ATCTable_Custom::Flush(uint32 addr)
1.1 root 625: {
1.1.1.10 root 626: for (int i = 0; i < LINES; i++) {
627: m68030ATCLine *a = &line[i];
628: if (a->GetTag() == addr) {
629: Invalidate(a);
1.1 root 630: }
631: }
632: }
633:
1.1.1.10 root 634: // この要素を先頭に移動。
635: inline void
1.1.1.15! root 636: m68030ATCTable_Custom::MoveHead(m68030ATCLine *a)
1.1 root 637: {
1.1.1.10 root 638: a->age = ++latest_age;
1.1.1.12 root 639:
640: if (__predict_false(a->age == 0)) {
641: // latest_age が1周したので、一旦全部リセット。
642: for (int i = 0; i < LINES; i++) {
643: line[i].age = 0;
644: }
645: a->age = ++latest_age;
646: }
1.1 root 647: }
648:
1.1.1.10 root 649: // このテーブルのうち最も古い要素を差し出す。
1.1 root 650: m68030ATCLine *
1.1.1.15! root 651: m68030ATCTable_Custom::Pop()
1.1 root 652: {
1.1.1.10 root 653: m68030ATCLine *a = NULL;
654: uint32 min = (uint32)~0;
655: uint32 minidx = 0;
1.1 root 656:
1.1.1.10 root 657: // 最後に使われたの (あるいは空き) を探す。
658: for (int i = 0; i < LINES; i++) {
659: if (line[i].IsInvalid()) {
1.1.1.12 root 660: return &line[i];
1.1 root 661: }
1.1.1.10 root 662: if (line[i].age < min) {
663: min = line[i].age;
1.1 root 664: minidx = i;
665: }
666: }
1.1.1.10 root 667: if (a == NULL) {
668: a = &line[minidx];
1.1 root 669: }
670:
1.1.1.10 root 671: // ハッシュから削除。
672: Invalidate(a);
1.1 root 673:
674: return a;
675: }
676:
1.1.1.10 root 677: // a からハッシュを作成する。
1.1.1.15! root 678: // TT.E 有効で a は有効であること。
1.1 root 679: void
1.1.1.15! root 680: m68030ATCTable_Custom::MakeHash(const m68030ATCLine *a)
1.1 root 681: {
1.1.1.15! root 682: //assert(cpu->mmu_tc.e);
! 683: //assert(a->IsValid());
! 684:
1.1.1.10 root 685: uint32 addr4k = a->GetLAddr() >> 12;
686: int idx = a - &line[0];
687:
688: if (__predict_true(ps4k == 0)) {
689: hash[addr4k] = idx;
690: } else if (ps4k > 0) {
1.1.1.14 root 691: for (int i = 0; i < (1U << ps4k); i++) {
1.1.1.10 root 692: hash[addr4k + i] = idx;
693: }
694: } else {
695: hash[addr4k] = HASH_SUB;
1.1 root 696: }
697: }
698:
1.1.1.10 root 699: // ATC a を無効にする。すでに無効なら何もしない。
1.1.1.15! root 700: //
! 701: // ハッシュから削除する際は TT を指していないか必ず確認すること。
! 702: // ATC エントリが出来た後で、そこに TT に設定すると起きうる。
! 703: // LRU の ATC ならそのエントリは参照されずに消えていくだけで害はないが、
! 704: // ハッシュは TT で上書きされているので消す際に注意する必要がある。
1.1 root 705: void
1.1.1.15! root 706: m68030ATCTable_Custom::Invalidate(m68030ATCLine *a)
1.1 root 707: {
1.1.1.10 root 708: if (a->IsValid()) {
709: // 有効ならハッシュから削除。
710: if (__predict_true(ps4k == 0)) {
711: uint32 addr4k = a->GetLAddr() >> 12;
1.1.1.15! root 712: if ((int8)hash[addr4k] >= 0) {
! 713: hash[addr4k] = HASH_NONE;
! 714: }
1.1.1.10 root 715: } else if (ps4k > 0) {
716: // PS>4KB
717: uint32 addr4k = a->GetLAddr() >> 12;
718: for (int i = 0; i < (1U << ps4k); i++) {
1.1.1.15! root 719: if ((int8)hash[addr4k + i] >= 0) {
! 720: hash[addr4k + i] = HASH_NONE;
! 721: }
1.1.1.10 root 722: }
723: } else {
724: // PS<4KB、自分が抜けることでこの枠が空くかどうか
725: InvalidateSub(a);
1.1 root 726: }
1.1.1.10 root 727:
728: a->Invalidate();
1.1 root 729: }
730: }
731:
1.1.1.10 root 732: // PS<4KB の場合に aa が抜けることでこの addr4k の枠が空けばハッシュから削除。
1.1 root 733: void
1.1.1.15! root 734: m68030ATCTable_Custom::InvalidateSub(const m68030ATCLine *aa)
1.1 root 735: {
1.1.1.10 root 736: const uint32 MASK_4K = 0xfffff000 | m68030ATCLine::INVALID;
737: uint32 target_laddr = aa->GetTag() & MASK_4K;
738: bool in_use = false;
739:
1.1.1.15! root 740: for (auto& e : line) {
! 741: m68030ATCLine *a = &e;
! 742:
1.1.1.10 root 743: // 自分自身は今から削除予定なので除く。
744: if (a == aa) {
745: continue;
746: }
747: // それ以外で有効なものがあるか。
748: if ((a->GetTag() & MASK_4K) == target_laddr) {
749: in_use = true;
750: break;
1.1 root 751: }
752: }
753:
1.1.1.10 root 754: // 誰もいなくなれば削除。
755: if (in_use == false) {
1.1.1.15! root 756: uint k = target_laddr >> 12;
! 757: if (hash[k] != HASH_SUB) {
! 758: hash[k] = HASH_NONE;
! 759: }
1.1 root 760: }
761: }
762:
1.1.1.10 root 763: // laddr を ATC から探す。見つからなければ NULL を返す。
764: m68030ATCLine *
1.1.1.15! root 765: m68030ATCTable_Custom::FindSub(const busaddr laddr)
1.1 root 766: {
1.1.1.15! root 767: uint32 addr = laddr.Addr() & lmask;
1.1.1.10 root 768:
769: // 同じエントリはないはずなので、順序通りに探さなくてもいいはず。
1.1.1.15! root 770: for (auto& e : line) {
! 771: m68030ATCLine *a = &e;
1.1.1.10 root 772: if (a->GetTag() == addr) {
773: return a;
1.1 root 774: }
775: }
1.1.1.10 root 776: return NULL;
1.1 root 777: }
1.1.1.10 root 778:
1.1.1.15! root 779: // 統計情報をリセットする。
! 780: void
! 781: m68030ATCTable_Custom::ResetStat()
! 782: {
! 783: stat.Clear();
! 784:
! 785: std::lock_guard<std::mutex> lock(hist_mtx);
! 786: hist.Clear();
! 787: hist_tt_once_hit = false;
! 788: }
! 789:
! 790: // デバッグ表示。FC は Table_Custom が持っている。
! 791: std::string
! 792: m68030ATCTable_Custom::LineToStr(const m68030ATCLine *a) const
! 793: {
! 794: return LineToStrCommon(a, fc);
! 795: }
! 796:
! 797: #else
! 798: //
! 799: // ATC テーブル (Motorola)
! 800: //
! 801:
! 802: // コンストラクタ
! 803: m68030ATCTable_Motorola::m68030ATCTable_Motorola(MPU68030Device *cpu_)
! 804: {
! 805: cpu = cpu_;
! 806:
! 807: for (uint pri = 0; pri < LINES; pri++) {
! 808: Set(pri, pri);
! 809: }
! 810: }
! 811:
! 812: // pri 番目に idx をセットする。
! 813: void
! 814: m68030ATCTable_Motorola::Set(uint pri, uint idx)
! 815: {
! 816: #if defined(ATC_ORDER64)
! 817: // セット先がクリアされてることが分かっている場合にはこれは不利。
! 818: uint i;
! 819: if (__predict_true(pri < ORDER0_COUNT)) {
! 820: i = 0;
! 821: } else {
! 822: i = 1;
! 823: pri -= ORDER0_COUNT;
! 824: }
! 825: order[i] &= ~(0x1fULL << (pri * 5));
! 826: order[i] |= (uint64)idx << (pri * 5);
! 827: #else
! 828: order[pri] = idx;
! 829: #endif
! 830: }
! 831:
! 832: // laddr が ATC の何番目かを返す。なければ -1 を返す。
! 833: int
! 834: m68030ATCTable_Motorola::Lookup(const busaddr laddr)
! 835: {
! 836: uint32 tag = MakeTag(laddr);
! 837:
! 838: #define COMPARE(idx, pri) do { \
! 839: m68030ATCLine *a = &line[(idx)]; \
! 840: if (__predict_true(a->GetTag() == tag)) \
! 841: return (pri); \
! 842: } while (0)
! 843:
! 844: #if defined(ATC_ORDER64)
! 845: uint j;
! 846: uint64 order_ = order[0];
! 847: for (j = 0; j < ORDER0_COUNT; j++) {
! 848: uint idx = order_ & 0x1f;
! 849: order_ >>= 5;
! 850: COMPARE(idx, j);
! 851: }
! 852: order_ = order[1];
! 853: for (; j < M68030_ATC_LINES; j++) {
! 854: uint idx = order_ & 0x1f;
! 855: order_ >>= 5;
! 856: COMPARE(idx, j);
! 857: }
! 858: #else
! 859: for (uint pri = 0; pri < LINES; pri++) {
! 860: uint idx = GetIdx(pri);
! 861: COMPARE(idx, pri);
! 862: }
! 863: #endif
! 864: return -1;
! 865: }
! 866:
! 867: // ATC から laddr を検索する。
! 868: // 見付かればこのエントリを先頭に移動してそれを返す。
! 869: // 見付からなければ NULL を返す。
! 870: m68030ATCLine *
! 871: m68030ATCTable_Motorola::LookupAndMove(const busaddr laddr)
! 872: {
! 873: int pri = Lookup(laddr);
! 874: if (__predict_true(pri >= 0)) {
! 875: // 先頭から何番目で見付かったか記録し、先頭に移動。
! 876: stat.hit[pri]++;
! 877:
! 878: const m68030ATCLine *a = GetLine(pri);
! 879: MMULOG(4, 3, "ATC.LookupAndMove: [%u]@%u: %u(%s)%08x",
! 880: GetIdx(pri), pri,
! 881: a->GetFC(),
! 882: MPU68030Device::FCstr(a->GetFC()),
! 883: a->GetPAddr());
! 884:
! 885: if (__predict_false(pri != 0)) {
! 886: MoveHead(pri);
! 887: }
! 888: return GetLine(0);
! 889: } else {
! 890: stat.miss++;
! 891: MMULOG(4, 3, "ATC.LookupAndMove: not match");
! 892: return NULL;
! 893: }
! 894: }
! 895:
! 896: // ATC から laddr を検索する。エントリは変更しない。(PTEST 用)
! 897: const m68030ATCLine *
! 898: m68030ATCTable_Motorola::LookupOnly(const busaddr laddr)
! 899: {
! 900: int pri = Lookup(laddr);
! 901: if (__predict_true(pri >= 0)) {
! 902: return GetLine(pri);
! 903: } else {
! 904: return NULL;
! 905: }
! 906: }
! 907:
! 908: // ATC をすべてフラッシュする。
! 909: void
! 910: m68030ATCTable_Motorola::Flush()
! 911: {
! 912: // 全部無効にするので、無効にしたのを最後尾に回す必要ない。
! 913: for (auto& e : line) {
! 914: m68030ATCLine *a = &e;
! 915: a->Invalidate();
! 916: }
! 917: }
! 918:
! 919: // ATC の fc, mask が一致するエントリをフラッシュする。
! 920: void
! 921: m68030ATCTable_Motorola::Flush(uint32 fc, uint32 mask)
! 922: {
! 923: for (uint pri = 0; pri < LINES; pri++) {
! 924: m68030ATCLine *a = GetLine(pri);
! 925: if (((a->GetFC() ^ fc) & mask) == 0) {
! 926: a->Invalidate();
! 927: MoveTail(pri);
! 928: }
! 929: }
! 930: }
! 931:
! 932: // ATC の fc, mask, addr が一致するエントリをフラッシュする。
! 933: void
! 934: m68030ATCTable_Motorola::Flush(uint32 fc, uint32 mask, uint32 addr)
! 935: {
! 936: for (uint pri = 0; pri < LINES; pri++) {
! 937: m68030ATCLine *a = GetLine(pri);
! 938: if (a->GetLAddr() == addr && ((a->GetFC() ^ fc) & mask) == 0) {
! 939: a->Invalidate();
! 940: MoveTail(pri);
! 941: }
! 942: }
! 943: }
! 944:
! 945: // pri 番目の要素を先頭に移動する。
! 946: // pri がゼロ(先頭)のケースは呼び出し元で事前に弾いておくこと。
! 947: void
! 948: m68030ATCTable_Motorola::MoveHead(uint pri)
! 949: {
! 950: uint idx = GetIdx(pri);
! 951: #if defined(ATC_ORDER64)
! 952: if (__predict_true(pri < ORDER0_COUNT)) {
! 953: uint64 maskl = (~0ULL) << (pri * 5);
! 954: uint64 maskh = maskl << 5;
! 955: uint64 tmpl = (order[0] & ~maskl) << 5;
! 956: uint64 tmph = (order[0] & maskh);
! 957: order[0] = tmph | tmpl | idx;
! 958: } else {
! 959: uint j = pri - ORDER0_COUNT;
! 960: uint64 maskl = (~0ULL) << (j * 5);
! 961: uint64 maskh = maskl << 5;
! 962: uint64 tmpl = (order[1] & ~maskl) << 5;
! 963: uint64 tmph = (order[1] & maskh);
! 964: uint64 tmpp = (order[0] >> ((ORDER0_COUNT - 1) * 5)) & 0x1f;
! 965: order[1] = tmph | tmpl | tmpp;
! 966: constexpr uint64 mask = (1ULL << (ORDER0_COUNT * 5)) - 1;
! 967: order[0] = ((order[0] << 5) & mask) | idx;
! 968: }
! 969: #else
! 970: for (uint d = pri; d > 0; d--) {
! 971: order[d] = order[d - 1];
! 972: }
! 973: order[0] = idx;
! 974: #endif
! 975: }
! 976:
! 977: // pri 番目の要素を末尾に移動する。
! 978: // pri が末尾でも呼び出してよい。
! 979: void
! 980: m68030ATCTable_Motorola::MoveTail(uint pri)
! 981: {
! 982: if (pri == LINES - 1) {
! 983: return;
! 984: }
! 985: uint idx = GetIdx(pri);
! 986: #if defined(ATC_ORDER64)
! 987: // コメント欄は 5ビットx22エントリを1文字x8桁で模式的に表現したもの。
! 988: // 110ビットより上に unused なビットがあるが、0 のままのはず。
! 989: // order = x'87654321
! 990: constexpr uint TAIL0 = (ORDER0_COUNT - 1) * 5;
! 991: constexpr uint TAIL1 = (ORDER1_COUNT - 1) * 5;
! 992: if (pri < ORDER0_COUNT) {
! 993: uint64 mask = (~0ULL) << (pri * 5); // F'FFFFF000 (ex. pri=3)
! 994: uint64 tmph = (order[0] >> 5) & mask; // x'x8765000 (xは0のはず)
! 995: uint64 tmpl = (order[0] & ~mask); // 0'00000321
! 996: uint64 tmpb = (order[1] & 0x1f) << TAIL0; // 0'n0000000
! 997: order[0] = tmph | tmpl | tmpb;
! 998: order[1] = (order[1] >> 5) | ((uint64)idx << TAIL1);
! 999: } else {
! 1000: uint j = pri - ORDER0_COUNT;
! 1001: uint64 mask = (~0ULL) << (j * 5);
! 1002: uint64 tmph = (order[1] >> 5) & mask;
! 1003: uint64 tmpl = (order[1] & ~mask);
! 1004: uint64 tmpb = (uint64)idx << TAIL1;
! 1005: order[1] = tmph | tmpl | tmpb;
! 1006: }
! 1007: #else
! 1008: uint d;
! 1009: for (d = pri; d < LINES - 2; d++) {
! 1010: order[d] = order[d + 1];
! 1011: }
! 1012: order[d] = idx;
! 1013: #endif
! 1014: }
! 1015:
! 1016: // 空き要素あるいは最も古い要素を先頭に移動する。
1.1.1.10 root 1017: m68030ATCLine *
1.1.1.15! root 1018: m68030ATCTable_Motorola::Pop()
1.1 root 1019: {
1.1.1.15! root 1020: MoveHead(LINES - 1);
! 1021: return GetLine(0);
! 1022: }
1.1.1.10 root 1023:
1.1.1.15! root 1024: // 統計情報をリセットする。
! 1025: void
! 1026: m68030ATCTable_Motorola::ResetStat()
! 1027: {
! 1028: stat.Clear();
! 1029: }
1.1.1.10 root 1030:
1.1.1.15! root 1031: // デバッグ表示。FC は a 自身が持っている。
! 1032: /*static*/ std::string
! 1033: m68030ATCTable_Motorola::LineToStr(const m68030ATCLine *a)
! 1034: {
! 1035: return LineToStrCommon(a, a->GetFC());
1.1 root 1036: }
1037:
1.1.1.15! root 1038: #endif
! 1039:
1.1.1.10 root 1040: // デバッグ表示。
1.1.1.15! root 1041: /*static*/ std::string
! 1042: m68030ATCTable::LineToStrCommon(const m68030ATCLine *a, uint fc_)
1.1 root 1043: {
1.1.1.10 root 1044: uint32 laddr = a->GetLAddr();
1045:
1.1.1.15! root 1046: if (!a->IsBusError()) {
! 1047: uint32 paddr = a->GetPAddr();
! 1048: char ci = a->IsCInhibit() ? 'C' : '-';
! 1049: char wp = a->IsWProtect() ? 'W' : '-';
! 1050: char mod = a->IsModified() ? 'M' : '-';
! 1051: return indent_format(1,
! 1052: "%u.%08x %08x %c%c%c",
! 1053: fc_, laddr, paddr, ci, wp, mod);
! 1054: } else {
! 1055: return indent_format(1,
! 1056: "%u.%08x BusError",
! 1057: fc_, laddr);
! 1058: }
! 1059: }
! 1060:
! 1061:
! 1062: //
! 1063: // ATC
! 1064: //
! 1065:
! 1066: // コンストラクタ
! 1067: m68030ATC::m68030ATC(MPU68030Device *cpu_)
! 1068: {
! 1069: cpu = cpu_;
! 1070:
! 1071: #if defined(M68030_CUSTOM_ATC)
! 1072: alltables[0].reset(new m68030ATCTable_Custom(cpu, 1));
! 1073: alltables[1].reset(new m68030ATCTable_Custom(cpu, 2));
! 1074: alltables[2].reset(new m68030ATCTable_Custom(cpu, 5));
! 1075: alltables[3].reset(new m68030ATCTable_Custom(cpu, 6));
! 1076:
! 1077: fctables[1] = alltables[0].get();
! 1078: fctables[2] = alltables[1].get();
! 1079: fctables[5] = alltables[2].get();
! 1080: fctables[6] = alltables[3].get();
! 1081: #else
! 1082: table.reset(new m68030ATCTable_Motorola(cpu));
! 1083: #endif
! 1084: }
! 1085:
! 1086: // ログレベルの設定。
! 1087: // これは Object のオーバーライドではないが、同じ使い方をする。
! 1088: void
! 1089: m68030ATC::SetLogLevel(int loglevel_)
! 1090: {
! 1091: loglevel = loglevel_;
! 1092:
! 1093: #if defined(M68030_CUSTOM_ATC)
! 1094: for (const auto& table : alltables) {
! 1095: table->loglevel = loglevel_;
! 1096: }
! 1097: #else
! 1098: table->loglevel = loglevel_;
! 1099: #endif
! 1100: }
! 1101:
! 1102: #if defined(M68030_CUSTOM_ATC)
! 1103: // ハッシュをすべて作り直す。
! 1104: void
! 1105: m68030ATC::Rehash()
! 1106: {
! 1107: for (const auto& table : alltables) {
! 1108: table->Rehash();
! 1109: }
! 1110: }
! 1111: #endif
! 1112:
! 1113: // TC がセットされたので、こちらの二次変数も追従する。
! 1114: void
! 1115: m68030ATC::SetTC(const m68030TC& tc)
! 1116: {
! 1117: #if defined(M68030_CUSTOM_ATC)
! 1118: for (const auto& table : alltables) {
! 1119: table->SetMask(tc.lmask);
! 1120: }
! 1121: #else
! 1122: table->SetMask(tc.lmask);
! 1123: #endif
! 1124: }
! 1125:
! 1126: // ATC をすべてフラッシュする。命令からコールされる
! 1127: void
! 1128: m68030ATC::FlushAll()
! 1129: {
! 1130: #if defined(M68030_CUSTOM_ATC)
! 1131: for (const auto& table : alltables) {
! 1132: table->Flush();
! 1133: }
! 1134: #else
! 1135: table->Flush();
! 1136: #endif
! 1137: }
! 1138:
! 1139: // ATC の fc, mask が一致するエントリをフラッシュする。
! 1140: // 命令からコールされる。
! 1141: void
! 1142: m68030ATC::Flush(uint32 fc, uint32 mask)
! 1143: {
! 1144: #if defined(M68030_CUSTOM_ATC)
! 1145: for (uint i = 1; i <= 6; i++) {
! 1146: if (((i ^ fc) & mask) == 0) {
! 1147: // FC が一致したテーブルを全フラッシュ。
! 1148: if ((bool)fctables[i]) {
! 1149: fctables[i]->Flush();
! 1150: }
! 1151: }
! 1152: }
! 1153: #else
! 1154: table->Flush(fc, mask);
! 1155: #endif
! 1156: }
! 1157:
! 1158: // ATC の fc, mask, addr が一致するエントリをフラッシュする。
! 1159: // 命令からコールされる。
! 1160: void
! 1161: m68030ATC::Flush(uint32 fc, uint32 mask, uint32 addr)
! 1162: {
! 1163: #if defined(M68030_CUSTOM_ATC)
! 1164: for (uint i = 1; i <= 6; i++) {
! 1165: if (((i ^ fc) & mask) == 0) {
! 1166: // FC が一致したテーブルから addr が一致したエントリをフラッシュ。
! 1167: if ((bool)fctables[i]) {
! 1168: fctables[i]->Flush(addr);
! 1169: }
! 1170: }
! 1171: }
! 1172: #else
! 1173: table->Flush(fc, mask, addr);
! 1174: #endif
! 1175: }
! 1176:
! 1177: // 統計情報をリセットする。
! 1178: void
! 1179: m68030ATC::ResetStat()
! 1180: {
! 1181: #if defined(M68030_CUSTOM_ATC)
! 1182: for (const auto& table : alltables) {
! 1183: table->ResetStat();
! 1184: }
! 1185: #else
! 1186: table->ResetStat();
! 1187: #endif
! 1188: }
! 1189:
! 1190: // 統計情報を更新する。
! 1191: void
! 1192: m68030ATC::CalcStat()
! 1193: {
! 1194: #if defined(M68030_CUSTOM_ATC)
! 1195: for (const auto& table : alltables) {
! 1196: // ここはロックなしでやってしまう
! 1197: m68030ATCStat s;
! 1198: s.total = table->stat.total;
! 1199: s.tthit = table->stat.tthit;
! 1200: s.miss = table->stat.miss;
! 1201: table->stat.total = 0;
! 1202: table->stat.tthit = 0;
! 1203: table->stat.miss = 0;
! 1204:
! 1205: // こっちはロックする
! 1206: {
! 1207: std::lock_guard<std::mutex> lock(table->hist_mtx);
! 1208:
! 1209: table->hist.EnqueueForce(s);
! 1210: if (__predict_false(s.tthit != 0)) {
! 1211: table->hist_tt_once_hit = true;
! 1212: }
! 1213: }
1.1 root 1214: }
1.1.1.15! root 1215: #else
! 1216: #endif
1.1 root 1217: }
1218:
1219:
1220: //
1.1.1.15! root 1221: // テーブルサーチ操作。
1.1 root 1222: //
1223:
1224: // コンストラクタ
1.1.1.15! root 1225: m68030PageTableOp::m68030PageTableOp(MPU68030Device *cpu_, int loglevel_,
! 1226: m68030PageTableOp::From from_)
1.1 root 1227: {
1.1.1.8 root 1228: cpu = cpu_;
1.1.1.9 root 1229: loglevel = loglevel_;
1.1.1.15! root 1230: from = from_;
! 1231: }
! 1232:
! 1233: // ページテーブルから laddr を検索する。
! 1234: // 結果はメンバ変数に保持する。
! 1235: m68030PageTableOp::Result
! 1236: m68030PageTableOp::Search(int maxlv, busaddr laddr)
! 1237: {
! 1238: // テーブルサーチで書き込みの時だけ、ディスクリプタの M ビットを更新。
! 1239: if (__predict_false(laddr.IsWrite()) &&
! 1240: __predict_true(from == ByTranslation))
! 1241: {
! 1242: writeback_m = true;
! 1243: }
! 1244:
! 1245: // テーブルサーチを行う。
! 1246: result = DoSearch(maxlv, laddr);
! 1247: if (__predict_true(result != Result::Invalid)) {
! 1248: m_paddr = busaddr(m_desc2 & cpu->mmu_tc.lmask);
! 1249: }
! 1250:
! 1251: // デバッグ表示。
! 1252: if (__predict_false(loglevel >= 4)) {
! 1253: std::string buf;
! 1254: switch (result) {
! 1255: case Result::Invalid:
! 1256: buf = "Invalid -> BusErr";
! 1257: break;
! 1258: case Result::Normal:
! 1259: buf = string_format("Normal paddr=%08x", m_paddr.Addr());
! 1260: break;
! 1261: case Result::Indirect:
! 1262: buf = string_format("Indirect paddr=%08x", m_paddr.Addr());
! 1263: break;
! 1264: case Result::Early:
! 1265: {
! 1266: // 残ってるビットも表示。
! 1267: uint32 paddr = m_paddr.Addr();
! 1268: buf = string_format("Early paddr=%08x", paddr);
! 1269: for (uint i = lv + 1; i <= cpu->mmu_tc.termlv; i++) {
! 1270: paddr |= m_idx[i] << cpu->mmu_tc.shift[i];
! 1271: buf += string_format(" +%s=%08x", m_lvname[i], paddr);
! 1272: }
! 1273: break;
! 1274: }
! 1275: }
! 1276: MMULOG(4, 2, "PageTable.%s: %s", lam_func, buf.c_str());
! 1277: }
1.1 root 1278:
1.1.1.15! root 1279: return result;
1.1 root 1280: }
1281:
1.1.1.15! root 1282: // デバッガからの呼び出し用。
! 1283: // maxlv 不要なのと、ATC を作って返す必要もないので、単に変換後のアドレス
! 1284: // (変換出来なければ BusErr) を返す。
! 1285: busaddr
! 1286: m68030PageTableOp::SearchByDebugger(busaddr laddr)
1.1.1.8 root 1287: {
1.1.1.15! root 1288: // writeback_m は false のまま。
! 1289:
! 1290: // テーブルサーチを行う。
! 1291: result = DoSearch(7, laddr);
! 1292:
! 1293: // ここからサーチ結果によって、結果の物理アドレスを作成。
! 1294: if (result == Result::Invalid) {
! 1295: return BusAddr::BusErr;
! 1296: }
! 1297:
! 1298: uint32 paddr = m_desc2 & cpu->mmu_tc.lmask;
! 1299: if (result == Result::Early) {
! 1300: paddr = AssembleEarlyRemains(paddr);
! 1301: }
! 1302:
! 1303: return busaddr(paddr);
1.1.1.8 root 1304: }
1305:
1.1.1.15! root 1306: // laddr についてテーブルサーチを行い結果コード Result を返す。
! 1307: // その他の状態はメンバに保持する。
! 1308: m68030PageTableOp::Result
! 1309: m68030PageTableOp::DoSearch(int maxlv, const busaddr laddr)
1.1 root 1310: {
1311: const char *rpname;
1.1.1.15! root 1312: uint dt;
1.1 root 1313:
1.1.1.15! root 1314: m_laddr = laddr;
1.1 root 1315:
1.1.1.15! root 1316: // m_laddr のビットを TIA .. TID に分解。
1.1 root 1317: //
1.1.1.15! root 1318: // 例えば TIA=$C, TIB=$A, (TIC = TID = 0), PS=$A の場合以下のようにする。
! 1319: // TIB の次の段は番兵。
1.1 root 1320: //
1321: // A B PS
1322: // +----------------+--------------+--------------+
1323: // $00A01A00 | 0000 0000 1010 | 0000 0001 10 | xx xxxx xxxx |
1324: // +----------------+--------------+--------------+
1.1.1.15! root 1325: // shift[TIA]=20 shift[TIB]=10
! 1326: // mask[TIA]=0xfff mask[TIB]=0x3ff
! 1327: // idx[TIA]=0xa idx[TIB]=0x6
1.1 root 1328: //
1.1.1.15! root 1329: for (int i = LV_TIA, end = cpu->mmu_tc.termlv; i <= end; i++) {
! 1330: uint shift = cpu->mmu_tc.shift[i];
! 1331: uint mask = cpu->mmu_tc.mask[i];
! 1332: m_idx[i] = (m_laddr.Addr() >> shift) & mask;
1.1 root 1333: }
1334:
1.1.1.9 root 1335: // この辺に図9-26 の初期化だがコンストラクタで初期化済み。
1.1 root 1336:
1337: // ここから図9-25。
1338:
1.1.1.15! root 1339: // 使用するルートポインタを決定。
! 1340: // m_desc2 のマスクは使うところで行っている。
! 1341: if (__predict_true(cpu->mmu_tc.sre && laddr.IsSuper())) {
1.1 root 1342: rpname = "SRP";
1343: m_desc1 = cpu->GetSRPh();
1344: m_desc2 = cpu->GetSRPl();
1345: } else {
1346: rpname = "CRP";
1347: m_desc1 = cpu->GetCRPh();
1348: m_desc2 = cpu->GetCRPl();
1349: }
1350:
1.1.1.15! root 1351: // ルートポインタの DT をチェック。
1.1 root 1352: dt = GET_DT(m_desc1);
1.1.1.15! root 1353: MMULOG((__predict_false(IsDebugger()) ? 5 : 4),
! 1354: 3, "%s: %s dt=%s", lam_func, rpname, dtname(dt));
1.1.1.12 root 1355: if (__predict_true(dt == DT_VALID4)) {
1356: // 有効 4 バイト。基本的にはこれしか使われないので優先。
1357: m_descsize = 4;
1358: } else if (dt == DT_VALID8) { // 有効 (8バイト)
1359: m_descsize = 8;
1360: } else if (dt == DT_PAGE) { // ページディスクリプタ
1.1.1.15! root 1361: return Result::Early;
1.1.1.12 root 1362: } else {
1363: // 無効は設定できないはずなので、ここには来ないはず。
1.1.1.15! root 1364: PANIC("%s.DT=%u corrupted?", rpname, dt);
1.1 root 1365: }
1366:
1.1.1.15! root 1367: // 必要ならファンクションコード・ルックアップ。
1.1.1.12 root 1368: if (__predict_false(cpu->mmu_tc.fcl)) {
1.1.1.8 root 1369: // たぶん検索レベルを1つ消費するはず。未確認。
1.1.1.15! root 1370: m_n++;
1.1.1.8 root 1371:
1.1.1.15! root 1372: uint32 tableaddr = m_desc2 & 0xfffffff0U;
! 1373: uint idx = m_laddr.GetFC();
! 1374: MMULOG((__predict_false(IsDebugger()) ? 5 : 4),
! 1375: 3, "%s: %s tableaddr=%08x idx=$%x", lam_func,
! 1376: m_lvname[lv], tableaddr, idx);
1.1 root 1377:
1.1.1.15! root 1378: // ディスクリプタをフェッチ。
! 1379: if (FETCH_DESC(tableaddr, idx) == false) {
! 1380: return Result::Invalid;
1.1 root 1381: }
1382:
1.1.1.15! root 1383: // ディスクリプタタイプをチェック。
1.1 root 1384: dt = GET_DT(m_desc1);
1385: switch (dt) {
1386: case DT_INVALID: // 無効
1.1.1.15! root 1387: return Result::Invalid;
1.1 root 1388:
1389: case DT_PAGE: // ページディスクリプタ
1.1.1.15! root 1390: return Result::Early;
1.1 root 1391:
1392: case DT_VALID4: // 有効4バイト
1393: case DT_VALID8: // 有効8バイト
1.1.1.14 root 1394: m_descsize = 1U << dt;
1.1 root 1395: break;
1396: }
1397: }
1398:
1399: // TIx のテーブルサーチに入る。図9-25 の下半分。
1400:
1.1.1.15! root 1401: for (lv = LV_TIA; ; lv++) {
! 1402: // N はステージ開始時に足すが、
! 1403: // 超えたかどうかはこのステージが完了したループ末尾で調べる。
! 1404: m_n++;
1.1 root 1405:
1.1.1.15! root 1406: // ロングディスクリプタなら、リミットチェック。
! 1407: if (__predict_false(m_descsize == 8)) {
! 1408: if (CheckLimit() == false) {
! 1409: m_lim = true;
! 1410: return Result::Invalid;
! 1411: }
! 1412: }
1.1 root 1413:
1.1.1.15! root 1414: // テーブルアドレス。
! 1415: uint32 tableaddr = m_desc2 & 0xfffffff0U;
! 1416: MMULOG((__predict_false(IsDebugger()) ? 5 : 4),
! 1417: 3, "%s: %s tableaddr=%08x idx=$%x", lam_func,
! 1418: m_lvname[lv], tableaddr, m_idx[lv]);
! 1419:
! 1420: // ディスクリプタをフェッチ。
! 1421: if (FETCH_DESC(tableaddr, m_idx[lv]) == false) {
! 1422: return Result::Invalid;
1.1 root 1423: }
1424:
1.1.1.15! root 1425: // ディスクリプタタイプをチェック。
1.1 root 1426: dt = GET_DT(m_desc1);
1427: switch (dt) {
1428: case DT_INVALID: // 無効
1.1.1.15! root 1429: return Result::Invalid;
1.1 root 1430:
1431: case DT_PAGE: // ページディスクリプタ
1.1.1.15! root 1432: // 最終段ならノーマル。
! 1433: // 最終段じゃないのにページが来たらアーリーターミネーション。
! 1434: if (__predict_true(lv == cpu->mmu_tc.termlv)) {
! 1435: return Result::Normal;
1.1 root 1436: } else {
1.1.1.15! root 1437: return Result::Early;
1.1 root 1438: }
1439:
1440: case DT_VALID4: // 有効4バイト
1441: case DT_VALID8: // 有効8バイト
1.1.1.15! root 1442: // 最終段なら、間接ディスクリプタ。
! 1443: if (__predict_false(lv == cpu->mmu_tc.termlv)) {
! 1444: // ディスクリプタをフェッチ。
! 1445: tableaddr = m_desc2 & 0xfffffff0U;
! 1446: if (FETCH_DESC(tableaddr, 0) == false) {
! 1447: return Result::Invalid;
1.1 root 1448: }
1449: if (GET_DT(m_desc1) != DT_PAGE) {
1.1.1.15! root 1450: return Result::Invalid;
1.1 root 1451: }
1.1.1.15! root 1452:
! 1453: return Result::Indirect;
1.1 root 1454: }
1.1.1.15! root 1455:
! 1456: // 次段のサーチを繰り返す。
! 1457: m_descsize = 1U << dt;
1.1.1.8 root 1458: break;
1.1 root 1459: }
1460:
1.1.1.15! root 1461: if (__predict_false(m_n >= maxlv)) {
! 1462: MMULOG((__predict_false(IsDebugger()) ? 5 : 4),
! 1463: 3, "%s: n reaches maxlv(%u)", lam_func, maxlv);
! 1464: break;
1.1.1.8 root 1465: }
1.1 root 1466: }
1.1.1.15! root 1467:
! 1468: return Result::Normal;
1.1 root 1469: }
1470:
1.1.1.15! root 1471: // ディスクリプタ中のフラグの更新 (を予約する)。
1.1 root 1472: #define UPDATE_DESC(flag) do { \
1473: if ((m_desc1 & (flag)) == 0) { \
1474: m_desc1 |= (flag); \
1475: do_write = true; \
1476: } \
1477: } while (0)
1478:
1.1.1.15! root 1479: // ディスクリプタのフェッチと更新を行う。
1.1 root 1480: // 図9-29。
1.1.1.15! root 1481: // ディスクリプタを取得出来れば true を返す。
! 1482: // ATC エントリの作成に向かう場合は false を返す。
1.1 root 1483: bool
1.1.1.15! root 1484: m68030PageTableOp::FetchDesc(uint32 tableaddr, uint idx)
1.1 root 1485: {
1486: uint32 descaddr;
1.1.1.15! root 1487: busdata bd;
1.1 root 1488: bool do_write;
1489: uint dt;
1490:
1491: // ディスクリプタのアドレスを計算。ここだけマニュアルの説明が雑すぎ。
1492: // インダイレクトなら idx = 0 でコールしてあるので、
1493: // tableaddr がそのまま PA になるという寸法。
1494: descaddr = tableaddr + idx * m_descsize;
1.1.1.15! root 1495: MMULOG(4, 4, "%s: descsize=%u descaddr=%08x", lam_func,
1.1 root 1496: m_descsize, descaddr);
1497:
1.1.1.15! root 1498: bd = cpu->read_phys_4(descaddr);
! 1499: if (__predict_false(bd.IsBusErr())) {
! 1500: goto buserr;
! 1501: }
! 1502: m_desc1 = bd.Data();
! 1503:
! 1504: if (__predict_true(m_descsize == 4)) {
! 1505: // ショートディスクリプタは m_desc1 と m_desc2 の各フィールドが
! 1506: // 衝突せず共存している状態とみなすことが出来る。
! 1507: m_desc2 = m_desc1;
1.1 root 1508: } else {
1.1.1.15! root 1509: bd = cpu->read_phys_4(descaddr + 4);
! 1510: if (bd.IsBusErr()) {
1.1 root 1511: goto buserr;
1512: }
1.1.1.15! root 1513: m_desc2 = bd.Data();
1.1 root 1514: }
1.1.1.15! root 1515: m_lastaddr = descaddr;
1.1 root 1516:
1.1.1.15! root 1517: // ディスクリプタタイプをチェック。
1.1 root 1518: do_write = false;
1519: dt = GET_DT(m_desc1);
1.1.1.15! root 1520: MMULOG(4, 4, "%s: desc=%s dt=%s", lam_func,
1.1 root 1521: sprintf_desc().c_str(), dtname(dt));
1522: switch (dt) {
1523: case DT_INVALID: // 無効
1524: return true;
1525:
1526: case DT_PAGE: // ページディスクリプタ
1.1.1.15! root 1527: // U ビットを更新。
! 1528: UPDATE_DESC(PAGEDESC_U);
1.1 root 1529:
1.1.1.15! root 1530: // テーブルサーチ中の書き込み操作なら M を更新。
! 1531: if (writeback_m) {
! 1532: UPDATE_DESC(PAGEDESC_M);
1.1 root 1533: }
1534: // 図にはないけど、ATC 更新のためと PTEST のために
1535: // ページディスクリプタの M ビットを記録。
1.1.1.15! root 1536: if ((m_desc1 & PAGEDESC_M)) {
1.1.1.9 root 1537: m_m = true;
1.1.1.15! root 1538: }
1.1 root 1539: break;
1540:
1541: case DT_VALID4: // 有効4バイト
1542: case DT_VALID8: // 有効8バイト
1.1.1.15! root 1543: // U ビットを更新。
! 1544: UPDATE_DESC(PAGEDESC_U);
1.1 root 1545: break;
1.1.1.12 root 1546:
1547: default:
1548: __unreachable();
1.1 root 1549: }
1550:
1.1.1.15! root 1551: // ライトライクルの実行。
1.1 root 1552: if (do_write) {
1.1.1.15! root 1553: busdata r = cpu->write_phys_4(descaddr, m_desc1);
! 1554: if (__predict_false(r.IsBusErr())) {
1.1 root 1555: goto buserr;
1556: }
1.1.1.15! root 1557: // バス動作正常終了。
! 1558: MMULOG(4, 4, "%s: desc=%s updated", lam_func, sprintf_desc().c_str());
1.1 root 1559: }
1560:
1.1.1.15! root 1561: // ステータス更新 (OR していく)。
1.1 root 1562: // XXX 実際には CI はページディスクリプタにしかないけど
1.1.1.15! root 1563: m_wp |= (m_desc1 & PAGEDESC_WP);
! 1564: m_ci |= (m_desc1 & PAGEDESC_CI);
1.1.1.9 root 1565: if (__predict_false(m_descsize == 8)) {
1.1.1.15! root 1566: m_s |= (m_desc1 & PAGEDESC_S);
1.1 root 1567: }
1568: return true;
1569:
1570: // バスエラー
1571: buserr:
1572: m_berr = true;
1573: return false;
1574: }
1575:
1.1.1.15! root 1576: // ディスクリプタをこっそり取得する。更新操作は行わない。
! 1577: // またディスクリプタの状態を内部フラグに反映するところも行わない。
! 1578: // 取得できれば m_desc1, m_desc2 に置いて true を返す。
! 1579: // 取得できなければ false を返す。
! 1580: bool
! 1581: m68030PageTableOp::PeekDesc(uint32 tableaddr, uint idx)
1.1 root 1582: {
1.1.1.15! root 1583: uint32 descaddr;
! 1584: uint64 data;
1.1 root 1585:
1.1.1.15! root 1586: // ディスクリプタのアドレスを計算。
! 1587: descaddr = tableaddr + idx * m_descsize;
1.1 root 1588:
1.1.1.15! root 1589: data = cpu->mainbus->Peek4(descaddr);
! 1590: if (__predict_false((int64)data < 0)) {
! 1591: return false;
! 1592: }
! 1593: m_desc1 = (uint32)data;
1.1 root 1594:
1.1.1.15! root 1595: if (__predict_true(m_descsize == 4)) {
! 1596: m_desc2 = m_desc1;
! 1597: } else {
! 1598: data = cpu->mainbus->Peek4(descaddr + 4);
! 1599: if ((int64)data < 0) {
! 1600: return false;
1.1 root 1601: }
1.1.1.15! root 1602: m_desc2 = (uint32)data;
! 1603: }
! 1604:
! 1605: // フラグやメンバーの更新は不要なのでこれで終わり。
! 1606: return true;
! 1607: }
! 1608:
! 1609: // 検索結果から ATC エントリを作成。
! 1610: // 図9-27 だが雑すぎ。
! 1611: void
! 1612: m68030PageTableOp::MakeATC(m68030ATC& atc, m68030ATCLine *a) const
! 1613: {
! 1614: #if defined(M68030_CUSTOM_ATC)
! 1615: // lmask はどのインスタンスでも同じ。
! 1616: auto table = atc.GetTable(6);
! 1617: #else
! 1618: auto table = atc.GetTable();
! 1619: #endif
! 1620:
! 1621: a->tag = table->MakeTag(m_laddr);
! 1622:
! 1623: if (result == Result::Invalid) {
! 1624: a->paddr = {};
! 1625: a->data = m68030ATCLine::BUSERROR;
! 1626: return;
1.1 root 1627: }
1628:
1.1.1.15! root 1629: a->paddr = m_paddr.Addr();
! 1630: if (__predict_false(result == Result::Early)) {
! 1631: // PERFORM LIMIT CHECK ??
! 1632:
! 1633: a->paddr = AssembleEarlyRemains(a->paddr);
! 1634: }
1.1 root 1635:
1.1.1.15! root 1636: a->data = 0;
! 1637: if (m_ci) {
1.1.1.9 root 1638: a->data |= m68030ATCLine::CINHIBIT;
1.1.1.15! root 1639: }
! 1640: if (m_wp) {
1.1 root 1641: a->data |= m68030ATCLine::WPROTECT;
1.1.1.15! root 1642: }
! 1643: if (m_m) {
1.1 root 1644: a->data |= m68030ATCLine::MODIFIED;
1.1.1.15! root 1645: }
1.1 root 1646: }
1647:
1.1.1.15! root 1648: // リミットチェックを実行。図9-28。
! 1649: // 無効のケースに落ちたら false を返す。
! 1650: bool
! 1651: m68030PageTableOp::CheckLimit() const
1.1 root 1652: {
1.1.1.15! root 1653: uint16 limit = (m_desc1 >> 16) & 0x7fff;
1.1 root 1654:
1.1.1.15! root 1655: // 最上位ビットが L/U
! 1656: if ((m_desc1 & 0x80000000U)) {
! 1657: if (m_idx[lv] < limit) {
! 1658: MMULOG(4, 4, "%s: $%04x < $%04x", lam_func, m_idx[lv], limit);
! 1659: return false;
! 1660: }
! 1661: } else {
! 1662: if (m_idx[lv] > limit) {
! 1663: MMULOG(4, 4, "%s: $%04x > $%04x", lam_func, m_idx[lv], limit);
! 1664: return false;
1.1 root 1665: }
1666: }
1.1.1.15! root 1667: return true;
! 1668: }
1.1.1.9 root 1669:
1.1.1.15! root 1670: // 早期終了時の残ってるビットを addr に足したものを返す。
! 1671: uint32
! 1672: m68030PageTableOp::AssembleEarlyRemains(uint32 addr) const
! 1673: {
! 1674: for (uint i = lv + 1; i <= cpu->mmu_tc.termlv; i++) {
! 1675: addr |= m_idx[i] << cpu->mmu_tc.shift[i];
! 1676: }
! 1677: return addr;
1.1 root 1678: }
1679:
1680: // ディスクリプタ表示用の文字列を返す。
1681: // m_descsize によって m_desc1, m_desc2 を適切に文字列にする。
1682: std::string
1.1.1.15! root 1683: m68030PageTableOp::sprintf_desc() const
1.1 root 1684: {
1685: std::string buf;
1686:
1687: if (m_descsize == 4) {
1688: buf = string_format("%08x", m_desc1);
1689: } else {
1690: buf = string_format("%08x_%08x", m_desc1, m_desc2);
1691: }
1692:
1693: // フラグの出現条件がややこしいので注意
1694: int dt = GET_DT(m_desc1);
1695: buf += string_format(" (%s%s%s%c%c)",
1.1.1.15! root 1696: ((m_descsize == 4) ? "x" : ((m_desc1 & PAGEDESC_S) ? "S" : "-")),
! 1697: ((dt != DT_PAGE) ? "x" : ((m_desc1 & PAGEDESC_CI) ? "C" : "-")),
! 1698: ((dt != DT_PAGE) ? "x" : ((m_desc1 & PAGEDESC_M) ? "M" : "-")),
! 1699: (m_desc1 & PAGEDESC_U) ? 'U' : '-',
! 1700: (m_desc1 & PAGEDESC_WP) ? 'W' : '-'
1.1 root 1701: );
1702:
1703: return buf;
1704: }
1705:
1706: // ディスクリプタタイプ文字列 (デバッグ用)
1.1.1.15! root 1707: /*static*/ const char *
! 1708: m68030PageTableOp::dtname(uint32 dt)
1.1 root 1709: {
1710: switch (dt) {
1711: case DT_INVALID: return "invalid";
1.1.1.4 root 1712: case DT_PAGE: return "page";
1.1 root 1713: case DT_VALID4: return "valid4";
1714: case DT_VALID8: return "valid8";
1.1.1.15! root 1715: default:
! 1716: return "corrupted?";
1.1 root 1717: }
1718: }
1719:
1.1.1.15! root 1720: /*static*/ const char * const
! 1721: m68030PageTableOp::m_lvname[] = {
1.1 root 1722: "TIA",
1723: "TIB",
1724: "TIC",
1725: "TID",
1.1.1.15! root 1726: "bad", // 番兵なので表示されないはず。
! 1727: "FCL", // 検索順ではなく、こいつだけ処理が違うのでここにある。
1.1 root 1728: };
1729:
1730:
1.1.1.15! root 1731: //
! 1732: // レジスタ
! 1733: //
! 1734:
! 1735: // リセット例外の CPU 固有部分。
! 1736: void
! 1737: MPU68030Device::ResetMMU()
! 1738: {
! 1739: SetTC(GetTC() & ~m68030TC::TC_E);
! 1740: SetTT(0, GetTT(0) & ~m68030TT::E);
! 1741: SetTT(1, GetTT(1) & ~m68030TT::E);
! 1742: }
! 1743:
! 1744: // 現在の TC:E、TT:E の状態に応じてアドレス変換の有無を切り替える。
! 1745: // mmu_enable ならアドレス変換ありのバスアクセスを使う。see m68030bus.cpp
! 1746: void
! 1747: MPU68030Device::mmu_enable_changed()
! 1748: {
! 1749: bool enable;
! 1750:
! 1751: // TC、TT0、TT1 のいずれかが有効ならアドレス変換あり
! 1752: enable = (mmu_tc.e || mmu_tt[0].e || mmu_tt[1].e);
! 1753:
! 1754: // 変化した時だけ
! 1755: if (mmu_enable && !enable) {
! 1756: // 無効にする
! 1757: mmu_enable = false;
! 1758: putlog(1, "MMU Translation Disabled");
! 1759: } else if (!mmu_enable && enable) {
! 1760: // 有効にする
! 1761: mmu_enable = true;
! 1762: putlog(1, "MMU Translation Enabled");
! 1763: }
! 1764: }
! 1765:
! 1766: bool
! 1767: MPU68030Device::SetSRP(uint32 h, uint32 l)
! 1768: {
! 1769: if (GET_DT(h) == DT_INVALID) {
! 1770: return false;
! 1771: }
! 1772: reg.srp.h = h & m68030RP::H_MASK;
! 1773: reg.srp.l = l;
! 1774: return true;
! 1775: }
! 1776:
! 1777: bool
! 1778: MPU68030Device::SetCRP(uint32 h, uint32 l)
! 1779: {
! 1780: if (GET_DT(h) == DT_INVALID) {
! 1781: return false;
! 1782: }
! 1783: reg.crp.h = h & m68030RP::H_MASK;
! 1784: reg.crp.l = l;
! 1785: return true;
! 1786: }
! 1787:
! 1788: void
! 1789: MPU68030Device::SetTT(int n, uint32 data)
! 1790: {
! 1791: // マスクしてレジスタイメージにセット
! 1792: reg.tt[n] = data & m68030TT::MASK;
! 1793:
! 1794: // メンバと二次変数を作成。
! 1795: mmu_tt[n].Set(data);
! 1796:
! 1797: #if defined(M68030_CUSTOM_ATC)
! 1798: atc.Rehash();
! 1799: #endif
! 1800:
! 1801: mmu_enable_changed();
! 1802: }
! 1803:
! 1804: // data を分解してメンバにセットする。
! 1805: void
! 1806: m68030TT::Set(uint32 data)
! 1807: {
! 1808: // 3 2 1 0
! 1809: // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
! 1810: // +---------------+---------------+-+-+-+-+-+-+-+-+-+-----+-+-----+
! 1811: // |LogicalAddrBase|LogicalAddrMask|E| |C|R|M| | FCB | | FCM |
! 1812: // +---------------+---------------+-+-+-+-+-+-+-+-+-+-----+-+-----+
! 1813:
! 1814: e = (data & m68030TT::E);
! 1815: ci = (data & m68030TT::CI);
! 1816:
! 1817: // 下位ロングワード
! 1818: // ・lbase は比較先アドレスで、ff000000の位置。
! 1819: // ・lmask はアドレスマスク。%1 が無視を表しているので NOT しておく。
! 1820: //
! 1821: // target AAAAAAAA xxxxxxxx xxxxxxxx xxxxxxxx : Aは論理アドレス
! 1822: // ^ : xは不問
! 1823: // lbase BBBBBBBB 00000000 00000000 00000000 : Bは論理アドレスベース
! 1824: // &
! 1825: // lmask MMMMMMMM 00000000 00000000 00000000 : Mは論理アドレスマスク
! 1826: //
! 1827: uint32 basel = (data & m68030TT::LBASE_MASK);
! 1828: uint32 maskl = (~data & m68030TT::LMASK_MASK) << 8;
! 1829:
! 1830: // 上位ロングワード
! 1831: // ・E ビットは位置はどこでもいいので TT:E ビットの
! 1832: // 位置をそのまま使用する。target 側のこの位置は必ずビットが落ちている
! 1833: // ため base 側にビットを立てておくと必ず不一致になることを利用して、
! 1834: // E ビットの評価も一度に行う。
! 1835: // ・RW ビットは BusAddr::R と比較するので 0x00000080 の位置。
! 1836: // ・RWM が %1 なら R/W は無視、RWM が %0 なら R/W の一致も検査するので
! 1837: // RWM が %0 の時は RW のマスクビットを立てておく。
! 1838: // ・FC は busaddr の配置に合わせて1ビット左シフトしたところを使う。
! 1839: // ・FCMask は %1 が無視を表しているので NOT しておく。
! 1840: //
! 1841: // target 00000000 00000000 00000000 R000FFFS : R はアクセス R/~W
! 1842: // ^ : F はアクセス FC
! 1843: // : S は FC2 のコピー
! 1844: // base 00000000 00000000 E0000000 R000FFF0 : R は期待する R/~W
! 1845: // & : F は期待する FC
! 1846: // : E は ~TT:E
! 1847: // mask 00000000 00000000 10000000 M000CCC0 : M は R/~W マスク
! 1848: // : C は FC マスク
! 1849: uint32 baseh;
! 1850: uint32 maskh;
! 1851: baseh = ~data & m68030TT::E;
! 1852: baseh |= (data & m68030TT::RW) >> 2;
! 1853: baseh |= (data & m68030TT::FCBASE_MASK) >> 3;
! 1854: maskh = m68030TT::E;
! 1855: if ((data & m68030TT::RWM) == 0) {
! 1856: maskh |= (uint64)BusAddr::R >> 32;
! 1857: }
! 1858: maskh |= (~data & m68030TT::FCMASK_MASK) << 1;
! 1859:
! 1860: base = (((uint64)baseh) << 32) | basel;
! 1861: mask = (((uint64)maskh) << 32) | maskl;
! 1862: }
! 1863:
! 1864: // アクセスがこの TT とマッチするか調べる。
! 1865: // laddr の上位ワードはそのまま使える。
! 1866: // 下位32ビットが論理アドレス (下24ビットは呼び出し側でマスクしなくてよい)。
! 1867: // この TT が有効でアドレスがマッチすれば true を返す。
! 1868: // (TT:E は演算に織り込んである、このすぐ上の SetTT() のコメント参照)
1.1.1.8 root 1869: bool
1.1.1.15! root 1870: m68030TT::Match(busaddr laddr) const
1.1 root 1871: {
1.1.1.15! root 1872: bool rv = (((laddr.Get() ^ base) & mask) == 0);
! 1873: return rv;
! 1874: }
1.1 root 1875:
1.1.1.15! root 1876: // 成功すれば true、MMU 構成例外が起きる場合は false を返す。
! 1877: // 呼び出し側で例外を起こすこと。
! 1878: bool
! 1879: MPU68030Device::SetTC(uint32 data)
! 1880: {
! 1881: // マスクしてレジスタイメージにセット
! 1882: reg.tc = data & m68030TC::MASK;
1.1 root 1883:
1.1.1.15! root 1884: // data を分解してメンバにセット。
! 1885: if (mmu_tc.Set(data) == false) {
! 1886: // エラーなら E をクリアして MMU 構成例外。
! 1887: // (例外処理自体は呼び出し側で行う)
! 1888: mmu_enable_changed();
! 1889: return false;
1.1 root 1890: }
1.1.1.15! root 1891:
! 1892: // ATC にも通知。
! 1893: atc.SetTC(mmu_tc);
! 1894:
! 1895: #if defined(M68030_CUSTOM_ATC)
! 1896: // ハッシュも全部作り直す。
! 1897: atc.Rehash();
! 1898: #endif
! 1899:
! 1900: // TC:E が変更されていればスイッチ更新
! 1901: mmu_enable_changed();
! 1902: return true;
1.1 root 1903: }
1904:
1.1.1.15! root 1905: // TC レジスタへの書き込み値をこのクラスにセットして二次変数も再構成する。
! 1906: // E をオンにした場合は構成チェックを行い、問題なければ true を返す。
! 1907: // 問題があった場合は E をオフにした上で false を返す。
! 1908: // E をオフにした場合はチェックは行わず true を返す。
1.1 root 1909: bool
1.1.1.15! root 1910: m68030TC::Set(uint32 data)
1.1 root 1911: {
1.1.1.15! root 1912: // 一次メンバにセット。
! 1913: e = data & TC_E;
! 1914: sre = data & TC_SRE;
! 1915: fcl = data & TC_FCL;
! 1916: tid = (data) & 0x0f;
! 1917: tic = (data >>= 4) & 0x0f;
! 1918: tib = (data >>= 4) & 0x0f;
! 1919: tia = (data >>= 4) & 0x0f;
! 1920: is = (data >>= 4) & 0x0f;
! 1921: ps = (data >>= 4) & 0x0f;
1.1 root 1922:
1.1.1.15! root 1923: config_ok = Check();
! 1924:
! 1925: // 構成が有効なら E に関わらず二次変数を用意しておく。
! 1926: if (config_ok) {
! 1927: MakeMask();
1.1 root 1928: }
1929:
1.1.1.15! root 1930: // E がオンで構成例外になる場合はオフにする。
! 1931: if (e && config_ok == false) {
! 1932: e = false;
1.1.1.10 root 1933: return false;
1934: }
1.1.1.15! root 1935:
1.1 root 1936: return true;
1937: }
1938:
1.1.1.15! root 1939: // MMU 構成例外になるケースなら false を返す。
1.1 root 1940: bool
1.1.1.15! root 1941: m68030TC::Check() const
1.1 root 1942: {
1.1.1.15! root 1943: if (ps < 8) {
! 1944: return false;
1.1.1.12 root 1945: }
1.1.1.15! root 1946: if (tia == 0) {
! 1947: return false;
1.1 root 1948: }
1.1.1.15! root 1949: if (tib == 0 && (tic > 0 || tid > 0)) {
1.1.1.10 root 1950: return false;
1951: }
1.1.1.15! root 1952: if (tic == 0 && tid > 0) {
! 1953: return false;
! 1954: }
! 1955: if (ps + is + tia + tib + tic + tid != 32) {
1.1.1.10 root 1956: return false;
1.1 root 1957: }
1958: return true;
1959: }
1960:
1.1.1.15! root 1961: // 二次変数を作成。(E には影響されない)
! 1962: void
! 1963: m68030TC::MakeMask()
1.1 root 1964: {
1.1.1.15! root 1965: // lmask (論理マスク) は TIA〜TID 部分のマスク。
! 1966: // pgmask (ページマスク) は PS 部分のマスク。
! 1967: //
! 1968: // 例えば IS=$8, TIA=$3, TIB=$4, TIC=$4, TID=$0, PS=$d
! 1969: // (X68030 IPL 内の MMU 判定のケース) なら
! 1970: //
! 1971: // 3 2 1 0
! 1972: // 10987654 32109876 54321098 76543210
! 1973: // +--------+--------+--------+--------+
! 1974: // |IIIIIIII AAABBBBC CCCPPPPP PPPPPPPP|
! 1975: // +--------+--------+--------+--------+
! 1976: // lmask = 00000000 11111111 11100000 00000000
! 1977: // pgmask = 00000000 00000000 00011111 11111111
! 1978: lmask = (1U << (tia + tib + tic + tid)) - 1;
! 1979: lmask <<= ps;
! 1980: pgmask = (1U << ps) - 1;
1.1 root 1981:
1.1.1.15! root 1982: // アドレスを (IS,) TIA 〜 TID に分解。
! 1983: //
! 1984: // TIA=$C, TIB=$A, (TIC = TID = 0), PS=$A の場合以下のようにする。
! 1985: //
! 1986: // A B PS
! 1987: // +----------------+--------------+--------------+
! 1988: // $00A01A00 | 0000 0000 1010 | 0000 0001 10 | xx xxxx xxxx |
! 1989: // +----------------+--------------+--------------+
! 1990: // shift[TIA]=20 shift[TIB]=10
! 1991: // mask[TIA]=0xfff mask[TIB]=0x3ff
! 1992: constexpr int LV_TIA = m68030PageTableOp::LV_TIA;
! 1993: constexpr int LV_TID = m68030PageTableOp::LV_TID;
! 1994: uint sh = ps;
! 1995: for (int i = LV_TID; i >= LV_TIA; i--) {
! 1996: if (tix[i] != 0) {
! 1997: shift[i] = sh;
! 1998: mask[i] = (1U << tix[i]) - 1;
! 1999: sh += tix[i];
1.1.1.12 root 2000: }
2001: }
2002:
1.1.1.15! root 2003: // 最終段を求めておく。
! 2004: termlv = LV_TID;
! 2005: for (int i = LV_TIA; i <= LV_TID; i++) {
! 2006: if (tix[i] == 0) {
! 2007: termlv = i - 1;
! 2008: break;
1.1.1.9 root 2009: }
1.1 root 2010: }
2011: }
2012:
1.1.1.15! root 2013: void
! 2014: MPU68030Device::SetMMUSR(uint16 data)
! 2015: {
! 2016: reg.mmusr = data & m68030MMUSR::MASK;
! 2017: }
! 2018:
! 2019:
1.1 root 2020: //
2021: // 命令
2022: //
2023:
1.1.1.8 root 2024: // ir2 の下位5ビットから FC を返す。
1.1 root 2025: // 不当パターンなら C++ の例外をスローする。
1.1.1.8 root 2026: uint32
1.1.1.11 root 2027: MPU68030Device::get_fc()
1.1 root 2028: {
1.1.1.8 root 2029: uint fcfield = ir2 & 0x1f;
1.1 root 2030:
2031: if (fcfield == 0) {
1.1.1.9 root 2032: return reg.GetSFC();
1.1 root 2033: } else if (fcfield == 1) {
1.1.1.9 root 2034: return reg.GetDFC();
1.1 root 2035: } else if (fcfield < 0x08) {
2036: // break;
2037: } else if (fcfield < 0x10) {
1.1.1.8 root 2038: return reg.D[fcfield & 7] & 7;
1.1 root 2039: } else if (fcfield < 0x18) {
2040: return fcfield & 7;
2041: }
2042: // 不当命令
1.1.1.8 root 2043: throw M68K::EXCEP_ILLEGAL;
1.1 root 2044: }
2045:
2046: // MMU 不当命令 (2ワード目で確定)
1.1.1.8 root 2047: void
1.1.1.15! root 2048: MPU68030Device::ops_mmu30_illg2()
1.1 root 2049: {
1.1.1.8 root 2050: putlog(1, "MMU illegal instruction %04x %04x", ir, ir2);
2051: Exception(M68K::EXCEP_FLINE);
1.1 root 2052: }
2053:
1.1.1.15! root 2054: // PFLUSHA 命令
! 2055: void
! 2056: MPU68030Device::ops_mmu30_pflusha()
! 2057: {
! 2058: atc.FlushAll();
! 2059: }
! 2060:
1.1 root 2061: // PFLUSH fc,#mask 命令 (EA を持たない方)
2062: void
1.1.1.15! root 2063: MPU68030Device::ops_mmu30_pflush()
1.1 root 2064: {
1.1.1.15! root 2065: uint32 mask = (ir2 >> 5) & 7;
! 2066: uint32 fc = get_fc();
1.1 root 2067:
1.1.1.15! root 2068: atc.Flush(fc, mask);
1.1 root 2069: }
2070:
2071: // PFLUSH fc,#mask,ea 命令
2072: void
1.1.1.15! root 2073: MPU68030Device::ops_mmu30_pflush_ea()
1.1 root 2074: {
1.1.1.15! root 2075: uint32 mask = (ir2 >> 5) & 7;
! 2076: uint32 fc = get_fc();
! 2077: uint32 ea = cea_copro();
1.1.1.12 root 2078: ea &= mmu_tc.lmask;
1.1.1.15! root 2079:
! 2080: atc.Flush(fc, mask, ea);
1.1 root 2081: }
2082:
2083: // PLOADR/PLOADW 命令
2084: void
1.1.1.15! root 2085: MPU68030Device::ops_mmu30_pload()
1.1 root 2086: {
2087: uint32 ea;
2088: uint32 fc;
1.1.1.10 root 2089: busaddr laddr;
1.1.1.15! root 2090: m68030ATCLine *a;
1.1 root 2091:
1.1.1.8 root 2092: if ((ir2 & 0x01e0) != 0) {
1.1.1.15! root 2093: ops_mmu30_illg2();
1.1 root 2094: return;
2095: }
1.1.1.8 root 2096: ea = cea_copro();
2097: fc = get_fc();
1.1 root 2098:
2099: // 実効アドレスを論理アドレスとする ATC を作成。
2100: // ってこれでいいのか?
1.1.1.8 root 2101: if ((ir2 & 0x0200)) {
1.1.1.10 root 2102: laddr = busaddr(ea) | busaddr::FC(fc) | BusAddr::R;
1.1 root 2103: } else {
1.1.1.10 root 2104: laddr = busaddr(ea) | busaddr::FC(fc) | BusAddr::W;
2105: }
2106:
1.1.1.15! root 2107: #if defined(M68030_CUSTOM_ATC)
! 2108: auto table = atc.GetTable(fc);
1.1.1.10 root 2109: uint8 n = table->hash[laddr.Addr() >> 12];
1.1.1.12 root 2110: // PS<4KB のケースを先に解決する。
1.1.1.15! root 2111: if (__predict_false(n == m68030ATCTable_Custom::HASH_SUB)) {
1.1.1.12 root 2112: a = table->FindSub(laddr);
2113: if (a) {
2114: n = a - &table->line[0];
1.1.1.10 root 2115: } else {
1.1.1.15! root 2116: n = m68030ATCTable_Custom::HASH_NONE;
1.1.1.10 root 2117: }
2118: }
1.1.1.12 root 2119: if ((int8)n >= 0) {
2120: // すでにエントリがある場合
2121: if (laddr.IsWrite()) {
2122: // 命令が ploadw でエントリが WProtect==0 && Modified==0 なら、
2123: // 一回破棄して書き込み用に作り直す。
2124: a = &table->line[n];
2125: if (a->IsWProtect() == false && a->IsModified() == false) {
2126: table->Invalidate(a);
1.1.1.15! root 2127: PageTableSearch(laddr, a);
1.1.1.12 root 2128: }
2129: }
1.1.1.15! root 2130: } else if (n == m68030ATCTable_Custom::HASH_NONE) {
1.1.1.10 root 2131: if (__predict_true(mmu_tc.e)) {
2132: // TC.E 有効で対応なしなら、テーブルサーチを行って
2133: // ATC を作成。
1.1.1.15! root 2134: a = table->Pop();
! 2135: PageTableSearch(laddr, a);
1.1.1.10 root 2136: } else {
2137: // TC.E 無効なら何もしない?
2138: }
1.1.1.15! root 2139: } else if (n == m68030ATCTable_Custom::HASH_TT) {
1.1.1.10 root 2140: // TT の領域なら何もしてはいけない。
1.1 root 2141: }
1.1.1.15! root 2142: #else
! 2143: // TT の領域なら何もしてはいけない。
! 2144: if (TTMatch(laddr).IsOK()) {
! 2145: return;
! 2146: }
! 2147: // TE.E 無効なら何もしない?
! 2148: if (__predict_false(mmu_tc.e == false)) {
! 2149: return;
! 2150: }
! 2151: auto table = atc.GetTable();
! 2152: a = table->LookupAndMove(laddr);
! 2153: if (a) {
! 2154: if (laddr.IsWrite()) {
! 2155: // 命令が ploadw でエントリが Modified==0 (&& WProtect==0) なら、
! 2156: // 一回破棄して Modified で作り直す。
! 2157: if (a->IsWProtect() == false && a->IsModified() == false) {
! 2158: PageTableSearch(laddr, a);
! 2159: }
! 2160: }
! 2161: } else {
! 2162: // テーブルサーチを行って ATC を作成。
! 2163: a = table->Pop();
! 2164: PageTableSearch(laddr, a);
! 2165: }
! 2166: #endif
1.1 root 2167: }
2168:
2169: // PTESTR/PTESTW 命令
2170: void
1.1.1.15! root 2171: MPU68030Device::ops_mmu30_ptest()
1.1 root 2172: {
1.1.1.15! root 2173: // 第2ワード
! 2174: // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
! 2175: // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
! 2176: // | 1 | 0 | 0 | Level |R/W| A | ARegNo | FC |
! 2177: // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
! 2178: uint level = (ir2 >> 10) & 7;
! 2179: bool isread = (ir2 & 0x0200);
! 2180: uint rn = (ir2 >> 5) & 0xf;
! 2181:
! 2182: uint32 ea = cea_copro();
! 2183: uint32 fc = get_fc();
1.1 root 2184: uint16 mmusr;
2185:
1.1.1.15! root 2186: busaddr laddr = busaddr(ea)
! 2187: | busaddr::FC(fc)
! 2188: | (isread ? BusAddr::R : BusAddr::W);
1.1 root 2189:
2190: if (level == 0) {
1.1.1.15! root 2191: // レベル0で A 指定なら Fライン例外。
! 2192: // A が 0 なら ARegNo も 0 でなければならない、とは書いてあるが
! 2193: // どうなるかは書いていない。とりあえず例外にしておく。
1.1.1.8 root 2194: if (rn != 0) {
1.1.1.15! root 2195: ops_mmu30_illg2();
1.1.1.8 root 2196: return;
2197: }
1.1.1.13 root 2198: CYCLE(22);
1.1.1.15! root 2199: mmusr = ptest_atc(laddr);
! 2200: } else {
! 2201: // レベル 1-7 ならテーブルサーチを実行。
1.1 root 2202:
1.1.1.15! root 2203: // A が 0 なら ARegNo も 0 でなければならない、とは書いてあるが
! 2204: // どうなるかは書いていない。とりあえず例外にしておく。
! 2205: if (__predict_false(1 <= rn && rn < 8)) {
! 2206: ops_mmu30_illg2();
! 2207: return;
1.1.1.10 root 2208: }
1.1.1.15! root 2209: CYCLE(88);
! 2210: mmusr = ptest_search(level, laddr, rn);
! 2211: }
! 2212:
! 2213: SetMMUSR(mmusr);
! 2214: }
! 2215:
! 2216: // PTESTR/PTESTW 命令の #0 の場合は ATC。
! 2217: uint32
! 2218: MPU68030Device::ptest_atc(const busaddr laddr)
! 2219: {
! 2220: const m68030ATCLine *a = NULL;
! 2221: uint16 mmusr = 0;
1.1.1.8 root 2222:
1.1.1.15! root 2223: #if defined(M68030_CUSTOM_ATC)
! 2224: uint fc = laddr.GetFC();
! 2225: auto table = atc.GetTable(fc);
! 2226: uint8 n = table->hash[laddr.Addr() >> 12];
! 2227: // PS<4KB のケースを先に解決する。
! 2228: if (__predict_false(n == m68030ATCTable_Custom::HASH_SUB)) {
! 2229: a = table->FindSub(laddr);
1.1.1.8 root 2230: if (a) {
1.1.1.15! root 2231: n = a - &table->line[0];
! 2232: } else {
! 2233: n = m68030ATCTable_Custom::HASH_NONE;
! 2234: a = NULL;
1.1 root 2235: }
1.1.1.15! root 2236: }
! 2237: if (__predict_true((int8)n >= 0)) {
! 2238: // ATC #(n) で見付かった。
! 2239: a = &table->line[n];
! 2240: } else if (n == m68030ATCTable_Custom::HASH_TT) {
! 2241: mmusr |= m68030MMUSR::T;
1.1 root 2242: } else {
1.1.1.15! root 2243: // ATC に見付からなかった。
! 2244: }
! 2245: #else
! 2246: if (TTMatch(laddr).IsOK()) {
! 2247: mmusr |= m68030MMUSR::T;
! 2248: }
! 2249: auto table = atc.GetTable();
! 2250: a = table->LookupOnly(laddr);
! 2251: #endif
! 2252:
! 2253: if (a) {
! 2254: // 見付かれば Modified 等の状態を返す。
! 2255: if (a->IsBusError()) {
1.1 root 2256: mmusr |= m68030MMUSR::B;
1.1.1.15! root 2257: } else if (a->IsWProtect()) {
1.1.1.8 root 2258: mmusr |= m68030MMUSR::W;
1.1.1.15! root 2259: } else if (a->IsModified()) {
1.1.1.8 root 2260: mmusr |= m68030MMUSR::M;
2261: }
1.1.1.15! root 2262: } else {
! 2263: // 見付からなかったら Invalid を立てる。
! 2264: mmusr |= m68030MMUSR::I;
! 2265: }
! 2266: return mmusr;
! 2267: }
1.1.1.8 root 2268:
1.1.1.15! root 2269: // PTESTR/PTESTW 命令の #1-7 の場合はテーブルサーチ。
! 2270: uint32
! 2271: MPU68030Device::ptest_search(uint level, const busaddr laddr, uint rn)
! 2272: {
! 2273: uint fc = laddr.GetFC();
! 2274: uint16 mmusr = 0;
! 2275:
! 2276: putlog(3, "ptest_search lv=%u %u(%s).%08x", level,
! 2277: fc, FCstr(fc),
! 2278: laddr.Addr());
! 2279:
! 2280: m68030PageTableOp pt(this, loglevel, m68030PageTableOp::ByPTEST);
! 2281: pt.Search(level, laddr);
! 2282:
! 2283: if (pt.m_berr) {
! 2284: mmusr |= m68030MMUSR::B;
! 2285: }
! 2286: if (pt.m_lim) {
! 2287: // XXX not tested
! 2288: mmusr |= m68030MMUSR::L;
! 2289: }
! 2290: if (pt.m_s && (fc & 4)) {
! 2291: // XXX not tested
! 2292: mmusr |= m68030MMUSR::S;
! 2293: }
! 2294: if (pt.m_wp) {
! 2295: mmusr |= m68030MMUSR::W;
! 2296: }
! 2297: if (pt.result == m68030PageTableOp::Result::Invalid) {
! 2298: mmusr |= m68030MMUSR::I;
! 2299: }
! 2300: if (pt.m_m) {
! 2301: mmusr |= m68030MMUSR::M;
! 2302: }
! 2303: mmusr |= pt.m_n;
! 2304:
! 2305: // アドレスレジスタ指定があれば、最後のディスクリプタアドレスを返す。
! 2306: // rn は有効なら 8-15。
! 2307: if (rn != 0) {
! 2308: // XXX 途中でバスエラーが起きるケースは未実装
! 2309: if (pt.m_berr == false) {
! 2310: reg.R[rn] = pt.m_lastaddr;
1.1.1.8 root 2311: }
1.1 root 2312: }
2313:
1.1.1.15! root 2314: return mmusr;
1.1 root 2315: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.