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