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

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: #define TIA            (0)
                     12: #define TIB            (1)
                     13: #define TIC            (2)
                     14: #define TID            (3)
                     15: #define FCL            (4)     // 便宜上ね
                     16: #define MAX_LV (5)
                     17: 
                     18: // ディスクリプタタイプ
                     19: #define GET_DT(x)      (x & 0x03)
                     20: #define DT_INVALID     (0)
                     21: #define DT_PAGE                (1)
                     22: #define DT_VALID4      (2)
                     23: #define DT_VALID8      (3)
                     24: 
                     25: // ディスクリプタ
                     26: #define MMU_DESC_S     (0x00000100)
                     27: #define MMU_DESC_CI    (0x00000040)
1.1.1.4   root       28: #define MMU_DESC_M     (0x00000010)
                     29: #define MMU_DESC_U     (0x00000008)
1.1       root       30: #define MMU_DESC_WP    (0x00000004)
                     31: #define MMU_DESC_DT    (0x00000003)
                     32: 
                     33: // エントリの状態
                     34: enum ATCtype {
                     35:        INVALID,
                     36:        NORMAL,
                     37:        EARLY,
                     38:        INDIRECT,
                     39: };
                     40: 
                     41: //
                     42: // デバッグ表示
                     43: //
                     44: 
                     45: // デバッグ表示のため値の表示に必要な桁数を返す
                     46: #define NIBBLE(x)              ( (x) == 0 ? 1 : ((x) + 3) / 4 )
                     47: 
1.1.1.6   root       48: // インデント付き string_format
                     49: static std::string indent_format(int indent, const char *fmt, ...)
                     50:        __printflike(2, 3);
                     51: static std::string
                     52: indent_format(int indent, const char *fmt, ...)
1.1       root       53: {
                     54:        va_list ap;
1.1.1.6   root       55:        char *buf;
1.1       root       56: 
                     57:        va_start(ap, fmt);
1.1.1.6   root       58:        vasprintf(&buf, fmt, ap);
1.1       root       59:        va_end(ap);
                     60: 
1.1.1.6   root       61:        std::string rv(indent, ' ');
                     62:        rv += buf;
                     63:        free(buf);
1.1       root       64: 
1.1.1.6   root       65:        return rv;
                     66: }
1.1       root       67: 
1.1.1.9   root       68: #define MMULOG(lv, indent, arg...) do { \
                     69:        if (__predict_false(loglevel >= (lv))) {        \
                     70:                cpu->putlogf(lv, [&, lam_func = __func__] { \
                     71:                        (void)lam_func; \
                     72:                        return indent_format(indent, arg); \
                     73:                });     \
                     74:        }       \
                     75: } while (0)
1.1       root       76: 
                     77: // テーブルサーチ操作クラス
1.1.1.3   root       78: class m68030MMU final
1.1       root       79: {
                     80:  public:
1.1.1.11  root       81:        m68030MMU(MPU68030Device *, int loglevel_);
1.1.1.8   root       82:        ~m68030MMU();
1.1       root       83: 
                     84:        // サーチを実行
1.1.1.10  root       85:        void search(int level, busaddr laddr);
1.1       root       86: 
                     87:        // ATC にエントリを作成
                     88:        void make_atc(m68030ATCLine *);
                     89: 
                     90:        // ATC にエントリを作成 (デバッガアクセス用)
1.1.1.9   root       91:        busaddr make_atc_debugger() const;
1.1       root       92: 
                     93:  private:
                     94:        // リミットチェック
                     95:        bool limitcheck() const;
                     96: 
                     97:        // ディスクリプタを取得
                     98:        bool fetch_desc(uint32, int);
                     99: 
                    100:        // ディスクリプタを表示用に (デバッグ用)
                    101:        std::string sprintf_desc();
                    102: 
                    103:        // ディスクリプタタイプ文字列 (デバッグ用)
                    104:        const char *dtname(uint32 dt);
                    105: 
                    106:  public:
1.1.1.9   root      107:        ATCtype m_type {};              // 状態
                    108:        bool    m_s {};                 // S ビット
                    109:        bool    m_wp {};                // WP ビット
                    110:        bool    m_ci {};                // CI ビット
                    111:        bool    m_m {};                 // ページディスクリプタの M が立っているか
                    112:        bool    m_lim {};               // サーチ中にリミットを越えた
                    113:        bool    m_berr {};              // テーブルサーチ中にバスエラー
                    114:        bool    m_ptest {};             // PTEST[RW] 命令中なら真
                    115:        bool    m_debugger {};  // デバッガからのアクセスなら true にする
                    116:        int     x {};                   // 段数 TIA〜TID, FCL
                    117:        uint32  m_lastaddr {};  // 最後にアクセスしたディスクリプタアドレス(PTEST)
1.1.1.8   root      118:  private:
1.1.1.9   root      119:        int     m_lv[MAX_LV] {};
                    120:        uint32  m_desc1 {};             // ディスクリプタ
                    121:        uint32  m_desc2 {};             // ロングディスクリプタの後半部
                    122:        int     m_descsize {};  // ディスクリプタサイズ (4 or 8)
                    123:        int     m_lastsize {};  // 前段のディスクリプタサイズ
1.1       root      124: 
                    125:        // デバッグ用文字列
                    126:        static const char * const m_lvname[MAX_LV];
                    127: 
1.1.1.9   root      128:        int loglevel {};
                    129: 
1.1.1.11  root      130:        MPU68030Device *cpu;
1.1       root      131: };
                    132: 
                    133: // 現在の TC:E、TT:E の状態に応じてアドレス変換の有無を切り替える。
                    134: // mmu_enable ならアドレス変換ありのバスアクセスを使う。see m68030bus.cpp
                    135: void
1.1.1.11  root      136: MPU68030Device::mmu_enable_changed()
1.1       root      137: {
                    138:        bool enable;
                    139: 
                    140:        // TC、TT0、TT1 のいずれかが有効ならアドレス変換あり
                    141:        enable = (mmu_tc.e || mmu_tt[0].e || mmu_tt[1].e);
                    142: 
                    143:        // 変化した時だけ
                    144:        if (mmu_enable && !enable) {
                    145:                // 無効にする
                    146:                mmu_enable = false;
1.1.1.8   root      147:                putlog(1, "MMU Translation Disabled");
1.1       root      148:        } else if (!mmu_enable && enable) {
                    149:                // 有効にする
                    150:                mmu_enable = true;
1.1.1.8   root      151:                putlog(1, "MMU Translation Enabled");
1.1       root      152:        }
                    153: }
                    154: 
                    155: 
                    156: //
                    157: // レジスタクラス
                    158: //
                    159: 
1.1.1.8   root      160: bool
1.1.1.11  root      161: MPU68030Device::SetSRP(uint32 h, uint32 l)
1.1       root      162: {
1.1.1.8   root      163:        if ((h & m68030RP::DT_MASK) == 0) {
                    164:                return false;
                    165:        }
1.1       root      166:        reg.srp.h = h & m68030RP::H_MASK;
                    167:        reg.srp.l = l & m68030RP::L_MASK;
1.1.1.8   root      168:        return true;
1.1       root      169: }
                    170: 
1.1.1.8   root      171: bool
1.1.1.11  root      172: MPU68030Device::SetCRP(uint32 h, uint32 l)
1.1       root      173: {
1.1.1.8   root      174:        if ((h & m68030RP::DT_MASK) == 0) {
                    175:                return false;
                    176:        }
1.1       root      177:        reg.crp.h = h & m68030RP::H_MASK;
                    178:        reg.crp.l = l & m68030RP::L_MASK;
1.1.1.8   root      179:        return true;
1.1       root      180: }
                    181: 
                    182: void
