--- nono/m88xx0/m88100core.cpp 2026/04/29 17:04:40 1.1.1.4 +++ nono/m88xx0/m88100core.cpp 2026/04/29 17:04:44 1.1.1.5 @@ -16,10 +16,13 @@ // リセット例外のようなもの? void -m88kcpu::Reset(uint32 reset_vector) +m88kcpu::Reset() { const uint32 Undefined = 0xcccccccc; + // 内部の割り込み状態をクリア + intr_pending = 0; + // 履歴を初期化。リセット前の履歴いらんよな? exhist.Clear(); brhist.Clear(); @@ -82,31 +85,41 @@ m88kcpu::Reset(uint32 reset_vector) // 例外履歴に記録 (例外発生はブランチ履歴にも記録) // リセット例外発生時の from にはあまり意味がないので 0 にするか。 - exhist.AddEntry(0, 0, 0xfc000000 | 0); - brhist.AddEntry(0, 0, 0xfc000000 | 0); + exhist.AddEntry(0 | 1, 0, 0xfc000000 | 0); + brhist.AddEntry(0 | 1, 0, 0xfc000000 | 0); // デバッガに通知 (例外ブレーク用) debugger_notify_exception(0); fetch(); } -// request サイクル分実行する。 +// 仮想時間 delta [nsec] だけ CPU を実行する。実際には大抵行き過ぎる。 +// 戻り値はフラグ。 +// +// 戻る時に仮想時刻を確定させなくても構わない。スケジューラはこれを呼び出した +// 後に必然的に仮想時刻を取得することになるため、そこで確定させられる。 uint32 -m88kcpu::Run(uint64 request) +m88kcpu::Run(uint32 delta) { - uint64 goal_cycle = total_cycle + request; 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; + } + if ((atomic_reqflag & CPU_REQ_STOP)) { - if ((atomic_reqflag & CPU_REQ_INTR) == 0) { - // STOP 状態のままで割り込みが無ければ、 + if ((atomic_reqflag & (CPU_REQ_INTR | CPU_REQ_PROMPT | CPU_REQ_RESET))) { + // 割り込み、デバッガ、リセットが来たら STOP 解除。 + atomic_reqflag &= ~CPU_REQ_STOP; + } else { // STOP 継続として戻る。 - total_cycle = goal_cycle; + used_cycle = goal_cycle; return atomic_reqflag; } - // 状態が変わったら STOP 解除。 - atomic_reqflag &= ~CPU_REQ_STOP; } while (!is_release) { @@ -122,11 +135,23 @@ m88kcpu::Run(uint64 request) if (atomic_reqflag != 0) { // 何かが起きた + if ((atomic_reqflag & CPU_REQ_RESET)) { + // リセット例外。 + atomic_reqflag &= ~CPU_REQ_RESET; + Reset(); + // XIP がリセットベクタを指すためにはもう一回フェッチが必要 + // なので、ここは continue が必要。 + continue; + } + // 割り込みの認識 // 割り込みの実行は命令実行完了後、次のプリフェッチの前 if ((atomic_reqflag & CPU_REQ_INTR)) { atomic_reqflag &= ~CPU_REQ_INTR; - is_intr = true; + if (intr_pending && IsIntrEnable()) { + // この命令のあとに割り込み処理 + is_intr = true; + } #if defined(TEST_INTERRUPT) atomic_reqflag |= CPU_REQ_PROMPT; #endif @@ -181,35 +206,50 @@ m88kcpu::Run(uint64 request) break; } - if (is_intr) { - if (!(psr & PSR_IND)) { - // XXX: 割り込み信号線はレベル割り込みなのだが - // まだ処理できないのでとりあえずラッチ割り込みにしておく。 - is_intr = false; - gMPU->putlog(4, "INTR take"); - ExceptionCore(M88K_EXCEP_INTERRUPT, ExceptionKind::INTR); - continue; - } else { - gMPU->putlog(4, "INTR DISABLE"); - } + if (is_intr && IsIntrEnable()) { + // 割り込み処理要求が来ていて、直前の命令が割り込みを禁止していなければ割り込み + // XXX 実際は違う気がする + is_intr = false; + gMPU->putlog(4, "INTR take"); + ExceptionCore(M88K_EXCEP_INTERRUPT, ExceptionKind::INTR); + continue; } - total_cycle++; + used_cycle++; if ((atomic_reqflag & CPU_REQ_STOP)) { // STOP 状態は、残りサイクルを消費したことにして戻る gMPU->putlog(2, "STOP 状態検出"); - total_cycle = goal_cycle; + used_cycle = goal_cycle; is_release = true; } - if (total_cycle >= goal_cycle) + if (used_cycle >= goal_cycle) break; } return atomic_reqflag; } +// 仕掛中のサイクルを仮想時刻に反映させる +void +m88kcpu::UpdateVTime() +{ + goal_cycle -= used_cycle; + total_vtime += Cycle2Vtime(used_cycle); + used_cycle = 0; +} + +// 仮想時刻 [nsec] を取得する +uint64 +m88kcpu::GetVTime() +{ + if (used_cycle != 0) { + UpdateVTime(); + } + return total_vtime; +} + // この命令後に実行を中断して VM に戻る void m88kcpu::Release() @@ -217,6 +257,13 @@ m88kcpu::Release() atomic_reqflag |= CPU_REQ_RELEASE; } +// リセット例外を発行する +void +m88kcpu::RequestReset() +{ + atomic_reqflag |= CPU_REQ_RESET; +} + // 通常の例外。 // DataAccessException, エラー、割り込み、TRAP からは使わない。 // vec はベクタ番号。 @@ -247,14 +294,16 @@ m88kcpu::ExceptionCore(int vec, Exceptio } syscall &= 0xfff; } - exhist.AddEntry(xip, 0, 0xfc000000 | (syscall << 12) | vec); - brhist.AddEntry(xip, 0, 0xfc000000 | (syscall << 12) | vec); + uint32 from = xip | (IsSuper() ? 1 : 0); + uint32 inst = 0xfc000000 | (syscall << 12) | vec; + exhist.AddEntry(from, 0, inst); + brhist.AddEntry(from, 0, inst); // デバッガに通知 (例外ブレーク用) debugger_notify_exception(vec); // 命令中で起きる例外はとりあえず全部2クロック追加? (Table.7-5) if (cause != ExceptionKind::INTR) { - total_cycle += 2; + AddCycle(2); } if (psr & PSR_SFRZ) { @@ -350,75 +399,146 @@ m88kcpu::ExceptionCore(int vec, Exceptio } } +inline +uint32 +m88kcpu::Calcdmt(uint32 flag) +{ + return + ((psr & PSR_BO_LE) ? DM_BO : 0) + | ((IsSuper() && !(flag & DM_USR)) ? DM_DAS : 0) + | DM_VALID; +} + // データアクセス例外(Read) +// ld.b, ld.h, ld.hu, ld 用 void -m88kcpu::ReadDataException(uint32 addr, uint32 flag) +m88kcpu::ReadDataException32(uint32 addr, uint32 flag) { // ミスアラインドはこの例外より前にチェックされていて、 // ここにはアラインドしかこない。 dma0 = addr & ~0x3; - dmt0 = ((psr & PSR_BO_LE) ? DM_BO : 0) - | (((psr & PSR_SUPER) && !(flag & DM_USR)) ? DM_DAS : 0) - | (flag & DM_DOUB1) // ダブルワードの第1アクセスのときセット - | (flag & DM_LOCK) // xmem でセット + dmt0 = Calcdmt(flag) | (FLD_D << 7) | (flag & DM_SIGNED) | ((flag & DM_EN_MASK) >> (addr & 0x03)) - | 0 // DM_WRITE = 0 - | DM_VALID; + ; + dmt1 &= ~DM_VALID; + dmt2 &= ~DM_VALID; gMPU->putlog(1, "ReadDAE xip=%x addr=%x", xip, addr); ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); } +// データアクセス例外(Read) +// ld.d 用 +void +m88kcpu::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ビット有効 + ; + + dmt2 &= ~DM_VALID; + + if ((flag & DM_DOUB1) == 0) { + // .d の2ワード目がバスエラー。第1ワードのアクセスは成功した。 + dmt0 &= ~DM_VALID; + } + + gMPU->putlog(1, "ReadDAE64 xip=%x addr=%x", xip, addr); + ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); +} + // データアクセス例外(Write) +// st.b, st.h, st.hu, st 用 void -m88kcpu::WriteDataException(uint32 addr, uint32 data, uint32 flag) +m88kcpu::WriteDataException32(uint32 addr, uint32 flag) { // ミスアラインドはこの例外より前にチェックされていて、 // ここにはアラインドしかこない。 dma0 = addr & ~0x3; - dmt0 = ((psr & PSR_BO_LE) ? DM_BO : 0) - | (((psr & PSR_SUPER) && !(flag & DM_USR)) ? DM_DAS : 0) - | (flag & DM_DOUB1) // ダブルワードの第1アクセスのときセット - | (flag & DM_LOCK) // xmem でセット + dmt0 = Calcdmt(flag) | (FLD_D << 7) - | (flag & DM_SIGNED) | ((flag & DM_EN_MASK) >> (addr & 0x03)) | DM_WRITE - | DM_VALID; + ; + dmd0 = rD; - dmd0 = data; + dmt1 &= ~DM_VALID; + dmt2 &= ~DM_VALID; gMPU->putlog(1, "WriteDAE xip=%x addr=%x", xip, addr); ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); } +// データアクセス例外(Write) +// st.d 用 +void +m88kcpu::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; + + dmt2 &= ~DM_VALID; + + if ((flag & DM_DOUB1) == 0) { + // .d の2ワード目がバスエラー。第1ワードのアクセスは成功した。 + dmt0 &= ~DM_VALID; + } + + gMPU->putlog(1, "WriteDAE64 xip=%x addr=%x", xip, addr); + ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); +} + // データアクセス例外(xmem) +// xmem には .d は無い void -m88kcpu::XmemDataException(uint32 addr, uint32 data, uint32 flag) +m88kcpu::XmemDataException(uint32 addr, uint32 flag) { dma0 = addr & ~0x3; - dmt0 = ((psr & PSR_BO_LE) ? DM_BO : 0) - | (((psr & PSR_SUPER) && !(flag & DM_USR)) ? DM_DAS : 0) - | (flag & DM_DOUB1) // ダブルワードの第1アクセスのときセット - | (flag & DM_LOCK) // xmem でセット + dmt0 = Calcdmt(flag) + | DM_LOCK // xmem でセット | (FLD_D << 7) | (flag & DM_SIGNED) | ((flag & DM_EN_MASK) >> (addr & 0x03)) - | 0 // DM_WRITE = 0 - | DM_VALID; + ; - dma1 = addr & ~0x3; - dmt1 = ((psr & PSR_BO_LE) ? DM_BO : 0) - | (((psr & PSR_SUPER) && !(flag & DM_USR)) ? DM_DAS : 0) - | (flag & DM_DOUB1) // ダブルワードの第1アクセスのときセット - | (flag & DM_LOCK) // xmem でセット + dma1 = dma0; + dmt1 = Calcdmt(flag) + | DM_LOCK // xmem でセット | (FLD_D << 7) | (flag & DM_SIGNED) | ((flag & DM_EN_MASK) >> (addr & 0x03)) | DM_WRITE - | DM_VALID; + ; + dmd1 = rD; - dmd1 = data; + dmt2 &= ~DM_VALID; // ミスアラインドはこの例外より前にチェックされていて、 // ここにはアラインドしかこない。 @@ -428,28 +548,39 @@ m88kcpu::XmemDataException(uint32 addr, // nop } else { // xmem の write 時点 - dma0 = dma1; - dmt0 = dmt1; - dmd0 = dmd1; - dmt1 &= ~DM_VALID; + dmt0 &= ~DM_VALID; } gMPU->putlog(1, "xmemDAE xip=%x addr=%x", xip, addr); ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); } +// FP 例外 +// fpex: FP例外コード M88K_FPEXCEP +void +m88kcpu::FPException(int fpex) +{ + // FDVZ 以上の例外が PRECISE。 + // 未満の FUNF, FOVF, FINX は IMPRECISE。 + int vec = (fpex >= M88K_FPEXCEP_FDVZ) ? M88K_EXCEP_SFU1_PRECISE : M88K_EXCEP_SFU1_IMPRECISE; + fpecr = fpex; + + Exception(vec); +} + void -m88kcpu::Interrupt() +m88kcpu::Interrupt(int level) { + // level は 0 か 1。 + intr_pending = level; atomic_reqflag |= CPU_REQ_INTR; -// gMPU->putlog(1, "INTERRUPT IGNORED"); - gMPU->putlog(4, "INTERRUPT ACK (アドレスは不正確)"); } void m88kcpu::fpu_unimpl() { printf("%s\n", __func__); + FPException(M88K_FPEXCEP_FUNIMP); } OP_DEF(illegal)