--- nono/m680x0/m68030core.cpp 2026/04/29 17:04:28 1.1 +++ nono/m680x0/m68030core.cpp 2026/04/29 17:05:08 1.1.1.6 @@ -1,23 +1,16 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "header.h" #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) #define OP_FUNC(name) __CONCAT(op_,name)(cpu) -// (エミュレータ的)未実装命令 -// 今はまだマクロ -#define op_unimpl(cpu) \ - PANIC("unimplemented instruction! %s()", __FUNCTION__) - #include "m68030bus.h" #include "m68030bus.cpp" #include "m68030excep.h" @@ -28,203 +21,46 @@ #include "m68030fpu.h" #include "m68030ops.cpp" -static void m68030_flush_interrupt(m68kcpu *cpu); - -// request サイクル(を超えるところまで)実行する。 -uint32 -m68030_run(m68kcpu *cpu, uint64 request) +// 1命令実行する。 +// 戻り値は消費したサイクル数。 +int +m68030_exec(m68kcpu *cpu) { - uint64 goal_cycle = cpu->total_cycle + request; + 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_INTR)) { - // 割り込みチェック - // 必要ならこの中で例外処理を起動する - m68030_flush_interrupt(cpu); - } - - if ((cpu->atomic_reqflag & CPU_REQ_STOP)) { - // STOP 状態は、残りサイクルを消費したことにして戻る - cpu->reg.state = CPU_REQ_STOP; - cpu->total_cycle = goal_cycle; - return cpu->atomic_reqflag; - } - - if ((cpu->atomic_reqflag & CPU_REQ_HALT)) { - cpu->reg.state = CPU_REQ_HALT; - PANIC("未実装 HALT 状態"); - } - - 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->total_cycle += cpu->cycle_cache; \ - cpu->total_cycle += cpu->cycle_nocache; \ - cpu->cycle_cache = 0; \ - cpu->cycle_nocache = 0; \ - if (cpu->total_cycle >= 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->total_cycle += cpu->cycle_cache; - cpu->total_cycle += cpu->cycle_nocache; - cpu->cycle_cache = 0; - cpu->cycle_nocache = 0; - if (cpu->total_cycle >= 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); } + + 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); } - __unreachable(); -} -// ペンディングの割り込みを確認 -static void -m68030_flush_interrupt(m68kcpu *cpu) -{ - // 割り込みマスクをレベル(1-7)に変換するテーブル - // b はペンディングになってる割り込みのうち今回処理する最上位の1つ - // を示すビットが立った状態。不正入力がないとすると入力は 0 〜 64。 - static uint8 mask2level[65] = { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0,7,6,0,5,0,0,0, 4,0,0,0,0,0,0,0, // $00 - 3,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, // $10 - 2,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, // $20 - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, // $30 - 1, - }; - int b; - - cpu->intrmtx.lock(); - - b = cpu->intr_pending & cpu->intr_mask; - if (b != 0) { - // 立っている最下位ビットだけを残す - b &= -b; - - // 該当フラグは落としておく - cpu->intr_pending &= ~b; - - // ストップ状態を解除 - cpu->atomic_reqflag &= ~CPU_REQ_STOP; - cpu->reg.state = 0; - - // b をレベルに変換 - int level = mask2level[b]; - int vector = cpu->intr_vector[level]; - - // ロックここまで。例外処理時に intrmtx は外しておくこと - cpu->intrmtx.unlock(); - - // 例外処理を起動 - m68030_exception_interrupt(cpu, level, vector); - - // 次の命令境界でも割り込みチェックを行う - // 更に下のレベルの割り込みがペンディングになっている可能性のため - } else { - // ペンディングの割り込みはなくなったので、 - // 次の命令境界では割り込みチェックは不要 - cpu->atomic_reqflag &= ~CPU_REQ_INTR; + // 外部割り込みの起動は VM 側で処理している。 - // ロックここまで (atomic なのでその前で解放してもいいけど) - cpu->intrmtx.unlock(); - } + return cpu->used_cycle - cycle_start; }