1.1.1.11  root      183: MPU68030Device::SetTT(int n, uint32 data)
1.1       root      184: {
                    185:        uint32 lbase;
                    186:        uint32 lmask;
                    187:        uint32 sbase;
                    188:        uint32 smask;
                    189: 
1.1.1.10  root      190:        // ハッシュから TT#n の成分を一旦クリアする。
                    191:        if (mmu_tt[n].e) {
                    192:                // 変更前の TT#n にマッチして、現在の TT#(1-n) にマッチしない
                    193:                // アドレスブロックに対応する 4096 エントリをクリア。
1.1.1.12! root      194:                for (int fc = 1; fc <= 6; fc++) {
1.1.1.10  root      195:                        auto *table = atc.fctables[fc];
                    196:                        if (table) {
                    197:                                busaddr laddr = busaddr(0) | busaddr::FC(fc);
                    198:                                for (int i = 0; i < 256; i++) {
                    199:                                        // XXX R/W は?
                    200:                                        if (mmu_tt[n].Match(laddr) &&
                    201:                                                mmu_tt[1 - n].Match(laddr) == false)
                    202:                                        {
                    203:                                                uint32 baseidx = i << (24 - 12);
                    204:                                                memset(&table->hash[baseidx],
                    205:                                                        m68030ATCTable::HASH_NONE, 4096);
                    206:                                        }
                    207:                                        laddr += 0x0100'0000;
                    208:                                }
                    209:                        }
                    210:                }
                    211:        }
                    212: 
1.1       root      213:        // マスクしてレジスタイメージにセット
                    214:        reg.tt[n] = data & m68030TT::MASK;
                    215: 
                    216:        // data を分解してメンバにセット。
                    217:        //
                    218:        //    3                   2                   1                   0
                    219:        //  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
                    220:        // +---------------+---------------+-+-+-+-+-+-+-+-+-+-----+-+-----+
                    221:        // |LogicalAddrBase|LogicalAddrMask|E|       |C|R|M| | FCB | | FCM |
                    222:        // +---------------+---------------+-+-+-+-+-+-+-+-+-+-----+-+-----+
                    223: 
                    224:        mmu_tt[n].e      = (data & m68030TT::E);
                    225:        mmu_tt[n].ci     = (data & m68030TT::CI);
                    226: 
                    227:        // 下位ロングワード
                    228:        // ・lbase は比較先アドレスで、ff000000の位置。
                    229:        // ・lmask はアドレスマスク。%1 が無視を表しているので NOT しておく。
                    230:        //
                    231:        //  target  AAAAAAAA xxxxxxxx xxxxxxxx xxxxxxxx : Aは論理アドレス
                    232:        //        ^                                     : xは不問
                    233:        //  lbase   BBBBBBBB 00000000 00000000 00000000 : Bは論理アドレスベース
                    234:        //        &
                    235:        //  lmask   MMMMMMMM 00000000 00000000 00000000 : Mは論理アドレスマスク
                    236:        //
                    237:        lbase  = (data & m68030TT::LBASE_MASK);
                    238:        lmask  = (~data & m68030TT::LMASK_MASK) << 8;
                    239: 
                    240:        // 上位ロングワード
                    241:        // ・E ビットは位置はどこでもいいので TT:E ビットの
                    242:        //   位置をそのまま使用する。target 側のこの位置は必ずビットが落ちている
                    243:        //   ため base 側にビットを立てておくと必ず不一致になることを利用して、
                    244:        //   E ビットの評価も一度に行う。
                    245:        // ・RW ビットは SSW の RW と比較するので、SSW_RW のビット位置を使う
1.1.1.8   root      246:        //   (0x00000040 の位置)。TT:RW も SSW_RW も Read が %1。
1.1       root      247:        // ・RWM が %1 なら R/W は無視、RWM が %0 なら R/W の一致も検査するので
                    248:        //   RWM が %0 の時は RW のマスクビットを立てておく。
1.1.1.9   root      249:        // ・FC は busaddr の配置に合わせて1ビット左シフトしたところを使う。
1.1       root      250:        // ・FCMask は %1 が無視を表しているので NOT しておく。
                    251:        //
1.1.1.9   root      252:        //  target  00000000 00000000 00000000 0R00FFFS : R はアクセス R/~W
1.1       root      253:        //        ^                                     : F はアクセス FC
1.1.1.9   root      254:        //                                              : S は FC2 のコピー
                    255:        //  base    00000000 00000000 E0000000 0R00FFF0 : R は期待する R/~W
1.1       root      256:        //        &                                     : F は期待する FC
                    257:        //                                              : E は ~TT:E
1.1.1.9   root      258:        //  mask    00000000 00000000 10000000 0M00CCC0 : M は R/~W マスク
1.1       root      259:        //                                              : C は FC マスク
1.1.1.8   root      260:        sbase  = ~data & m68030TT::E;
                    261:        sbase |= (data & m68030TT::RW) >> 3;
1.1.1.9   root      262:        sbase |= (data & m68030TT::FCBASE_MASK) >> 3;
1.1       root      263:        smask  = m68030TT::E;
                    264:        if ((data & m68030TT::RWM) == 0) {
1.1.1.11  root      265:                smask |= M68030::SSW_RW;
1.1       root      266:        }
1.1.1.9   root      267:        smask |= (~data & m68030TT::FCMASK_MASK) << 1;
1.1       root      268: 
                    269:        mmu_tt[n].base = lbase | (((uint64)sbase) << 32);
                    270:        mmu_tt[n].mask = lmask | (((uint64)smask) << 32);
                    271: 
1.1.1.10  root      272:        // 更新後にこの TT が有効ならハッシュも更新。
                    273:        if (mmu_tt[n].e) {
                    274:                // この TT#n にマッチするアドレスブロックに対応する 4096 エントリを
                    275:                // TT 印にする。
1.1.1.12! root      276:                for (uint fc = 1; fc <= 6; fc++) {
1.1.1.10  root      277:                        auto *table = atc.fctables[fc];
                    278:                        if (table) {
                    279:                                busaddr laddr = busaddr(0) | busaddr::FC(fc);
                    280:                                for (uint i = 0; i < 256; i++) {
                    281:                                        // XXX R/W は?
                    282:                                        if (mmu_tt[n].Match(laddr)) {
                    283:                                                uint32 baseidx = i << (24 - 12);
                    284:                                                memset(&table->hash[baseidx],
                    285:                                                        m68030ATCTable::HASH_TT, 4096);
                    286:                                        }
                    287:                                        laddr += 0x0100'0000;
                    288:                                }
                    289:                        }
1.1       root      290:                }
                    291:        }
                    292: 
                    293:        mmu_enable_changed();
                    294: }
                    295: 
1.1.1.5   root      296: // アクセスがこの TT とマッチするか調べる。
1.1.1.9   root      297: // laddr の上位ワードはそのまま使える。
                    298: // 下位32ビットが論理アドレス (下24ビットは呼び出し側でマスクしなくてよい)。
1.1.1.5   root      299: // この TT が有効でアドレスがマッチすれば true を返す。
                    300: // (TT:E は演算に織り込んである、このすぐ上の SetTT() のコメント参照)
1.1       root      301: bool
1.1.1.9   root      302: m68030TT::Match(busaddr laddr) const
1.1       root      303: {
1.1.1.9   root      304:        bool rv = (((laddr.Get() ^ base) & mask) == 0);
1.1       root      305:        return rv;
                    306: }
                    307: 
                    308: // 成功すれば true、MMU 構成例外が起きる場合は false を返す。
                    309: // 呼び出し側で例外を起こすこと。
                    310: bool
1.1.1.11  root      311: MPU68030Device::SetTC(uint32 data)
1.1       root      312: {
                    313:        // マスクしてレジスタイメージにセット
                    314:        reg.tc = data & m68030TC::MASK;
                    315: 
                    316:        // data を分解してメンバにセット
                    317:        mmu_tc.Set(data);
                    318: 
                    319:        // 有効にする場合は一貫性を確認
                    320:        if (mmu_tc.e) {
                    321:                if (mmu_tc.Check() == false) {
                    322:                        // エラーなら E をクリアして MMU 構成例外。
                    323:                        // (例外処理自体は呼び出し側で行う)
1.1.1.9   root      324:                        mmu_tc.e = false;
1.1       root      325:                        mmu_enable_changed();
                    326:                        return false;
                    327:                }
                    328:        }
                    329: 
                    330:        // マスクをあらかじめ用意しておく
                    331:        mmu_tc.MakeMask();
                    332: 
1.1.1.10  root      333:        // ATC でも PS を覚えておく必要がある。
                    334:        for (int i = 0; i < countof(atc.tables); i++) {
                    335:                auto *table = atc.tables[i].get();
                    336:                table->ps4k = mmu_tc.ps - 12;
                    337:        }
                    338: 
1.1       root      339:        // TC:E が変更されていればスイッチ更新
                    340:        mmu_enable_changed();
                    341:        return true;
                    342: 
                    343: }
                    344: 
                    345: // TC レジスタへの書き込み値を m68030TC クラスにセットする。
                    346: void
                    347: m68030TC::Set(uint32 data)
                    348: {
                    349:        e   = data & TC_E;
                    350:        sre = data & TC_SRE;
                    351:        fcl = data & TC_FCL;
                    352:        tid = (data)       & 0x0f;
                    353:        tic = (data >>= 4) & 0x0f;
                    354:        tib = (data >>= 4) & 0x0f;
                    355:        tia = (data >>= 4) & 0x0f;
                    356:        is  = (data >>= 4) & 0x0f;
                    357:        ps  = (data >>= 4) & 0x0f;
                    358: }
                    359: 
                    360: // 現在の値が MMU 構成例外になるなら false を返す。
                    361: bool
                    362: m68030TC::Check() const
                    363: {
                    364:        if (ps < 8)
                    365:                return false;
                    366:        if (tia == 0)
                    367:                return false;
                    368:        if (tib == 0 && (tic > 0 || tid > 0))
                    369:                return false;
                    370:        if (tic == 0 && tid > 0)
                    371:                return false;
                    372:        if (ps + is + tia + tib + tic + tid != 32)
                    373:                return false;
                    374: 
                    375:        return true;
                    376: }
                    377: 
                    378: // 論理マスク、ページマスクを作成。(テーブルサーチで参照される)
                    379: void
                    380: m68030TC::MakeMask()
                    381: {
                    382:        // lmask (論理マスク) は TIA〜TID 部分のマスク。
                    383:        // pgmask (ページマスク) は PS 部分のマスク。
                    384:        //
                    385:        // 例えば IS=$8, TIA=$3, TIB=$4, TIC=$4, TID=$0, PS=$d
                    386:        // (X68030 IPL 内の MMU 判定のケース) なら
                    387:        //
                    388:        //           3          2          1          0
                    389:        //          10987654 32109876 54321098 76543210
                    390:        //         +--------+--------+--------+--------+
                    391:        //         |IIIIIIII AAABBBBC CCCPPPPP PPPPPPPP|
                    392:        //         +--------+--------+--------+--------+
                    393:        // lmask  = 00000000 11111111 11100000 00000000
                    394:        // pgmask = 00000000 00000000 00011111 11111111
                    395:        lmask = (1 << (tia + tib + tic + tid)) - 1;
                    396:        lmask <<= ps;
                    397:        pgmask = (1 << ps) - 1;
                    398: 
                    399:        // アドレスを (IS,) TIA 〜 TID に分解。
                    400:        //
                    401:        // TIA=$C, TIB=$A, (TIC = TID = 0), PS=$A の場合以下のようにする。
                    402:        // 使ってない TIC, TID については tix[TIC] == 0 で判断する。
                    403:        //
                    404:        //                    A              B             PS
                    405:        //           +----------------+--------------+--------------+
                    406:        // $00A01A00 | 0000 0000 1010 | 0000 0001 10 | xx xxxx xxxx |
                    407:        //           +----------------+--------------+--------------+
                    408:        //            shift[TIA]=20     shift[TIB]=10
                    409:        //            mask[TIA]=0xfff   mask[TIB]=0x3ff
1.1.1.10  root      410:        uint sh = ps;
1.1       root      411:        for (int i = TID; i >= TIA; i--) {
                    412:                if (tix[i] > 0) {
                    413:                        shift[i] = sh;
                    414:                        mask[i] = (1 << tix[i]) - 1;
                    415:                        sh += tix[i];
                    416:                }
                    417:        }
                    418: }
                    419: 
                    420: void
1.1.1.11  root      421: MPU68030Device::SetMMUSR(uint16 data)
1.1       root      422: {
                    423:        reg.mmusr = data & m68030MMUSR::MASK;
                    424: }
                    425: 
1.1.1.11  root      426: // リセット例外の CPU 固有部分。
                    427: void
                    428: MPU68030Device::ResetMMU()
                    429: {
                    430:        SetTC(GetTC() & ~m68030TC::TC_E);
                    431:        SetTT(0, GetTT(0) & ~m68030TT::E);
                    432:        SetTT(1, GetTT(1) & ~m68030TT::E);
                    433: }
                    434: 
1.1       root      435: 
                    436: //
                    437: // ATC
                    438: //
1.1.1.10  root      439: // 今となっては独立したクラスである必要はなくなっているが、これを
1.1.1.11  root      440: // MPU68030Device にマージするとパフォーマンスが下がるので維持してある。
1.1.1.10  root      441: //
1.1       root      442: 
                    443: // コンストラクタ
1.1.1.11  root      444: m68030ATC::m68030ATC(MPU68030Device *cpu_)
1.1.1.8   root      445: {
                    446:        cpu = cpu_;
1.1.1.9   root      447: 
1.1.1.10  root      448:        tables[0].reset(new m68030ATCTable(cpu, 1));
                    449:        tables[1].reset(new m68030ATCTable(cpu, 2));
                    450:        tables[2].reset(new m68030ATCTable(cpu, 5));
                    451:        tables[3].reset(new m68030ATCTable(cpu, 6));
                    452: 
                    453:        fctables[1] = tables[0].get();
                    454:        fctables[2] = tables[1].get();
                    455:        fctables[5] = tables[2].get();
                    456:        fctables[6] = tables[3].get();
1.1.1.8   root      457: }
                    458: 
                    459: // デストラクタ
                    460: m68030ATC::~m68030ATC()
