--- nono/m680x0/m68030core.cpp 2026/04/29 17:04:43 1.1.1.3 +++ nono/m680x0/m68030core.cpp 2026/04/29 17:05:08 1.1.1.6 @@ -5,9 +5,7 @@ // #include "m68030core.h" -#include "debugger.h" -#include "cvprompt.h" -#include "mpu.h" +#include "vectortable.h" #define OP_PROTO(name) static inline void __CONCAT(op_,name)(m68kcpu *cpu) #define OP_DEF(name) static inline void __CONCAT(op_,name)(m68kcpu *cpu) @@ -23,182 +21,46 @@ #include "m68030fpu.h" #include "m68030ops.cpp" -static void m68030_flush_interrupt(m68kcpu *cpu); - -// 仮想時間 delta [nsec] だけ CPU を実行する。実際には大抵行き過ぎる。 -// 戻り値はフラグ。 -// -// 戻る時に仮想時刻を確定させなくても構わない。スケジューラはこれを呼び出した -// 後に必然的に仮想時刻を取得することになるため、そこで確定させられる。 -uint32 -m68030_run(m68kcpu *cpu, uint32 delta) +// 1命令実行する。 +// 戻り値は消費したサイクル数。 +int +m68030_exec(m68kcpu *cpu) { - // XXX DMAC がデバイスアクセスして時間が進むので - // 今の所ここに来た時点で常に 0 にはならない - //assert(cpu->used_cycle == 0); - - cpu->goal_cycle = cpu->Vtime2Cycle(delta); - if (__predict_false(cpu->goal_cycle == 0)) { - cpu->goal_cycle = 1; - } + uint64 cycle_start = cpu->used_cycle; - // メインループ - while (1) { - if (cpu->atomic_reqflag != 0) { - // 何かが起きた - - // デバッグ用 - if (0) { - static int oldreq = -1; - uint32 _req = cpu->atomic_reqflag; - if ((_req & (CPU_REQ_TRACE | CPU_REQ_PROMPT)) != oldreq) { - oldreq = _req & (CPU_REQ_TRACE | CPU_REQ_PROMPT); - printf("reqflag=%c%c\n", - (_req & CPU_REQ_TRACE) ? 'T' : '-', - (_req & CPU_REQ_PROMPT) ? 'P' : '-'); - } - } - - // XXX 発生頻度を観測すること。 - // そして並び替えるとか、フラグの割り当てを考えるとか。 - - // このフラグ処理の優先度はたぶん次のような感じになるはず。 - // 1. スケジューラ依頼による処理の中断 - // 2. 命令途中で起きた例外の処理 - // 3. 割り込みチェック - // 4. STOP、HALT 状態の対応 - - if ((cpu->atomic_reqflag & CPU_REQ_RELEASE)) { - // ここで実行を中断してスケジューラに戻る - cpu->atomic_reqflag &= ~CPU_REQ_RELEASE; - return cpu->atomic_reqflag; - } - - if ((cpu->atomic_reqflag & CPU_REQ_RESET)) { - // リセット例外 - m68030_exception_reset(cpu); - cpu->atomic_reqflag &= ~CPU_REQ_RESET; - } - - if ((cpu->atomic_reqflag & CPU_REQ_INTR)) { - // 割り込みチェック (必要なら例外処理を起動する) - m68030_flush_interrupt(cpu); - cpu->atomic_reqflag &= ~CPU_REQ_INTR; - } - - if ((cpu->atomic_reqflag & CPU_REQ_STOP)) { - // STOP 状態は、残りサイクルを消費したことにして戻る - cpu->reg.state = CPU_REQ_STOP; - cpu->used_cycle = cpu->goal_cycle; - return cpu->atomic_reqflag; - } - - if ((cpu->atomic_reqflag & CPU_REQ_HALT)) { - // HALT 状態も、残りサイクルを消費したことにして戻る - cpu->reg.state = CPU_REQ_HALT; - cpu->used_cycle = cpu->goal_cycle; - return cpu->atomic_reqflag; - } - - if ((cpu->atomic_reqflag & CPU_REQ_TRACE)) { - // デバッガプロンプトを出すかどうかを判断する - cpu->inst_count++; - if (debugger_check()) { - cpu->atomic_reqflag |= CPU_REQ_PROMPT; - } - } - - if ((cpu->atomic_reqflag & CPU_REQ_PROMPT)) { - // デバッガプロンプトのための実行停止 - - // プロンプト獲得を通知 - gCVPrompt->NotifyAcquire(); - - // プロンプト解放を待機 - gCVPrompt->WaitRelease(); - - // プロンプトを抜けたのでここでフラグを落とす - cpu->atomic_reqflag &= ~CPU_REQ_PROMPT; - } - } - - try { -#define USE_COMPUTED_GOTO -#if defined(USE_COMPUTED_GOTO) - // computed-goto を使う方法。 - // JUMP が1回目の分岐。CHECK_AND_JUMP が命令実行後の分岐。 - // outer は命令実行前にチェックしてはいけないので、2つに - // 分かれている。1回目の分岐も goto.h の中で出力してある。 -#define JUMP \ - RegPPC = RegPC; \ - cpu->flag_pi = 0; \ - cpu->flag_pd = 0; \ - RegIR = m68030_fetch_16(cpu); \ - __assume((RegIR >> 6) < 1024); \ - goto *jumptable[RegIR >> 6] - -#define CHECK_AND_JUMP \ - cpu->used_cycle += cpu->cycle_nocache; \ - cpu->cycle_nocache = 0; \ - if (cpu->used_cycle >= cpu->goal_cycle) \ - return cpu->atomic_reqflag; \ - if (cpu->atomic_reqflag != 0) goto exit_inner_loop; \ - JUMP - -#include "m68030goto.h" - exit_inner_loop:; -#else - // switch-case を使う方法。 - do { - RegPPC = RegPC; - cpu->flag_pi = 0; - cpu->flag_pd = 0; - RegIR = m68030_fetch_16(cpu); - - // uint16 を 6ビット右シフトしたら 1024 以上になるはずは - // ないのだが、デフォルトでは gcc/clang どちらも律儀に - // 1024 以上かどうか比較して分岐しようとする。 - // gcc は __assume() を入れることでこの比較・分岐を出力 - // しないようにすることが出来た(がパフォーマンスの差は - // 見えない程度でしかなかった)。 - // clang ではどうやったら効くか不明。 - uint16 ir = RegIR >> 6; - __assume(ir < 1024); - switch (ir) { + try { + // 1命令版では switch-case を使うしかない。 + RegPPC = RegPC; + cpu->flag_pi = 0; + cpu->flag_pd = 0; + RegIR = m68030_fetch_16(cpu); + + // uint16 を 6ビット右シフトしたら 1024 以上になるはずは + // ないのだが、デフォルトでは gcc/clang どちらも律儀に + // 1024 以上かどうか比較して分岐しようとする。 + // gcc は __assume() を入れることでこの比較・分岐を出力 + // しないようにすることが出来た(がパフォーマンスの差は + // 見えない程度でしかなかった)。 + // clang ではどうやったら効くか不明。 + uint16 ir = RegIR >> 6; + __assume(ir < 1024); + switch (ir) { #include "m68030switch.h" - } - - cpu->used_cycle += cpu->cycle_nocache; - cpu->cycle_nocache = 0; - if (cpu->used_cycle >= cpu->goal_cycle) - return cpu->atomic_reqflag; - - } while (cpu->atomic_reqflag == 0); -#endif - } catch (int vector) { - // ここで(C++の)例外スローをキャッチするのは - // - メモリ空間をアクセスした先でのバスエラー例外 - // - EA パース中の不当命令例外 - // のみ。それ以外の命令途中例外はその場で処理できる。 - cpulog(1, "%s", m68030_get_exception_name(vector)); - m68030_exception(cpu, vector); } - } - __unreachable(); -} -// ペンディングの割り込みを確認 -static void -m68030_flush_interrupt(m68kcpu *cpu) -{ - if (cpu->intr_pending > cpu->reg.intr_level) { - // マスクを超えるレベルの割り込みが起きた + cpu->used_cycle += cpu->cycle_nocache; + cpu->cycle_nocache = 0; + } catch (int vector) { + // ここで(C++の)例外スローをキャッチするのは + // - メモリ空間をアクセスした先でのバスエラー例外 + // - EA パース中の不当命令例外 + // のみ。それ以外の命令途中例外はその場で処理できる。 + const char *name = gVectorTable->GetExceptionName(vector); + cpulog(1, "%s", name ?: "(reserved vector?)"); + m68030_exception(cpu, vector); + } - // ストップ状態を解除 - cpu->atomic_reqflag &= ~CPU_REQ_STOP; - cpu->reg.state = 0; + // 外部割り込みの起動は VM 側で処理している。 - // 例外処理を起動 - m68030_exception_interrupt(cpu, cpu->intr_pending); - } + return cpu->used_cycle - cycle_start; }