|
|
1.1 root 1: //
2: // nono
1.1.1.2 ! root 3: // Copyright (C) 2020 nono project
! 4: // Licensed under nono-license.txt
1.1 root 5: //
6:
7: #include "m68030core.h"
8: #include "debugger.h"
9: #include "cvprompt.h"
10: #include "mpu.h"
11:
12: #define OP_PROTO(name) static inline void __CONCAT(op_,name)(m68kcpu *cpu)
13: #define OP_DEF(name) static inline void __CONCAT(op_,name)(m68kcpu *cpu)
14: #define OP_FUNC(name) __CONCAT(op_,name)(cpu)
15:
16: #include "m68030bus.h"
17: #include "m68030bus.cpp"
18: #include "m68030excep.h"
19: #include "m68030ea.h"
20: #include "m68030acc.h"
21: #include "m68030ops.h"
22: #include "m68030mmu.h"
23: #include "m68030fpu.h"
24: #include "m68030ops.cpp"
25:
26: static void m68030_flush_interrupt(m68kcpu *cpu);
27:
28: // request サイクル(を超えるところまで)実行する。
29: uint32
30: m68030_run(m68kcpu *cpu, uint64 request)
31: {
32: uint64 goal_cycle = cpu->total_cycle + request;
33:
34: // メインループ
35: while (1) {
36: if (cpu->atomic_reqflag != 0) {
37: // 何かが起きた
38:
39: // デバッグ用
40: if (0) {
41: static int oldreq = -1;
42: uint32 _req = cpu->atomic_reqflag;
43: if ((_req & (CPU_REQ_TRACE | CPU_REQ_PROMPT)) != oldreq) {
44: oldreq = _req & (CPU_REQ_TRACE | CPU_REQ_PROMPT);
45: printf("reqflag=%c%c\n",
46: (_req & CPU_REQ_TRACE) ? 'T' : '-',
47: (_req & CPU_REQ_PROMPT) ? 'P' : '-');
48: }
49: }
50:
51: // XXX 発生頻度を観測すること。
52: // そして並び替えるとか、フラグの割り当てを考えるとか。
53:
54: // このフラグ処理の優先度はたぶん次のような感じになるはず。
55: // 1. スケジューラ依頼による処理の中断
56: // 2. 命令途中で起きた例外の処理
57: // 3. 割り込みチェック
58: // 4. STOP、HALT 状態の対応
59:
60: if ((cpu->atomic_reqflag & CPU_REQ_RELEASE)) {
61: // ここで実行を中断してスケジューラに戻る
62: cpu->atomic_reqflag &= ~CPU_REQ_RELEASE;
63: return cpu->atomic_reqflag;
64: }
65:
66: if ((cpu->atomic_reqflag & CPU_REQ_INTR)) {
67: // 割り込みチェック
68: // 必要ならこの中で例外処理を起動する
69: m68030_flush_interrupt(cpu);
70: }
71:
72: if ((cpu->atomic_reqflag & CPU_REQ_STOP)) {
73: // STOP 状態は、残りサイクルを消費したことにして戻る
74: cpu->reg.state = CPU_REQ_STOP;
75: cpu->total_cycle = goal_cycle;
76: return cpu->atomic_reqflag;
77: }
78:
79: if ((cpu->atomic_reqflag & CPU_REQ_HALT)) {
1.1.1.2 ! root 80: // HALT 状態も、残りサイクルを消費したことにして戻る
1.1 root 81: cpu->reg.state = CPU_REQ_HALT;
1.1.1.2 ! root 82: cpu->total_cycle = goal_cycle;
! 83: return cpu->atomic_reqflag;
1.1 root 84: }
85:
86: if ((cpu->atomic_reqflag & CPU_REQ_TRACE)) {
87: // デバッガプロンプトを出すかどうかを判断する
88: cpu->inst_count++;
89: if (debugger_check()) {
90: cpu->atomic_reqflag |= CPU_REQ_PROMPT;
91: }
92: }
93:
94: if ((cpu->atomic_reqflag & CPU_REQ_PROMPT)) {
95: // デバッガプロンプトのための実行停止
96:
97: // プロンプト獲得を通知
98: gCVPrompt->NotifyAcquire();
99:
100: // プロンプト解放を待機
101: gCVPrompt->WaitRelease();
102:
103: // プロンプトを抜けたのでここでフラグを落とす
104: cpu->atomic_reqflag &= ~CPU_REQ_PROMPT;
105: }
106: }
107:
108: try {
109: #define USE_COMPUTED_GOTO
110: #if defined(USE_COMPUTED_GOTO)
111: // computed-goto を使う方法。
112: // JUMP が1回目の分岐。CHECK_AND_JUMP が命令実行後の分岐。
113: // outer は命令実行前にチェックしてはいけないので、2つに
114: // 分かれている。1回目の分岐も goto.h の中で出力してある。
115: #define JUMP \
116: RegPPC = RegPC; \
117: cpu->flag_pi = 0; \
118: cpu->flag_pd = 0; \
119: RegIR = m68030_fetch_16(cpu); \
120: __assume((RegIR >> 6) < 1024); \
121: goto *jumptable[RegIR >> 6]
122:
123: #define CHECK_AND_JUMP \
124: cpu->total_cycle += cpu->cycle_cache; \
125: cpu->total_cycle += cpu->cycle_nocache; \
126: cpu->cycle_cache = 0; \
127: cpu->cycle_nocache = 0; \
128: if (cpu->total_cycle >= goal_cycle) return cpu->atomic_reqflag; \
129: if (cpu->atomic_reqflag != 0) goto exit_inner_loop; \
130: JUMP
131:
132: #include "m68030goto.h"
133: exit_inner_loop:;
134: #else
135: // switch-case を使う方法。
136: do {
137: RegPPC = RegPC;
138: cpu->flag_pi = 0;
139: cpu->flag_pd = 0;
140: RegIR = m68030_fetch_16(cpu);
141:
142: // uint16 を 6ビット右シフトしたら 1024 以上になるはずは
143: // ないのだが、デフォルトでは gcc/clang どちらも律儀に
144: // 1024 以上かどうか比較して分岐しようとする。
145: // gcc は __assume() を入れることでこの比較・分岐を出力
146: // しないようにすることが出来た(がパフォーマンスの差は
147: // 見えない程度でしかなかった)。
148: // clang ではどうやったら効くか不明。
149: uint16 ir = RegIR >> 6;
150: __assume(ir < 1024);
151: switch (ir) {
152: #include "m68030switch.h"
153: }
154:
155: cpu->total_cycle += cpu->cycle_cache;
156: cpu->total_cycle += cpu->cycle_nocache;
157: cpu->cycle_cache = 0;
158: cpu->cycle_nocache = 0;
159: if (cpu->total_cycle >= goal_cycle)
160: return cpu->atomic_reqflag;
161:
162: } while (cpu->atomic_reqflag == 0);
163: #endif
164: } catch (int vector) {
165: // ここで(C++の)例外スローをキャッチするのは
166: // - メモリ空間をアクセスした先でのバスエラー例外
167: // - EA パース中の不当命令例外
168: // のみ。それ以外の命令途中例外はその場で処理できる。
169: cpulog(1, "%s", m68030_get_exception_name(vector));
170: m68030_exception(cpu, vector);
171: }
172: }
173: __unreachable();
174: }
175:
176: // ペンディングの割り込みを確認
177: static void
178: m68030_flush_interrupt(m68kcpu *cpu)
179: {
180: // 割り込みマスクをレベル(1-7)に変換するテーブル
181: // b はペンディングになってる割り込みのうち今回処理する最上位の1つ
182: // を示すビットが立った状態。不正入力がないとすると入力は 0 〜 64。
183: static uint8 mask2level[65] = {
184: // 0 1 2 3 4 5 6 7 8 9 A B C D E F
185: 0,7,6,0,5,0,0,0, 4,0,0,0,0,0,0,0, // $00
186: 3,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, // $10
187: 2,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, // $20
188: 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, // $30
189: 1,
190: };
191: int b;
192:
193: cpu->intrmtx.lock();
194:
195: b = cpu->intr_pending & cpu->intr_mask;
196: if (b != 0) {
197: // 立っている最下位ビットだけを残す
198: b &= -b;
199:
200: // 該当フラグは落としておく
201: cpu->intr_pending &= ~b;
202:
203: // ストップ状態を解除
204: cpu->atomic_reqflag &= ~CPU_REQ_STOP;
205: cpu->reg.state = 0;
206:
207: // b をレベルに変換
208: int level = mask2level[b];
209: int vector = cpu->intr_vector[level];
210:
211: // ロックここまで。例外処理時に intrmtx は外しておくこと
212: cpu->intrmtx.unlock();
213:
214: // 例外処理を起動
215: m68030_exception_interrupt(cpu, level, vector);
216:
217: // 次の命令境界でも割り込みチェックを行う
218: // 更に下のレベルの割り込みがペンディングになっている可能性のため
219: } else {
220: // ペンディングの割り込みはなくなったので、
221: // 次の命令境界では割り込みチェックは不要
222: cpu->atomic_reqflag &= ~CPU_REQ_INTR;
223:
224: // ロックここまで (atomic なのでその前で解放してもいいけど)
225: cpu->intrmtx.unlock();
226: }
227: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.