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