--- nono/m88xx0/m88100core.cpp 2026/04/29 17:04:49 1.1.1.7 +++ nono/m88xx0/m88100core.cpp 2026/04/29 17:05:07 1.1.1.11 @@ -7,9 +7,7 @@ #include "m88100.h" #include "m88100excep.h" #include "debugger.h" -#include "cvprompt.h" -#include "mpu.h" -#include "scheduler.h" +#include "mpu88xx0.h" #define OP_DEF(name) void __CONCAT(m88kcpu::op_,name)() #define OP_FUNC(name) __CONCAT(op_,name)() @@ -22,13 +20,43 @@ } \ } while (0) -// リセット例外のようなもの? +// 電源オン void +m88kcpu::PowerOn() +{ + // サイクルを初期化。 + used_cycle = 0; + + // 履歴は電源オン時だけ初期化。 + // (LUNA88K は ROM が正常パスで CPU リセットを行ったりするので) + exhist.Clear(); + brhist.Clear(); + + // この後起きるリセット例外で xip を初期化する前に参照することになるので + // これだけここでも初期化しておく。 + xip = 0; +} + +// リセット例外のようなもの? +uint64 m88kcpu::Reset() { + uint64 cycle_start = used_cycle; + + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + // リセット例外も発生時の XIP を記録する。(LUNA88K の ROM 1.20) + exhist.AddEntry(xip | 1, 0, 0xfc000000 | 0); + brhist.AddEntry(xip | 1, 0, 0xfc000000 | 0); + // デバッガに通知 (例外ブレーク用) + debugger_notify_exception(0); + // 内部の割り込み状態をクリア intr_pending = 0; - atomic_reqflag &= ~(CPU_REQ_RESET | CPU_REQ_INTR | CPU_REQ_STOP); + ChangeState(CPU_STATE_NORMAL); + + // 実際どこかは分からんけど PSR 設定の前にリセットしておきたい + cmmu[0].Reset(); + cmmu[1].Reset(); // Table 6-8 (p6-47) psr = 0x800003ff; @@ -42,11 +70,7 @@ m88kcpu::Reset() SetPSR(); - // 実際には m88k MPU はリセット直後は 0 番地から実行を開始するので、 - // 大抵の場合システムでリセット直後は 0 番地付近に ROM を見せるように - // してあるのだと思うが、ここではその代わりに ROM のアドレスを指定して、 - // 直接そこから実行を開始することにする。 - fip = reset_vector; + fip = 0; // レジスタのうち明記されてるものをクリア。 // 不定と明記されてるものはリセットでは触らないでおく。 @@ -62,50 +86,19 @@ m88kcpu::Reset() dmt1 &= ~DM_VALID; dmt2 &= ~DM_VALID; - cmmu[0].Reset(); - cmmu[1].Reset(); - - // 履歴を初期化 - exhist.Clear(); - brhist.Clear(); - - // 例外履歴に記録 (例外発生はブランチ履歴にも記録) - // リセット例外発生時の from にはあまり意味がないので 0 にするか。 - exhist.AddEntry(0 | 1, 0, 0xfc000000 | 0); - brhist.AddEntry(0 | 1, 0, 0xfc000000 | 0); - // デバッガに通知 (例外ブレーク用) - debugger_notify_exception(0); - fetch(); + + return used_cycle - cycle_start; } -// 仮想時間 delta [nsec] だけ CPU を実行する。実際には大抵行き過ぎる。 -// 戻り値は CPU 状態 (Scheduler::SCHED_CPU_*)。 -uint32 -m88kcpu::Run(uint32 delta) +// 1命令実行する。 +// 戻り値は消費したサイクル数。 +int +m88kcpu::Exec() { - bool is_release = false; - bool is_intr = false; - - assert(used_cycle == 0); - - goal_cycle = Vtime2Cycle(delta); - if (__predict_false(goal_cycle == 0)) { - goal_cycle = 1; - } + uint64 cycle_start = used_cycle; - if ((atomic_reqflag & CPU_REQ_STOP)) { - if ((atomic_reqflag & (CPU_REQ_INTR | CPU_REQ_PROMPT | CPU_REQ_RESET))) { - // 割り込み、デバッガ、リセットが来たら STOP 解除。 - atomic_reqflag &= ~CPU_REQ_STOP; - } else { - // STOP 継続として戻る。 - used_cycle = goal_cycle; - goto exit; - } - } - - while (!is_release) { + { // shift pipeline opX = opF; xip = nip; @@ -115,68 +108,10 @@ m88kcpu::Run(uint32 delta) // この時点で実行しようとする命令が opX に入っている - if (atomic_reqflag != 0) { - // 何かが起きた - - if ((atomic_reqflag & CPU_REQ_RESET)) { - // リセット例外。 - Reset(); - // XIP がリセットベクタを指すためにはもう一回フェッチが必要 - // なので、ここは continue が必要。 - continue; - } - - // 割り込みの認識 - // 割り込みの実行は命令実行完了後、次のプリフェッチの前 - if ((atomic_reqflag & CPU_REQ_INTR)) { - atomic_reqflag &= ~CPU_REQ_INTR; - if (intr_pending && IsIntrEnable()) { - // この命令のあとに割り込み処理 - is_intr = true; - } -#if defined(TEST_INTERRUPT) - atomic_reqflag |= CPU_REQ_PROMPT; -#endif - } - - if ((atomic_reqflag & CPU_REQ_TRACE)) { - // デバッガプロンプトを出すかどうかを判断する - //inst_count++; - if (debugger_check()) { - atomic_reqflag |= CPU_REQ_PROMPT; - } - } - - if ((atomic_reqflag & CPU_REQ_PROMPT)) { - // デバッガプロンプトのための実行停止 - - // プロンプト獲得を通知 - gCVPrompt->NotifyAcquire(); - - // プロンプト解放を待機 - gCVPrompt->WaitRelease(); - - // プロンプトを抜けたのでここでフラグを落とす - atomic_reqflag &= ~CPU_REQ_PROMPT; - - // プロンプトから戻ってきたら一旦スケジューラまで戻る(?)。 - // reset コマンドのようにデバッガからスケジューラに出した - // リクエストを(なるはやで?)回収するため。 - atomic_reqflag |= CPU_REQ_RELEASE; - } - - if ((atomic_reqflag & CPU_REQ_RELEASE)) { - // 中断リクエスト受信 - // この命令を実行し終わったら抜ける - atomic_reqflag &= ~CPU_REQ_RELEASE; - is_release = true; - } - } - if (OpIsBusErr(opX)) { // プリフェッチしていた命令がバスエラー Exception(M88K_EXCEP_INST); - continue; + goto exit; } uint32 op12 = op32_to_12(opX); @@ -188,53 +123,37 @@ m88kcpu::Run(uint32 delta) break; } - if (is_intr && IsIntrEnable()) { - // 割り込み処理要求が来ていて、直前の命令が割り込みを禁止していなければ割り込み + if (intr_pending && IsIntrEnable()) { + // 割り込み処理要求が来ていて、 + // 直前の命令が割り込みを禁止していなければ割り込み // XXX 実際は違う気がする - is_intr = false; cpulog(4, "INTR take"); ExceptionCore(M88K_EXCEP_INTERRUPT, ExceptionKind::INTR); - continue; + goto exit; } used_cycle++; - if ((atomic_reqflag & CPU_REQ_STOP)) { - // STOP 状態は、残りサイクルを消費したことにして戻る - cpulog(2, "STOP 状態検出"); - if (used_cycle < goal_cycle) { - used_cycle = goal_cycle; - } - } - - if (used_cycle >= goal_cycle) - break; } exit: - total_vtime += Cycle2Vtime(used_cycle); - used_cycle = 0; - - // HALT 状態がないので NORMAL か STOP かだけでいい - return (atomic_reqflag & CPU_REQ_STOP) - ? Scheduler::SCHED_CPU_STOP - : Scheduler::SCHED_CPU_NORMAL; + return used_cycle - cycle_start; } -// この命令後に実行を中断して VM に戻る -void -m88kcpu::Release() +// STOP 状態で 1 命令実行する。 +// 戻り値は消費したサイクル数。 +int +m88kcpu::ExecPseudoStop() { - atomic_reqflag |= CPU_REQ_RELEASE; -} + instruction_in_stop++; + if (instruction_in_stop > 4) { + ChangeState(CPU_STATE_NORMAL); + } -// リセット例外を発行する -void -m88kcpu::RequestReset() -{ - atomic_reqflag |= CPU_REQ_RESET; + return Exec(); } + // 通常の例外。 // DataAccessException, エラー、割り込み、TRAP からは使わない。 // vec はベクタ番号。 @@ -370,8 +289,7 @@ m88kcpu::ExceptionCore(int vec, Exceptio } } -inline -uint32 +inline uint32 m88kcpu::Calcdmt(uint32 flag) { return @@ -423,7 +341,11 @@ m88kcpu::ReadDataException64(uint32 addr if ((flag & DM_DOUB1) == 0) { // .d の2ワード目がバスエラー。第1ワードのアクセスは成功した。 - dmt0 &= ~DM_VALID; + // DMx0 が例外を起こしたアクセスを示す。 + // Manual 6.7.3 + dma0 = dma1; + dmt0 = dmt1; + dmt1 &= ~DM_VALID; } cpulog(1, "ReadDAE64 xip=%x addr=%x", xip, addr); @@ -479,7 +401,12 @@ m88kcpu::WriteDataException64(uint32 add if ((flag & DM_DOUB1) == 0) { // .d の2ワード目がバスエラー。第1ワードのアクセスは成功した。 - dmt0 &= ~DM_VALID; + // DMx0 が例外を起こしたアクセスを示す。 + // Manual 6.7.3 + dma0 = dma1; + dmt0 = dmt1; + dmd0 = dmd1; + dmt1 &= ~DM_VALID; } cpulog(1, "WriteDAE64 xip=%x addr=%x", xip, addr); @@ -526,17 +453,170 @@ m88kcpu::XmemDataException(uint32 addr, ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); } -// FP 例外 -// fpex: FP例外コード M88K_FPEXCEP +// FPxS レジスタをセット +void +m88kcpu::SetFPxS(uint32& h, uint32& l, double src, uint32 ts) +{ + // FPHS[12]、FPLS[12] は以下の構造 + // + // 3 2 1 0 + // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + // +-+---------------------+---------------------------------------+ + // |S| Exponent(11bit) | High Order 20 bits of Mantissa | FPHSn + // +-+---------------------+---------------------------------------+ + // + // +---------------------------------------------------------------+ + // | Low Order bits of Mantissa, or Integer | FPLSn + // +---------------------------------------------------------------+ + // + // ts が 1 (double) の場合は IEEE754 double と同じ。 + // ts が 0 (single) の場合は float を double にキャスト (拡大変換) した + // ものではないことに注意。 + // S(符号ビット)はそのまま。float の Mantissa (23bit) のうち上位 20bit + // を FPHSn に、残り 3bit を FPLSn の上位 3bit に。ここまではいい。 + // Exponent は float の指数部(8bit、バイアス=127) の値を *そのまま* + // 符号拡張(?)する。 + // (float)1.0 は 1.0 * 2^0 なのでバイアス込みの指数部は 127、 + // (float)2.0 は 1.0 * 2^1 なのでバイアス込みの指数部は 128。 + // バイアス込みにした値は符号なし数のはずだが、ここではこれを 11bit に + // 符号拡張して FPHSn の Exponent 部に格納するようだ。取り出す際に下位 + // 8bit しか取り出さなければ害はないし、符号拡張する方がハードウェア的に + // 都合がよかったとかだろうか。 + // 仮に普通に double に変換すると double の指数部バイアスは 1023 なので + // バイアス込みの指数部は先程の例だとそれぞれ 1023、1024 になるが + // これではないということ。 + + union64 u; + u.q = d2u(src); + if (ts == 0) { + // single + + uint32 exp; + // src は普通の double なのでこの指数部を取り出す + uint32 double_biased = (u.h >> 20) & 0x7ff; + if (double_biased == 0x7ff) { + // Inf, NAN + exp = 0x7ff00000; + } else { + // double のバイアスを外して float のバイアスを足す + int float_biased = (double_biased - 1023) + 127; + // この 8bit 値を 11bit に符号拡張して所定の位置へ + exp = ((int32)(float_biased << 24)) >> 4; + exp &= 0x7ff00000; + } + + h = (u.h & 0x800fffff) | exp; + l = u.l; + } else { + // double + h = u.h; + l = u.l; + } +} + +// FPR[HL] レジスタに double 値をセット +// - それ以外のフィールドは保存する XXX ここでクリアしてもいいか? +// - XXX Guard, Round, Sticky (, AddOne) はまだない void -m88kcpu::FPException(int fpex) +m88kcpu::SetFPRx(double src) { - // FDVZ 以上の例外が PRECISE。 - // 未満の FUNF, FOVF, FINX は IMPRECISE。 - int vec = (fpex >= M88K_FPEXCEP_FDVZ) ? M88K_EXCEP_SFU1_PRECISE : M88K_EXCEP_SFU1_IMPRECISE; - fpecr = fpex; + union64 u; + uint32 exp; + + u.q = d2u(src); - Exception(vec); + // Sign は FPRH + fprh &= ~(FPRH_SIGN | FPRH_MANT); // Mantissa もついでにクリア + fprh |= u.h & 0x80000000; + + // Exp は FPIT。 + // double は 11bit だが FPIT::RESEXP は 12bit なので 1bit 符号拡張する。 + // 符号付き数(正確には負数)の右シフトは実装依存だが gcc, clang は + // 算術右シフトになる。 + // + // 3 2 + // 1 0 9 8 7 6 5 4 3 2 1 0 9 + // +-+---------------------+- + // double |S| Exponent(11bit) | + // +-+---------------------+- + // + // +---------------------+-+- + // u.h<<1 | Exponent(11bit) |X| + // +---------------------+-+- + // + // +-----------------------+- + // exp>>1 | Exponent(12bit) | + // +-----------------------+- + exp = u.h << 1; + exp = (int32)exp >> 1; + fpit &= ~FPIT_RESEXP; + fpit |= exp & FPIT_RESEXP; + + // Mantissa は FPRH, FPRL + // 上位側(FPRH)には最上位(実数桁)の隠しビットを含む。 + fprh |= (u.h & 0x000fffff); + fprh |= FPRH_1; + fprl = u.l; +} + +// FP Precise 例外。浮動小数点数1つの場合。 +void +m88kcpu::FPPreciseException(uint32 cause, double s2) +{ + SetFPS2(s2, m88100opf_FP_T2(opX)); + FPPreciseException(cause); +} + +// FP Precise 例外。浮動小数点数2つの場合。 +void +m88kcpu::FPPreciseException(uint32 cause, double s1, double s2) +{ + SetFPS1(s1, m88100opf_FP_T1(opX)); + SetFPS2(s2, m88100opf_FP_T2(opX)); + FPPreciseException(cause); +} + +// FP Precise 例外。 +void +m88kcpu::FPPreciseException(uint32 cause) +{ + assert(cause == FPECR_FIOV || + cause == FPECR_FUNIMP || + cause == FPECR_FPRV || + cause == FPECR_FROP || + cause == FPECR_FDVZ); + + fpecr = cause; + + // FPPT(FP Precise operation Type Register) + fppt = opX & 0xffe0; // Opcode, T1, T2, TD は opX と同じ位置 + fppt |= m88100opf_D(opX); // DEST + + Exception(M88K_EXCEP_SFU1_PRECISE); +} + +// FP Imprecise 例外。 +void +m88kcpu::FPImpreciseException(uint32 cause) +{ + assert(cause == FPECR_FUNF || + cause == FPECR_FOVF || + cause == FPECR_FINX); + + fpecr = cause; + + // FPIT(FP Imprecise operation Type Register) + fpit &= FPIT_RESEXP; + fpit |= opX & FPIT_OPCODE; + fpit |= (opX & 0x00000020) << 5; // DESTSIZ + fpit |= (fpcr & 0x0000001f) << 5; // EFINV,EFDVZ,EFUNF,EFOVR,EFINX + fpit |= m88100opf_D(opX); // DEST + + // FPRH(FP Result High Register) + fprh &= ~FPRH_RNDMODE; + fprh |= (fpcr & FPCR_RM) << 14; + + Exception(M88K_EXCEP_SFU1_IMPRECISE); } void @@ -544,14 +624,18 @@ m88kcpu::Interrupt(int level) { // level は 0 か 1。 intr_pending = level; - atomic_reqflag |= CPU_REQ_INTR; + + if (intr_pending && IsIntrEnable() && (cpu_state == CPU_STATE_STOP)) { + // 割り込みを受け付けたら STOP 解除。 + ChangeState(CPU_STATE_NORMAL); + } } void m88kcpu::fpu_unimpl() { printf("%s\n", __func__); - FPException(M88K_FPEXCEP_FUNIMP); + FPPreciseException(FPECR_FUNIMP); } OP_DEF(illegal) @@ -559,3 +643,17 @@ OP_DEF(illegal) // 本来は不当命令例外? cpulog(0, "Illegal instruction %08x", (uint32)opX); } + +void +m88kcpu::ChangeState(uint32 new_state) +{ + if (new_state == CPU_STATE_STOP) { + instruction_in_stop = 0; + } + + if (cpu_state != new_state) { + cpu_state = new_state; + // スケジューラに通知 + dev->NotifyCPUMode(cpu_state); + } +}