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