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