--- nono/m680x0/m68030subr.cpp 2026/04/29 17:04:28 1.1 +++ nono/m680x0/m68030subr.cpp 2026/04/29 17:05:08 1.1.1.9 @@ -1,26 +1,26 @@ // // nono -// Copyright (C) 2017 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // -#include "header.h" #include "m68030core.h" #include "m68030bus.h" #include "m68030fpu.h" +#include "debugger.h" +#include "mpu680x0.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(MPU680x0Device *dev_) { m68kcpu *cpu; cpu = new m68kcpu(); fpu_init(cpu); + cpu->dev = dev_; for (int i = 0; i < 16; i++) { RegR(i) = 0xcccccccc; @@ -29,115 +29,50 @@ m68030_init(uint32 reset_vector) // あるため、A6 を概ね RAM のあるあたりに指定しておく…。 RegA(6) = 0x000ccccc; - // 手抜きのためリセットベクタを知っている - cpu->reset_vector = reset_vector; - return cpu; } -// 二重バスフォールト -// XXX どうするかね +// CPU の状態 (通常/STOP/HALT) を変更する static void -m68030_double_bus_fault(m68kcpu *cpu, const char *where) +m68030_change_state(m68kcpu *cpu, uint32 new_state) { - PANIC("double bus fault in %s", where); + if (cpu->state != new_state) { + cpu->state = new_state; + // スケジューラに通知 + cpu->dev->NotifyCPUMode(cpu->state); + } } -// リセット例外を発生させる。 -// 各レジスタを所定の状態にして、リセットベクタを読み込む。 -// リセットベクタが読み込めなければ HALT 状態に移行。 -void -m68030_exception_reset(m68kcpu *cpu) +// 二重バスフォールト +static void +m68030_double_bus_fault(m68kcpu *cpu, const char *where) { - // 1. T0, T1 クリア - // 2. S をセット、M をクリア - // 3. 割り込みレベルを 7 - - // ここは set_sr を使わずに設定する。 - cpu->reg.s = true; - cpu->reg.m = false; - cpu->bus.SetFC2(cpu->reg.s); - // ここは割り込みレベル変化のチェックは不要でセットだけでいい。 - m68030_set_intr(cpu, 7); - - // 4. VBR をクリア - RegVBR = 0; - // 5. キャッシュ - RegCACR = 0; // XXX - // 6. キャッシュエントリクリア - // 7. TC:E、TTn:E をクリア - cpu->SetTC(cpu->GetTC() & ~m68030TC::TC_E); - cpu->SetTT(0, cpu->GetTT(0) & ~m68030TT::E); - cpu->SetTT(1, cpu->GetTT(1) & ~m68030TT::E); - // 8. リセットベクタ生成 - - // 9. SP をロード - // 10. PC をロード + m68030_change_state(cpu, m68kcpu::CPU_STATE_HALT); - // 手抜き。 - // 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); - } catch (int) { - m68030_double_bus_fault(cpu, "reset exception"); + // コールバックがあれば呼び出す + if (cpu->halt_callback) { + cpu->halt_callback(cpu->halt_arg); } } -// MPU 外部からの割り込み受付。 -// オートベクタの場合は vector にオートベクタの例外ベクタを指定のこと。 +// 電源オン void -m68030_interrupt(m68kcpu *cpu, int level, int vector) +m68030_power_on(m68kcpu *cpu) { - std::unique_lock lock(cpu->intrmtx); + // サイクルを初期化。 + cpu->used_cycle = 0; - // この割り込みレベルがすでにペンディングなら何もしない - if ((int8)(cpu->intr_pending << level) < 0) { - return; - } + // 履歴を初期化。電源(再)投入時のみ行う。 + cpu->exhist.Clear(); + cpu->brhist.Clear(); - // 割り込みを受理 - cpu->intr_pending |= (0x80U >> level); - cpu->intr_vector[level] = vector; - cpu->atomic_reqflag |= CPU_REQ_INTR; + // この後起きるリセット例外で PC を初期化する前に参照することになるので + // これだけここでも初期化しておく。 + RegPC = 0; } -// 割り込みレベルの設定のみを行う。 -// 割り込みチェックは行わない。 -static void -m68030_set_intr(m68kcpu *cpu, int level) -{ - cpu->reg.intr_level = level; - cpu->intr_mask = 0x7f >> level; -} - -// 割り込みのチェックとレベルの変更。 -// ステータスレジスタの割り込みレベルを変更し、必要な割り込みチェックを行う。 -static void -m68030_change_intr(m68kcpu *cpu, int level) -{ - int oldlevel = cpu->reg.intr_level; - - if (oldlevel < 7 && level == 7) { - // レベル 7 に引き上げられたら割り込み確認 - cpu->atomic_reqflag |= CPU_REQ_INTR; - } else if (oldlevel == 7 && level < 7) { - // レベル 7 から引き下げられたら割り込み確認 - cpu->atomic_reqflag |= CPU_REQ_INTR; - } else if (oldlevel != level) { - // それ以外でレベルが変わったら割り込み確認? - cpu->atomic_reqflag |= CPU_REQ_INTR; - } - m68030_set_intr(cpu, level); -} - -// SR (と CCR) に data を代入する。 -// リセット例外時には呼ばれないのであっちで必要に応じて似た処理をする必要が -// ある。XXX なんとかしたほうがいい +// SR (と CCR) をセットする。 +// リセット例外以外から使う (リセット例外はやや異なるため)。 void m68030_set_sr(m68kcpu *cpu, uint16 data) { @@ -156,7 +91,15 @@ m68030_set_sr(m68kcpu *cpu, uint16 data) CCR.Set(data); cpu->reg.s = ((data & M68K_SR_S) != 0); cpu->reg.m = ((data & M68K_SR_M) != 0); - m68030_change_intr(cpu, (data >> 8) & 7); + + // 割り込みマスクレジスタを更新して.. + uint new_mask = (data >> 8) & 7; + if (cpu->reg.intr_mask != new_mask) { + cpu->reg.intr_mask = new_mask; + + // 次の命令境界で割り込みをチェックさせる + cpu->dev->CheckIntr(); + } // 新しい SP を用意 if (cpu->reg.s == false) { @@ -199,53 +142,31 @@ m68030_restore_reg_pd(m68kcpu *cpu) } } -// 例外処理について。 -// -// 0. リセット例外 -// リセット例外は外部から m68030_exception_reset() を呼び出す。 -// -// 1. バスエラー例外 -// 物理アドレス空間アクセス後のバスエラーは、物理空間側(VM側)から -// (C++の)例外がスローされるので、catch したところで処理する。 -// -// MMU による論理アドレスでのバスエラーについては後で考える。 -// -// 2. アドレスエラー例外 -// アドレスエラーは m68030_jump() 内でのみ発生するはずで、その場で -// 例外処理をするはず。 -// -// 3. 命令中で起きる例外 -// 命令途中で起きる(確定的な)例外は m68030_exception() を呼び出す。 -// これに該当するのは -// o 特権違反 -// o A系列/F系列命令 -// o TRAP* 系命令、CHK/CHK2 命令 -// o トレース例外 (未実装) -// o ゼロ除算? -// o MMU 構成例外 -// o 不当命令 (*) -// である。 -// ただし、不当命令についてはどこで不当命令と判定されるかによって -// フローが複数存在することに注意。 -// o 1ワード目が不当命令 -// -> 命令がそれぞれ m68030_exception() で処理。 -// o 1ワード目の EA が不当命令 -// -> (C++の)例外スローして catch 内で処理。 -// o 2ワード目が不当命令 -// -> 命令がそれぞれ m68030_exception() で処理。 -// -// 4. 割り込み例外 -// zzz - // vector にジャンプする。vector はベクタ番号(アドレスではない)。 static inline void m68030_jump_vector(m68kcpu *cpu, int vector) { + uint32 vecaddr; uint32 newpc; - RegPC = RegVBR + vector * 4; + vecaddr = RegVBR + vector * 4; + RegPC = vecaddr; newpc = m68030_fetch_32(cpu); - m68030_jump(cpu, newpc); + // VBR 込みのジャンプ先が確定したところで今度はブランチ履歴に記録。 + // この時のブランチ元はベクタアドレス + // (RegPC は fetch によって進んでしまうので先に保存) + cpu->brhist.AddEntry(vecaddr | (cpu->reg.s ? 1 : 0), newpc, 0x8000); + + CHECK_ADDRESS_ERROR(newpc); + RegPC = newpc; + + // 実際はここでパイプライン充填のために3ワードフェッチするので、 + // ベクタテーブルはフェッチ出来たけどそのジャンプ先がバスエラーに + // なるケースはここでバスエラーが出なければいけないが、まだ + // キャッシュを実装していないので、ここで1ワードフェッチしてみて + // PC を戻すあほっぽい方法を取る。 + m68030_fetch_16(cpu); + RegPC -= 2; } // 例外スタックフレーム $0 を生成する。 @@ -328,8 +249,8 @@ m68030_generate_exframe_b(m68kcpu *cpu, // で DF=1 ならプリデクリメント発生前の An に戻すという処理をする。 // RTE 時点で DF=0 なら命令を再実行しないため An も巻き戻してはならず、 // そのためプリデクリメント分の巻き戻しは RTE 時点まで行えない。 - // 一方、ポストインクリメントのケースはこの少し下の m68030_exception() - // 内のコメント参照。 + // 一方、ポストインクリメントのケースはこの少し下の + // m68030_exception_buserr() 内のコメント参照。 for (int i = 7; i >= 0; i--) { m68030_push_32(cpu, cpu->save_pd[i]); } @@ -355,101 +276,352 @@ m68030_generate_exframe_b(m68kcpu *cpu, m68030_push_16(cpu, sr); // 例外入り口で保存した SR } +// 例外処理について。 +// +// 0. リセット例外 +// リセット例外は外部から m68030_exception_reset() を呼び出す。 +// +// 1. バスエラー例外 +// MMU による論理アドレスでのバスエラーは (m68030_mmu_translate_*() が +// false を返した後) m68030bus.cpp ルーチンが (C++の) 例外をスローする。 +// 物理アドレス空間アクセス後のバスエラーは (VM側の read*()/write*() が +// (int64)-1 を返した後) m68030bus.cpp ルーチンが (C++) の例外をスローする。 +// どちらのケースも m68030core.cpp のメインループで catch し、その catch +// ブロック内で m68030_exception() を呼び出す。 +// +// 2. アドレスエラー例外 +// アドレスエラーは m68030_jump() 内でのみ発生するはずで、その場で +// m68030_exception() を呼んで処理する。 +// +// 3. 命令中で起きる例外 +// 命令途中で起きる(確定的な)例外は m68030_exception() を呼び出す。 +// これに該当するのは +// o 特権違反 +// o A系列/F系列命令 +// o TRAP* 系命令、CHK/CHK2 命令 +// o トレース例外 (未実装) +// o ゼロ除算? +// o MMU 構成例外 +// o 不当命令 (*) +// である。 +// ただし、不当命令についてはどこで不当命令と判定されるかによって +// フローが複数存在することに注意。 +// o 1ワード目が不当命令 +// -> 命令がそれぞれ m68030_exception() で処理。 +// o 1ワード目の EA が不当命令 +// -> (C++の)例外をスローして catch 内で m68030_exception() を実行。 +// o 2ワード目が不当命令 +// -> 命令がそれぞれ m68030_exception() で処理。 +// +// 4. 割り込み例外 +// 別コメント(下のほう)参照。 +// + +// リセット例外処理。 +// 各レジスタを所定の状態にして、リセットベクタを読み込む。 +// リセットベクタが読み込めなければ HALT 状態に移行。 +// 戻り値は消費したサイクル数。 +int +m68030_exception_reset(m68kcpu *cpu) +{ + uint64 cycle_start = cpu->used_cycle; + + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + // m68k では今の所リセット例外発生時の PC にはあまり意味がないけど、 + // m88k と動作を揃えておく。 + // 履歴のリセットベクタは 0 の代わりに 1 を使う仕様になっている。 + const uint vector = 0; + cpu->exhist.AddEntry(RegPC | 1, 0, 0x8000 | (vector + 1)); + cpu->brhist.AddEntry(RegPC | 1, 0, 0x8000 | (vector + 1)); + // デバッガに通知 (例外ブレーク用)。こっちのベクタは 0 のままでよい。 + debugger_notify_exception(vector); + + // RESET 信号線をネゲートしてから SP の読み込み開始まで 4クロック + // (Section 7.8, Figure 7-64) + cpu->used_cycle += 4; + + // STOP 状態とホールト状態を解除。 + m68030_change_state(cpu, m68kcpu::CPU_STATE_NORMAL); + + // 1. T0, T1 クリア + // 2. S をセット、M をクリア + // 3. 割り込みレベルを 7 + + // ここは set_sr を使わずに設定する。 + cpu->reg.s = true; + cpu->reg.m = false; + cpu->bus.SetFC2(cpu->reg.s); + // ここは割り込みレベル変化のチェックは不要でセットだけでいい。 + cpu->reg.intr_mask = 7; + + // 4. VBR をクリア + RegVBR = 0; + // 5. キャッシュ + RegCACR = 0; // XXX + // 6. キャッシュエントリクリア + // 7. TC:E、TTn:E をクリア + cpu->SetTC(cpu->GetTC() & ~m68030TC::TC_E); + cpu->SetTT(0, cpu->GetTT(0) & ~m68030TT::E); + cpu->SetTT(1, cpu->GetTT(1) & ~m68030TT::E); + // 8. リセットベクタ生成 + + // 9. SP をロード + // 10. PC をロード + try { + RegPC = 0; + RegA(7) = m68030_fetch_32(cpu); + uint32 pc = m68030_fetch_32(cpu); + // 大丈夫のはずだけど m68030_fetch_32() が RegPC を書き換えるので一応 + RegPC = pc; + // リセットベクタは常に 0, 4 番地から取得したことになっている + cpu->brhist.AddEntry(0x00000004 | 1, RegPC, 0x8000); + } catch (int) { + m68030_double_bus_fault(cpu, "reset exception"); + } + + // SP のアドレスエラーはリセット例外ではチェックしないようだ。 + // PC のアドレスエラーは本来ならここにあるはずの3ワードのプリフェッチで + // 行われるのだが、それがないので、とりあえずここで。 + if ((RegPC & 1) != 0) { + m68030_double_bus_fault(cpu, "reset exception"); + } + + return cpu->used_cycle - cycle_start; +} + // 例外処理 (リセット・割り込み以外) を行う。 // vector はベクタ番号。 void m68030_exception(m68kcpu *cpu, int vector) { + // バスエラー・アドレスエラーは別で処理。 + if (vector <= M68K_EXCEP_ADDRERR) { + m68030_exception_buserr(cpu, vector); + return; + } + + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + 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); + // 状態を保存 uint16 sr = RegSR; - switch (vector) { - case M68K_EXCEP_BUSERR: // バスエラー - case M68K_EXCEP_ADDRERR: // アドレスエラー - // SR を書き換える前に! A7 が切り替わってしまう。 - - // ここで (An)+ で変化したレジスタを命令実行前の状態に巻き戻す。 - // EA 処理の時点で先にインクリメントしているのだが、バスエラーが - // 起きたということは実際にはポストインクリメントは発生しないので、 - // ここで無条件に巻き戻してよい。 - // 一方、プリデクリメントのケースはこの少し上の exframe_b の中の - // コメント参照。 - m68030_restore_reg_pi(cpu); + // S を立てて T1,T0 を落とす + m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T); + + try { + switch (vector) { + case M68K_EXCEP_ILLEGAL: // 不当命令 + case M68K_EXCEP_PRIV: // 特権違反例外 + case M68K_EXCEP_ALINE: // Aライン命令例外 + case M68K_EXCEP_FLINE: // Fライン命令例外 + // スタックに積む PC はこの命令のアドレス + m68030_generate_exframe_0(cpu, vector, RegPPC, sr); + break; + + case M68K_EXCEP_TRAP0 // TRAP#0 + ... M68K_EXCEP_TRAP15: // TRAP#15 + // スタックに積む PC は次の命令 + m68030_generate_exframe_0(cpu, vector, RegPC, sr); + break; + + case M68K_EXCEP_ZERODIV: // ゼロ除算 + case M68K_EXCEP_CHK: // CHK + case M68K_EXCEP_TRAPV: // TRAPV + case M68K_EXCEP_MMU_CONFIG: // MMU 構成例外 + m68030_generate_exframe_2(cpu, vector, sr); + break; + + default: + PANIC("$%08x exception vector %d ?", RegPPC, vector); + break; + } + + // ベクタにジャンプ + m68030_jump_vector(cpu, vector); + } catch (int vector2) { + assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector); + + // バスエラー処理へ + m68030_exception_buserr(cpu, vector2); } +} + +// バスエラー・アドレスエラー例外処理を行う。 +// 他の例外とは違いが多いので分けた。 +void +m68030_exception_buserr(m68kcpu *cpu, int vector) +{ + assertmsg((vector == M68K_EXCEP_BUSERR || vector == M68K_EXCEP_ADDRERR), + "vector=%d", 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); + + // 状態を保存 + uint16 sr = RegSR; + + // ここで (An)+ で変化したレジスタを命令実行前の状態に巻き戻す。 + // EA 処理の時点で先にインクリメントしているのだが、バスエラーが + // 起きたということは実際にはポストインクリメントは発生しないので、 + // ここで無条件に巻き戻してよい。SR を書き換えると A7 が切り替わって + // しまうので、この巻き戻しは SR を書き換える前のここで行うこと。 + // 一方、プリデクリメントのケースはこの少し上の exframe_b の中の + // コメント参照。 + m68030_restore_reg_pi(cpu); // S を立てて T1,T0 を落とす m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T); - switch (vector) { - case M68K_EXCEP_BUSERR: // バスエラー - case M68K_EXCEP_ADDRERR: // アドレスエラー - + try { + // フレームを作成。 // 実際はショート($A)、ロング($B)の2種類あって、本来ならショートが // 使えればショートを使うのだが、ここでは全部ロングを使う。 - try { - m68030_generate_exframe_b(cpu, vector, sr); - } catch (...) { - m68030_double_bus_fault(cpu, "exception"); - return; - } - break; + m68030_generate_exframe_b(cpu, vector, sr); + // ベクタにジャンプ + m68030_jump_vector(cpu, vector); + } catch (int vector2) { + // ここに来るのはバスエラーだけのはず + assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector); + + // バスエラー・アドレスエラー処理中のバスエラーは + // ダブルバスフォールト。 + m68030_double_bus_fault(cpu, "bus/address error handling"); + } +} + +// 割り込み処理。 +// +// 割り込みは、本来命令境界ごとにチェックして条件を満たせば割り込み例外が +// 起動するのだが、割り込み例外が起きうる要因は +// o 割り込み信号線が変化した時 +// o 割り込みマスクを変更した時 +// しかないはずなので、この時だけチェックすればよいはず。 +// +// 外部割り込み信号線が変化した時 m68030_interrupt(level) が VM から呼ばれる。 +// m68030_interrupt() は割り込みレベルがマスクより高いか、レベルが下位から 7 +// に変化すれば true を返す。 +// これにより VM から割り込み処理 m68030_exception_interrupt() が呼ばれる。 +// +// 命令によって割り込みマスクを変更する場合、命令は m68030_change_intrmask() +// を呼ぶ。これが VM 側で次の命令実行コールバックを差し替えるため、 +// 次命令境界で割り込みのチェックが呼ばれる。これが m68030_check_interrupt()。 +// こちらは割り込みレベルがマスクより高いかどうかだけをチェックする (レベルは +// 変化していないので下位から 7 に変化したかは調べない)。レベルのほうが高く +// なっていれば true を返す。 +// これにより VM から割り込み処理 m68030_exception_interrupt() が呼ばれる。 + +// 割り込みレベル変更の受け付け。 +// 発生している最も高い割り込みレベルが変化した時にここに通知される。 +// 全員が下ろしたことによって 0 に遷移したことも通知されることに留意。 +// 割り込み例外を起動するなら true を返す。 +bool +m68030_interrupt(m68kcpu *cpu, int level) +{ + int mask = cpu->reg.intr_mask; + + // 信号線が 2 クロック期間安定していること云々は省略 + + // ホールトなら何もしない + if (cpu->state == m68kcpu::CPU_STATE_HALT) { + return false; + } + + // 割り込みを起こすかどうかに関わらずここで更新 + cpu->intr_level = level; - case M68K_EXCEP_ILLEGAL: // 不当命令 - case M68K_EXCEP_PRIV: // 特権違反例外 - case M68K_EXCEP_ALINE: // Aライン命令例外 - case M68K_EXCEP_FLINE: // Fライン命令例外 - // スタックに積む PC はこの命令のアドレス - m68030_generate_exframe_0(cpu, vector, RegPPC, sr); - break; - - case M68K_EXCEP_TRAP0 // TRAP#0 - ... M68K_EXCEP_TRAP15: // TRAP#15 - // スタックに積む PC は次の命令 - m68030_generate_exframe_0(cpu, vector, RegPC, sr); - break; - - case M68K_EXCEP_ZERODIV: // ゼロ除算 - case M68K_EXCEP_CHK: // CHK - case M68K_EXCEP_TRAPV: // TRAPV - case M68K_EXCEP_MMU_CONFIG: // MMU 構成例外 - m68030_generate_exframe_2(cpu, vector, sr); - break; - - default: - PANIC("$%08x exception vector %d ?", RegPPC, vector); - break; + // 割り込み例外を起動するのは + // o レベルがマスクより高い + // o マスクに関係なく、レベルが下位から 7 に変化した場合 + // (ここは変化した時だけ呼ばれるのと 7 より上はないので + // level==7 なら必ず「下位から 7 に変化」のはず) + if (level > mask || level == 7) { + return true; } - // ベクタにジャンプ - m68030_jump_vector(cpu, vector); + return false; } -// 割り込み例外処理を行う。 -// intrmtx なしで呼び出すこと。 -// 割り込みチェックは m68030core.cpp::m68030_flush_interrupt() で判定している。 -void -m68030_exception_interrupt(m68kcpu *cpu, int level, int vector) +// 割り込みをチェックする。 +// 割り込みマスク変更後の命令境界で VM 側から呼ばれる。 +// 割り込みを起こすなら true を返す。 +bool +m68030_check_interrupt(m68kcpu *cpu) { - if (0) { - cpulog(2, "Interrupt lv=%d vec=%d occured: %s", - level, vector, m68030_get_exception_name(vector)); + int mask = cpu->reg.intr_mask; + int level = cpu->intr_level; + + // こちらは HALT では呼び出されない + + // 割り込みレベルは変化していないので、ここでは Level 7 の特別処理は不要 + if (level > mask) { + return true; } + return false; +} + +// 割り込み例外処理を行う。 +// 割り込みの受け付けとレベルのチェック等の後、次命令境界で呼ばれるやつ。 +int +m68030_exception_interrupt(m68kcpu *cpu) +{ + int cycle_start = cpu->used_cycle; + int level = cpu->intr_level; + + // ユーザーズマニュアル(日本語) p.399 図8-5 の中ほど + // 「/IPEND をネゲート、割り込みアクノリッジサイクルを実行」 + // 以降に相当。 + + // STOP なら解除 + m68030_change_state(cpu, m68kcpu::CPU_STATE_NORMAL); + + // 割り込みアクノリッジサイクルを実行 + int vector = m68030_interrupt_acknowledge(level); + if (vector == -1) { + vector = 24 + level; + } + + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + 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); // 状態を保存 - uint16 sr = RegSR; + uint16 oldsr = RegSR; - // S を立てて T1,T0 を落とす - m68030_set_sr(cpu, (RegSR | M68K_SR_S) & ~M68K_SR_T); - // 割り込みレベルを変更 - m68030_change_intr(cpu, level); + // S を立てて T1,T0 を落とす。割り込みレベルも変更。 + uint16 newsr = RegSR & ~(M68K_SR_T | M68K_SR_IMASK); + newsr |= M68K_SR_S; + newsr |= level << 8; + m68030_set_sr(cpu, newsr); + + try { + // 例外スタックフレーム $0 を生成。 + // スタックに積む PC は次の命令位置。 + // M ビットは未実装なのでスローアウェイとか無視 + m68030_generate_exframe_0(cpu, vector, RegPC, oldsr); + + // ベクタにジャンプ + m68030_jump_vector(cpu, vector); + } catch (int vector2) { + assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector); - // 例外スタックフレーム $0 を生成。 - // スタックに積む PC は次の命令位置。 - // M ビットは未実装なのでスローアウェイとか無視 - m68030_generate_exframe_0(cpu, vector, RegPC, sr); + // バスエラー処理へ + m68030_exception_buserr(cpu, vector2); + } - // ベクタにジャンプ - m68030_jump_vector(cpu, vector); + return cpu->used_cycle - cycle_start; } // RTE 命令。 @@ -461,150 +633,78 @@ m68030_rte(m68kcpu *cpu) uint16 sr; uint16 frame; - sr = m68030_pop_16(cpu); - pc = m68030_pop_32(cpu); - frame = m68030_pop_16(cpu); - switch (frame >> 12) { - case 0: - CYCLE2(18, 20); - m68030_set_sr(cpu, sr); - m68030_jump(cpu, pc); - return; - - case 2: - CYCLE2(18, 20); - // +08.L INSTRUCTION ADDRESS を読み捨てる - RegA(7) += 4; - m68030_set_sr(cpu, sr); - m68030_jump(cpu, pc); - return; - - case 0x0b: - { - uint32 stage_b; - uint16 ssw; - - CYCLE(76); - RegA(7) += 2; // $08.W - ssw = m68030_pop_16(cpu); // $0a.W SSW - RegA(7) += 4 * 6; // $0c.L*6 - stage_b = m68030_pop_32(cpu); // $24.L Stage B - RegA(7) += 2 * 9; // $28.W*9 - // $3a..$58 ここが save_pd の取り出し - cpu->flag_pd = m68030_pop_16(cpu); - for (int i = 0; i < 8; i++) { - cpu->save_pd[i] = m68030_pop_32(cpu); - } - // A7 レジスタをリストアするかもしれない前に Super/User を - // 復帰しておかないといけない - m68030_set_sr(cpu, sr); - - // DFビットセットなら命令を再実行するため、-(An) で変化 - // したレジスタを巻き戻す。 - // DFビットクリアなら例外要因の命令はハンドラで処理された - // とみなして、次の命令に進む。 - if ((ssw & SSW_DF)) { - m68030_restore_reg_pd(cpu); - } else { - pc = stage_b - 2; + try { + sr = m68030_pop_16(cpu); + pc = m68030_pop_32(cpu); + frame = m68030_pop_16(cpu); + switch (frame >> 12) { + case 0: + CYCLE2(18, 20); + m68030_set_sr(cpu, sr); + m68030_jump(cpu, pc); + break; + + case 2: + CYCLE2(18, 20); + // +08.L INSTRUCTION ADDRESS を読み捨てる + RegA(7) += 4; + m68030_set_sr(cpu, sr); + m68030_jump(cpu, pc); + break; + + case 0x0b: + { + uint32 stage_b; + uint16 ssw; + + CYCLE(76); + RegA(7) += 2; // $08.W + ssw = m68030_pop_16(cpu); // $0a.W SSW + RegA(7) += 4 * 6; // $0c.L*6 + stage_b = m68030_pop_32(cpu); // $24.L Stage B + RegA(7) += 2 * 9; // $28.W*9 + // $3a..$58 ここが save_pd の取り出し + cpu->flag_pd = m68030_pop_16(cpu); + for (int i = 0; i < 8; i++) { + cpu->save_pd[i] = m68030_pop_32(cpu); + } + // A7 レジスタをリストアするかもしれない前に Super/User を + // 復帰しておかないといけない + m68030_set_sr(cpu, sr); + + // DFビットセットなら命令を再実行するため、-(An) で変化 + // したレジスタを巻き戻す。 + // DFビットクリアなら例外要因の命令はハンドラで処理された + // とみなして、次の命令に進む。 + if ((ssw & SSW_DF)) { + m68030_restore_reg_pd(cpu); + } else { + pc = stage_b - 2; + } + m68030_jump(cpu, pc); + break; + } + + default: + PANIC("RTE frame=$%x not supported", frame >> 12); + break; } - m68030_jump(cpu, pc); - return; - } + } catch (int vector2) { + assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector2=%d", vector2); - default: - break; + // RTE 処理中のバスエラーはダブルバスフォールト。 + m68030_double_bus_fault(cpu, "bus/address error handling"); } - PANIC("RTE frame=$%x not supported", frame >> 12); } -static const char * const exception_name_table[] = { - /* 0 */ "Reset", - /* 1 */ NULL, - /* 2 */ "Bus Error", - /* 3 */ "Address Error", - /* 4 */ "Illegal Instruction", - /* 5 */ "Div by Zero", - /* 6 */ "CHK/CHK2 Instruction", - /* 7 */ "TRAP Instruction", - /* 8 */ "Priviledge Violation", - /* 9 */ "Trace", - /* 10 */ "A line emulation", - /* 11 */ "F line emulation", - /* 12 */ NULL, - /* 13 */ "Co-processor", - /* 14 */ "Format", - /* 15 */ NULL, - /* 16 */ "Uninitialized Interrupt", - /* 17 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, - /* 24 */ "Spurious Interrupt", - /* 25 */ "Level 1 Interrupt", - /* 26 */ "Level 2 Interrupt", - /* 27 */ "Level 3 Interrupt", - /* 28 */ "Level 4 Interrupt", - /* 29 */ "Level 5 Interrupt", - /* 30 */ "Level 6 Interrupt", - /* 31 */ "Level 7 Interrupt", - /* 32 */ "Trap #0", - /* 33 */ "Trap #1", - /* 34 */ "Trap #2", - /* 35 */ "Trap #3", - /* 36 */ "Trap #4", - /* 37 */ "Trap #5", - /* 38 */ "Trap #6", - /* 39 */ "Trap #7", - /* 40 */ "Trap #8", - /* 41 */ "Trap #9", - /* 42 */ "Trap #10", - /* 43 */ "Trap #11", - /* 44 */ "Trap #12", - /* 45 */ "Trap #13", - /* 46 */ "Trap #14", - /* 47 */ "Trap #15", - /* 48 */ "FPCP Branch", - /* 49 */ "FPCP Inexcact", - /* 50 */ "FPCP Div by Zero", - /* 51 */ "FPCP UnderFlow", - /* 52 */ "FPCP Operation Error", - /* 53 */ "FPCP OverFlow", - /* 54 */ "FPCP SNAN", - /* 55 */ NULL, - /* 56 */ "MMU Configuration Error", - /* 57 */ NULL, - /* 58 */ NULL, - /* 59 */ NULL, - /* 60 */ NULL, - /* 61 */ NULL, - /* 62 */ NULL, - /* 63 */ NULL, - /* 64 */ "MFP Alarm", - /* 65 */ "MFP EXPON", - /* 66 */ "MFP POWSW", - /* 67 */ "MFP FMIRQ", - /* 68 */ "MFP Timer-D", - /* 69 */ "MFP Timer-C", - /* 70 */ "MFP V-Disp", - /* 71 */ "MFP GPIP5", - /* 72 */ "MFP Timer-B", - /* 73 */ "MFP TXError", - /* 74 */ "MFP TXEmpty", - /* 75 */ "MFP RXError", - /* 76 */ "MFP RXFull", - /* 77 */ "MFP Timer-A", - /* 78 */ "MFP CIRQ", - /* 79 */ "MFP H-Sync", -}; - -// ベクタ番号から表示名を返す -const char * -m68030_get_exception_name(int vector) -{ - const char *name = NULL; - if (vector < countof(exception_name_table)) { - name = exception_name_table[vector]; - } - if (name == NULL) { - name = "(reserved?)"; - } - return name; +// STOP 命令。 +// 動作制御が必要なのでこっちに置いておくほうが便利。 +void +m68030_stop(m68kcpu *cpu) +{ + uint32 data = m68030_fetch_16(cpu); + m68030_set_sr(cpu, data); + m68030_change_state(cpu, m68kcpu::CPU_STATE_STOP); + + cpu->dev->MakeNextFull(); }