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