--- nono/m88xx0/m88100core.cpp 2026/04/29 17:04:31 1.1 +++ nono/m88xx0/m88100core.cpp 2026/04/29 17:04:34 1.1.1.2 @@ -1,6 +1,7 @@ // // nono -// Copyright (C) 2020 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // #include "m88100.h" @@ -22,13 +23,18 @@ m88kcpu::Reset(uint32 reset_vector) // SFD1 = 0; psr &= ~PSR_SFD1; -#if TEST_INTERRUPT +#if defined(TEST_INTERRUPT) psr &= ~PSR_IND; psr &= ~PSR_SFRZ; #endif + SetPSR(psr); + vbr = 0; + cmmu[0].Reset(); + cmmu[1].Reset(); + // 実際には m88k MPU はリセット直後は 0 番地から実行を開始するので、 // 大抵の場合システムでリセット直後は 0 番地付近に ROM を見せるように // してあるのだと思うが、ここではその代わりに ROM のアドレスを指定して、 @@ -48,6 +54,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,7 +83,7 @@ m88kcpu::Run(uint64 request) if ((atomic_reqflag & CPU_REQ_INTR)) { atomic_reqflag &= ~CPU_REQ_INTR; is_intr = true; -#if TEST_INTERRUPT +#if defined(TEST_INTERRUPT) atomic_reqflag |= CPU_REQ_PROMPT; #endif } @@ -111,12 +128,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 +146,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 +169,7 @@ m88kcpu::Release() } // 通常の例外。 -// エラー、割り込み、TRAP からは使いません。 +// DataAccessException, エラー、割り込み、TRAP からは使わない。 // vec はベクタ番号。 void m88kcpu::Exception(int vec) @@ -162,6 +190,9 @@ 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; @@ -169,8 +200,7 @@ m88kcpu::ExceptionCore(int vec, Exceptio sxip |= SIP_V; snip |= SIP_V; // XXX:実機と異なり、fip は常にフェッチ前なので V=0 - // ^^^^ コレ自体怪しい. INTERRUPT とか。 - // でもたぶん例外ハンドラでここのビット見ないだろうなあ + // ^^^^ コレ自体怪しい。INTERRUPT とか。 if (OpIsBusErr(opX)) { sxip |= SIP_E; // E bit @@ -184,10 +214,53 @@ 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); + brhist.AddEntry(xip, fip); fetch(); if (OpIsBusErr(opF)) { if (cause == ExceptionKind::ERROR) { @@ -216,7 +289,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 +310,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 +374,9 @@ m88kcpu::fpu_unimpl() { printf("%s\n", __func__); } + +OP_DEF(illegal) +{ + // 本来は不当命令例外? + gMPU->putlog(0, "Illegal instruction %08x", (uint32)opX); +}