|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.