--- nono/m88xx0/m88100core.cpp 2026/04/29 17:05:07 1.1.1.11 +++ nono/m88xx0/m88100core.cpp 2026/04/29 17:05:47 1.1.1.17 @@ -4,458 +4,491 @@ // Licensed under nono-license.txt // +// 例外処理について。 +// 例外のうち、リセット(優先度1)、命令アクセス(2)、未実装命令(3)、特権違反(4) +// とその他の命令内部発生例外(5、トラップやゼロ除算など) は、この命令に +// 直接起因しているため precise 例外に分類される。 +// 一方、割り込み(優先度6)、FPU imprecise(7)、データアクセス(8) は imprecise +// 例外に分類される。 +// +// データアクセス例外について。 +// Figure 6-3 参照。例外 (例えば割り込み例外) 起動時にデータユニットが稼働中 +// ならデータアクセスの完了を待つ。データアクセスが失敗(fault)すればその状態 +// でシャドウレジスタをフリーズするので、割り込みの受け付けとデータアクセス +// 例外が同じ命令内で起きると、その命令直後の境界での例外はベクタは割り込み +// 例外 (1番)だが DMTx が VALID という混合状態になる。 +// 実際 OpenBSD のカーネルはこの状態を認識してわざわざ処理している。 +// +// 実機は、上記の内部例外を起こす整数ユニットとデータユニットが並行して +// 動けるため、割り込み以外の例外でもデータアクセス例外が同時に起きるかも +// 知れないが、ここでは考えない。 +// また割り込みと FPU imprecise も同時に起きる可能性はあるが未調査。 +// FPU imprecise とデータアクセスについても未調査。 + #include "m88100.h" -#include "m88100excep.h" #include "debugger.h" +#include "event.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() -{ - // サイクルを初期化。 - 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; - ChangeState(CPU_STATE_NORMAL); - - // 実際どこかは分からんけど PSR 設定の前にリセットしておきたい - cmmu[0].Reset(); - cmmu[1].Reset(); - - // 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; +// プリフェッチステージ(?)。 +// 命令サイクルは、自然に書けば、 +// (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(); - - return used_cycle - cycle_start; } -// 1命令実行する。 -// 戻り値は消費したサイクル数。 -int -m88kcpu::Exec() +// 1命令実行のコールバック。 +void +MPU88xx0Device::ExecNormal(Event *ev) { uint64 cycle_start = used_cycle; + uint32 op12; - { - // shift pipeline - opX = opF; - xip = nip; - - // prefetch next inst - fetch(); - - // この時点で実行しようとする命令が opX に入っている - - if (OpIsBusErr(opX)) { - // プリフェッチしていた命令がバスエラー - Exception(M88K_EXCEP_INST); - goto exit; - } + buswait = 0; - uint32 op12 = op32_to_12(opX); + // この時点で実行しようとする命令が opX に入っている + if (__predict_false(OpIsBusErr(reg.opX))) { + // プリフェッチしていた命令がバスエラー + Exception(EXCPRI_INST); + } else { + op12 = op32_to_12(reg.opX); switch (op12) { #include "m88100switch.inc" default: OP_FUNC(illegal); break; } + } - if (intr_pending && IsIntrEnable()) { - // 割り込み処理要求が来ていて、 - // 直前の命令が割り込みを禁止していなければ割り込み - // XXX 実際は違う気がする - cpulog(4, "INTR take"); - ExceptionCore(M88K_EXCEP_INTERRUPT, ExceptionKind::INTR); - goto exit; + // 例外発生か。 + if (__predict_false(excep_pending != 0)) { + if (__predict_false(resetting)) { + // 命令実行の先でリセットが起きたら以降を全部キャンセルして帰る。 + // システムコントローラのリセットポートアクセスで起きる。 + return; } - used_cycle++; - - } - - exit: - return used_cycle - cycle_start; -} + // 内部(precise)例外のうち優先度の最も高いのを一つ実行。 + // x & (-x) で立ってる最下位ビットだけにする。 + uint32 pri = excep_pending & (-excep_pending); -// STOP 状態で 1 命令実行する。 -// 戻り値は消費したサイクル数。 -int -m88kcpu::ExecPseudoStop() -{ - instruction_in_stop++; - if (instruction_in_stop > 4) { - ChangeState(CPU_STATE_NORMAL); + excep_pending &= ~pri; + ExceptionCore(pri); } - return Exec(); -} + used_cycle++; + Prefetch(); -// 通常の例外。 -// DataAccessException, エラー、割り込み、TRAP からは使わない。 -// vec はベクタ番号。 -void -m88kcpu::Exception(int vec) -{ - ExceptionCore(vec, ExceptionKind::NORMAL); + int cycle = used_cycle - cycle_start; + ev->time = cycle * clock_nsec + buswait; + scheduler->StartEvent(ev); } -// 例外処理コア部分 -// vec はベクタ番号。 +// 例外処理。ExecNormal() の下請け。 +// 例外を発生させるのはこれではなく、この下にある *Exception() のほう。 void -m88kcpu::ExceptionCore(int vec, ExceptionKind cause) +MPU88xx0Device::ExceptionCore(uint32 pri) { + uint32 vec; uint syscall = 0; - // 例外履歴に記録 (例外発生はブランチ履歴にも記録) - // OpenBSD システムコールなら番号もついでに記録。 + // 例外種別(優先度)からベクタ番号に変換。 + uint primask = pri; + switch (pri) { + case EXCPRI_ERROR: + vec = EXCVEC_ERROR; + break; + case EXCPRI_INST: + vec = EXCVEC_INST; + break; + case EXCPRI_UNIMPL_OP: + vec = EXCVEC_UNIMPL_OP; + break; + case EXCPRI_PRIV: + vec = EXCVEC_PRIV; + break; + case EXCPRI_INTERNAL: + case EXCPRI_TRAP: + // ベクタは別に指定されている。 + vec = excep_vector; + excep_vector = 0; + break; + + case EXCPRI_OUTER: + // 割り込みとデータアクセス例外は同時に処理する。 + // OUTER は、割り込み、データアクセス、FPU imprecise の少なくとも + // どれかが立っているという代表値なので、ここで欲しいのは + // excep_pending の残っているビット (複数かも知れない) のほう。 + // FPU imprecise は未調査。 + primask = excep_pending; + excep_pending = 0; + + pri = primask & (-primask); + switch (pri) { + case EXCPRI_INTERRUPT: + vec = EXCVEC_INTERRUPT; + break; + case EXCPRI_SFU_IMPRECISE: + vec = EXCVEC_SFU1_IMPRECISE; + break; + case EXCPRI_DATA: + vec = EXCVEC_DATA; + break; + default: + assert(false); + break; + } + break; + + default: + case EXCPRI_RESET: // 先に弾いてるので来ない。 + assert(false); + break; + } + + // 例外履歴に記録 (例外発生はブランチ履歴にも記録)。 + // ベクタ 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 == 450 || vec == 128) { + 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 (cause != ExceptionKind::TRAP) { - cpulog(2, "SFRZ Error Exception"); - vec = M88K_EXCEP_ERROR; - cause = ExceptionKind::ERROR; + if ((reg.psr & PSR_SFRZ)) { + if (pri != EXCPRI_TRAP) { + putlog(0, "Exception (primask=$%x) during SFRZ!", primask); + + // エラー例外でもう一度報告。 + // BranchHistory に繰り返しと判定されないよう to を変えておく。 + pri = EXCPRI_ERROR; + vec = EXCVEC_ERROR; + info = 0xfc000000 | vec; + exhist.AddEntry(from, 1, info); + brhist.AddEntry(from, 1, info); + debugger->NotifyExceptionMain(vec); + } else { + putlog(0, "Trap during Exception"); } } else { - cpulog(2, "Exception %x", vec); - if (vec != 1) { - cpulog(1, "Exception %x x=%x n=%x f=%x", vec, xip, nip, fip); + if (vec == EXCVEC_INTERRUPT) { + putlog(2, "Exception (Interrupt)"); + } else { + 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; - - if (OpIsBusErr(opX)) { - sxip |= SIP_E; // E bit - } - if (OpIsBusErr(opF)) { - snip |= SIP_E; // E bit + if (pri != EXCPRI_UNIMPL_OP) { + reg.sxip |= SIP_V; } + reg.snip |= SIP_V; + reg.sfip |= SIP_V; - if (vec == M88K_EXCEP_UNIMPL_OP) { - sxip &= ~SIP_V; + if (OpIsBusErr(reg.opX)) { + reg.sxip |= SIP_E; + } + if (OpIsBusErr(reg.opF)) { + reg.snip |= SIP_E; } } - if (cause != ExceptionKind::DATA) { - // データアクセス例外以外は、 - // データパイプラインはフラッシュ状態とする。 - // ほんとうは違う。かも。 - dmt0 = 0; + if ((primask & EXCPRI_DATA)) { + // パイプラインが未実装なので、PROM の DAE ハンドラが無限ループに + // なるのをごまかすハックは vm/prom.cpp 参照。 } else { - // xip: ld/st/xmem - // nip: next - // fip: fetch - - // rom1.2 は DAE が起きると、 - - // ROM挙動 CPU の RTE 挙動 - // sfip = sxip - // sxip -> ~V (INVALID) CPU はそもそも sxip を評価しない - // snip = 0 (INVALID) CPU はこれを nop 相当に処理する - // rte - - // してくる。そのまま読むと命令を再実行しようとしているように - // 読めるが、再実行すると無限ループになってしまう。 - // DAE の時点で xip が進んでいることを期待して利用している? - - // openbsd は - // xmem の write バスエラーを検知すると命令を再実行するために - // fip = nip, nip = xip して rte する。ということは xip = xmem に - // なっていることを期待している。 - - // というわけで - // ld で DataAccessException のときは、実CPUはすでに次の命令を - // 実行している? - // もう少し考えると、スコアボードでレジスタの干渉がなければ、 - // 実行している? - - // 仕方がないのでハックでごまかす。 - // rte のほうも参照のこと。 - - if (IsSuper() && xip >= 0x41000000 && xip < 0x42000000) { - // 遅延例外機構がないので、ROM 合わせのハック。 - sxip = snip; - } + // データアクセス例外でなければ DMT の V ビットはクリア。(Table.6-3) + reg.dmt0 &= ~DM_VALID; + reg.dmt1 &= ~DM_VALID; + reg.dmt2 &= ~DM_VALID; } - epsr = psr; - SetPSR(psr | PSR_SUPER | PSR_SFD1 | PSR_IND | PSR_SFRZ); + // XXX Figure 6-3 のフローだとトラップ例外の時は EPSR を保存しない + // ようだが本当だろうか。本文はトラップ例外の記載が少ない。 + 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 (cause == ExceptionKind::ERROR) { + if (OpIsBusErr(reg.opF)) { + if (pri == EXCPRI_ERROR) { // double bus fault // 実機は無限ループ状態 PANIC("Infinite ERROR (double bus fault)"); } else { - ExceptionCore(M88K_EXCEP_ERROR, ExceptionKind::ERROR); + putlog(0, "Bus error on fetching $%08x (vector $%x)!", + reg.vbr + (vec << 3), vec); + Exception(EXCPRI_ERROR); } } } -inline uint32 -m88kcpu::Calcdmt(uint32 flag) +// EXCPRI_* の内部例外発生を予約する。 +// 例外はこの場で処理するのではなく、命令境界で優先度の高い方から処理される +// ので、発生時に出来るのは予約することだけ。 +void +MPU88xx0Device::Exception(uint32 pri) +{ + excep_pending |= pri; +} + +// 外部優先度の例外の発生を予約する。 +void +MPU88xx0Device::OuterException(uint32 pri) +{ + excep_pending |= pri; + excep_pending |= EXCPRI_OUTER; +} + +// 内部例外の発生を予約する。 +// vec は発生させる例外のベクタ番号。 +void +MPU88xx0Device::InternalException(uint32 vec) +{ + excep_vector = vec; + Exception(EXCPRI_INTERNAL); +} + +// トラップ例外の発生を予約する。 +// vec は発生させる例外のベクタ番号。 +void +MPU88xx0Device::TrapException(uint32 vec) +{ + excep_vector = vec; + Exception(EXCPRI_TRAP); +} + +// flag から DMT の共通部分を作って返す。 +uint32 +MPU88xx0Device::MakeDMT(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 用 +// データアクセス例外(Read)、ld.[b,h,(w)] 用。 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.dmt0 = MakeDMT(flag); + reg.dmt0 |= FLD_D << 7; + reg.dmt0 |= flag & DM_SIGNED; + reg.dmt0 |= (flag & DM_EN_MASK) >> (addr & 0x03); + reg.dma0 = addr & ~0x3; + + reg.dmt1 &= ~DM_VALID; + reg.dmt2 &= ~DM_VALID; + + putlog(1, "ReadDAE xip=%x addr=%x", reg.xip, addr); + OuterException(EXCPRI_DATA); } -// データアクセス例外(Read) -// ld.d 用 +// データアクセス例外(Read)、ld.d 用。 +// どちらのワードで例外が起きても first_addr は第1ワードのアドレス。 void -m88kcpu::ReadDataException64(uint32 addr, uint32 flag) +MPU88xx0Device::ReadDataException64(uint32 first_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ビット有効 - ; - - dmt2 &= ~DM_VALID; - - if ((flag & DM_DOUB1) == 0) { - // .d の2ワード目がバスエラー。第1ワードのアクセスは成功した。 - // DMx0 が例外を起こしたアクセスを示す。 - // Manual 6.7.3 - dma0 = dma1; - dmt0 = dmt1; - dmt1 &= ~DM_VALID; + + uint32 dmt_2nd; + uint32 dma_2nd; + + reg.dmt1 &= ~DM_VALID; + reg.dmt2 &= ~DM_VALID; + + // 先に2ワード目の情報を作っておく。 + dmt_2nd = MakeDMT(flag); + dmt_2nd |= FLD_D2 << 7; + dmt_2nd |= DM_EN_MASK; // 32ビット有効 + dma_2nd = first_addr + 4; + + uint32 doub1 = (flag & DM_DOUB1); + if (doub1) { + // 1ワード目がバスエラーなら、DMx0 と DMx1 を使う。 + reg.dmt0 = MakeDMT(flag); + reg.dmt0 |= doub1; + reg.dmt0 |= FLD_D << 7; + reg.dmt0 |= DM_EN_MASK; // 32ビット有効 + reg.dma0 = first_addr; + + reg.dmt1 = dmt_2nd; + reg.dma1 = dma_2nd; + } else { + // 2ワード目がバスエラーなら、DMx0 に2ワード目の情報だけ。 + reg.dmt0 = dmt_2nd; + reg.dma0 = dma_2nd; } - cpulog(1, "ReadDAE64 xip=%x addr=%x", xip, addr); - ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); + putlog(1, "ReadDAE64 xip=%x addr=%x", reg.xip, reg.dma0); + OuterException(EXCPRI_DATA); } -// データアクセス例外(Write) -// st.b, st.h, st.hu, st 用 +// データアクセス例外(Write)、st.[b,h,(w)] 用。 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.dmt0 = MakeDMT(flag); + reg.dmt0 |= (flag & DM_EN_MASK) >> (addr & 0x03); + reg.dmt0 |= DM_WRITE; + reg.dma0 = addr & ~0x3; + reg.dmd0 = rD; + + reg.dmt1 &= ~DM_VALID; + reg.dmt2 &= ~DM_VALID; + + putlog(1, "WriteDAE xip=%x addr=%x", reg.xip, addr); + OuterException(EXCPRI_DATA); } -// データアクセス例外(Write) -// st.d 用 +// データアクセス例外(Write)、st.d 用。 +// どちらのワードで例外が起きても first_addr は第1ワードのアドレス。 void -m88kcpu::WriteDataException64(uint32 addr, uint32 flag) +MPU88xx0Device::WriteDataException64(uint32 first_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; - - dmt2 &= ~DM_VALID; - - if ((flag & DM_DOUB1) == 0) { - // .d の2ワード目がバスエラー。第1ワードのアクセスは成功した。 - // DMx0 が例外を起こしたアクセスを示す。 - // Manual 6.7.3 - dma0 = dma1; - dmt0 = dmt1; - dmd0 = dmd1; - dmt1 &= ~DM_VALID; + + uint32 dmt_2nd; + uint32 dma_2nd; + uint32 dmd_2nd; + + reg.dmt1 &= ~DM_VALID; + reg.dmt2 &= ~DM_VALID; + + // 先に2ワード目の情報を作っておく。 + dmt_2nd = MakeDMT(flag); + dmt_2nd |= DM_EN_MASK; + dmt_2nd |= DM_WRITE; + dma_2nd = first_addr + 4; + dmd_2nd = rD2; + + uint32 doub1 = (flag & DM_DOUB1); + if (doub1) { + // 1ワード目がバスエラーなら、DMx0 と DMx1 を使う。 + reg.dmt0 = MakeDMT(flag); + reg.dmt0 |= DM_DOUB1; // ダブルワードの第1アクセスのときセット + reg.dmt0 |= DM_EN_MASK; + reg.dmt0 |= DM_WRITE; + reg.dma0 = first_addr; + reg.dmd0 = rD; + + reg.dmt1 = dmt_2nd; + reg.dma1 = dma_2nd; + reg.dmd1 = dmd_2nd; + } else { + // 2ワード目がバスエラーなら、DMx0 に2ワード目の情報だけ。 + reg.dmt0 = dmt_2nd; + reg.dma0 = dma_2nd; + reg.dmd0 = dmd_2nd; } - cpulog(1, "WriteDAE64 xip=%x addr=%x", xip, addr); - ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); + putlog(1, "WriteDAE64 xip=%x addr=%x", reg.xip, reg.dma0); + OuterException(EXCPRI_DATA); } // データアクセス例外(xmem) -// xmem には .d は無い +// xmem は .bu と (.w) しかない。 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; - - dmt2 &= ~DM_VALID; - // ミスアラインドはこの例外より前にチェックされていて、 - // ここにはアラインドしかこない。 - if (cmmu[1].fault_code != m88200::FAULT_CODE_BUSERR - || cmmu[1].acc_read) { - // アドレス変換フォルトまたは read 時点 - // nop + // ここには (アクセスサイズでの) アラインドしかこない。 + + uint32 dmt_rd; + uint32 dmt_wr; + uint32 dma; + uint32 dmd_wr; + + reg.dmt1 &= ~DM_VALID; + reg.dmt2 &= ~DM_VALID; + + dmt_rd = MakeDMT(flag); + dmt_rd |= DM_LOCK; + dmt_rd |= FLD_D << 7; + dmt_rd |= flag & DM_SIGNED; + dmt_rd |= (flag & DM_EN_MASK) >> (addr & 0x03); + dma = addr & ~0x3U; + + dmt_wr = dmt_rd | DM_WRITE; + dmd_wr = rD; + + if (cmmu[1]->fault_code != m88200::FAULT_CODE_BUSERR + || cmmu[1]->acc_read) { + // アドレス変換フォルトまたは read 時点のバスエラー。 + reg.dmt0 = dmt_rd; + reg.dma0 = dma; + + reg.dmt1 = dmt_wr; + reg.dma1 = dma; + reg.dmd1 = dmd_wr; } else { - // xmem の write 時点 - dmt0 &= ~DM_VALID; + // xmem の write 時点のバスエラー。 + reg.dmt0 = dmt_wr; + reg.dma0 = dma; + reg.dmd0 = dmd_wr; } - cpulog(1, "xmemDAE xip=%x addr=%x", xip, addr); - ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); + putlog(1, "xmemDAE xip=%x addr=%x", reg.xip, addr); + OuterException(EXCPRI_DATA); } // FPxS レジスタをセット -void -m88kcpu::SetFPxS(uint32& h, uint32& l, double src, uint32 ts) +/*static*/ void +MPU88xx0Device::SetFPxS(uint32 *h, uint32 *l, double src, uint32 ts) { // FPHS[12]、FPLS[12] は以下の構造 // @@ -494,7 +527,10 @@ m88kcpu::SetFPxS(uint32& h, uint32& l, d uint32 exp; // src は普通の double なのでこの指数部を取り出す uint32 double_biased = (u.h >> 20) & 0x7ff; - if (double_biased == 0x7ff) { + if (double_biased == 0) { + // Zero + exp = 0; + } else if (double_biased == 0x7ff) { // Inf, NAN exp = 0x7ff00000; } else { @@ -502,23 +538,49 @@ m88kcpu::SetFPxS(uint32& h, uint32& l, d int float_biased = (double_biased - 1023) + 127; // この 8bit 値を 11bit に符号拡張して所定の位置へ exp = ((int32)(float_biased << 24)) >> 4; - exp &= 0x7ff00000; + exp &= ~0x80000000; } - h = (u.h & 0x800fffff) | exp; - l = u.l; + *h = (u.h & 0x800fffff) | exp; + *l = u.l; } else { // double - h = u.h; - l = u.l; + *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); +} + +void +MPU88xx0Device::SetFPS1(uint32 src) +{ + reg.fpls1 = src; +} + +void +MPU88xx0Device::SetFPS2(uint32 src) +{ + reg.fpls2 = src; +} + // FPR[HL] レジスタに double 値をセット // - それ以外のフィールドは保存する XXX ここでクリアしてもいいか? // - XXX Guard, Round, Sticky (, AddOne) はまだない void -m88kcpu::SetFPRx(double src) +MPU88xx0Device::SetFPRx(double src) { union64 u; uint32 exp; @@ -526,8 +588,8 @@ m88kcpu::SetFPRx(double src) u.q = d2u(src); // Sign は FPRH - fprh &= ~(FPRH_SIGN | FPRH_MANT); // Mantissa もついでにクリア - fprh |= u.h & 0x80000000; + reg.fprh &= ~(FPRH_SIGN | FPRH_MANT); // Mantissa もついでにクリア + reg.fprh |= u.h & 0x80000000; // Exp は FPIT。 // double は 11bit だが FPIT::RESEXP は 12bit なので 1bit 符号拡張する。 @@ -549,36 +611,36 @@ m88kcpu::SetFPRx(double src) // +-----------------------+- exp = u.h << 1; exp = (int32)exp >> 1; - fpit &= ~FPIT_RESEXP; - fpit |= exp & FPIT_RESEXP; + reg.fpit &= ~FPIT_RESEXP; + reg.fpit |= exp & FPIT_RESEXP; // Mantissa は FPRH, FPRL // 上位側(FPRH)には最上位(実数桁)の隠しビットを含む。 - fprh |= (u.h & 0x000fffff); - fprh |= FPRH_1; - fprl = u.l; + reg.fprh |= (u.h & 0x000fffff); + reg.fprh |= FPRH_1; + reg.fprl = u.l; } // FP Precise 例外。浮動小数点数1つの場合。 void -m88kcpu::FPPreciseException(uint32 cause, double s2) +MPU88xx0Device::FPPreciseException(uint32 cause, double s2) { - SetFPS2(s2, m88100opf_FP_T2(opX)); + SetFPS2(s2, m88100opf_FP_T2(reg.opX)); FPPreciseException(cause); } // FP Precise 例外。浮動小数点数2つの場合。 void -m88kcpu::FPPreciseException(uint32 cause, double s1, double s2) +MPU88xx0Device::FPPreciseException(uint32 cause, double s1, double s2) { - SetFPS1(s1, m88100opf_FP_T1(opX)); - SetFPS2(s2, m88100opf_FP_T2(opX)); + SetFPS1(s1, m88100opf_FP_T1(reg.opX)); + SetFPS2(s2, m88100opf_FP_T2(reg.opX)); FPPreciseException(cause); } // FP Precise 例外。 void -m88kcpu::FPPreciseException(uint32 cause) +MPU88xx0Device::FPPreciseException(uint32 cause) { assert(cause == FPECR_FIOV || cause == FPECR_FUNIMP || @@ -586,74 +648,84 @@ m88kcpu::FPPreciseException(uint32 cause cause == FPECR_FROP || cause == FPECR_FDVZ); - fpecr = cause; + reg.fpecr = cause; // FPPT(FP Precise operation Type Register) - fppt = opX & 0xffe0; // Opcode, T1, T2, TD は opX と同じ位置 - fppt |= m88100opf_D(opX); // DEST + reg.fppt = reg.opX & 0xffe0; // Opcode, T1, T2, TD は opX と同じ位置 + reg.fppt |= m88100opf_D(reg.opX); // DEST - Exception(M88K_EXCEP_SFU1_PRECISE); + InternalException(EXCVEC_SFU1_PRECISE); } // FP Imprecise 例外。 void -m88kcpu::FPImpreciseException(uint32 cause) +MPU88xx0Device::FPImpreciseException(uint32 cause) { assert(cause == FPECR_FUNF || cause == FPECR_FOVF || cause == FPECR_FINX); - fpecr = cause; + reg.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 + 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) - fprh &= ~FPRH_RNDMODE; - fprh |= (fpcr & FPCR_RM) << 14; + reg.fprh &= ~FPRH_RNDMODE; + reg.fprh |= (reg.fpcr & FPCR_RM) << 14; - Exception(M88K_EXCEP_SFU1_IMPRECISE); -} - -void -m88kcpu::Interrupt(int level) -{ - // level は 0 か 1。 - intr_pending = level; - - if (intr_pending && IsIntrEnable() && (cpu_state == CPU_STATE_STOP)) { - // 割り込みを受け付けたら STOP 解除。 - ChangeState(CPU_STATE_NORMAL); - } -} - -void -m88kcpu::fpu_unimpl() -{ - printf("%s\n", __func__); - FPPreciseException(FPECR_FUNIMP); + OuterException(EXCPRI_SFU_IMPRECISE); } OP_DEF(illegal) { - // 本来は不当命令例外? - cpulog(0, "Illegal instruction %08x", (uint32)opX); + // 不当命令例外。 + putlog(0, "Illegal instruction %08x", (uint32)reg.opX); + Exception(EXCPRI_UNIMPL_OP); } -void -m88kcpu::ChangeState(uint32 new_state) -{ - if (new_state == CPU_STATE_STOP) { - instruction_in_stop = 0; - } +// +// m88100reg の static 変数 +// - if (cpu_state != new_state) { - cpu_state = new_state; - // スケジューラに通知 - dev->NotifyCPUMode(cpu_state); - } -} +// 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", +};