--- nono/m680x0/m68030subr.cpp 2026/04/29 17:04:40 1.1.1.4 +++ nono/m680x0/m68030subr.cpp 2026/04/29 17:04:52 1.1.1.8 @@ -8,15 +8,15 @@ #include "m68030bus.h" #include "m68030fpu.h" #include "debugger.h" +#include "scheduler.h" static void m68030_change_intr(m68kcpu *, int); static void m68030_set_intr(m68kcpu *, int); static void m68030_double_bus_fault(m68kcpu *, const char *); // CPU コアの領域を確保して初期化 -// reset_vector はリセット例外時に 0 番地に見せる(通常ROMの)アドレス。 m68kcpu * -m68030_init(uint32 reset_vector) +m68030_init() { m68kcpu *cpu; @@ -30,12 +30,41 @@ m68030_init(uint32 reset_vector) // あるため、A6 を概ね RAM のあるあたりに指定しておく…。 RegA(6) = 0x000ccccc; - // 手抜きのためリセットベクタを知っている - cpu->reset_vector = reset_vector; - return cpu; } +// 仮想時刻を返す +uint64 +m68030_get_vtime(m68kcpu *cpu) +{ + return cpu->total_vtime + cpu->Cycle2Vtime(cpu->used_cycle); +} + +// クロックを設定する。 +void +m68030_set_clockspeed(m68kcpu *cpu, uint32 clock_khz_) +{ + cpu->clock_khz = clock_khz_; + + // サイクル数を nsec に変換する際の係数。25MHz なら c2v は 40 [nsec/clock] + cpu->c2v = 1000 * 1000 / cpu->clock_khz; + + // nsec をクロック数に変換する際の係数、つまり c2v の逆数。 + // (http://hp.vector.co.jp/authors/VA003988/how_to_o.htm#27_2) + int b = 32 - __builtin_clz(cpu->c2v) - 1; + int r = 32 + b; + // 小数部を近接丸め(0捨1入)するため一桁余分に計算しておいて.. + uint64 f = (1ULL << (r + 1)) / cpu->c2v; + // 最下位ビットを丸める + if ((f & 1) != 0) { + f += 1; + } + f >>= 1; + + cpu->v2c_factor = f; + cpu->v2c_shift = r; +} + // 二重バスフォールト static void m68030_double_bus_fault(m68kcpu *cpu, const char *where) @@ -48,22 +77,39 @@ m68030_double_bus_fault(m68kcpu *cpu, co } } -// リセット例外を発生させる。 -// 各レジスタを所定の状態にして、リセットベクタを読み込む。 -// リセットベクタが読み込めなければ HALT 状態に移行。 +// 電源オン void -m68030_exception_reset(m68kcpu *cpu) +m68030_power_on(m68kcpu *cpu) { - // 履歴を初期化。リセット前の履歴いらんよな? + // 履歴を初期化。電源(再)投入時のみ行う。 cpu->exhist.Clear(); cpu->brhist.Clear(); + // この後起きるリセット例外で PC を初期化する前に参照することになるので + // これだけここでも初期化しておく。 + RegPC = 0; +} + +// リセット例外を発生させる。(外部ルーチン) +void +m68030_request_reset_exception(m68kcpu *cpu) +{ + cpu->atomic_reqflag |= CPU_REQ_RESET; +} + +// リセット例外処理。 +// 各レジスタを所定の状態にして、リセットベクタを読み込む。 +// リセットベクタが読み込めなければ HALT 状態に移行。 +void +m68030_exception_reset(m68kcpu *cpu) +{ // 例外履歴に記録 (例外発生はブランチ履歴にも記録) - // リセット例外発生時の PC にはあまり意味がないので 0 にするか。 + // m68k では今の所リセット例外発生時の PC にはあまり意味がないけど、 + // m88k と動作を揃えておく。 // 履歴のリセットベクタは 0 の代わりに 1 を使う仕様になっている。 const uint vector = 0; - cpu->exhist.AddEntry(0, 0, 0x8000 | (vector + 1)); - cpu->brhist.AddEntry(0, 0, 0x8000 | (vector + 1)); + cpu->exhist.AddEntry(RegPC | 1, 0, 0x8000 | (vector + 1)); + cpu->brhist.AddEntry(RegPC | 1, 0, 0x8000 | (vector + 1)); // デバッガに通知 (例外ブレーク用)。こっちのベクタは 0 のままでよい。 debugger_notify_exception(vector); @@ -71,10 +117,11 @@ m68030_exception_reset(m68kcpu *cpu) // 2. S をセット、M をクリア // 3. 割り込みレベルを 7 - // 受け付け中だった割り込みをクリア - cpu->intrmtx.lock(); - memset(&cpu->intr_vector, 0, sizeof(cpu->intr_vector)); - cpu->intrmtx.unlock(); + // 受け付け中だった割り込みをクリア。 + // STOP 状態とホールト状態を解除。 + cpu->intr_pending = 0; + cpu->atomic_reqflag &= ~(CPU_REQ_INTR | CPU_REQ_STOP | CPU_REQ_HALT); + cpu->reg.state = Scheduler::SCHED_CPU_NORMAL; // ここは set_sr を使わずに設定する。 cpu->reg.s = true; @@ -98,36 +145,27 @@ m68030_exception_reset(m68kcpu *cpu) // 10. PC をロード // 手抜き。 - // XXX 本当は ROM を 0番地に見せかけてから 0番地からロードするのだが - // ぶっちゃけそれを外部から観測する手段もないのでなー。 // XXX FC を気にしないといけない // XXX 読めなければ二重バスフォールト // 本当はアドレスエラーもチェックするのだが、起きないので省略。 try { - RegA(7) = m68030_read_32(cpu, cpu->reset_vector); - RegPC = m68030_read_32(cpu, cpu->reset_vector + 4); + RegA(7) = m68030_read_32(cpu, 0); + RegPC = m68030_read_32(cpu, 4); // リセットベクタは常に 0, 4 番地から取得したことになっている - cpu->brhist.AddEntry(0x00000004, RegPC, 0x8000); + cpu->brhist.AddEntry(0x00000004 | 1, RegPC, 0x8000); } catch (int) { m68030_double_bus_fault(cpu, "reset exception"); } } -// MPU 外部からの割り込み受付。 -// オートベクタの場合は vector にオートベクタの例外ベクタを指定のこと。 +// 割り込みを受理 void -m68030_interrupt(m68kcpu *cpu, int level, int vector) +m68030_interrupt(m68kcpu *cpu, int level) { - std::unique_lock lock(cpu->intrmtx); - - // この割り込みレベルがすでにペンディングなら何もしない - if ((int8)(cpu->intr_pending << level) < 0) { - return; - } - - // 割り込みを受理 - cpu->intr_pending |= (0x80U >> level); - cpu->intr_vector[level] = vector; + // レベル比較等は VM 側で行ってあるので、ここは割り込みの受理。 + // といってもフラグを立てるだけで実際の処理はメインループから呼ばれる + // m68030_exception_interrupt() (少し下にある) で行っている。 + cpu->intr_pending = level; cpu->atomic_reqflag |= CPU_REQ_INTR; } @@ -137,7 +175,6 @@ static void m68030_set_intr(m68kcpu *cpu, int level) { cpu->reg.intr_level = level; - cpu->intr_mask = 0x7f >> level; } // 割り込みのチェックとレベルの変更。 @@ -280,7 +317,7 @@ m68030_jump_vector(m68kcpu *cpu, int vec // VBR 込みのジャンプ先が確定したところで今度はブランチ履歴に記録。 // この時のブランチ元はベクタアドレス // (RegPC は fetch によって進んでしまうので先に保存) - cpu->brhist.AddEntry(vecaddr, newpc, 0x8000); + cpu->brhist.AddEntry(vecaddr | (cpu->reg.s ? 1 : 0), newpc, 0x8000); CHECK_ADDRESS_ERROR(newpc); RegPC = newpc; @@ -413,8 +450,9 @@ m68030_exception(m68kcpu *cpu, int vecto } // 例外履歴に記録 (例外発生はブランチ履歴にも記録) - cpu->exhist.AddEntry(RegPPC, 0, 0x8000 | vector); - cpu->brhist.AddEntry(RegPPC, 0, 0x8000 | vector); + uint32 from = RegPPC | (cpu->reg.s ? 1 : 0); + cpu->exhist.AddEntry(from, 0, 0x8000 | vector); + cpu->brhist.AddEntry(from, 0, 0x8000 | vector); // デバッガに通知 (例外ブレーク用) debugger_notify_exception(vector); @@ -471,8 +509,9 @@ m68030_exception_buserr(m68kcpu *cpu, in "vector=%d", vector); // 例外履歴に記録 (例外発生はブランチ履歴にも記録) - cpu->exhist.AddEntry(RegPPC, 0, 0x8000 | vector); - cpu->brhist.AddEntry(RegPPC, 0, 0x8000 | vector); + uint32 from = RegPPC | (cpu->reg.s ? 1 : 0); + cpu->exhist.AddEntry(from, 0, 0x8000 | vector); + cpu->brhist.AddEntry(from, 0, 0x8000 | vector); // デバッガに通知 (例外ブレーク用) debugger_notify_exception(vector); @@ -509,14 +548,25 @@ m68030_exception_buserr(m68kcpu *cpu, in } // 割り込み例外処理を行う。 -// intrmtx なしで呼び出すこと。 // 割り込みチェックは m68030core.cpp::m68030_flush_interrupt() で判定している。 void -m68030_exception_interrupt(m68kcpu *cpu, int level, int vector) +m68030_exception_interrupt(m68kcpu *cpu, int level) { + // ユーザーズマニュアル(日本語) p.399 図8-5 の中ほど + // 「/IPEND をネゲート、割り込みアクノリッジサイクルを実行」 + // 以降に相当。 + + // 割り込みアクノリッジサイクルを実行 + int vector = m68030_interrupt_acknowledge(level); + if (vector == -1) { + vector = 24 + level; + } + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) - cpu->exhist.AddEntry(RegPPC, 0, 0x8000 | (level << 8) | vector); - cpu->brhist.AddEntry(RegPPC, 0, 0x8000 | (level << 8) | vector); + uint32 from = RegPPC | (cpu->reg.s ? 1 : 0); + uint32 inst = 0x8000 | (level << 8) | vector; + cpu->exhist.AddEntry(from, 0, inst); + cpu->brhist.AddEntry(from, 0, inst); // デバッガに通知 (例外ブレーク用) debugger_notify_exception(vector);