1.1       root      461: {
                    462: }
                    463: 
1.1.1.9   root      464: // ログレベルの設定。
                    465: // これは Object のオーバーライドではないが、同じ使い方をする。
                    466: void
                    467: m68030ATC::SetLogLevel(int loglevel_)
                    468: {
                    469:        loglevel = loglevel_;
1.1.1.10  root      470:        for (int i = 0; i < countof(tables); i++) {
                    471:                auto *table = tables[i].get();
                    472:                table->loglevel = loglevel_;
1.1.1.9   root      473:        }
                    474: }
                    475: 
1.1.1.10  root      476: // ATC をすべてフラッシュする。命令からコールされる
1.1       root      477: void
1.1.1.10  root      478: m68030ATC::flush()
1.1       root      479: {
1.1.1.10  root      480:        for (int i = 0; i < countof(tables); i++) {
                    481:                auto *table = tables[i].get();
                    482:                table->Flush();
1.1       root      483:        }
                    484: }
                    485: 
1.1.1.10  root      486: // ATC の fc, mask が一致するエントリをフラッシュする。
                    487: // 命令からコールされる。
1.1       root      488: void
1.1.1.10  root      489: m68030ATC::flush(uint32 fc, uint32 mask)
1.1       root      490: {
1.1.1.12! root      491:        for (uint i = 1; i <= 6; i++) {
1.1.1.10  root      492:                if (((i ^ fc) & mask) == 0) {
                    493:                        // FC が一致したテーブルを全フラッシュ
                    494:                        auto *table = fctables[i];
                    495:                        if (table) {
                    496:                                table->Flush();
                    497:                        }
1.1       root      498:                }
                    499:        }
                    500: }
                    501: 
1.1.1.10  root      502: // ATC の fc, mask, addr が一致するエントリをフラッシュする。
                    503: // 命令からコールされる。
1.1       root      504: void
1.1.1.10  root      505: m68030ATC::flush(uint32 fc, uint32 mask, uint32 addr)
1.1       root      506: {
1.1.1.12! root      507:        for (uint i = 1; i <= 6; i++) {
1.1.1.10  root      508:                if (((i ^ fc) & mask) == 0) {
                    509:                        // FC が一致したテーブルから addr が一致したエントリをフラッシュ
                    510:                        auto *table = fctables[i];
                    511:                        if (table) {
                    512:                                table->Flush(addr);
                    513:                        }
                    514:                }
1.1       root      515:        }
                    516: }
                    517: 
                    518: 
1.1.1.10  root      519: //
                    520: // ATC テーブル
                    521: //
1.1       root      522: 
1.1.1.10  root      523: // アドレス変換の本来のフローはこんな感じだが、
                    524: //
                    525: //  if (TT*.E || TC.E) {
                    526: //     if (TT*.E が有効) {
                    527: //             laddr,RW が TT[01] とマッチしたら、paddr = laddr,CI
                    528: //     }
                    529: //     if (TC.E が有効) {
                    530: //             ATC とマッチしたら、paddr = atc[n].paddr,CI
                    531: //             そうでなければ、 paddr = テーブルサーチ
                    532: //     }
                    533: //  } else {
                    534: //     paddr = laddr (アドレス変換オフ)
                    535: //  }
                    536: //
                    537: // ハッシュで一度に引く。
                    538: //     switch (hash[laddr>>12]) {
                    539: //   case HASH_TT:
                    540: //     laddr,RW が TT[01] とマッチしたら、paddr = laddr,CI
                    541: //   case HASH_ATC:
                    542: //     paddr = atc[n].paddr,CI
                    543: //   case HASH_NONE:
                    544: //     TC.E なら paddr = テーブルサーチ
                    545: //     そうでなければ、paddr = laddr (アドレス変換オフ)
                    546: //   case HASH_SUB:
                    547: //     PS が 4KB 未満だとハッシュの1エントリに2つ以上が混在するので、
                    548: //     この場合は諦めて ATC を線形探索。通常起きないので性能無視。
                    549: //  }
                    550: 
                    551: // ATC テーブルは本来は 1つ(FC混合) x 22 ラインだが、
                    552: // 高速化のため FC別(4テーブル) x 22 ラインのモードを用意する。
                    553: // FC 混合版ではテーブルは tables[0] のみで、FC(1,0,2) は busaddr の
                    554: // 34-32 ビット部分として laddr と一緒に検索キーにする。
                    555: // FC 分離版ではテーブルは fctables[fc] で検索キーは laddr(下位32ビット部分)。
                    556: //
                    557: // ハッシュなのでライン間の順序関係はないが、LRU で古いのを捨てる時や
                    558: // PS<4K の時に前から探索する時のために、age を使った順序管理をする。
                    559: // ラインを使用するたびに age に ++latest_age を代入しておくことで、
                    560: // 順序が必要な時には age の大きい順にすれば最近使った順になる。
                    561: // 32 ビット符号なし整数が折り返すと事故るがたぶんそれより高い頻度で
                    562: // フラッシュが起きるんじゃないかな…
1.1       root      563: 
1.1.1.10  root      564: // コンストラクタ
1.1.1.11  root      565: m68030ATCTable::m68030ATCTable(MPU68030Device *cpu_, uint fc_)
1.1       root      566: {
1.1.1.10  root      567:        cpu = cpu_;
                    568:        fc = fc_;
1.1       root      569: 
1.1.1.10  root      570:        std::fill(hash.begin(), hash.end(), HASH_NONE);
1.1       root      571: 
1.1.1.10  root      572:        latest_age = 0;
                    573:        Flush();
1.1       root      574: }
                    575: 
1.1.1.10  root      576: // デストラクタ
                    577: m68030ATCTable::~m68030ATCTable()
1.1       root      578: {
                    579: }
                    580: 
1.1.1.10  root      581: // この ATC テーブルのエントリをすべてフラッシュする。
                    582: void
                    583: m68030ATCTable::Flush()
1.1       root      584: {
1.1.1.10  root      585:        for (int i = 0; i < LINES; i++) {
                    586:                Invalidate(&line[i]);
1.1.1.12! root      587:                line[i].age = 0;
1.1.1.10  root      588:        }
1.1.1.12! root      589:        latest_age = 0;
1.1       root      590: }
                    591: 
1.1.1.10  root      592: // この ATC テーブルから addr が一致するエントリをすべてフラッシュする。
                    593: void
                    594: m68030ATCTable::Flush(uint32 addr)
1.1       root      595: {
1.1.1.10  root      596:        for (int i = 0; i < LINES; i++) {
                    597:                m68030ATCLine *a = &line[i];
                    598:                if (a->GetTag() == addr) {
                    599:                        Invalidate(a);
1.1       root      600:                }
                    601:        }
                    602: }
                    603: 
1.1.1.10  root      604: // この要素を先頭に移動。
                    605: inline void
                    606: m68030ATCTable::MoveHead(m68030ATCLine *a)
1.1       root      607: {
1.1.1.10  root      608:        a->age = ++latest_age;
1.1.1.12! root      609: 
        !           610:        if (__predict_false(a->age == 0)) {
        !           611:                // latest_age が1周したので、一旦全部リセット。
        !           612:                for (int i = 0; i < LINES; i++) {
        !           613:                        line[i].age = 0;
        !           614:                }
        !           615:                a->age = ++latest_age;
        !           616:        }
1.1       root      617: }
                    618: 
1.1.1.10  root      619: // このテーブルのうち最も古い要素を差し出す。
1.1       root      620: m68030ATCLine *
1.1.1.10  root      621: m68030ATCTable::RemoveTail()
1.1       root      622: {
1.1.1.10  root      623:        m68030ATCLine *a = NULL;
                    624:        uint32 min = (uint32)~0;
                    625:        uint32 minidx = 0;
1.1       root      626: 
1.1.1.10  root      627:        // 最後に使われたの (あるいは空き) を探す。
                    628:        for (int i = 0; i < LINES; i++) {
                    629:                if (line[i].IsInvalid()) {
1.1.1.12! root      630:                        return &line[i];
1.1       root      631:                }
1.1.1.10  root      632:                if (line[i].age < min) {
                    633:                        min = line[i].age;
1.1       root      634:                        minidx = i;
                    635:                }
                    636:        }
1.1.1.10  root      637:        if (a == NULL) {
                    638:                a = &line[minidx];
1.1       root      639:        }
                    640: 
1.1.1.10  root      641:        // ハッシュから削除。
                    642:        Invalidate(a);
1.1       root      643: 
                    644:        return a;
                    645: }
                    646: 
1.1.1.10  root      647: // a からハッシュを作成する。
1.1       root      648: void
1.1.1.12! root      649: m68030ATCTable::MakeHash(const m68030ATCLine *a)
1.1       root      650: {
1.1.1.10  root      651:        uint32 addr4k = a->GetLAddr() >> 12;
                    652:        int idx = a - &line[0];
                    653: 
                    654:        if (__predict_true(ps4k == 0)) {
                    655:                hash[addr4k] = idx;
                    656:        } else if (ps4k > 0) {
                    657:                for (int i = 0; i < (1 << ps4k); i++) {
                    658:                        hash[addr4k + i] = idx;
                    659:                }
                    660:        } else {
                    661:                hash[addr4k] = HASH_SUB;
1.1       root      662:        }
                    663: }
                    664: 
1.1.1.10  root      665: // ATC a を無効にする。すでに無効なら何もしない。
1.1       root      666: void
1.1.1.10  root      667: m68030ATCTable::Invalidate(m68030ATCLine *a)
1.1       root      668: {
1.1.1.10  root      669:        if (a->IsValid()) {
                    670:                // 有効ならハッシュから削除。
                    671:                if (__predict_true(ps4k == 0)) {
                    672:                        uint32 addr4k = a->GetLAddr() >> 12;
                    673:                        hash[addr4k] = HASH_NONE;
                    674:                } else if (ps4k > 0) {
                    675:                        // PS>4KB
                    676:                        uint32 addr4k = a->GetLAddr() >> 12;
                    677:                        for (int i = 0; i < (1U << ps4k); i++) {
                    678:                                hash[addr4k + i] = HASH_NONE;
                    679:                        }
                    680:                } else {
                    681:                        // PS<4KB、自分が抜けることでこの枠が空くかどうか
                    682:                        InvalidateSub(a);
1.1       root      683:                }
1.1.1.10  root      684: 
                    685:                a->Invalidate();
1.1       root      686:        }
                    687: }
                    688: 
