--- nono/m88xx0/m88100core.cpp 2026/04/29 17:04:52 1.1.1.8 +++ nono/m88xx0/m88100core.cpp 2026/04/29 17:05:39 1.1.1.16 @@ -5,246 +5,92 @@ // #include "m88100.h" -#include "m88100excep.h" #include "debugger.h" -#include "cvprompt.h" -#include "mpu.h" +#include "mpu88xx0.h" #include "scheduler.h" -#define OP_DEF(name) void __CONCAT(m88kcpu::op_,name)() +#define OP_DEF(name) void __CONCAT(MPU88xx0Device::op_,name)() #define OP_FUNC(name) __CONCAT(op_,name)() #include "m88100ops.cpp" -// 文字列を作る前にレベルを評価するためマクロ -#define cpulog(lv, fmt...) do { \ - if (__predict_false(gMPU->loglevel >= (lv))) { \ - gMPU->putlogn(fmt); \ - } \ -} while (0) - -void -m88kcpu::PowerOn() -{ - // 履歴は電源オン時だけ初期化。 - // (LUNA88K は ROM が正常パスで CPU リセットを行ったりするので) - exhist.Clear(); - brhist.Clear(); - - // この後起きるリセット例外で xip を初期化する前に参照することになるので - // これだけここでも初期化しておく。 - xip = 0; -} - -// リセット例外のようなもの? -void -m88kcpu::Reset() -{ - // 例外履歴に記録 (例外発生はブランチ履歴にも記録) - // リセット例外も発生時の 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); - - // Table 6-8 (p6-47) - psr = 0x800003ff; - // XXX ただし SFU1 は未実装なので下げておく - psr &= ~PSR_SFD1; - -#if defined(TEST_INTERRUPT) - psr &= ~PSR_IND; - psr &= ~PSR_SFRZ; -#endif - - SetPSR(); - - fip = 0; - - // レジスタのうち明記されてるものをクリア。 - // 不定と明記されてるものはリセットでは触らないでおく。 - // scoreboard: cleared - nip = 0; - xip = 0; - vbr = 0; - fpecr = 0; - fpcr = 0; - fpsr = 0; - // DMTx は bit0(Valid) をクリア。他は不定 - dmt0 &= ~DM_VALID; - dmt1 &= ~DM_VALID; - dmt2 &= ~DM_VALID; - - cmmu[0].Reset(); - cmmu[1].Reset(); +// プリフェッチステージ(?)。 +// 命令サイクルは、自然に書けば、 +// (1) パイプラインをシフトし、FIP の指すところをフェッチ。 +// (2) デバッガはここに入れたい。 +// (3) XIP(opX) を解釈、実行。 +// の繰り返しになるが、デバッガは Exec() から戻ったところでスケジューラから +// 呼び出される構造のため、順序をずらして 1回の Exec() では (3)->(1) を実行 +// してからスケジューラに戻って (2) を実行する、の順にする。 +// そのため、リセット例外では1回プリフェッチを実行しておく必要がある。 +void +MPU88xx0Device::Prefetch() +{ + // shift pipeline + reg.opX = reg.opF; + reg.xip = reg.nip; + // prefetch next inst fetch(); } -// 仮想時間 delta [nsec] だけ CPU を実行する。実際には大抵行き過ぎる。 -// 戻り値は CPU 状態 (Scheduler::SCHED_CPU_*)。 -uint32 -m88kcpu::Run(uint32 delta) +// 1命令実行のコールバック。 +void +MPU88xx0Device::ExecNormal(Event& ev) { - bool is_release = false; - bool is_intr = false; + uint64 cycle_start = used_cycle; + uint32 op12; - assert(used_cycle == 0); + buswait = 0; - goal_cycle = Vtime2Cycle(delta); - if (__predict_false(goal_cycle == 0)) { - goal_cycle = 1; - } + // この時点で実行しようとする命令が opX に入っている - 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; - } + if (OpIsBusErr(reg.opX)) { + // プリフェッチしていた命令がバスエラー + Exception(EXCEP_INST); + goto exit; } - while (!is_release) { - // shift pipeline - opX = opF; - xip = nip; - - // prefetch next inst - fetch(); - - // この時点で実行しようとする命令が 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; - } - - uint32 op12 = op32_to_12(opX); - - switch (op12) { + op12 = op32_to_12(reg.opX); + switch (op12) { #include "m88100switch.inc" - default: - OP_FUNC(illegal); - break; - } - - if (is_intr && IsIntrEnable()) { - // 割り込み処理要求が来ていて、直前の命令が割り込みを禁止していなければ割り込み + default: + OP_FUNC(illegal); + break; + } + + if (__predict_false(intr_pending != 0)) { + if (resetting) { + // 命令実行の先でリセットが起きたら以降を全部キャンセルして帰る。 + // システムコントローラのリセットポートで起きる。 + // リセットは実際には intr_pending ではないが、 + // ここでフラグ処理をまとめるために立ててある。 + return; + } + if (IsIntrEnable()) { + // 割り込み処理要求が来ていて、 + // 直前の命令が割り込みを禁止していなければ割り込み。 // XXX 実際は違う気がする - is_intr = false; - cpulog(4, "INTR take"); - ExceptionCore(M88K_EXCEP_INTERRUPT, ExceptionKind::INTR); - continue; - } - - used_cycle++; - - if ((atomic_reqflag & CPU_REQ_STOP)) { - // STOP 状態は、残りサイクルを消費したことにして戻る - cpulog(2, "STOP 状態検出"); - if (used_cycle < goal_cycle) { - used_cycle = goal_cycle; - } + putlog(4, "INTR take"); + ExceptionCore(EXCEP_INTERRUPT, ExceptionKind::INTR); + goto exit; } - - if (used_cycle >= goal_cycle) - break; } - exit: - total_vtime += Cycle2Vtime(used_cycle); - used_cycle = 0; + used_cycle++; - // HALT 状態がないので NORMAL か STOP かだけでいい - return (atomic_reqflag & CPU_REQ_STOP) - ? Scheduler::SCHED_CPU_STOP - : Scheduler::SCHED_CPU_NORMAL; -} + exit: + Prefetch(); -// この命令後に実行を中断して VM に戻る -void -m88kcpu::Release() -{ - atomic_reqflag |= CPU_REQ_RELEASE; -} - -// リセット例外を発行する -void -m88kcpu::RequestReset() -{ - atomic_reqflag |= CPU_REQ_RESET; + int cycle = used_cycle - cycle_start; + ev.time = cycle * clock_nsec + buswait; + scheduler->StartEvent(ev); } // 通常の例外。 // DataAccessException, エラー、割り込み、TRAP からは使わない。 // vec はベクタ番号。 void -m88kcpu::Exception(int vec) +MPU88xx0Device::Exception(int vec) { ExceptionCore(vec, ExceptionKind::NORMAL); } @@ -252,64 +98,67 @@ m88kcpu::Exception(int vec) // 例外処理コア部分 // vec はベクタ番号。 void -m88kcpu::ExceptionCore(int vec, ExceptionKind cause) +MPU88xx0Device::ExceptionCore(int vec, ExceptionKind cause) { uint syscall = 0; - // 例外履歴に記録 (例外発生はブランチ履歴にも記録) - // OpenBSD システムコールなら番号もついでに記録。 + // 例外履歴に記録 (例外発生はブランチ履歴にも記録)。 + // ベクタ 450 なら OpenBSD システムコール番号もついでに記録。 // システムコール番号は tb0 発行時点で r13 にセットされている。 // ただし 0 番は syscall(2) (間接システムコール) で、実際の番号は r2。 // 198番 __syscall(2) だと r2:r3 (なので実質 r3)。 - if (vec == 450) { - syscall = r[13]; + // 非公式 NetBSD/luna88k はベクタ 128 を使っているようだ。 + excep_counter[vec]++; + if (vec == 128 || vec == 450) { + syscall = reg.r[13]; if (syscall == 0) { - syscall = r[2]; + syscall = reg.r[2]; } else if (syscall == 198) { - syscall = r[3]; + syscall = reg.r[3]; } syscall &= 0xfff; } - uint32 from = xip | (IsSuper() ? 1 : 0); - uint32 inst = 0xfc000000 | (syscall << 12) | vec; - exhist.AddEntry(from, 0, inst); - brhist.AddEntry(from, 0, inst); + uint32 from = reg.xip | (IsSuper() ? 1 : 0); + uint32 info = 0xfc000000 | (syscall << 12) | vec; + exhist.AddEntry(from, 0, info); + brhist.AddEntry(from, 0, info); // デバッガに通知 (例外ブレーク用) - debugger_notify_exception(vec); + debugger->NotifyExceptionMain(vec); // 命令中で起きる例外はとりあえず全部2クロック追加? (Table.7-5) if (cause != ExceptionKind::INTR) { AddCycle(2); } - if (psr & PSR_SFRZ) { + if ((reg.psr & PSR_SFRZ)) { if (cause != ExceptionKind::TRAP) { - cpulog(2, "SFRZ Error Exception"); - vec = M88K_EXCEP_ERROR; + putlog(2, "SFRZ Error Exception"); + vec = EXCEP_ERROR; cause = ExceptionKind::ERROR; } } else { - cpulog(2, "Exception %x", vec); + putlog(2, "Exception %x", vec); if (vec != 1) { - cpulog(1, "Exception %x x=%x n=%x f=%x", vec, xip, nip, fip); + putlog(1, "Exception %x x=%x n=%x f=%x", + vec, reg.xip, reg.nip, reg.fip); } - sxip = xip; - snip = nip; - sfip = fip; + reg.sxip = reg.xip; + reg.snip = reg.nip; + reg.sfip = reg.fip; - sxip |= SIP_V; - snip |= SIP_V; - sfip |= SIP_V; + reg.sxip |= SIP_V; + reg.snip |= SIP_V; + reg.sfip |= SIP_V; - if (OpIsBusErr(opX)) { - sxip |= SIP_E; // E bit + if (OpIsBusErr(reg.opX)) { + reg.sxip |= SIP_E; // E bit } - if (OpIsBusErr(opF)) { - snip |= SIP_E; // E bit + if (OpIsBusErr(reg.opF)) { + reg.snip |= SIP_E; // E bit } - if (vec == M88K_EXCEP_UNIMPL_OP) { - sxip &= ~SIP_V; + if (vec == EXCEP_UNIMPL_OP) { + reg.sxip &= ~SIP_V; } } @@ -317,13 +166,13 @@ m88kcpu::ExceptionCore(int vec, Exceptio // データアクセス例外以外は、 // データパイプラインはフラッシュ状態とする。 // ほんとうは違う。かも。 - dmt0 = 0; + reg.dmt0 = 0; } else { // xip: ld/st/xmem // nip: next // fip: fetch - // rom1.2 は DAE が起きると、 + // PROM 1.20 は DAE が起きると、 // ROM挙動 CPU の RTE 挙動 // sfip = sxip @@ -349,218 +198,437 @@ m88kcpu::ExceptionCore(int vec, Exceptio // 仕方がないのでハックでごまかす。 // rte のほうも参照のこと。 - if (IsSuper() && xip >= 0x41000000 && xip < 0x42000000) { + if (IsSuper() && reg.xip >= 0x41000000 && reg.xip < 0x42000000) { // 遅延例外機構がないので、ROM 合わせのハック。 - sxip = snip; + reg.sxip = reg.snip; } } - epsr = psr; - SetPSR(psr | PSR_SUPER | PSR_SFD1 | PSR_IND | PSR_SFRZ); + reg.epsr = reg.psr; + SetPSR(reg.psr | PSR_SUPER | PSR_SFD1 | PSR_IND | PSR_SFRZ); - fip = vbr + (vec << 3); + reg.fip = reg.vbr + (vec << 3); fetch(); // ここでベクタに飛ぶのだが、ベクタ内の2命令でおそらく必ずもう一度 // 分岐してそっちでも履歴が残るので、ここでブランチ履歴を残すのは // ちょっと冗長という気もする。 - if (OpIsBusErr(opF)) { + if (OpIsBusErr(reg.opF)) { if (cause == ExceptionKind::ERROR) { // double bus fault // 実機は無限ループ状態 PANIC("Infinite ERROR (double bus fault)"); } else { - ExceptionCore(M88K_EXCEP_ERROR, ExceptionKind::ERROR); + ExceptionCore(EXCEP_ERROR, ExceptionKind::ERROR); } } } -inline -uint32 -m88kcpu::Calcdmt(uint32 flag) +inline uint32 +MPU88xx0Device::Calcdmt(uint32 flag) { - return - ((psr & PSR_BO_LE) ? DM_BO : 0) - | ((IsSuper() && !(flag & DM_USR)) ? DM_DAS : 0) - | DM_VALID; + uint32 dmt; + + dmt = DM_VALID; + if ((reg.psr & PSR_BO_LE)) + dmt |= DM_BO; + if (IsSuper() && !(flag & DM_USR)) + dmt |= DM_DAS; + + return dmt; } // データアクセス例外(Read) // ld.b, ld.h, ld.hu, ld 用 void -m88kcpu::ReadDataException32(uint32 addr, uint32 flag) +MPU88xx0Device::ReadDataException32(uint32 addr, uint32 flag) { // ミスアラインドはこの例外より前にチェックされていて、 // ここにはアラインドしかこない。 - dma0 = addr & ~0x3; - dmt0 = Calcdmt(flag) - | (FLD_D << 7) - | (flag & DM_SIGNED) - | ((flag & DM_EN_MASK) >> (addr & 0x03)) - ; - dmt1 &= ~DM_VALID; - dmt2 &= ~DM_VALID; - cpulog(1, "ReadDAE xip=%x addr=%x", xip, addr); - ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); + reg.dma0 = addr & ~0x3; + reg.dmt0 = Calcdmt(flag); + reg.dmt0 |= FLD_D << 7; + reg.dmt0 |= flag & DM_SIGNED; + reg.dmt0 |= (flag & DM_EN_MASK) >> (addr & 0x03); + + reg.dmt1 &= ~DM_VALID; + reg.dmt2 &= ~DM_VALID; + putlog(1, "ReadDAE xip=%x addr=%x", reg.xip, addr); + ExceptionCore(EXCEP_DATA, ExceptionKind::DATA); } // データアクセス例外(Read) // ld.d 用 void -m88kcpu::ReadDataException64(uint32 addr, uint32 flag) +MPU88xx0Device::ReadDataException64(uint32 addr, uint32 flag) { // ミスアラインドはこの例外より前にチェックされていて、 // ここにはアラインドしかこない。 - dma0 = addr; - dmt0 = Calcdmt(flag) - | DM_DOUB1 // ダブルワードの第1アクセスのときセット - | (FLD_D << 7) - | DM_EN_MASK // 32ビット有効 - ; - - dma1 = dma0 + 4; - dmt1 = Calcdmt(flag) - | (FLD_D2 << 7) - | DM_EN_MASK // 32ビット有効 - ; + reg.dma0 = addr; + reg.dmt0 = Calcdmt(flag); + reg.dmt0 |= DM_DOUB1; // ダブルワードの第1アクセスのときセット + reg.dmt0 |= FLD_D << 7; + reg.dmt0 |= DM_EN_MASK; // 32ビット有効 + + reg.dma1 = reg.dma0 + 4; + reg.dmt1 = Calcdmt(flag); + reg.dmt1 |= FLD_D2 << 7; + reg.dmt1 |= DM_EN_MASK; // 32ビット有効 - dmt2 &= ~DM_VALID; + reg.dmt2 &= ~DM_VALID; if ((flag & DM_DOUB1) == 0) { // .d の2ワード目がバスエラー。第1ワードのアクセスは成功した。 - dmt0 &= ~DM_VALID; + // DMx0 が例外を起こしたアクセスを示す。 + // Manual 6.7.3 + reg.dma0 = reg.dma1; + reg.dmt0 = reg.dmt1; + reg.dmt1 &= ~DM_VALID; } - cpulog(1, "ReadDAE64 xip=%x addr=%x", xip, addr); - ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); + putlog(1, "ReadDAE64 xip=%x addr=%x", reg.xip, addr); + ExceptionCore(EXCEP_DATA, ExceptionKind::DATA); } // データアクセス例外(Write) // st.b, st.h, st.hu, st 用 void -m88kcpu::WriteDataException32(uint32 addr, uint32 flag) +MPU88xx0Device::WriteDataException32(uint32 addr, uint32 flag) { // ミスアラインドはこの例外より前にチェックされていて、 // ここにはアラインドしかこない。 - dma0 = addr & ~0x3; - dmt0 = Calcdmt(flag) - | (FLD_D << 7) - | ((flag & DM_EN_MASK) >> (addr & 0x03)) - | DM_WRITE - ; - dmd0 = rD; - - dmt1 &= ~DM_VALID; - dmt2 &= ~DM_VALID; - cpulog(1, "WriteDAE xip=%x addr=%x", xip, addr); - ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); + reg.dma0 = addr & ~0x3; + reg.dmt0 = Calcdmt(flag); + reg.dmt0 |= FLD_D << 7; + reg.dmt0 |= (flag & DM_EN_MASK) >> (addr & 0x03); + reg.dmt0 |= DM_WRITE; + reg.dmd0 = rD; + + reg.dmt1 &= ~DM_VALID; + reg.dmt2 &= ~DM_VALID; + putlog(1, "WriteDAE xip=%x addr=%x", reg.xip, addr); + ExceptionCore(EXCEP_DATA, ExceptionKind::DATA); } // データアクセス例外(Write) // st.d 用 void -m88kcpu::WriteDataException64(uint32 addr, uint32 flag) +MPU88xx0Device::WriteDataException64(uint32 addr, uint32 flag) { // ミスアラインドはこの例外より前にチェックされていて、 // ここにはアラインドしかこない。 - dma0 = addr & ~0x3; - dmt0 = Calcdmt(flag) - | DM_DOUB1 // ダブルワードの第1アクセスのときセット - | (FLD_D << 7) - | DM_EN_MASK - | DM_WRITE - ; - dmd0 = rD; - - dma1 = dma0 + 4; - dmt1 = Calcdmt(flag) - | (FLD_D2 << 7) - | DM_EN_MASK - | DM_WRITE - ; - dmd1 = rD2; + reg.dma0 = addr & ~0x3; + reg.dmt0 = Calcdmt(flag); + reg.dmt0 |= DM_DOUB1; // ダブルワードの第1アクセスのときセット + reg.dmt0 |= FLD_D << 7; + reg.dmt0 |= DM_EN_MASK; + reg.dmt0 |= DM_WRITE; + reg.dmd0 = rD; + + reg.dma1 = reg.dma0 + 4; + reg.dmt1 = Calcdmt(flag); + reg.dmt1 |= FLD_D2 << 7; + reg.dmt1 |= DM_EN_MASK; + reg.dmt1 |= DM_WRITE; + reg.dmd1 = rD2; - dmt2 &= ~DM_VALID; + reg.dmt2 &= ~DM_VALID; if ((flag & DM_DOUB1) == 0) { // .d の2ワード目がバスエラー。第1ワードのアクセスは成功した。 - dmt0 &= ~DM_VALID; + // DMx0 が例外を起こしたアクセスを示す。 + // Manual 6.7.3 + reg.dma0 = reg.dma1; + reg.dmt0 = reg.dmt1; + reg.dmd0 = reg.dmd1; + reg.dmt1 &= ~DM_VALID; } - cpulog(1, "WriteDAE64 xip=%x addr=%x", xip, addr); - ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); + putlog(1, "WriteDAE64 xip=%x addr=%x", reg.xip, addr); + ExceptionCore(EXCEP_DATA, ExceptionKind::DATA); } // データアクセス例外(xmem) // xmem には .d は無い void -m88kcpu::XmemDataException(uint32 addr, uint32 flag) +MPU88xx0Device::XmemDataException(uint32 addr, uint32 flag) { - dma0 = addr & ~0x3; - dmt0 = Calcdmt(flag) - | DM_LOCK // xmem でセット - | (FLD_D << 7) - | (flag & DM_SIGNED) - | ((flag & DM_EN_MASK) >> (addr & 0x03)) - ; - - dma1 = dma0; - dmt1 = Calcdmt(flag) - | DM_LOCK // xmem でセット - | (FLD_D << 7) - | (flag & DM_SIGNED) - | ((flag & DM_EN_MASK) >> (addr & 0x03)) - | DM_WRITE - ; - dmd1 = rD; + reg.dma0 = addr & ~0x3; + reg.dmt0 = Calcdmt(flag); + reg.dmt0 |= DM_LOCK; // xmem でセット + reg.dmt0 |= FLD_D << 7; + reg.dmt0 |= flag & DM_SIGNED; + reg.dmt0 |= (flag & DM_EN_MASK) >> (addr & 0x03); + + reg.dma1 = reg.dma0; + reg.dmt1 = Calcdmt(flag); + reg.dmt1 |= DM_LOCK; // xmem でセット + reg.dmt1 |= FLD_D << 7; + reg.dmt1 |= flag & DM_SIGNED; + reg.dmt1 |= (flag & DM_EN_MASK) >> (addr & 0x03); + reg.dmt1 |= DM_WRITE; + reg.dmd1 = rD; - dmt2 &= ~DM_VALID; + reg.dmt2 &= ~DM_VALID; // ミスアラインドはこの例外より前にチェックされていて、 // ここにはアラインドしかこない。 - if (cmmu[1].fault_code != m88200::FAULT_CODE_BUSERR - || cmmu[1].acc_read) { + if (cmmu[1]->fault_code != m88200::FAULT_CODE_BUSERR + || cmmu[1]->acc_read) { // アドレス変換フォルトまたは read 時点 // nop } else { // xmem の write 時点 - dmt0 &= ~DM_VALID; + reg.dmt0 &= ~DM_VALID; + } + + putlog(1, "xmemDAE xip=%x addr=%x", reg.xip, addr); + ExceptionCore(EXCEP_DATA, ExceptionKind::DATA); +} + +// FPxS レジスタをセット +/*static*/ void +MPU88xx0Device::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; } +} + +// FPxS1 レジスタをセット +void +MPU88xx0Device::SetFPS1(double src, uint32 t1) +{ + SetFPxS(®.fphs1, ®.fpls1, src, t1); +} + +// FPxS2 レジスタをセット +void +MPU88xx0Device::SetFPS2(double src, uint32 t2) +{ + SetFPxS(®.fphs2, ®.fpls2, src, t2); +} - cpulog(1, "xmemDAE xip=%x addr=%x", xip, addr); - ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); +void +MPU88xx0Device::SetFPS1(uint32 src) +{ + reg.fpls1 = src; +} + +void +MPU88xx0Device::SetFPS2(uint32 src) +{ + reg.fpls2 = src; } -// FP 例外 -// fpex: FP例外コード M88K_FPEXCEP +// FPR[HL] レジスタに double 値をセット +// - それ以外のフィールドは保存する XXX ここでクリアしてもいいか? +// - XXX Guard, Round, Sticky (, AddOne) はまだない void -m88kcpu::FPException(int fpex) +MPU88xx0Device::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; - Exception(vec); + u.q = d2u(src); + + // Sign は FPRH + reg.fprh &= ~(FPRH_SIGN | FPRH_MANT); // Mantissa もついでにクリア + reg.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; + reg.fpit &= ~FPIT_RESEXP; + reg.fpit |= exp & FPIT_RESEXP; + + // Mantissa は FPRH, FPRL + // 上位側(FPRH)には最上位(実数桁)の隠しビットを含む。 + reg.fprh |= (u.h & 0x000fffff); + reg.fprh |= FPRH_1; + reg.fprl = u.l; } +// FP Precise 例外。浮動小数点数1つの場合。 void -m88kcpu::Interrupt(int level) +MPU88xx0Device::FPPreciseException(uint32 cause, double s2) { - // level は 0 か 1。 - intr_pending = level; - atomic_reqflag |= CPU_REQ_INTR; + SetFPS2(s2, m88100opf_FP_T2(reg.opX)); + FPPreciseException(cause); } +// FP Precise 例外。浮動小数点数2つの場合。 void -m88kcpu::fpu_unimpl() +MPU88xx0Device::FPPreciseException(uint32 cause, double s1, double s2) +{ + SetFPS1(s1, m88100opf_FP_T1(reg.opX)); + SetFPS2(s2, m88100opf_FP_T2(reg.opX)); + FPPreciseException(cause); +} + +// FP Precise 例外。 +void +MPU88xx0Device::FPPreciseException(uint32 cause) +{ + assert(cause == FPECR_FIOV || + cause == FPECR_FUNIMP || + cause == FPECR_FPRV || + cause == FPECR_FROP || + cause == FPECR_FDVZ); + + reg.fpecr = cause; + + // FPPT(FP Precise operation Type Register) + reg.fppt = reg.opX & 0xffe0; // Opcode, T1, T2, TD は opX と同じ位置 + reg.fppt |= m88100opf_D(reg.opX); // DEST + + Exception(EXCEP_SFU1_PRECISE); +} + +// FP Imprecise 例外。 +void +MPU88xx0Device::FPImpreciseException(uint32 cause) +{ + assert(cause == FPECR_FUNF || + cause == FPECR_FOVF || + cause == FPECR_FINX); + + reg.fpecr = cause; + + // FPIT(FP Imprecise operation Type Register) + reg.fpit &= FPIT_RESEXP; + reg.fpit |= reg.opX & FPIT_OPCODE; + reg.fpit |= (reg.opX & 0x00000020) << 5; // DESTSIZ + reg.fpit |= (reg.fpcr & 0x0000001f) << 5; // EFINV,EFDVZ,EFUNF,EFOVR,EFINX + reg.fpit |= m88100opf_D(reg.opX); // DEST + + // FPRH(FP Result High Register) + reg.fprh &= ~FPRH_RNDMODE; + reg.fprh |= (reg.fpcr & FPCR_RM) << 14; + + Exception(EXCEP_SFU1_IMPRECISE); +} + +void +MPU88xx0Device::fpu_unimpl() { printf("%s\n", __func__); - FPException(M88K_FPEXCEP_FUNIMP); + FPPreciseException(FPECR_FUNIMP); } OP_DEF(illegal) { // 本来は不当命令例外? - cpulog(0, "Illegal instruction %08x", (uint32)opX); + putlog(0, "Illegal instruction %08x", (uint32)reg.opX); } + +// +// m88100reg の static 変数 +// + +// fcr のマスク +/*static*/ const uint32 m88100reg::fcr_mask[11] = { + M88100::FPECR_MASK, + 0xffffffff, // fphs1 + 0xffffffff, // fpls1 + 0xffffffff, // fphs2 + 0xffffffff, // fpls2 + M88100::FPPT_MASK, + M88100::FPRH_MASK, + 0xffffffff, // fprl + M88100::FPIT_MASK, + M88100::FPSR_MASK, + M88100::FPCR_MASK, +}; + +/*static*/ const char * const m88100reg::sipname[3] = { + "sxip", "snip", "sfip", +}; + +/*static*/ const char * const m88100reg::dmt_en_str[16] = { + "----", + "---B", + "--B-", + "--HH", + "-B--", + "-1-1", // not used normally + "-11-", // not used normally + "-111", // not used normally + "B---", + "1--1", // not used normally + "1-1-", // not used normally + "1-11", // not used normally + "HH--", + "11-1", // not used normally + "111-", // not used normally + "LLLL", +};