--- nono/m88xx0/m88100core.cpp 2026/04/29 17:04:31 1.1.1.1 +++ nono/m88xx0/m88100core.cpp 2026/04/29 17:04:40 1.1.1.4 @@ -1,6 +1,7 @@ // // nono -// Copyright (C) 2020 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // #include "m88100.h" @@ -17,17 +18,27 @@ void m88kcpu::Reset(uint32 reset_vector) { - // TODO: 未完成 + const uint32 Undefined = 0xcccccccc; + + // 履歴を初期化。リセット前の履歴いらんよな? + exhist.Clear(); + brhist.Clear(); + + // Table 6-8 (p6-47) psr = 0x800003ff; - // SFD1 = 0; + // XXX ただし SFU1 は未実装なので下げておく psr &= ~PSR_SFD1; -#if TEST_INTERRUPT -psr &= ~PSR_IND; -psr &= ~PSR_SFRZ; +#if defined(TEST_INTERRUPT) + psr &= ~PSR_IND; + psr &= ~PSR_SFRZ; #endif - vbr = 0; + SetPSR(); + + epsr = Undefined; + // scoreboard: cleared + ssbr = Undefined; // 実際には m88k MPU はリセット直後は 0 番地から実行を開始するので、 // 大抵の場合システムでリセット直後は 0 番地付近に ROM を見せるように @@ -36,6 +47,45 @@ psr &= ~PSR_SFRZ; fip = reset_vector; nip = 0; xip = 0; + sfip = Undefined; + snip = Undefined; + sxip = Undefined; + + vbr = 0; + // DMTx は bit0(Valid) をクリア。他は不定。 + dmt0 &= ~DM_VALID; + dmt1 &= ~DM_VALID; + dmt2 &= ~DM_VALID; + dma0 = Undefined; + dma1 = Undefined; + dma2 = Undefined; + dmd0 = Undefined; + dmd1 = Undefined; + dmd2 = Undefined; + for (int i = 1; i < 32; i++) { + r[i] = Undefined; + } + + fpecr = 0; + for (int i = 1; i < 9; i++) { + fcr[i] = Undefined; + } + fpcr = 0; + fpsr = 0; + + cmmu[0].Reset(); + cmmu[1].Reset(); + + // 履歴を初期化 + exhist.Clear(); + brhist.Clear(); + + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + // リセット例外発生時の from にはあまり意味がないので 0 にするか。 + exhist.AddEntry(0, 0, 0xfc000000 | 0); + brhist.AddEntry(0, 0, 0xfc000000 | 0); + // デバッガに通知 (例外ブレーク用) + debugger_notify_exception(0); fetch(); } @@ -48,6 +98,17 @@ m88kcpu::Run(uint64 request) bool is_release = false; bool is_intr = false; + if ((atomic_reqflag & CPU_REQ_STOP)) { + if ((atomic_reqflag & CPU_REQ_INTR) == 0) { + // STOP 状態のままで割り込みが無ければ、 + // STOP 継続として戻る。 + total_cycle = goal_cycle; + return atomic_reqflag; + } + // 状態が変わったら STOP 解除。 + atomic_reqflag &= ~CPU_REQ_STOP; + } + while (!is_release) { // shift pipeline opX = opF; @@ -66,8 +127,8 @@ m88kcpu::Run(uint64 request) if ((atomic_reqflag & CPU_REQ_INTR)) { atomic_reqflag &= ~CPU_REQ_INTR; is_intr = true; -#if TEST_INTERRUPT - atomic_reqflag |= CPU_REQ_PROMPT; +#if defined(TEST_INTERRUPT) + atomic_reqflag |= CPU_REQ_PROMPT; #endif } @@ -90,6 +151,11 @@ m88kcpu::Run(uint64 request) // プロンプトを抜けたのでここでフラグを落とす atomic_reqflag &= ~CPU_REQ_PROMPT; + + // プロンプトから戻ってきたら一旦スケジューラまで戻る(?)。 + // reset コマンドのようにデバッガからスケジューラに出した + // リクエストを(なるはやで?)回収するため。 + atomic_reqflag |= CPU_REQ_RELEASE; } if ((atomic_reqflag & CPU_REQ_RELEASE)) { @@ -111,12 +177,15 @@ m88kcpu::Run(uint64 request) switch (op12) { #include "m88100switch.inc" default: - __unreachable(); + OP_FUNC(illegal); + break; } if (is_intr) { - is_intr = false; if (!(psr & PSR_IND)) { + // XXX: 割り込み信号線はレベル割り込みなのだが + // まだ処理できないのでとりあえずラッチ割り込みにしておく。 + is_intr = false; gMPU->putlog(4, "INTR take"); ExceptionCore(M88K_EXCEP_INTERRUPT, ExceptionKind::INTR); continue; @@ -126,6 +195,14 @@ m88kcpu::Run(uint64 request) } total_cycle++; + + if ((atomic_reqflag & CPU_REQ_STOP)) { + // STOP 状態は、残りサイクルを消費したことにして戻る + gMPU->putlog(2, "STOP 状態検出"); + total_cycle = goal_cycle; + is_release = true; + } + if (total_cycle >= goal_cycle) break; } @@ -141,7 +218,7 @@ m88kcpu::Release() } // 通常の例外。 -// エラー、割り込み、TRAP からは使いません。 +// DataAccessException, エラー、割り込み、TRAP からは使わない。 // vec はベクタ番号。 void m88kcpu::Exception(int vec) @@ -154,6 +231,32 @@ m88kcpu::Exception(int vec) void m88kcpu::ExceptionCore(int vec, ExceptionKind cause) { + uint syscall = 0; + + // 例外履歴に記録 (例外発生はブランチ履歴にも記録) + // OpenBSD システムコールなら番号もついでに記録。 + // システムコール番号は tb0 発行時点で r13 にセットされている。 + // ただし 0 番は syscall(2) (間接システムコール) で、実際の番号は r2。 + // 198番 __syscall(2) だと r2:r3 (なので実質 r3)。 + if (vec == 450) { + syscall = r[13]; + if (syscall == 0) { + syscall = r[2]; + } else if (syscall == 198) { + syscall = r[3]; + } + syscall &= 0xfff; + } + exhist.AddEntry(xip, 0, 0xfc000000 | (syscall << 12) | vec); + brhist.AddEntry(xip, 0, 0xfc000000 | (syscall << 12) | vec); + // デバッガに通知 (例外ブレーク用) + debugger_notify_exception(vec); + + // 命令中で起きる例外はとりあえず全部2クロック追加? (Table.7-5) + if (cause != ExceptionKind::INTR) { + total_cycle += 2; + } + if (psr & PSR_SFRZ) { if (cause != ExceptionKind::TRAP) { gMPU->putlog(2, "SFRZ Error Exception"); @@ -162,15 +265,16 @@ m88kcpu::ExceptionCore(int vec, Exceptio } } else { gMPU->putlog(2, "Exception %x", vec); + if (vec != 1) { + gMPU->putlog(1, "Exception %x x=%x n=%x f=%x", vec, xip, nip, fip); + } sxip = xip; snip = nip; sfip = fip; sxip |= SIP_V; snip |= SIP_V; - // XXX:実機と異なり、fip は常にフェッチ前なので V=0 - // ^^^^ コレ自体怪しい. INTERRUPT とか。 - // でもたぶん例外ハンドラでここのビット見ないだろうなあ + sfip |= SIP_V; if (OpIsBusErr(opX)) { sxip |= SIP_E; // E bit @@ -184,11 +288,57 @@ m88kcpu::ExceptionCore(int vec, Exceptio } } + if (cause != ExceptionKind::DATA) { + // データアクセス例外以外は、 + // データパイプラインはフラッシュ状態とする。 + // ほんとうは違う。かも。 + dmt0 = 0; + } 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; + } + } + epsr = psr; - psr |= PSR_SUPER | PSR_SFD1 | PSR_IND | PSR_SFRZ; + SetPSR(psr | PSR_SUPER | PSR_SFD1 | PSR_IND | PSR_SFRZ); fip = vbr + (vec << 3); fetch(); + // ここでベクタに飛ぶのだが、ベクタ内の2命令でおそらく必ずもう一度 + // 分岐してそっちでも履歴が残るので、ここでブランチ履歴を残すのは + // ちょっと冗長という気もする。 + if (OpIsBusErr(opF)) { if (cause == ExceptionKind::ERROR) { // double bus fault @@ -216,7 +366,8 @@ m88kcpu::ReadDataException(uint32 addr, | ((flag & DM_EN_MASK) >> (addr & 0x03)) | 0 // DM_WRITE = 0 | DM_VALID; - Exception(M88K_EXCEP_DATA); + gMPU->putlog(1, "ReadDAE xip=%x addr=%x", xip, addr); + ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); } // データアクセス例外(Write) @@ -236,21 +387,62 @@ m88kcpu::WriteDataException(uint32 addr, | DM_WRITE | DM_VALID; - // dmd は Byte Enable 位置に配置される - if (flag & DM_SIZE_B) { - dmd0 = data << ((3 - (addr & 0x03)) * 8); - } else if (flag & DM_SIZE_H) { - dmd0 = data << ((2 - (addr & 0x03)) * 16); + dmd0 = data; + gMPU->putlog(1, "WriteDAE xip=%x addr=%x", xip, addr); + ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); +} + +// データアクセス例外(xmem) +void +m88kcpu::XmemDataException(uint32 addr, uint32 data, 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 でセット + | (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 でセット + | (FLD_D << 7) + | (flag & DM_SIGNED) + | ((flag & DM_EN_MASK) >> (addr & 0x03)) + | DM_WRITE + | DM_VALID; + + dmd1 = data; + + // ミスアラインドはこの例外より前にチェックされていて、 + // ここにはアラインドしかこない。 + if (cmmu[1].fault_code != m88200::FAULT_CODE_BUSERR + || cmmu[1].acc_read) { + // アドレス変換フォルトまたは read 時点 + // nop } else { - dmd0 = data; + // xmem の write 時点 + dma0 = dma1; + dmt0 = dmt1; + dmd0 = dmd1; + dmt1 &= ~DM_VALID; } - Exception(M88K_EXCEP_DATA); + + gMPU->putlog(1, "xmemDAE xip=%x addr=%x", xip, addr); + ExceptionCore(M88K_EXCEP_DATA, ExceptionKind::DATA); } void m88kcpu::Interrupt() { atomic_reqflag |= CPU_REQ_INTR; +// gMPU->putlog(1, "INTERRUPT IGNORED"); gMPU->putlog(4, "INTERRUPT ACK (アドレスは不正確)"); } @@ -259,3 +451,9 @@ m88kcpu::fpu_unimpl() { printf("%s\n", __func__); } + +OP_DEF(illegal) +{ + // 本来は不当命令例外? + gMPU->putlog(0, "Illegal instruction %08x", (uint32)opX); +}