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