|
|
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;
74: return cpu->atomic_reqflag;
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.3 ! root 92: cpu->used_cycle = cpu->goal_cycle;
1.1 root 93: return cpu->atomic_reqflag;
94: }
95:
96: if ((cpu->atomic_reqflag & CPU_REQ_HALT)) {
1.1.1.2 root 97: // HALT 状態も、残りサイクルを消費したことにして戻る
1.1 root 98: cpu->reg.state = CPU_REQ_HALT;
1.1.1.3 ! root 99: cpu->used_cycle = cpu->goal_cycle;
1.1.1.2 root 100: return cpu->atomic_reqflag;
1.1 root 101: }
102:
103: if ((cpu->atomic_reqflag & CPU_REQ_TRACE)) {
104: // デバッガプロンプトを出すかどうかを判断する
105: cpu->inst_count++;
106: if (debugger_check()) {
107: cpu->atomic_reqflag |= CPU_REQ_PROMPT;
108: }
109: }
110:
111: if ((cpu->atomic_reqflag & CPU_REQ_PROMPT)) {
112: // デバッガプロンプトのための実行停止
113:
114: // プロンプト獲得を通知
115: gCVPrompt->NotifyAcquire();
116:
117: // プロンプト解放を待機
118: gCVPrompt->WaitRelease();
119:
120: // プロンプトを抜けたのでここでフラグを落とす
121: cpu->atomic_reqflag &= ~CPU_REQ_PROMPT;
122: }
123: }
124:
125: try {
126: #define USE_COMPUTED_GOTO
127: #if defined(USE_COMPUTED_GOTO)
128: // computed-goto を使う方法。
129: // JUMP が1回目の分岐。CHECK_AND_JUMP が命令実行後の分岐。
130: // outer は命令実行前にチェックしてはいけないので、2つに
131: // 分かれている。1回目の分岐も goto.h の中で出力してある。
132: #define JUMP \
133: RegPPC = RegPC; \
134: cpu->flag_pi = 0; \
135: cpu->flag_pd = 0; \
136: RegIR = m68030_fetch_16(cpu); \
137: __assume((RegIR >> 6) < 1024); \
138: goto *jumptable[RegIR >> 6]
139:
140: #define CHECK_AND_JUMP \
1.1.1.3 ! root 141: cpu->used_cycle += cpu->cycle_nocache; \
1.1 root 142: cpu->cycle_nocache = 0; \
1.1.1.3 ! root 143: if (cpu->used_cycle >= cpu->goal_cycle) \
! 144: return cpu->atomic_reqflag; \
1.1 root 145: if (cpu->atomic_reqflag != 0) goto exit_inner_loop; \
146: JUMP
147:
148: #include "m68030goto.h"
149: exit_inner_loop:;
150: #else
151: // switch-case を使う方法。
152: do {
153: RegPPC = RegPC;
154: cpu->flag_pi = 0;
155: cpu->flag_pd = 0;
156: RegIR = m68030_fetch_16(cpu);
157:
158: // uint16 を 6ビット右シフトしたら 1024 以上になるはずは
159: // ないのだが、デフォルトでは gcc/clang どちらも律儀に
160: // 1024 以上かどうか比較して分岐しようとする。
161: // gcc は __assume() を入れることでこの比較・分岐を出力
162: // しないようにすることが出来た(がパフォーマンスの差は
163: // 見えない程度でしかなかった)。
164: // clang ではどうやったら効くか不明。
165: uint16 ir = RegIR >> 6;
166: __assume(ir < 1024);
167: switch (ir) {
168: #include "m68030switch.h"
169: }
170:
1.1.1.3 ! root 171: cpu->used_cycle += cpu->cycle_nocache;
1.1 root 172: cpu->cycle_nocache = 0;
1.1.1.3 ! root 173: if (cpu->used_cycle >= cpu->goal_cycle)
1.1 root 174: return cpu->atomic_reqflag;
175:
176: } while (cpu->atomic_reqflag == 0);
177: #endif
178: } catch (int vector) {
179: // ここで(C++の)例外スローをキャッチするのは
180: // - メモリ空間をアクセスした先でのバスエラー例外
181: // - EA パース中の不当命令例外
182: // のみ。それ以外の命令途中例外はその場で処理できる。
183: cpulog(1, "%s", m68030_get_exception_name(vector));
184: m68030_exception(cpu, vector);
185: }
186: }
187: __unreachable();
188: }
189:
190: // ペンディングの割り込みを確認
191: static void
192: m68030_flush_interrupt(m68kcpu *cpu)
193: {
1.1.1.3 ! root 194: if (cpu->intr_pending > cpu->reg.intr_level) {
! 195: // マスクを超えるレベルの割り込みが起きた
1.1 root 196:
197: // ストップ状態を解除
198: cpu->atomic_reqflag &= ~CPU_REQ_STOP;
199: cpu->reg.state = 0;
200:
201: // 例外処理を起動
1.1.1.3 ! root 202: m68030_exception_interrupt(cpu, cpu->intr_pending);
1.1 root 203: }
204: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.