1.1.1.10  root      689: // PS<4KB の場合に aa が抜けることでこの addr4k の枠が空けばハッシュから削除。
1.1       root      690: void
1.1.1.10  root      691: m68030ATCTable::InvalidateSub(const m68030ATCLine *aa)
1.1       root      692: {
1.1.1.10  root      693:        const uint32 MASK_4K = 0xfffff000 | m68030ATCLine::INVALID;
                    694:        uint32 target_laddr = aa->GetTag() & MASK_4K;
                    695:        bool in_use = false;
                    696: 
                    697:        for (int i = 0; i < LINES; i++) {
                    698:                auto *a = &line[i];
                    699:                // 自分自身は今から削除予定なので除く。
                    700:                if (a == aa) {
                    701:                        continue;
                    702:                }
                    703:                // それ以外で有効なものがあるか。
                    704:                if ((a->GetTag() & MASK_4K) == target_laddr) {
                    705:                        in_use = true;
                    706:                        break;
1.1       root      707:                }
                    708:        }
                    709: 
1.1.1.10  root      710:        // 誰もいなくなれば削除。
                    711:        if (in_use == false) {
                    712:                hash[target_laddr >> 12] = HASH_NONE;
1.1       root      713:        }
                    714: }
                    715: 
1.1.1.10  root      716: // laddr を ATC から探す。見つからなければ NULL を返す。
                    717: m68030ATCLine *
                    718: m68030ATCTable::FindSub(const busaddr laddr)
1.1       root      719: {
1.1.1.10  root      720:        uint32 addr = laddr.Addr() & cpu->mmu_tc.lmask;
                    721: 
                    722:        // 同じエントリはないはずなので、順序通りに探さなくてもいいはず。
                    723:        for (int i = 0; i < LINES; i++) {
                    724:                auto *a = &line[i];
                    725:                if (a->GetTag() == addr) {
                    726:                        return a;
1.1       root      727:                }
                    728:        }
1.1.1.10  root      729:        return NULL;
1.1       root      730: }
1.1.1.10  root      731: 
                    732: // テーブルサーチを行う。
                    733: // テーブルという用語がぶつかっているので MMU Search にしてある。
                    734: m68030ATCLine *
                    735: m68030ATCTable::MMUSearch(busaddr laddr)
1.1       root      736: {
1.1.1.10  root      737:        m68030ATCLine *a = RemoveTail();
                    738:        a->tag = laddr.Addr() & cpu->mmu_tc.lmask;
                    739: 
                    740:        m68030MMU mmu(cpu, loglevel);
                    741:        mmu.search(7, laddr);
                    742:        mmu.make_atc(a);
                    743: 
                    744:        MakeHash(a);
                    745:        return a;
1.1       root      746: }
                    747: 
1.1.1.10  root      748: // デバッグ表示。
                    749: std::string
                    750: m68030ATCTable::LineToStr(const m68030ATCLine *a) const
1.1       root      751: {
1.1.1.10  root      752:        uint32 laddr = a->GetLAddr();
                    753: 
                    754:        if (!a->IsBusError()) {
                    755:                uint32 paddr = a->GetPAddr();
                    756:                uint32 ci    = a->IsCInhibit();
                    757:                uint32 wp    = a->IsWProtect();
                    758:                uint32 mod   = a->IsModified();
                    759:                return indent_format(1,
                    760:                        "ATC: match %u:$%08x -> $%08x CI=%u WP=%u M=%u",
                    761:                        fc, laddr, paddr, ci, wp, mod);
                    762:        } else {
                    763:                return indent_format(1,
                    764:                        "ATC: match %u:$%08x -> BusErr",
                    765:                        fc, laddr);
1.1       root      766:        }
                    767: }
                    768: 
                    769: 
                    770: //
                    771: // テーブルサーチ
                    772: //
                    773: 
                    774: // コンストラクタ
1.1.1.11  root      775: m68030MMU::m68030MMU(MPU68030Device *cpu_, int loglevel_)
1.1       root      776: {
1.1.1.8   root      777:        cpu = cpu_;
1.1.1.9   root      778:        loglevel = loglevel_;
1.1       root      779: 
                    780:        m_type = INVALID;
                    781:        x = -1;
                    782: }
                    783: 
1.1.1.8   root      784: // デストラクタ
                    785: m68030MMU::~m68030MMU()
                    786: {
                    787: }
                    788: 
1.1       root      789: // テーブルサーチのメイン部分。
                    790: // m_type および ATC 作成に必要な情報を埋めてから帰る。
                    791: // ATC は帰った先で作成する。
                    792: void
