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