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

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

unix.superglobalmegacorp.com

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