1.1.1.10  root      793: m68030MMU::search(int level, busaddr laddr)
1.1       root      794: {
                    795:        const char *rpname;
1.1.1.10  root      796:        uint32 addr;
                    797:        uint32 fc;
1.1       root      798:        int dt;
                    799:        bool is_super;
                    800: 
1.1.1.10  root      801:        addr = laddr.Addr();
                    802:        fc   = laddr.GetFC();
1.1       root      803: 
1.1.1.10  root      804:        is_super = (fc & 0x04);
1.1       root      805: 
                    806:        // アドレスを (IS,) TIA .. TID に分解。
                    807:        //
                    808:        // TIA=$C, TIB=$A, (TIC = TID = 0), PS=$A の場合以下のようにする。
                    809:        // 使ってない TIC, TID については cpu->mmu_tc->{tic,tid} == 0 で判断する。
                    810:        //
                    811:        //                    A              B             PS
                    812:        //           +----------------+--------------+--------------+
                    813:        // $00A01A00 | 0000 0000 1010 | 0000 0001 10 | xx xxxx xxxx |
                    814:        //           +----------------+--------------+--------------+
                    815:        //            shift[TIA] =20   shift[TIB] =10
                    816:        //            lv[TIA].idx=0xA  lv[TIB].idx=0x6
                    817:        //
                    818:        for (int i = TID; i >= TIA; i--) {
                    819:                if (cpu->mmu_tc.tix[i] > 0) {
                    820:                        uint32 mask = cpu->mmu_tc.mask[i];
1.1.1.10  root      821:                        m_lv[i] = (addr >> cpu->mmu_tc.shift[i]) & mask;
1.1       root      822:                }
                    823:        }
1.1.1.10  root      824:        m_lv[FCL] = fc;
1.1       root      825: 
1.1.1.8   root      826:        cpu->putlogf(3, [&] {
1.1       root      827:                std::string buf;
                    828: 
1.1.1.10  root      829:                buf = indent_format(2, "search: laddr=$%08x ->", addr);
1.1       root      830:                if (cpu->mmu_tc.is > 0)
1.1.1.10  root      831:                        buf += string_format(" IS=%u", cpu->mmu_tc.is);
1.1       root      832:                if (cpu->mmu_tc.fcl)
1.1.1.10  root      833:                        buf += string_format(" FC=%u", m_lv[FCL]);
1.1       root      834:                for (int i = TIA; i <= TID; i++) {
                    835:                        if (cpu->mmu_tc.tix[i] > 0) {
1.1.1.10  root      836:                                buf += string_format(" %s=%u($%0*x)",
1.1       root      837:                                        m_lvname[i], cpu->mmu_tc.tix[i],
                    838:                                        NIBBLE(cpu->mmu_tc.tix[i]), m_lv[i]);
                    839:                        }
                    840:                }
1.1.1.6   root      841:                return buf;
                    842:        });
1.1       root      843: 
1.1.1.9   root      844:        // この辺に図9-26 の初期化だがコンストラクタで初期化済み。
1.1       root      845: 
                    846:        // ここから図9-25。
                    847: 
                    848:        // 使用するルートポインタを決定
1.1.1.12! root      849:        if (__predict_true(cpu->mmu_tc.sre) && is_super) {
1.1       root      850:                rpname = "SRP";
                    851:                m_desc1 = cpu->GetSRPh();
                    852:                m_desc2 = cpu->GetSRPl();
                    853:        } else {
                    854:                rpname = "CRP";
                    855:                m_desc1 = cpu->GetCRPh();
                    856:                m_desc2 = cpu->GetCRPl();
                    857:        }
                    858: 
                    859:        // ルートポインタの DT をチェック
                    860:        dt = GET_DT(m_desc1);
1.1.1.9   root      861:        MMULOG(3, 2, "%s: %s dt=%s", lam_func, rpname, dtname(dt));
1.1.1.12! root      862:        if (__predict_true(dt == DT_VALID4)) {
        !           863:                // 有効 4 バイト。基本的にはこれしか使われないので優先。
        !           864:                m_descsize = 4;
        !           865:                m_lastsize = 8;
        !           866:        } else if (dt == DT_VALID8) {   // 有効 (8バイト)
        !           867:                m_descsize = 8;
        !           868:                m_lastsize = 8;
        !           869:        } else if (dt == DT_PAGE) {             // ページディスクリプタ
1.1       root      870:                m_type = EARLY;
                    871:                return;
1.1.1.12! root      872:        } else {
        !           873:                // 無効は設定できないはずなので、ここには来ないはず。
        !           874:                PANIC("%s.DT==Invalid", rpname);
1.1       root      875:        }
                    876: 
                    877:        // 必要ならファンクションコード・ルックアップ
1.1.1.12! root      878:        if (__predict_false(cpu->mmu_tc.fcl)) {
1.1       root      879:                x = FCL;
1.1.1.8   root      880:                // たぶん検索レベルを1つ消費するはず。未確認。
                    881:                level--;
                    882: 
1.1.1.9   root      883:                MMULOG(3, 2, "%s: %s tableaddr=$%08x idx=$%x", lam_func,
1.1       root      884:                        m_lvname[x], m_desc2, m_lv[x]);
                    885: 
                    886:                // ディスクリプタをフェッチ
                    887:                if (fetch_desc(m_desc2, m_lv[x]) == false) {
                    888:                        return;
                    889:                }
                    890: 
                    891:                // ディスクリプタタイプをチェック
                    892:                dt = GET_DT(m_desc1);
                    893:                switch (dt) {
                    894:                 case DT_INVALID:       // 無効
                    895:                        m_type = INVALID;
                    896:                        return;
                    897: 
                    898:                 case DT_PAGE:          // ページディスクリプタ
                    899:                        m_type = EARLY;
                    900:                        return;
                    901: 
                    902:                 case DT_VALID4:        // 有効4バイト
                    903:                 case DT_VALID8:        // 有効8バイト
                    904:                        m_lastsize = m_descsize;
                    905:                        m_descsize = 1 << dt;
                    906:                        break;
                    907:                }
                    908:        }
                    909: 
                    910:        // TIx のテーブルサーチに入る。図9-25 の下半分。
                    911: 
1.1.1.8   root      912:        for (x = TIA; x < level; x++) {
1.1       root      913:                // リミットチェック
1.1.1.12! root      914:                if (__predict_false(m_lastsize == 8) && limitcheck() == false) {
1.1       root      915:                        m_type = INVALID;
1.1.1.9   root      916:                        m_lim = true;
1.1       root      917:                        return;
                    918:                }
                    919: 
                    920:                // テーブルアドレス
                    921:                uint32 tableaddr;
                    922:                tableaddr = (m_lastsize == 4) ? m_desc1 : m_desc2;
                    923:                tableaddr &= 0xfffffff0;
1.1.1.9   root      924:                MMULOG(3, 2, "%s: %s tableaddr=$%08x idx=$%x", lam_func,
1.1       root      925:                        m_lvname[x], tableaddr, m_lv[x]);
                    926: 
                    927:                // ディスクリプタをフェッチ
                    928:                if (fetch_desc(tableaddr, m_lv[x]) == false) {
                    929:                        return;
                    930:                }
                    931: 
                    932:                // ディスクリプタタイプをチェック
                    933:                dt = GET_DT(m_desc1);
                    934:                switch (dt) {
                    935:                 case DT_INVALID:       // 無効
                    936:                        m_type = INVALID;
                    937:                        return;
                    938: 
                    939:                 case DT_PAGE:          // ページディスクリプタ
                    940:                        if (x == TID) {
                    941:                                m_type = NORMAL;
                    942:                        } else {
                    943:                                if (cpu->mmu_tc.tix[x + 1] == 0) {
                    944:                                        m_type = NORMAL;
                    945:                                } else {
                    946:                                        m_type = EARLY;
                    947:                                }
                    948:                        }
                    949:                        return;
                    950: 
                    951:                 case DT_VALID4:        // 有効4バイト
                    952:                 case DT_VALID8:        // 有効8バイト
                    953:                        m_lastsize = m_descsize;
                    954:                        m_descsize = 1 << dt;
                    955: 
1.1.1.12! root      956:                        if (__predict_false(x == TID || cpu->mmu_tc.tix[x + 1] == 0)) {
1.1       root      957:                                // 間接
                    958:                                m_type = INDIRECT;
                    959: 
                    960:                                // ディスクリプタをフェッチ
                    961:                                tableaddr = (m_lastsize == 4) ? m_desc1 : m_desc2;
                    962:                                tableaddr &= 0xfffffff0;
                    963:                                if (fetch_desc(tableaddr, 0) == false) {
                    964:                                        return;
                    965:                                }
                    966:                                if (GET_DT(m_desc1) != DT_PAGE) {
                    967:                                        m_type = INVALID;
                    968:                                }
                    969:                                return;
                    970:                        }
                    971:                        // 次段のサーチを繰り返す
1.1.1.8   root      972:                        break;
1.1       root      973:                }
                    974:        }
1.1.1.8   root      975: 
                    976:        // レベル上限にひっかかった場合、x は過ぎているので一つ戻しておく。
                    977:        x--;
1.1       root      978: }
                    979: 
                    980: // リミットチェックを実行。図9-28。
                    981: // 無効のケースに落ちたら false を返す。
                    982: bool
                    983: m68030MMU::limitcheck() const
                    984: {
1.1.1.8   root      985:        uint16 limit = (m_desc1 >> 16) & 0x7fff;
1.1       root      986: 
1.1.1.8   root      987:        // 最上位ビットが L/U
                    988:        if ((int32)m_desc1 < 0) {
                    989:                if (m_lv[x] < limit) {
1.1.1.9   root      990:                        MMULOG(3, 2, "%s: $%04x < $%04x", lam_func, m_lv[x], limit);
1.1.1.8   root      991:                        return false;
                    992:                }
                    993:        } else {
                    994:                if (m_lv[x] > limit) {
1.1.1.9   root      995:                        MMULOG(3, 2, "%s: $%04x > $%04x", lam_func, m_lv[x], limit);
1.1.1.8   root      996:                        return false;
                    997:                }
1.1       root      998:        }
                    999:        return true;
                   1000: }
                   1001: 
                   1002: // ディスクリプタ中のフラグの更新 (を予約する)
                   1003: #define UPDATE_DESC(flag)      do { \
                   1004:        if ((m_desc1 & (flag)) == 0) { \
                   1005:                m_desc1 |= (flag); \
                   1006:                do_write = true; \
                   1007:        } \
                   1008: } while (0)
                   1009: 
                   1010: // ディスクリプタのフェッチと、ヒストリおよびステータスの更新。
                   1011: // 図9-29。
                   1012: // ディスクリプタを取得できれば true、
                   1013: // ATC エントリの作成に向かう場合は false。
                   1014: bool
                   1015: m68030MMU::fetch_desc(uint32 tableaddr, int idx)
                   1016: {
                   1017:        uint32 descaddr;
                   1018:        bool do_write;
                   1019:        uint dt;
                   1020: 
                   1021:        // ディスクリプタのアドレスを計算。ここだけマニュアルの説明が雑すぎ。
                   1022:        // インダイレクトなら idx = 0 でコールしてあるので、
                   1023:        // tableaddr がそのまま PA になるという寸法。
                   1024:        descaddr = tableaddr + idx * m_descsize;
1.1.1.10  root     1025:        MMULOG(3, 2, "%s: descsize=%u descaddr=$%08x", lam_func,
1.1       root     1026:                m_descsize, descaddr);
                   1027: 
                   1028:        // バスエラー発生フラグをクリアしておく
                   1029:        m_berr = false;
                   1030: 
                   1031:        // ディスクリプタを取得
1.1.1.12! root     1032:        if (__predict_false(m_debugger)) {
1.1       root     1033:                // デバッガアクセス
                   1034:                uint64 data;
1.1.1.10  root     1035:                data = cpu->mainbus->Peek4(descaddr);
1.1       root     1036:                if ((int64)data < 0) {
                   1037:                        goto buserr;
                   1038:                }
                   1039:                m_desc1 = data;
                   1040: 
                   1041:                if (m_descsize == 8) {
1.1.1.10  root     1042:                        data = cpu->mainbus->Peek4(descaddr + 4);
1.1       root     1043:                        if ((int64)data < 0) {
                   1044:                                goto buserr;
                   1045:                        }
                   1046:                        m_desc2 = data;
                   1047:                }
                   1048:        } else {
1.1.1.9   root     1049:                busdata bd;
1.1       root     1050: 
1.1.1.11  root     1051:                bd = cpu->read_phys_4(descaddr);
1.1.1.12! root     1052:                if (__predict_false(bd.IsBusErr())) {
1.1       root     1053:                        goto buserr;
                   1054:                }
1.1.1.9   root     1055:                m_desc1 = bd.Data();
1.1       root     1056: 
1.1.1.12! root     1057:                if (__predict_false(m_descsize == 8)) {
1.1.1.11  root     1058:                        bd = cpu->read_phys_4(descaddr + 4);
1.1.1.9   root     1059:                        if (bd.IsBusErr()) {
1.1       root     1060:                                goto buserr;
                   1061:                        }
1.1.1.9   root     1062:                        m_desc2 = bd.Data();
1.1       root     1063:                }
1.1.1.8   root     1064: 
                   1065:                m_lastaddr = descaddr;
1.1       root     1066:        }
                   1067: 
                   1068:        // ディスクリプタタイプをチェック
                   1069:        // DT は必ず最初のロングワードの下位2ビット。
                   1070:        do_write = false;
                   1071:        dt = GET_DT(m_desc1);
1.1.1.9   root     1072:        MMULOG(3, 2, "%s: desc=%s dt=%s", lam_func,
1.1       root     1073:                sprintf_desc().c_str(), dtname(dt));
                   1074:        switch (dt) {
                   1075:         case DT_INVALID:       // 無効
                   1076:                return true;
                   1077: 
                   1078:         case DT_PAGE:          // ページディスクリプタ
                   1079:                // U ビットを更新
                   1080:                UPDATE_DESC(MMU_DESC_U);
                   1081: 
                   1082:                // 書き込み操作なら M を更新
                   1083:                if (cpu->bus.IsWrite() && !m_ptest) {
                   1084:                        UPDATE_DESC(MMU_DESC_M);
                   1085:                }
                   1086:                // 図にはないけど、ATC 更新のためと PTEST のために
                   1087:                // ページディスクリプタの M ビットを記録。
                   1088:                if ((m_desc1 & MMU_DESC_M))
1.1.1.9   root     1089:                        m_m = true;
1.1       root     1090:                break;
                   1091: 
                   1092:         case DT_VALID4:        // 有効4バイト
                   1093:         case DT_VALID8:        // 有効8バイト
                   1094:                // U ビットを更新
                   1095:                UPDATE_DESC(MMU_DESC_U);
                   1096:                break;
1.1.1.12! root     1097: 
        !          1098:         default:
        !          1099:                __unreachable();
1.1       root     1100:        }
                   1101: 
                   1102:        // デバッガ自身がアドレス変換を実施している時は書き込みは行わない
1.1.1.12! root     1103:        if (__predict_false(m_debugger)) {
1.1       root     1104:                do_write = false;
                   1105:        }
                   1106: 
                   1107:        // ライトライクルの実行
                   1108:        if (do_write) {
1.1.1.11  root     1109:                busdata bd = cpu->write_phys_4(descaddr, m_desc1);
1.1.1.12! root     1110:                if (__predict_false(bd.IsBusErr())) {
1.1       root     1111:                        goto buserr;
                   1112:                }
                   1113:                // バス動作正常終了
1.1.1.9   root     1114:                MMULOG(3, 2, "%s: desc=%s updated", lam_func, sprintf_desc().c_str());
1.1       root     1115:        }
                   1116: 
1.1.1.9   root     1117:        // ステータス更新 (OR していく)
1.1       root     1118:        // XXX 実際には CI はページディスクリプタにしかないけど
1.1.1.9   root     1119:        m_wp |= (m_desc1 & MMU_DESC_WP);
                   1120:        m_ci |= (m_desc1 & MMU_DESC_CI);
                   1121:        if (__predict_false(m_descsize == 8)) {
                   1122:                m_s |= (m_desc1 & MMU_DESC_S);
1.1       root     1123:        }
                   1124: 
                   1125:        return true;
                   1126: 
                   1127:        // バスエラー
                   1128:  buserr:
                   1129:        m_type = INVALID;
                   1130:        m_berr = true;
                   1131:        return false;
                   1132: }
                   1133: 
                   1134: // ATC エントリを作成。実行系用。
                   1135: // 図9-27 だが雑すぎ。
                   1136: void
                   1137: m68030MMU::make_atc(m68030ATCLine *a)
                   1138: {
                   1139:        std::string buf;
                   1140:        uint32 paddr;
                   1141: 
                   1142:        a->data = 0;
                   1143:        switch (m_type) {
                   1144:         case INVALID:  // 無効
                   1145:                a->data |= m68030ATCLine::BUSERROR;
1.1.1.9   root     1146:                MMULOG(3, 2, "%s: type=INVALID -> BusErr", lam_func);
1.1       root     1147:                return;
                   1148: 
                   1149:         case NORMAL:   // ノーマル
                   1150:         case INDIRECT: // 間接
                   1151:                paddr = (m_descsize == 4) ? m_desc1 : m_desc2;
1.1.1.9   root     1152:                MMULOG(3, 2, "%s: type=NORMAL paddr=$%08x", lam_func,
                   1153:                        paddr & 0xffffff00);
1.1       root     1154:                break;
                   1155: 
                   1156:         case EARLY:    // アーリーターミネーション
                   1157:                paddr = (m_descsize == 4) ? m_desc1 : m_desc2;
1.1.1.9   root     1158:                buf = string_format("type=EARLY paddr=$%08x", paddr & 0xffffff00);
1.1       root     1159: 
                   1160:                // 残ってるビットも足して返す
                   1161:                for (int i = x + 1; i <= TID; i++) {
                   1162:                        if (cpu->mmu_tc.tix[i] > 0) {
                   1163:                                paddr |= m_lv[i] << cpu->mmu_tc.shift[i];
                   1164:                                buf += string_format(" +%s=%08x", m_lvname[i], paddr);
                   1165:                        }
                   1166:                }
1.1.1.9   root     1167:                MMULOG(3, 2, "%s: %s", lam_func, buf.c_str());
1.1       root     1168:                break;
                   1169:         default:
1.1.1.10  root     1170:                PANIC("corrupted m_type=%u", (uint)m_type);
1.1       root     1171:        }
                   1172: 
                   1173:        // PS の部分は反映しない
                   1174:        a->paddr = paddr & cpu->mmu_tc.lmask;
                   1175: 
1.1.1.9   root     1176:        if (m_ci)
                   1177:                a->data |= m68030ATCLine::CINHIBIT;
1.1       root     1178:        if (m_wp)
                   1179:                a->data |= m68030ATCLine::WPROTECT;
                   1180:        if (m_m)
                   1181:                a->data |= m68030ATCLine::MODIFIED;
                   1182: }
                   1183: 
                   1184: // make_atc() と同様にテーブルサーチの結果から物理ページアドレスを返す。
