Annotation of nono/m680x0/m68030mmu.cpp, revision 1.1.1.15

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.