--- nono/m88xx0/m88100ops.cpp 2026/04/29 17:04:31 1.1.1.1 +++ nono/m88xx0/m88100ops.cpp 2026/04/29 17:04:41 1.1.1.4 @@ -1,19 +1,22 @@ // // nono -// Copyright (C) 2020 isaki@NetBSD.org +// Copyright (C) 2020 nono project +// Licensed under nono-license.txt // +#include "m88100acc.h" + // 命令のフィールド取り出し -#define FLD_D ((opX >> 21) & 0x1f) -#define FLD_S1 ((opX >> 16) & 0x1f) -#define FLD_S2 ((opX ) & 0x1f) -#define FLD_CR ((opX >> 5) & 0x2f) -#define B5 ((opX >> 21) & 0x1f) -#define M5 ((opX >> 21) & 0x1f) -#define W5 ((opX >> 5) & 0x1f) -#define O5 ((opX ) & 0x1f) -#define IMM16 (opX & 0x0000ffffU) -#define VEC9 (opX & 0x000001ffU) +#define FLD_D m88100opf_D(opX) +#define FLD_S1 m88100opf_S1(opX) +#define FLD_S2 m88100opf_S2(opX) +#define FLD_CR m88100opf_CR(opX) +#define B5 m88100opf_B5(opX) +#define M5 m88100opf_M5(opX) +#define W5 m88100opf_W5(opX) +#define O5 m88100opf_O5(opX) +#define IMM16 m88100opf_IMM16(opX) +#define VEC9 m88100opf_VEC9(opX) // 符号拡張・シフト済みのディスプレースメント // XXX: (負数の右シフト)GCC only @@ -26,12 +29,59 @@ #define rD (r[FLD_D]) #define rS1 (r[FLD_S1]) #define rS2 (r[FLD_S2]) +// ダブルワードの2ワード目 +#define rD2 (r[(FLD_D + 1) & 0x1f]) // ブランチ命令入り口 inline void -m88kcpu::EnterBranch() +m88kcpu::EnterBranch(uint32 toaddr) { + // ブランチヒストリに登録しつつ、 + // ブランチヒストリからブランチ状況を取得 + auto e = brhist.AddEntry(xip, toaddr, opX); + fip = toaddr; + + // STOP 検出。 + // m88k には STOP 状態 (何もせず割り込みだけ待つ状態。m68k の STOP 命令 + // による STOP 状態に相当するもの) は存在せず、割り込みが起きるまで NOP + // 相当の命令を無限ループで実行し続ける方法が一般的にとられる。 + // 実機であれば (電力が無駄なこと以外は) これで特段困らないかも知れないが + // エミュレータ (特に高速モード) ではこのような無限ループはホストの CPU + // がぶん回る状況になるため避けたい。そのためこの無限ループを検出したい。 + // ただし特定の(または定番の)命令列があるわけではなく、任意の無限ループが + // 用いられているため、汎用的に自動検出する必要がある。 + + // ループの 1 回目のブランチで初期化して、 + // 2 回目のブランチで STOP だったかどうかをチェックする。 + // 3 回目以降の場合は STOP でなかったので、もうチェックする必要はない。 + + // m68k の STOP 命令が特権命令なので、それに倣ってここでも特権状態のみを + // 対象とする。 + + // 通常のブランチで来る回数がはるかに多いと考えられるので、 + // 先にカウント数をチェック対象かどうか調べる。 + if (e.count < 3 && IsSuper()) { + if (e.count == 1) { + // 新規ブランチ + // 初期化 + nop_counter = 0; + } else { + // e.count == 2 のときだけ STOP の検証をすれば良い。 + + // d はブランチ間の命令数 + uint32 d = (xip - toaddr) / sizeof(uint32); + // 遅延ブランチなら 1 命令余分に実行するはずなので 1 足す + if (opX & (1 << 26)) { + d++; + } + // ブランチ間の命令がすべて NOP なら STOP ということにする。 + if (nop_counter == d) { + atomic_reqflag |= CPU_REQ_STOP; + } + } + // STOP ではなかったら、あとは放置していい + } } // 通常(即時)ブランチ出口 @@ -43,17 +93,19 @@ m88kcpu::ExitBranch() } // 遅延ブランチ出口 +// 速度を稼ぐため、ビット反転だけでこれを区別しているので、 +// 同じ op 中で複数回呼び出さないこと。 inline void m88kcpu::ExitDelayBranch() { - opF = OpSetDelay(opF); + opF ^= DELAYSLOT; } // ロードストアユニットの usr の処理。 // 正常なら true を返す (ので呼び出し側は実行継続)、 // 例外が起きたら処理して false を返すので呼び出し側は即リターンすること。 inline bool -m88kcpu::ldst_usr(uint32 size, uint32& usr) +m88kcpu::ldst_usr(uint32& usr) { usr = (opX & (1 << 8)) ? DM_USR : 0; if (IsUser() && usr) { @@ -63,6 +115,15 @@ m88kcpu::ldst_usr(uint32 size, uint32& u return true; } +// MEMO: ロードストアでの usr の処理で、CMMU に対して +// usr を引数ではなくSetSuper() の呼び出しで渡すようにしている。 +// データのロードストアだけがあるのなら usr を引数で渡すほうが +// 効率的だが、CMMU は 命令/データで同じものを使うため、 +// 命令 CMMU では 100% 無駄な引数の引き渡しが必要になってしまう。 +// そして命令アクセスのほうが何倍も比率が多い。 +// .usr 指定は比率としてはレアなので、状態変更メソッドによる +// 方法にしてみた。 + // ロードストアユニットの scale の処理。addr を返す。 inline uint32 m88kcpu::ldst_scale(uint32 size) @@ -93,7 +154,7 @@ m88kcpu::ldst_align(uint32 size, uint32& // ロードストアユニットの usr と scale の処理 (呼び出し用マクロ) #define LDST_USR_SCALE(size) do { \ - if (ldst_usr(size, usr) == false) \ + if (ldst_usr(usr) == false) \ return; \ addr = ldst_scale(size); \ } while (0) @@ -171,24 +232,6 @@ isovf_sub(uint32 s1, uint32 s2, uint32 r return (int32)((s1 ^ s2) & (s1 ^ res)) < 0; } -// cmp のやつ -static inline uint32 -acc_cmp(uint32 a, uint32 b) -{ - uint32 rv = 0; - if (a >= b) rv |= (1 << 11); - if (a < b) rv |= (1 << 10); - if (a <= b) rv |= (1 << 9); - if (a > b) rv |= (1 << 8); - if ((int32)a >= (int32)b) rv |= (1 << 7); - if ((int32)a < (int32)b) rv |= (1 << 6); - if ((int32)a <= (int32)b) rv |= (1 << 5); - if ((int32)a > (int32)b) rv |= (1 << 4); - if ((int32)a != (int32)b) rv |= (1 << 3); - if ((int32)a == (int32)b) rv |= (1 << 2); - return rv; -} - // bcnd のやつ static inline int acc_cnd(uint32 r) @@ -234,14 +277,12 @@ OP_DEF(xmem_bu_imm) addr = rS1 + IMM16; LDST_ALIGN(1); + total_cycle += 4; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_8(addr); + tmp = cmmu[1].xmem_8(addr, rD); if ((int64)tmp < 0) { - ReadDataException(addr, DM_BU | DM_LOCK); - return; - } - if ((int64)cmmu[1].store_8(addr, rD) < 0) { - WriteDataException(addr, rD, DM_BU | DM_LOCK); + XmemDataException(addr, rD, DM_BU | DM_LOCK); return; } if (FLD_D == 0) @@ -258,14 +299,12 @@ OP_DEF(xmem_w_imm) addr = rS1 + IMM16; LDST_ALIGN(4); + total_cycle += 4; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_32(addr); + tmp = cmmu[1].xmem_32(addr, rD); if ((int64)tmp < 0) { - ReadDataException(addr, DM_W | DM_LOCK); - return; - } - if ((int64)cmmu[1].store_32(addr, rD) < 0) { - WriteDataException(addr, rD, DM_W | DM_LOCK); + XmemDataException(addr, rD, DM_W | DM_LOCK); return; } if (FLD_D == 0) @@ -277,66 +316,72 @@ OP_DEF(xmem_w_imm) OP_DEF(ld_hu_imm) { uint32 addr; - uint64 tmp; + uint64 data; addr = rS1 + IMM16; LDST_ALIGN(2); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_16(addr); - if ((int64)tmp < 0) { + data = cmmu[1].load_16(addr); + if ((int64)data < 0) { ReadDataException(addr, DM_HU); return; } if (FLD_D == 0) return; - rD = tmp; + rD = data; } // 000011_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(ld_bu_imm) { uint32 addr; - uint64 tmp; + uint64 data; addr = rS1 + IMM16; LDST_ALIGN(1); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_8(addr); - if ((int64)tmp < 0) { + data = cmmu[1].load_8(addr); + if ((int64)data < 0) { ReadDataException(addr, DM_BU); return; } if (FLD_D == 0) return; - rD = tmp; + rD = data; } // 000100_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(ld_d_imm) { uint32 addr; - uint64 tmp, tmp2; + uint64 data1, data2; addr = rS1 + IMM16; LDST_ALIGN(8); + total_cycle += 4; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_32(addr); - if ((int64)tmp < 0) { + data1 = cmmu[1].load_32(addr); + if ((int64)data1 < 0) { ReadDataException(addr, DM_D1); return; } addr += 4; - tmp2 = cmmu[1].load_32(addr); - if ((int64)tmp2 < 0) { + data2 = cmmu[1].load_32(addr); + if ((int64)data2 < 0) { ReadDataException(addr, DM_D2); return; } - rD = tmp; - r[(FLD_D + 1) & 0x1f] = tmp2; + rD = data1; + rD2 = data2; r[0] = 0; } @@ -344,78 +389,89 @@ OP_DEF(ld_d_imm) OP_DEF(ld_w_imm) { uint32 addr; - uint64 tmp; + uint64 data; addr = rS1 + IMM16; LDST_ALIGN(4); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_32(addr); - if ((int64)tmp < 0) { + data = cmmu[1].load_32(addr); + if ((int64)data < 0) { ReadDataException(addr, DM_W); return; } if (FLD_D == 0) return; - rD = tmp; + rD = data; } // 000110_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(ld_h_imm) { uint32 addr; - uint64 tmp; + uint64 data; addr = rS1 + IMM16; LDST_ALIGN(2); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_16(addr); - if ((int64)tmp < 0) { + data = cmmu[1].load_16(addr); + if ((int64)data < 0) { ReadDataException(addr, DM_H); return; } if (FLD_D == 0) return; - rD = (uint32)(int32)(int16)tmp; + rD = (uint32)(int32)(int16)data; } // 000111_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(ld_b_imm) { uint32 addr; - uint64 tmp; + uint64 data; addr = rS1 + IMM16; LDST_ALIGN(1); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_8(addr); - if ((int64)tmp < 0) { + data = cmmu[1].load_8(addr); + if ((int64)data < 0) { ReadDataException(addr, DM_B); return; } if (FLD_D == 0) return; - rD = (uint32)(int32)(int8)tmp; + rD = (uint32)(int32)(int8)data; } // 001000_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(st_d_imm) { uint32 addr; + uint64 rv; addr = rS1 + IMM16; LDST_ALIGN(8); + total_cycle += 1 + 4; // Table.7-3 + lastaddr = addr; - if ((int64)cmmu[1].store_32(addr, rD) < 0) { + rv = cmmu[1].store_32(addr, rD); + if ((int64)rv < 0) { WriteDataException(addr, rD, DM_D1); return; } addr += 4; - if ((int64)cmmu[1].store_32(addr, r[(FLD_D + 1) & 0x1f]) < 0) { - WriteDataException(addr, r[(FLD_D + 1) & 0x1f], DM_D2); + rv = cmmu[1].store_32(addr, rD2); + if ((int64)rv < 0) { + WriteDataException(addr, rD2, DM_D2); return; } } @@ -424,12 +480,16 @@ OP_DEF(st_d_imm) OP_DEF(st_w_imm) { uint32 addr; + uint64 rv; addr = rS1 + IMM16; LDST_ALIGN(4); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - if ((int64)cmmu[1].store_32(addr, rD) < 0) { + rv = cmmu[1].store_32(addr, rD); + if ((int64)rv < 0) { WriteDataException(addr, rD, DM_W); return; } @@ -439,12 +499,16 @@ OP_DEF(st_w_imm) OP_DEF(st_h_imm) { uint32 addr; + uint64 rv; addr = rS1 + IMM16; LDST_ALIGN(2); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - if ((int64)cmmu[1].store_16(addr, rD) < 0) { + rv = cmmu[1].store_16(addr, rD); + if ((int64)rv < 0) { WriteDataException(addr, rD, DM_H); return; } @@ -454,12 +518,16 @@ OP_DEF(st_h_imm) OP_DEF(st_b_imm) { uint32 addr; + uint64 rv; addr = rS1 + IMM16; LDST_ALIGN(1); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - if ((int64)cmmu[1].store_8(addr, rD) < 0) { + rv = cmmu[1].store_8(addr, rD); + if ((int64)rv < 0) { WriteDataException(addr, rD, DM_B); return; } @@ -584,6 +652,9 @@ OP_DEF(divu_imm) fpu_unimpl(); return; } + + total_cycle += 38; // Table.7-6 (ただしざっくり) + if (IMM16 == 0) { Exception(M88K_EXCEP_INT_DIV); return; @@ -600,6 +671,9 @@ OP_DEF(mul_imm) fpu_unimpl(); return; } + + total_cycle += 4; // Table.7-6 (ただしざっくり) + if (FLD_D == 0) return; rD = rS1 * IMM16; @@ -640,7 +714,10 @@ OP_DEF(div_imm) fpu_unimpl(); return; } - if (rS1 < 0 || IMM16 == 0) { + + total_cycle += 38; // Table.7-6 (ただしざっくり) + + if ((int32)rS1 < 0 || IMM16 == 0) { Exception(M88K_EXCEP_INT_DIV); return; } @@ -720,6 +797,9 @@ OP_DEF(stcr) return; } cr[FLD_CR] = rS1; + if (FLD_CR == 1) { + SetPSR(); + } } // 100000_zzzzzSSSSS_10001n_nnnnnsssss @@ -768,6 +848,9 @@ OP_DEF(xcr) tmp = rS1; rD = cr[FLD_CR]; cr[FLD_CR] = tmp; + if (FLD_CR == 1) { + SetPSR(); + } r[0] = 0; } @@ -860,24 +943,24 @@ OP_DEF(fdiv) // 110000_nnnnnnnnnn_nnnnnn_nnnnnnnnnn OP_DEF(br) { - EnterBranch(); - fip = xip + D26; + total_cycle += 1; // Table.7-5 + EnterBranch(xip + D26); ExitBranch(); } // 110001_nnnnnnnnnn_nnnnnn_nnnnnnnnnn OP_DEF(br_n) { - EnterBranch(); - fip = xip + D26; + total_cycle += 0; // Table.7-5 + EnterBranch(xip + D26); ExitDelayBranch(); } // 110010_nnnnnnnnnn_nnnnnn_nnnnnnnnnn OP_DEF(bsr) { - EnterBranch(); - fip = xip + D26; + total_cycle += 1; // Table.7-5 + EnterBranch(xip + D26); r[1] = nip; ExitBranch(); } @@ -885,8 +968,8 @@ OP_DEF(bsr) // 110011_nnnnnnnnnn_nnnnnn_nnnnnnnnnn OP_DEF(bsr_n) { - EnterBranch(); - fip = xip + D26; + total_cycle += 0; // Table.7-5 + EnterBranch(xip + D26); r[1] = nip + 4; ExitDelayBranch(); } @@ -895,8 +978,8 @@ OP_DEF(bsr_n) OP_DEF(bb0) { if ((rS1 & (1 << B5)) == 0) { - EnterBranch(); - fip = xip + D16; + total_cycle += 1; // Table.7-5 + EnterBranch(xip + D16); ExitBranch(); } } @@ -905,8 +988,7 @@ OP_DEF(bb0) OP_DEF(bb0_n) { if ((rS1 & (1 << B5)) == 0) { - EnterBranch(); - fip = xip + D16; + EnterBranch(xip + D16); ExitDelayBranch(); } } @@ -915,8 +997,8 @@ OP_DEF(bb0_n) OP_DEF(bb1) { if ((rS1 & (1 << B5)) != 0) { - EnterBranch(); - fip = xip + D16; + total_cycle += 1; // Table.7-5 + EnterBranch(xip + D16); ExitBranch(); } } @@ -925,8 +1007,7 @@ OP_DEF(bb1) OP_DEF(bb1_n) { if ((rS1 & (1 << B5)) != 0) { - EnterBranch(); - fip = xip + D16; + EnterBranch(xip + D16); ExitDelayBranch(); } } @@ -935,8 +1016,8 @@ OP_DEF(bb1_n) OP_DEF(bcnd) { if (acc_cnd(rS1) & M5) { - EnterBranch(); - fip = xip + D16; + total_cycle += 1; // Table.7-5 + EnterBranch(xip + D16); ExitBranch(); } } @@ -945,8 +1026,7 @@ OP_DEF(bcnd) OP_DEF(bcnd_n) { if (acc_cnd(rS1) & M5) { - EnterBranch(); - fip = xip + D16; + EnterBranch(xip + D16); ExitDelayBranch(); } } @@ -1064,14 +1144,16 @@ OP_DEF(xmem_bu) LDST_USR_SCALE_ALIGN(1); + total_cycle += 4; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_8(addr); + + // 例外安全のために都度 S/U ビットを制御 + if (__predict_false(usr)) cmmu[1].SetSuper(false); + tmp = cmmu[1].xmem_8(addr, rD); + if (__predict_false(usr)) cmmu[1].SetSuper(true); if ((int64)tmp < 0) { - ReadDataException(addr, DM_BU | DM_LOCK | usr); - return; - } - if ((int64)cmmu[1].store_8(addr, rD) < 0) { - WriteDataException(addr, rD, DM_BU | DM_LOCK | usr); + XmemDataException(addr, rD, DM_BU | DM_LOCK | usr); return; } if (FLD_D == 0) @@ -1088,16 +1170,19 @@ OP_DEF(xmem_w) LDST_USR_SCALE_ALIGN(4); + total_cycle += 4; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_32(addr); + + // 例外安全のために都度 S/U ビットを制御 + if (__predict_false(usr)) cmmu[1].SetSuper(false); + tmp = cmmu[1].xmem_32(addr, rD); + if (__predict_false(usr)) cmmu[1].SetSuper(true); if ((int64)tmp < 0) { - ReadDataException(addr, DM_W | DM_LOCK | usr); - return; - } - if ((int64)cmmu[1].store_32(addr, rD) < 0) { - WriteDataException(addr, rD, DM_W | DM_LOCK | usr); + XmemDataException(addr, rD, DM_W | DM_LOCK | usr); return; } + if (FLD_D == 0) return; rD = tmp; @@ -1106,145 +1191,200 @@ OP_DEF(xmem_w) // 111101_DDDDDSSSSS_000010_xU000sssss OP_DEF(ld_hu) { - uint64 tmp; + uint64 data; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(2); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_16(addr); - if ((int64)tmp < 0) { + + if (__predict_false(usr)) cmmu[1].SetSuper(false); + data = cmmu[1].load_16(addr); + if (__predict_false(usr)) cmmu[1].SetSuper(true); + + if ((int64)data < 0) { ReadDataException(addr, DM_HU | usr); return; } if (FLD_D == 0) return; - rD = tmp; + rD = data; } // 111101_DDDDDSSSSS_000011_xU000sssss OP_DEF(ld_bu) { - uint64 tmp; + uint64 data; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(1); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_8(addr); - if ((int64)tmp < 0) { + + if (__predict_false(usr)) cmmu[1].SetSuper(false); + data = cmmu[1].load_8(addr); + if (__predict_false(usr)) cmmu[1].SetSuper(true); + + if ((int64)data < 0) { ReadDataException(addr, DM_BU | usr); return; } if (FLD_D == 0) return; - rD = tmp; + rD = data; } // 111101_DDDDDSSSSS_000100_xU000sssss OP_DEF(ld_d) { - uint64 tmp, tmp2; + uint64 data1, data2; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(8); + total_cycle += 4; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_32(addr); - if ((int64)tmp < 0) { + + // 例外安全のために都度 S/U ビットを制御 + if (__predict_false(usr)) cmmu[1].SetSuper(false); + data1 = cmmu[1].load_32(addr); + if (__predict_false(usr)) cmmu[1].SetSuper(true); + + if ((int64)data1 < 0) { ReadDataException(addr, DM_D1 | usr); return; } addr += 4; - tmp2 = cmmu[1].load_32(addr); - if ((int64)tmp < 0) { + + if (__predict_false(usr)) cmmu[1].SetSuper(false); + data2 = cmmu[1].load_32(addr); + if (__predict_false(usr)) cmmu[1].SetSuper(true); + + if ((int64)data2 < 0) { ReadDataException(addr, DM_D2 | usr); return; } - rD = tmp; - r[(FLD_D + 1) & 0x1f] = tmp2; + rD = data1; + rD2 = data2; r[0] = 0; } // 111101_DDDDDSSSSS_000101_xU000sssss OP_DEF(ld_w) { - uint64 tmp; + uint64 data; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(4); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_32(addr); - if ((int64)tmp < 0) { + + if (__predict_false(usr)) cmmu[1].SetSuper(false); + data = cmmu[1].load_32(addr); + if (__predict_false(usr)) cmmu[1].SetSuper(true); + + if ((int64)data < 0) { ReadDataException(addr, DM_W | usr); return; } if (FLD_D == 0) return; - rD = tmp; + rD = data; } // 111101_DDDDDSSSSS_000110_xU000sssss OP_DEF(ld_h) { - uint64 tmp; + uint64 data; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(2); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_16(addr); - if ((int64)tmp < 0) { + + if (__predict_false(usr)) cmmu[1].SetSuper(false); + data = cmmu[1].load_16(addr); + if (__predict_false(usr)) cmmu[1].SetSuper(true); + + if ((int64)data < 0) { ReadDataException(addr, DM_H | usr); return; } if (FLD_D == 0) return; - rD = (uint32)(int32)(int16)tmp; + rD = (uint32)(int32)(int16)data; } // 111101_DDDDDSSSSS_000111_xU000sssss OP_DEF(ld_b) { - uint64 tmp; + uint64 data; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(1); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_8(addr); - if ((int64)tmp < 0) { + + if (__predict_false(usr)) cmmu[1].SetSuper(false); + data = cmmu[1].load_8(addr); + if (__predict_false(usr)) cmmu[1].SetSuper(true); + + if ((int64)data < 0) { ReadDataException(addr, DM_B | usr); return; } if (FLD_D == 0) return; - rD = (uint32)(int32)(int8)tmp; + rD = (uint32)(int32)(int8)data; } // 111101_DDDDDSSSSS_001000_xU000sssss OP_DEF(st_d) { + uint64 rv; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(8); + total_cycle += 1 + 4; // Table.7-3 + lastaddr = addr; - if ((int64)cmmu[1].store_32(addr, rD) < 0) { + + if (__predict_false(usr)) cmmu[1].SetSuper(false); + rv = cmmu[1].store_32(addr, rD); + if (__predict_false(usr)) cmmu[1].SetSuper(true); + + if ((int64)rv < 0) { WriteDataException(addr, rD, DM_D1 | usr); return; } + addr += 4; - if ((int64)cmmu[1].store_32(addr, r[(FLD_D + 1) & 0x1f]) < 0) { - WriteDataException(addr, r[(FLD_D + 1) & 0x1f], DM_D2 | usr); + + if (__predict_false(usr)) cmmu[1].SetSuper(false); + rv = cmmu[1].store_32(addr, rD2); + if (__predict_false(usr)) cmmu[1].SetSuper(true); + + if ((int64)rv < 0) { + WriteDataException(addr, rD2, DM_D2 | usr); return; } } @@ -1252,13 +1392,21 @@ OP_DEF(st_d) // 111101_DDDDDSSSSS_001001_xU000sssss OP_DEF(st_w) { + uint64 rv; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(4); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - if ((int64)cmmu[1].store_32(addr, rD) < 0) { + + if (__predict_false(usr)) cmmu[1].SetSuper(false); + rv = cmmu[1].store_32(addr, rD); + if (__predict_false(usr)) cmmu[1].SetSuper(true); + + if ((int64)rv < 0) { WriteDataException(addr, rD, DM_W | usr); return; } @@ -1267,13 +1415,21 @@ OP_DEF(st_w) // 111101_DDDDDSSSSS_001010_xU000sssss OP_DEF(st_h) { + uint64 rv; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(2); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - if ((int64)cmmu[1].store_16(addr, rD) < 0) { + + if (__predict_false(usr)) cmmu[1].SetSuper(false); + rv = cmmu[1].store_16(addr, rD); + if (__predict_false(usr)) cmmu[1].SetSuper(true); + + if ((int64)rv < 0) { WriteDataException(addr, rD, DM_H | usr); return; } @@ -1282,13 +1438,21 @@ OP_DEF(st_h) // 111101_DDDDDSSSSS_001011_xU000sssss OP_DEF(st_b) { + uint64 rv; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(1); + total_cycle += 3; // Table.7-3 + lastaddr = addr; - if ((int64)cmmu[1].store_8(addr, rD) < 0) { + + if (__predict_false(usr)) cmmu[1].SetSuper(false); + rv = cmmu[1].store_8(addr, rD); + if (__predict_false(usr)) cmmu[1].SetSuper(true); + + if ((int64)rv < 0) { WriteDataException(addr, rD, DM_B | usr); return; } @@ -1381,8 +1545,10 @@ OP_DEF(xor_c) // 111101_DDDDDSSSSS_010110_00000sssss OP_DEF(or) { - if (FLD_D == 0) + if (FLD_D == 0) { + nop_counter++; return; + } rD = rS1 | rS2; } @@ -1397,12 +1563,12 @@ OP_DEF(or_c) // 111101_DDDDDSSSSS_011000_nn000sssss OP_DEF(addu) { - uint64 res = rS1 + rS2; + uint64 res = (uint64)rS1 + rS2; if (IsCI) { res += GetCY(); } if (IsCO) { - SetCY((res >> 32) & 1); + SetCY(res >> 32); } if (FLD_D == 0) return; @@ -1412,12 +1578,18 @@ OP_DEF(addu) // 111101_DDDDDSSSSS_011001_nn000sssss OP_DEF(subu) { - uint64 res = rS1 - rS2; + // sub 命令では CY ビットは Z80 とかと逆で、0 ならボロー発生。 + // 88100 のマニュアルには書いてない。88110 のマニュアルにある。 + + // 1 の補数をとって足す。 + uint64 res = (uint64)rS1 + (~rS2); if (IsCI) { - res -= GetCY(); + res += GetCY(); + } else { + res += 1; } if (IsCO) { - SetCY((res >> 32) & 1); + SetCY(res >> 32); } if (FLD_D == 0) return; @@ -1431,6 +1603,9 @@ OP_DEF(divu) fpu_unimpl(); return; } + + total_cycle += 38; // Table.7-6 (ただしざっくり) + if (rS2 == 0) { Exception(M88K_EXCEP_INT_DIV); return; @@ -1447,6 +1622,9 @@ OP_DEF(mul) fpu_unimpl(); return; } + + total_cycle += 4; // Table.7-6 (ただしざっくり) + if (FLD_D == 0) return; rD = rS1 * rS2; @@ -1455,14 +1633,14 @@ OP_DEF(mul) // 111101_DDDDDSSSSS_011100_nn000sssss OP_DEF(add) { - uint64 res = rS1 + rS2; + uint64 res = (uint64)rS1 + rS2; if (IsCI) { res += GetCY(); } // XXX: オーバーフローが起きたときにキャリーが更新されるかどうか // ドキュメントされてない気がする if (IsCO) { - SetCY((res >> 32) & 1); + SetCY(res >> 32); } if (isovf_add(rS1, rS2, res)) { Exception(M88K_EXCEP_INT_OVF); @@ -1476,14 +1654,20 @@ OP_DEF(add) // 111101_DDDDDSSSSS_011101_nn000sssss OP_DEF(sub) { - uint64 res = rS1 - rS2; + // sub 命令では CY ビットは Z80 とかと逆で、0 ならボロー発生。 + // 88100 のマニュアルには書いてない。88110 のマニュアルにある。 + + // 1 の補数をとって足す。 + uint64 res = (uint64)rS1 + (~rS2); if (IsCI) { - res -= GetCY(); + res += GetCY(); + } else { + res += 1; } // XXX: オーバーフローが起きたときにキャリーが更新されるかどうか // ドキュメントされてない気がする if (IsCO) { - SetCY((res >> 32) & 1); + SetCY(res >> 32); } if (isovf_sub(rS1, rS2, res)) { Exception(M88K_EXCEP_INT_OVF); @@ -1501,7 +1685,10 @@ OP_DEF(div) fpu_unimpl(); return; } - if (rS1 < 0 || rS2 <= 0) { + + total_cycle += 38; // Table.7-6 (ただしざっくり) + + if ((int32)rS1 < 0 || (int32)rS2 <= 0) { Exception(M88K_EXCEP_INT_DIV); return; } @@ -1592,24 +1779,24 @@ OP_DEF(rot_2) // 111101_zzzzzzzzzz_110000_00000sssss OP_DEF(jmp) { - EnterBranch(); - fip = rS2; + total_cycle += 1; // Table.7-5 + EnterBranch(rS2); ExitBranch(); } // 111101_zzzzzzzzzz_110001_00000sssss OP_DEF(jmp_n) { - EnterBranch(); - fip = rS2; + total_cycle += 0; // Table.7-5 + EnterBranch(rS2); ExitDelayBranch(); } // 111101_zzzzzzzzzz_110010_00000sssss OP_DEF(jsr) { - EnterBranch(); - fip = rS2; + total_cycle += 1; // Table.7-5 + EnterBranch(rS2); r[1] = nip; ExitBranch(); } @@ -1617,26 +1804,85 @@ OP_DEF(jsr) // 111101_zzzzzzzzzz_110011_00000sssss OP_DEF(jsr_n) { - EnterBranch(); - fip = rS2; + total_cycle += 0; // Table.7-5 + EnterBranch(rS2); r[1] = nip + 4; ExitDelayBranch(); } +// 111101_DDDDDzzzzz_111010_00000sssss +OP_DEF(ff1) +{ + int i; + uint32 m = 0x80000000U; + + if (FLD_D == 0) + return; + + if (rS2 == 0) { + rD = 32; + } else { + for (i = 31; i >= 0; i--, m >>= 1) { + if ((rS2 & m) != 0) { + break; + } + } + rD = i; + } + +} + +// 111101_DDDDDzzzzz_111011_00000sssss +OP_DEF(ff0) +{ + int i; + uint32 m = 0x80000000U; + + if (FLD_D == 0) + return; + + if (rS2 == 0xffffffffU) { + rD = 32; + } else { + for (i = 31; i >= 0; i--, m >>= 1) { + if ((rS2 & m) == 0) { + break; + } + } + rD = i; + } +} + // 111101_zzzzzzzzzz_111111_0000000000 OP_DEF(rte) { - psr = epsr; + total_cycle += 2; // Table.7-5 - fip = snip & SIP_MASK; - fetch(); - fip = sfip & SIP_MASK; + SetPSR(epsr); + + // LUNA88K の ROM1.2 は sxip, snip, sfip を制御してくる。 + // ROM1.2 の DAE ハンドラは、 + // sxip = INVALID + // snip = 0 (INVALID) + // sfip = original sxip + // で rte してくる。 + // CPU は sxip は評価しない。snip は 0 なので、無効として nop 扱い。 + // (実際はクロックが消費されるかも?) + // sfip が VALID なので、そこから実行を再開する。 + if (snip & SIP_V) { + EnterBranch(snip & SIP_MASK); + ExitBranch(); + fip = sfip & SIP_MASK; + } else { + EnterBranch(sfip & SIP_MASK); + ExitBranch(); + } // ディレイスロットにいた命令かどうかをやんわり復元する。 // 88110 ではこの情報が E?IP に保持されているが // 88100 にはない。 if (nip + 4 != fip) { - opF = OpSetDelay(opF); + ExitDelayBranch(); } }