1.1.1.9   root     1185: // バスエラーになるケースなら BusErr を返す。
1.1       root     1186: // デバッガアクセスなので、VM に一切干渉しない。
1.1.1.9   root     1187: busaddr
                   1188: m68030MMU::make_atc_debugger() const
1.1       root     1189: {
1.1.1.10  root     1190:        busaddr paddr;
1.1       root     1191: 
                   1192:        switch (m_type) {
                   1193:         case INVALID:  // 無効
1.1.1.10  root     1194:                paddr = BusAddr::BusErr;
1.1       root     1195:                break;
                   1196: 
                   1197:         case NORMAL:   // ノーマル
                   1198:         case INDIRECT: // 間接
1.1.1.10  root     1199:         {
                   1200:                uint32 descaddr;
                   1201:                descaddr = (m_descsize == 4) ? m_desc1 : m_desc2;
                   1202:                descaddr &= 0xffffff00;
                   1203:                paddr = busaddr(descaddr);
1.1       root     1204:                break;
1.1.1.10  root     1205:         }
1.1       root     1206: 
                   1207:         case EARLY:    // アーリーターミネーション
1.1.1.10  root     1208:         {
                   1209:                uint32 descaddr;
                   1210:                descaddr = (m_descsize == 4) ? m_desc1 : m_desc2;
                   1211:                descaddr &= 0xffffff00;
1.1       root     1212:                // 残ってるビットも足して返す
                   1213:                for (int i = x + 1; i <= TID; i++) {
                   1214:                        if (cpu->mmu_tc.tix[i] > 0) {
1.1.1.10  root     1215:                                descaddr |= m_lv[i] << cpu->mmu_tc.shift[i];
1.1       root     1216:                        }
                   1217:                }
1.1.1.10  root     1218:                paddr = busaddr(descaddr);
1.1       root     1219:                break;
1.1.1.10  root     1220:         }
1.1.1.9   root     1221: 
1.1       root     1222:         default:
1.1.1.10  root     1223:                PANIC("corrupted m_type=%u", (uint)m_type);
1.1       root     1224:        }
1.1.1.9   root     1225: 
1.1.1.10  root     1226:        paddr |= BusAddr::TableSearched;
1.1       root     1227:        return paddr;
                   1228: }
                   1229: 
                   1230: // ディスクリプタ表示用の文字列を返す。
                   1231: // m_descsize によって m_desc1, m_desc2 を適切に文字列にする。
                   1232: std::string
                   1233: m68030MMU::sprintf_desc()
                   1234: {
                   1235:        std::string buf;
                   1236: 
                   1237:        if (m_descsize == 4) {
                   1238:                buf = string_format("%08x", m_desc1);
                   1239:        } else {
                   1240:                buf = string_format("%08x_%08x", m_desc1, m_desc2);
                   1241:        }
                   1242: 
                   1243:        // フラグの出現条件がややこしいので注意
                   1244:        int dt = GET_DT(m_desc1);
                   1245:        buf += string_format(" (%s%s%s%c%c)",
                   1246:                ((m_descsize == 4) ? "x" : ((m_desc1 & MMU_DESC_S) ? "S" : "-")),
                   1247:                ((dt != DT_PAGE)   ? "x" : ((m_desc1 & MMU_DESC_CI) ? "C" : "-")),
                   1248:                ((dt != DT_PAGE)   ? "x" : ((m_desc1 & MMU_DESC_M)  ? "M" : "-")),
                   1249:                (m_desc1 & MMU_DESC_U)  ? 'U' : '-',
                   1250:                (m_desc1 & MMU_DESC_WP) ? 'W' : '-'
                   1251:        );
                   1252: 
                   1253:        return buf;
                   1254: }
                   1255: 
                   1256: // ディスクリプタタイプ文字列 (デバッグ用)
                   1257: const char *
                   1258: m68030MMU::dtname(uint32 dt)
                   1259: {
                   1260:        static char buf[32];
                   1261:        switch (dt) {
                   1262:         case DT_INVALID:       return "invalid";
1.1.1.4   root     1263:         case DT_PAGE:          return "page";
1.1       root     1264:         case DT_VALID4:        return "valid4";
                   1265:         case DT_VALID8:        return "valid8";
                   1266:        }
1.1.1.10  root     1267:        snprintf(buf, sizeof(buf), "(invalid DT %u)", dt);
1.1       root     1268:        return buf;
                   1269: }
                   1270: 
                   1271: // デバッグ用文字列
                   1272: const char * const m68030MMU::m_lvname[MAX_LV] = {
                   1273:        "TIA",
                   1274:        "TIB",
                   1275:        "TIC",
                   1276:        "TID",
                   1277:        "FCL",  // この順ではないが、便宜上ここにおいてある
                   1278: };
                   1279: 
                   1280: //
                   1281: // アドレス変換
                   1282: //
                   1283: 
1.1.1.9   root     1284: // 論理アドレスが TT0/TT1 どちらかと一致すれば (bus.ci を更新して) true を返す。
1.1       root     1285: // 一致しなければ false を返す。
1.1.1.8   root     1286: bool
1.1.1.11  root     1287: MPU68030Device::CheckTT()
1.1       root     1288: {
                   1289:        bool ci;
                   1290:        int match;
                   1291: 
                   1292:        match = 0;
                   1293:        ci = false;
                   1294:        for (int i = 0; i < 2; i++) {
1.1.1.8   root     1295:                const m68030TT& tt = mmu_tt[i];
1.1.1.9   root     1296:                if (tt.Match(bus.laddr)) {
1.1       root     1297:                        // 一致した
                   1298:                        match += i + 1;
                   1299:                        ci |= tt.ci != 0;
1.1.1.10  root     1300:                        putlog(3, " %s: TT%u match", __func__, i);
1.1       root     1301:                }
                   1302:        }
                   1303: 
                   1304:        // 一致したら PA <- LA
                   1305:        if (match != 0) {
1.1.1.9   root     1306:                putlog(3, "%s: result %08x (match TT%s)", __func__,
1.1.1.10  root     1307:                        bus.laddr.Addr(),
1.1       root     1308:                        (match == 3) ? "0 and TT1" : ((match == 1) ? "0" : "1"));
1.1.1.9   root     1309:                bus.ci = ci;
1.1       root     1310:                return true;
                   1311:        }
                   1312:        return false;
                   1313: }
                   1314: 
1.1.1.10  root     1315: // リード/フェッチ用のアドレス変換を行う。
1.1       root     1316: // 変換できれば true、バスエラーなら false を返す。
                   1317: bool
1.1.1.11  root     1318: MPU68030Device::TranslateRead()
1.1       root     1319: {
1.1.1.10  root     1320:        m68030ATCLine *a;
                   1321:        busaddr& laddr = bus.laddr;
                   1322:        uint32 addr = laddr.Addr();
                   1323:        uint32 fc   = laddr.GetFC();
1.1.1.9   root     1324: 
1.1       root     1325:        // 状態表示
1.1.1.9   root     1326:        putlog(3, "%s:  enter  %c:$%08x pc=%c:$%08x", __func__,
1.1.1.10  root     1327:                (laddr.IsSuper() ? 'S' : 'U'),
                   1328:                addr,
1.1.1.8   root     1329:                IsSuper() ? 'S' : 'U',
                   1330:                ppc);
1.1       root     1331: 
1.1.1.10  root     1332:        m68030ATCTable *table = atc.fctables[fc];
                   1333:        uint8 n = table->hash[addr >> 12];
                   1334:        if (__predict_true((int8)n >= 0)) {
                   1335:                // ATC #(n)
                   1336:                table->atcstat.total++;
                   1337:                a = &table->line[n];
1.1.1.12! root     1338:        } else if (n == m68030ATCTable::HASH_NONE) {
        !          1339:                if (__predict_true(mmu_tc.e)) {
        !          1340:                        // TC.E 有効で対応なしなら、テーブルサーチ。
1.1.1.10  root     1341:                        table->atcstat.total++;
1.1.1.12! root     1342:                        table->atcstat.miss++;
        !          1343:                        a = table->MMUSearch(laddr);
1.1.1.10  root     1344:                } else {
1.1.1.12! root     1345:                        // TC.E 無効。(total もカウントしない)
        !          1346:                        return true;
        !          1347:                }
        !          1348:        } else if (n == m68030ATCTable::HASH_TT) {
        !          1349:                // TT。CheckTT で外れたかどうかはカウントしない。ほぼ 0 なので。
        !          1350:                table->atcstat.total++;
        !          1351:                table->atcstat.tthit++;
        !          1352:                return CheckTT();
        !          1353:        } else {
        !          1354:                // PS<4KB でマッチしたので再検査。
        !          1355:                table->atcstat.total++;
        !          1356:                a = table->FindSub(laddr);
        !          1357:                if (a == NULL) {
        !          1358:                        table->atcstat.miss++;
        !          1359:                        a = table->MMUSearch(laddr);
1.1       root     1360:                }
                   1361:        }
                   1362: 
1.1.1.10  root     1363:        // ATC a でマッチした
                   1364:        table->MoveHead(a);
                   1365:        putlog(3, "%s", table->LineToStr(a).c_str());
                   1366:        if (__predict_false(a->IsBusError())) {
                   1367:                return false;
                   1368:        }
                   1369:        bus.paddr.ChangeAddr(a->GetPAddr() | (addr & mmu_tc.pgmask));
                   1370:        bus.ci = a->IsCInhibit();
1.1       root     1371:        return true;
                   1372: }
                   1373: 
                   1374: // ライト用のアドレス変換を行う。
                   1375: // 変換できれば true、バスエラーなら false を返す。
                   1376: bool
