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

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

unix.superglobalmegacorp.com

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