--- nono/m680x0/m68030subr.cpp 2026/04/29 17:04:28 1.1.1.1 +++ nono/m680x0/m68030subr.cpp 2026/04/29 17:04:34 1.1.1.2 @@ -1,9 +1,9 @@ // // 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" @@ -36,11 +36,15 @@ m68030_init(uint32 reset_vector) } // 二重バスフォールト -// XXX どうするかね static void m68030_double_bus_fault(m68kcpu *cpu, const char *where) { - PANIC("double bus fault in %s", where); + cpu->atomic_reqflag |= CPU_REQ_RELEASE | CPU_REQ_HALT; + + // コールバックがあれば呼び出す + if (cpu->halt_callback) { + cpu->halt_callback(cpu->halt_arg); + } } // リセット例外を発生させる。 @@ -246,6 +250,14 @@ m68030_jump_vector(m68kcpu *cpu, int vec RegPC = RegVBR + vector * 4; newpc = m68030_fetch_32(cpu); m68030_jump(cpu, newpc); + + // 実際はここでパイプライン充填のために3ワードフェッチするので、 + // ベクタテーブルはフェッチ出来たけどそのジャンプ先がバスエラーに + // なるケースはここでバスエラーが出なければいけないが、まだ + // キャッシュを実装していないので、ここで1ワードフェッチしてみて + // PC を戻すあほっぽい方法を取る。 + m68030_fetch_16(cpu); + RegPC -= 2; } // 例外スタックフレーム $0 を生成する。 @@ -328,8 +340,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]); } @@ -360,68 +372,94 @@ m68030_generate_exframe_b(m68kcpu *cpu, void m68030_exception(m68kcpu *cpu, int vector) { + // バスエラー・アドレスエラーは別で処理。 + if (vector <= M68K_EXCEP_ADDRERR) { + m68030_exception_buserr(cpu, vector); + return; + } + // 状態を保存 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); - switch (vector) { - case M68K_EXCEP_BUSERR: // バスエラー - case M68K_EXCEP_ADDRERR: // アドレスエラー - - // 実際はショート($A)、ロング($B)の2種類あって、本来ならショートが - // 使えればショートを使うのだが、ここでは全部ロングを使う。 - try { - m68030_generate_exframe_b(cpu, vector, sr); - } catch (...) { - m68030_double_bus_fault(cpu, "exception"); - return; + 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; } - break; - 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; + // ベクタにジャンプ + m68030_jump_vector(cpu, vector); + } catch (int vector2) { + assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector); - 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_exception_buserr(cpu, vector2); } +} + +// バスエラー・アドレスエラー例外処理を行う。 +// 他の例外とは違いが多いので分けた。 +void +m68030_exception_buserr(m68kcpu *cpu, int vector) +{ + assertmsg((vector == M68K_EXCEP_BUSERR || vector == M68K_EXCEP_ADDRERR), + "vector=%d", vector); + + // 状態を保存 + uint16 sr = RegSR; - // ベクタにジャンプ - m68030_jump_vector(cpu, vector); + // ここで (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); + + try { + // フレームを作成。 + // 実際はショート($A)、ロング($B)の2種類あって、本来ならショートが + // 使えればショートを使うのだが、ここでは全部ロングを使う。 + 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"); + } } // 割り込み例外処理を行う。 @@ -443,13 +481,20 @@ m68030_exception_interrupt(m68kcpu *cpu, // 割り込みレベルを変更 m68030_change_intr(cpu, level); - // 例外スタックフレーム $0 を生成。 - // スタックに積む PC は次の命令位置。 - // M ビットは未実装なのでスローアウェイとか無視 - m68030_generate_exframe_0(cpu, vector, RegPC, sr); + try { + // 例外スタックフレーム $0 を生成。 + // スタックに積む PC は次の命令位置。 + // M ビットは未実装なのでスローアウェイとか無視 + m68030_generate_exframe_0(cpu, vector, RegPC, sr); - // ベクタにジャンプ - m68030_jump_vector(cpu, vector); + // ベクタにジャンプ + m68030_jump_vector(cpu, vector); + } catch (int vector2) { + assertmsg(vector2 == M68K_EXCEP_BUSERR, "vector=%d", vector); + + // バスエラー処理へ + m68030_exception_buserr(cpu, vector2); + } } // RTE 命令。 @@ -461,61 +506,68 @@ 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[] = {