1.1.1.11  root     1377: MPU68030Device::TranslateWrite()
1.1       root     1378: {
1.1.1.10  root     1379:        m68030ATCLine *a;
                   1380:        busaddr& laddr = bus.laddr;
                   1381:        uint32 addr = laddr.Addr();
                   1382:        uint32 fc   = laddr.GetFC();
1.1.1.9   root     1383: 
1.1       root     1384:        // 状態表示
1.1.1.9   root     1385:        putlog(3, "%s: enter  %c:$%08x pc=%c:$%08x", __func__,
1.1.1.10  root     1386:                (laddr.IsSuper() ? 'S' : 'U'),
                   1387:                addr,
1.1.1.8   root     1388:                IsSuper() ? 'S' : 'U',
                   1389:                ppc);
1.1       root     1390: 
1.1.1.10  root     1391:        m68030ATCTable *table = atc.fctables[fc];
                   1392:        uint8 n = table->hash[addr >> 12];
                   1393:        if (__predict_true((int8)n >= 0)) {
                   1394:                // ATC #(n)
                   1395:                table->atcstat.total++;
                   1396:                a = &table->line[n];
1.1.1.12! root     1397:        } else if (n == m68030ATCTable::HASH_NONE) {
        !          1398:                if (__predict_true(mmu_tc.e)) {
        !          1399:                        // TC.E 有効で対応なしなら、テーブルサーチ。
1.1.1.10  root     1400:                        table->atcstat.total++;
1.1.1.12! root     1401:                        goto tablesearch;
1.1.1.10  root     1402:                } else {
1.1.1.12! root     1403:                        // TC.E 無効。(total もカウントしない)
        !          1404:                        return true;
1.1       root     1405:                }
1.1.1.12! root     1406:        } else if (n == m68030ATCTable::HASH_TT) {
        !          1407:                // TT。CheckTT で外れたかどうかはカウントしない。ほぼ 0 なので。
        !          1408:                table->atcstat.total++;
        !          1409:                table->atcstat.tthit++;
        !          1410:                return CheckTT();
        !          1411:        } else {
        !          1412:                // PS<4KB でマッチしたので再検査。
        !          1413:                table->atcstat.total++;
        !          1414:                a = table->FindSub(laddr);
        !          1415:                if (a == NULL) {
        !          1416:                        goto tablesearch;
        !          1417:                }
        !          1418:        }
        !          1419: 
        !          1420:        // ライトアクセスなので、WP でなく Modified == 0 なら
        !          1421:        // Modified を立てるため、このエントリを破棄して改めて
        !          1422:        // テーブルサーチを実行する。
        !          1423:        if (a->IsWProtect() == false && a->IsModified() == false) {
        !          1424:                table->Invalidate(a);
        !          1425:  tablesearch:
        !          1426:                table->atcstat.miss++;
        !          1427:                a = table->MMUSearch(laddr);
1.1       root     1428:        }
                   1429: 
1.1.1.10  root     1430:        // ATC a でマッチした
                   1431:        table->MoveHead(a);
1.1       root     1432: 
1.1.1.10  root     1433:        if (__predict_false(a->IsBusError())) {
                   1434:                return false;
                   1435:        }
                   1436:        if (__predict_false(a->IsWProtect())) {
                   1437:                // Write アクセスで WP が立ってたらバスエラー
                   1438:                putlog(3, "%s: result Buserr (ATC:WP)", __func__);
                   1439:                return false;
1.1       root     1440:        }
                   1441: 
1.1.1.10  root     1442:        putlog(3, "%s", table->LineToStr(a).c_str());
                   1443:        bus.paddr.ChangeAddr(a->GetPAddr() | (addr & mmu_tc.pgmask));
                   1444:        bus.ci = a->IsCInhibit();
1.1       root     1445:        return true;
                   1446: }
                   1447: 
                   1448: // ピーク用のアドレス変換を行う。
1.1.1.9   root     1449: // 変換できれば対応する物理アドレスを、バスエラーなら BusErr を返す。
                   1450: // テーブルサーチを行ったら TableSearched ビットを立てる。
                   1451: busaddr
1.1.1.11  root     1452: MPU68030Device::TranslatePeek(busaddr laddr)
1.1       root     1453: {
1.1.1.10  root     1454:        const m68030ATCLine *a;
1.1.1.9   root     1455:        busaddr paddr;
1.1.1.10  root     1456:        uint32 addr = laddr.Addr();
                   1457:        uint32 fc   = laddr.GetFC();
1.1       root     1458: 
                   1459:        // 状態表示
1.1.1.12! root     1460:        putlog(5, "%s:  enter  %c:$%08x", __func__,
1.1.1.10  root     1461:                (laddr.IsSuper() ? 'S' : 'U'),
                   1462:                addr);
1.1       root     1463: 
1.1.1.10  root     1464:        m68030ATCTable *table = atc.fctables[fc];
                   1465:        uint8 n = table->hash[addr >> 12];
1.1.1.12! root     1466:        // PS<4KB のケースを先に解決する。
        !          1467:        if (__predict_false(n == m68030ATCTable::HASH_SUB)) {
        !          1468:                a = table->FindSub(laddr);
        !          1469:                if (a) {
        !          1470:                        n = a - &table->line[0];
        !          1471:                } else {
        !          1472:                        n = m68030ATCTable::HASH_NONE;
        !          1473:                }
        !          1474:        }
1.1.1.10  root     1475:        if (__predict_true((int8)n >= 0)) {
                   1476:                // ATC #(n)
                   1477:                a = &table->line[n];
                   1478:                paddr = busaddr(a->GetPAddr());
                   1479:                paddr |= addr & mmu_tc.pgmask;
1.1.1.12! root     1480:                putlog(5, "%s:  result $%08x (match atc)", __func__, paddr.Addr());
1.1.1.10  root     1481:                return paddr;
1.1.1.12! root     1482:        } else if (n == m68030ATCTable::HASH_NONE) {
        !          1483:                if (__predict_true(mmu_tc.e)) {
        !          1484:                        // TC.E 有効で対応なしなら、こっそりテーブルサーチ。
        !          1485: 
        !          1486:                        // mmu.search() はさすがに bus を使うので事前に退避。
        !          1487:                        m68kbus backup = bus;
        !          1488: 
        !          1489:                        m68030MMU mmu(this, loglevel);
        !          1490:                        mmu.m_debugger = true;
        !          1491:                        mmu.search(7, laddr);
        !          1492:                        paddr = mmu.make_atc_debugger();
        !          1493:                        // リストア
        !          1494:                        bus = backup;
        !          1495: 
        !          1496:                        if (paddr.IsBusErr()) {
        !          1497:                                // TC が有効だがヒットしなかったらバスエラー
        !          1498:                                putlog(5, "%s:  result BusErr", __func__);
1.1.1.10  root     1499:                        } else {
1.1.1.12! root     1500:                                paddr |= addr & mmu_tc.pgmask;
        !          1501:                                putlog(5, "%s:  result $%08x (table search)",
1.1.1.10  root     1502:                                        __func__, paddr.Addr());
                   1503:                        }
1.1.1.12! root     1504:                        return paddr;
1.1.1.9   root     1505:                } else {
1.1.1.12! root     1506:                        // TC.E 無効
        !          1507:                        paddr = laddr;
        !          1508:                        putlog(5, "%s:  result $%08x (no translation)",
        !          1509:                                __func__, paddr.Addr());
        !          1510:                        return paddr;
        !          1511:                }
        !          1512:        } else {
        !          1513:                // TT をこっそりチェック。
        !          1514:                for (int i = 0; i < 2; i++) {
        !          1515:                        m68030TT& tt = mmu_tt[i];
        !          1516:                        if (tt.Match(laddr)) {
        !          1517:                                // 一致した。
        !          1518:                                // CI はここでは使わないため、一致したことだけわかればよい
        !          1519:                                putlog(5, "%s:  result $%08x (match tt)",
        !          1520:                                        __func__, paddr.Addr());
        !          1521:                                return paddr;
1.1.1.10  root     1522:                        }
1.1.1.9   root     1523:                }
1.1.1.12! root     1524:                return BusAddr::BusErr;
1.1       root     1525:        }
                   1526: }
                   1527: 
                   1528: //
                   1529: // 命令
                   1530: //
                   1531: 
1.1.1.8   root     1532: // ir2 の下位5ビットから FC を返す。
1.1       root     1533: // 不当パターンなら C++ の例外をスローする。
1.1.1.8   root     1534: uint32
1.1.1.11  root     1535: MPU68030Device::get_fc()
1.1       root     1536: {
1.1.1.8   root     1537:        uint fcfield = ir2 & 0x1f;
1.1       root     1538: 
                   1539:        if (fcfield == 0) {
1.1.1.9   root     1540:                return reg.GetSFC();
1.1       root     1541:        } else if (fcfield == 1) {
1.1.1.9   root     1542:                return reg.GetDFC();
1.1       root     1543:        } else if (fcfield < 0x08) {
                   1544:                // break;
                   1545:        } else if (fcfield < 0x10) {
1.1.1.8   root     1546:                return reg.D[fcfield & 7] & 7;
1.1       root     1547:        } else if (fcfield < 0x18) {
                   1548:                return fcfield & 7;
                   1549:        }
                   1550:        // 不当命令
1.1.1.8   root     1551:        throw M68K::EXCEP_ILLEGAL;
1.1       root     1552: }
                   1553: 
                   1554: // MMU 不当命令 (2ワード目で確定)
1.1.1.8   root     1555: void
1.1.1.11  root     1556: MPU68030Device::mmu_op_illg2()
1.1       root     1557: {
1.1.1.8   root     1558:        putlog(1, "MMU illegal instruction %04x %04x", ir, ir2);
                   1559:        Exception(M68K::EXCEP_FLINE);
1.1       root     1560: }
                   1561: 
                   1562: // PFLUSH fc,#mask 命令 (EA を持たない方)
                   1563: void
1.1.1.11  root     1564: MPU68030Device::mmu_op_pflush()
1.1       root     1565: {
                   1566:        uint32 mask;
                   1567:        uint32 fc;
                   1568: 
1.1.1.8   root     1569:        mask = (ir2 >> 5) & 7;
                   1570:        fc = get_fc();
                   1571:        atc.flush(fc, mask);
1.1       root     1572: }
                   1573: 
                   1574: // PFLUSH fc,#mask,ea 命令
                   1575: void
1.1.1.11  root     1576: MPU68030Device::mmu_op_pflush_ea()
1.1       root     1577: {
                   1578:        uint32 ea;
                   1579:        uint32 mask;
                   1580:        uint32 fc;
                   1581: 
1.1.1.8   root     1582:        ea = cea_copro();
1.1.1.12! root     1583:        ea &= mmu_tc.lmask;
1.1.1.8   root     1584:        mask = (ir2 >> 5) & 7;
                   1585:        fc = get_fc();
                   1586:        atc.flush(fc, mask, ea);
1.1       root     1587: }
                   1588: 
                   1589: // PLOADR/PLOADW 命令
                   1590: void
1.1.1.11  root     1591: MPU68030Device::mmu_op_pload()
1.1       root     1592: {
                   1593:        uint32 ea;
                   1594:        uint32 fc;
1.1.1.10  root     1595:        busaddr laddr;
1.1       root     1596: 
1.1.1.8   root     1597:        if ((ir2 & 0x01e0) != 0) {
                   1598:                mmu_op_illg2();
1.1       root     1599:                return;
                   1600:        }
1.1.1.8   root     1601:        ea = cea_copro();
                   1602:        fc = get_fc();
1.1       root     1603: 
                   1604:        // 実効アドレスを論理アドレスとする ATC を作成。
                   1605:        // ってこれでいいのか?
1.1.1.8   root     1606:        if ((ir2 & 0x0200)) {
1.1.1.10  root     1607:                laddr = busaddr(ea) | busaddr::FC(fc) | BusAddr::R;
1.1       root     1608:        } else {
1.1.1.10  root     1609:                laddr = busaddr(ea) | busaddr::FC(fc) | BusAddr::W;
                   1610:        }
                   1611: 
                   1612:        m68030ATCTable *table = atc.fctables[fc];
1.1.1.12! root     1613:        m68030ATCLine *a;
1.1.1.10  root     1614:        uint8 n = table->hash[laddr.Addr() >> 12];
1.1.1.12! root     1615:        // PS<4KB のケースを先に解決する。
        !          1616:        if (__predict_false(n == m68030ATCTable::HASH_SUB)) {
        !          1617:                a = table->FindSub(laddr);
        !          1618:                if (a) {
        !          1619:                        n = a - &table->line[0];
1.1.1.10  root     1620:                } else {
1.1.1.12! root     1621:                        n = m68030ATCTable::HASH_NONE;
1.1.1.10  root     1622:                }
                   1623:        }
1.1.1.12! root     1624:        if ((int8)n >= 0) {
        !          1625:                // すでにエントリがある場合
        !          1626:                if (laddr.IsWrite()) {
        !          1627:                        // 命令が ploadw でエントリが WProtect==0 && Modified==0 なら、
        !          1628:                        // 一回破棄して書き込み用に作り直す。
        !          1629:                        a = &table->line[n];
        !          1630:                        if (a->IsWProtect() == false && a->IsModified() == false) {
        !          1631:                                table->Invalidate(a);
        !          1632:                                table->MMUSearch(laddr);
        !          1633:                        }
        !          1634:                }
        !          1635:        } else if (n == m68030ATCTable::HASH_NONE) {
1.1.1.10  root     1636:                if (__predict_true(mmu_tc.e)) {
                   1637:                        // TC.E 有効で対応なしなら、テーブルサーチを行って
                   1638:                        // ATC を作成。
1.1.1.12! root     1639:                        table->MMUSearch(laddr);
1.1.1.10  root     1640:                } else {
                   1641:                        // TC.E 無効なら何もしない?
                   1642:                }
                   1643:        } else if (n == m68030ATCTable::HASH_TT) {
                   1644:                // TT の領域なら何もしてはいけない。
1.1       root     1645:        }
                   1646: }
                   1647: 
                   1648: // PTESTR/PTESTW 命令
                   1649: void
1.1.1.11  root     1650: MPU68030Device::mmu_op_ptest()
1.1       root     1651: {
1.1.1.8   root     1652:        uint level = (ir2 >> 10) & 7;
1.1.1.10  root     1653:        busaddr rw = (ir2 & 0x0200) ? BusAddr::R : BusAddr::W;
1.1.1.8   root     1654:        uint rn    = (ir2 >> 5) & 0xf;
1.1       root     1655:        uint32 ea;
                   1656:        uint32 fc;
                   1657:        uint16 mmusr;
                   1658: 
1.1.1.8   root     1659:        ea = cea_copro();
                   1660:        fc = get_fc();
                   1661:        mmusr = 0;
1.1       root     1662: 
                   1663:        if (level == 0) {
1.1.1.8   root     1664:                if (rn != 0) {
                   1665:                        // レベル0で An 指定なら Fライン例外
                   1666:                        mmu_op_illg2();
                   1667:                        return;
                   1668:                }
1.1       root     1669: 
1.1.1.8   root     1670:                // laddr は IS と PS のところを落としたもの。
                   1671:                // ssw_laddr のアドレスは IS を落とさないもの (PSは不問)。
1.1.1.10  root     1672:                busaddr laddr = busaddr(ea) | busaddr::FC(fc) | rw;
                   1673:                uint32 addr = ea & mmu_tc.lmask;
                   1674: 
                   1675:                m68030ATCTable *table = atc.fctables[fc];
                   1676:                m68030ATCLine *a = NULL;
                   1677:                uint8 n = table->hash[addr >> 12];
1.1.1.12! root     1678:                // PS<4KB のケースを先に解決する。
        !          1679:                if (__predict_false(n == m68030ATCTable::HASH_SUB)) {
        !          1680:                        a = table->FindSub(laddr);
        !          1681:                        if (a) {
        !          1682:                                n = a - &table->line[0];
        !          1683:                        } else {
        !          1684:                                n = m68030ATCTable::HASH_NONE;
        !          1685:                        }
        !          1686:                }
1.1.1.10  root     1687:                if (__predict_true((int8)n >= 0)) {
                   1688:                        // ATC #(n) で見付かった。
                   1689:                        a = &table->line[n];
1.1.1.12! root     1690:                } else if (n == m68030ATCTable::HASH_NONE) {
        !          1691:                        // ATC に見付からなかった。Invalid を立てる。
        !          1692:                        mmusr = m68030MMUSR::I;
        !          1693:                } else if (n == m68030ATCTable::HASH_TT) {
        !          1694:                        for (int i = 0; i < 2; i++) {
        !          1695:                                m68030TT& tt = mmu_tt[i];
        !          1696:                                if (tt.Match(laddr)) {
        !          1697:                                        mmusr |= m68030MMUSR::T;
1.1.1.10  root     1698:                                }
                   1699:                        }
                   1700:                }
1.1.1.8   root     1701: 
                   1702:                if (a) {
1.1.1.10  root     1703:                        // 見付かれば Modified 等の状態を返す。
1.1.1.8   root     1704:                        if (a->IsBusError()) {
                   1705:                                mmusr |= m68030MMUSR::B;
                   1706:                        } else if (a->IsWProtect()) {
                   1707:                                mmusr |= m68030MMUSR::W;
                   1708:                        } else if (a->IsModified()) {
                   1709:                                mmusr |= m68030MMUSR::M;
                   1710:                        }
1.1       root     1711:                }
                   1712:        } else {
1.1.1.8   root     1713:                // レベル 1-7 ならテーブルサーチを実行。
                   1714: 
1.1.1.10  root     1715:                m68030MMU mmu(this, loglevel);
                   1716:                mmu.m_ptest = true;
                   1717:                mmu.search(level, busaddr(ea) | busaddr::FC(fc) | rw);
1.1.1.8   root     1718: 
1.1.1.10  root     1719:                if (mmu.m_berr) {
1.1       root     1720:                        mmusr |= m68030MMUSR::B;
                   1721:                }
1.1.1.10  root     1722:                if (mmu.m_lim) {
1.1.1.8   root     1723:                        // XXX not tested
                   1724:                        mmusr |= m68030MMUSR::L;
                   1725:                }
1.1.1.10  root     1726:                if (mmu.m_s && (fc & 4)) {
1.1.1.8   root     1727:                        // XXX not tested
                   1728:                        mmusr |= m68030MMUSR::S;
                   1729:                }
1.1.1.10  root     1730:                if (mmu.m_wp) {
1.1.1.8   root     1731:                        mmusr |= m68030MMUSR::W;
                   1732:                }
1.1.1.10  root     1733:                if (mmu.m_type == INVALID) {
1.1.1.8   root     1734:                        mmusr |= m68030MMUSR::I;
                   1735:                }
1.1.1.10  root     1736:                if (mmu.m_m) {
1.1.1.8   root     1737:                        mmusr |= m68030MMUSR::M;
                   1738:                }
                   1739:                // N を求める。
                   1740:                // もともとそういう風には出来ていないので逆算している。
                   1741:                int n;
                   1742:                if (mmu_tc.fcl) {
                   1743:                        // たぶんファンクションテーブルで1段消費する? (未確認)
1.1.1.10  root     1744:                        if (mmu.x == FCL) {
1.1.1.8   root     1745:                                n = 1;
                   1746:                        } else {
1.1.1.10  root     1747:                                n = mmu.x + 2;
1.1.1.8   root     1748:                        }
                   1749:                } else {
1.1.1.10  root     1750:                        n = mmu.x + 1;
1.1.1.8   root     1751:                }
                   1752:                mmusr |= n;
                   1753: 
                   1754:                // アドレスレジスタ指定があれば、最後のディスクリプタアドレスを返す。
                   1755:                // rn は有効なら 8-15。
                   1756:                if (rn != 0) {
                   1757:                        // XXX 途中でバスエラーが起きるケースは未実装
1.1.1.10  root     1758:                        if (mmu.m_berr == false) {
                   1759:                                reg.R[rn] = mmu.m_lastaddr;
1.1.1.8   root     1760:                        }
                   1761:                }
1.1       root     1762:        }
                   1763: 
1.1.1.8   root     1764:        SetMMUSR(mmusr);
1.1       root     1765: }

unix.superglobalmegacorp.com

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