--- nono/m88xx0/m88100ops.cpp 2026/04/29 17:04:38 1.1.1.3 +++ nono/m88xx0/m88100ops.cpp 2026/04/29 17:05:07 1.1.1.12 @@ -5,6 +5,9 @@ // #include "m88100acc.h" +#include "bitops.h" +#include +#include // 命令のフィールド取り出し #define FLD_D m88100opf_D(opX) @@ -29,21 +32,52 @@ #define rD (r[FLD_D]) #define rS1 (r[FLD_S1]) #define rS2 (r[FLD_S2]) +// ダブルワードの2ワード目 +#define FLD_D2 ((FLD_D + 1) & 0x1f) +#define rD2 (r[FLD_D2]) + +// FP フィールド +#define FP_T1 m88100opf_FP_T1(opX) +#define FP_T2 m88100opf_FP_T2(opX) +#define FP_TD m88100opf_FP_TD(opX) + +// XXX t(サイズ)が原因で Reserved Operand になった時の S1, S2 はどうなる? +#define FP_GET(v, t, n) \ +do { \ + if (t == 0) { \ + v = u2f(r[n]); \ + } else if (t == 1) { \ + v = u2d(((uint64)r[n] << 32) | r[(n + 1) & 31]); \ + } else { \ + FPPreciseException(FPECR_FROP); \ + return; \ + } \ +} while (0) +#define FP_SET(t, n, v) \ +do { \ + if (t == 0) { \ + r[n] = f2u(v); \ + } else if (t == 1) { \ + uint64 tmp = d2u(v); \ + r[n] = tmp >> 32; \ + if (__predict_true(n != 31)) \ + r[n + 1] = (uint32)tmp; \ + } else { \ + FPPreciseException(FPECR_FROP); \ + return; \ + } \ +} while (0) // ブランチ命令入り口 inline void -m88kcpu::EnterBranch() -{ -} - -// ブランチ本体 -inline void -m88kcpu::DoBranch(uint32 toaddr) +m88kcpu::EnterBranch(uint32 toaddr) { + // ジャンプ先アドレスの下位2bitはマスクされる + toaddr &= ~3; // ブランチヒストリに登録しつつ、 // ブランチヒストリからブランチ状況を取得 - auto e = brhist.AddEntry(xip, toaddr, opX); + auto e = brhist.AddEntry(xip | (IsSuper() ? 1 : 0), toaddr, opX); fip = toaddr; // STOP 検出。 @@ -70,19 +104,28 @@ m88kcpu::DoBranch(uint32 toaddr) // 新規ブランチ // 初期化 nop_counter = 0; - } else { + } else if (pseudo_stop_enable) { + // 疑似 STOP 状態有効のときに、 // e.count == 2 のときだけ STOP の検証をすれば良い。 // d はブランチ間の命令数 uint32 d = (xip - toaddr) / sizeof(uint32); // 遅延ブランチなら 1 命令余分に実行するはずなので 1 足す - if (opX & (1 << 26)) { + if ((opX & (1 << 26)) != 0) { d++; } // ブランチ間の命令がすべて NOP なら STOP ということにする。 if (nop_counter == d) { - atomic_reqflag |= CPU_REQ_STOP; + ChangeState(CPU_STATE_STOP); } + + // OpenBSD 6.6 では + // L1: tb1 #1 r0, #0xff + // ld r13, r21, #0x168 + // bcnd.n eq0, r13, L1 + // or r2, r25, #0x5ea0 + // の命令列が使用されている。 + // これについては tb1 で実装してある。 } // STOP ではなかったら、あとは放置していい } @@ -97,10 +140,12 @@ m88kcpu::ExitBranch() } // 遅延ブランチ出口 +// 速度を稼ぐため、ビット反転だけでこれを区別しているので、 +// 同じ op 中で複数回呼び出さないこと。 inline void m88kcpu::ExitDelayBranch() { - opF = OpSetDelay(opF); + opF ^= DELAYSLOT; } // ロードストアユニットの usr の処理。 @@ -130,7 +175,7 @@ m88kcpu::ldst_usr(uint32& usr) inline uint32 m88kcpu::ldst_scale(uint32 size) { - if (opX & (1 << 9)) { + if ((opX & (1 << 9)) != 0) { return rS1 + rS2 * size; } else { return rS1 + rS2; @@ -143,7 +188,7 @@ m88kcpu::ldst_scale(uint32 size) inline bool m88kcpu::ldst_align(uint32 size, uint32& addr) { - if (addr & (size - 1)) { + if ((addr & (size - 1)) != 0) { if (IsMXM()) { addr &= ~(size - 1); } else { @@ -207,20 +252,6 @@ LSR(uint32 a, int n) return a >> n; } - -// 右ローテート -static inline uint32 -ROR(uint32 a, int n) -{ - // TODO: 各種コンパイラごとにビルトインがあったりなかったりするらしい - n &= 31; - if (n == 0) { - return a; - } else { - return (a >> n) | (a << (32 - n)); - } -} - // add でのオーバーフロー条件 static inline bool isovf_add(uint32 s1, uint32 s2, uint32 res) @@ -239,7 +270,7 @@ static inline int acc_cnd(uint32 r) { int rv; - if (r & 0x80000000) { + if ((r & 0x80000000) != 0) { if (r == 0x80000000) { rv = 0x8; } else { @@ -268,6 +299,16 @@ acc_bf(int w, int ofs) return m << ofs; } +// m88100 FPCR の RndMode (0..3) から の丸めモード値への変換。 +// 大抵ここに渡す前に 3 で AND しているはずなので範囲外は考慮しない。 +static int rm_to_round[4] = { + FE_TONEAREST, + FE_TOWARDZERO, + FE_DOWNWARD, + FE_UPWARD, +}; + + // ここから命令定義 // 000000_DDDDDSSSSS_nnnnnn_nnnnnnnnnn @@ -279,10 +320,12 @@ OP_DEF(xmem_bu_imm) addr = rS1 + IMM16; LDST_ALIGN(1); + AddCycle(4); // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].xmem_8(addr, rD); + tmp = cmmu[1].xmem_8(addr, rD & 0xff); if ((int64)tmp < 0) { - XmemDataException(addr, rD, DM_BU | DM_LOCK); + XmemDataException(addr, DM_BU | DM_LOCK); return; } if (FLD_D == 0) @@ -299,10 +342,12 @@ OP_DEF(xmem_w_imm) addr = rS1 + IMM16; LDST_ALIGN(4); + AddCycle(4); // Table.7-3 + lastaddr = addr; tmp = cmmu[1].xmem_32(addr, rD); if ((int64)tmp < 0) { - XmemDataException(addr, rD, DM_W | DM_LOCK); + XmemDataException(addr, DM_W | DM_LOCK); return; } if (FLD_D == 0) @@ -314,66 +359,72 @@ OP_DEF(xmem_w_imm) OP_DEF(ld_hu_imm) { uint32 addr; - uint64 tmp; + uint64 data; addr = rS1 + IMM16; LDST_ALIGN(2); + AddCycle(3); // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_16(addr); - if ((int64)tmp < 0) { - ReadDataException(addr, DM_HU); + data = cmmu[1].load_16(addr); + if ((int64)data < 0) { + ReadDataException32(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); + AddCycle(3); // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_8(addr); - if ((int64)tmp < 0) { - ReadDataException(addr, DM_BU); + data = cmmu[1].load_8(addr); + if ((int64)data < 0) { + ReadDataException32(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); + AddCycle(4); // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_32(addr); - if ((int64)tmp < 0) { - ReadDataException(addr, DM_D1); + data1 = cmmu[1].load_32(addr); + if ((int64)data1 < 0) { + ReadDataException64(addr, DM_D1); return; } addr += 4; - tmp2 = cmmu[1].load_32(addr); - if ((int64)tmp2 < 0) { - ReadDataException(addr, DM_D2); + data2 = cmmu[1].load_32(addr); + if ((int64)data2 < 0) { + ReadDataException64(addr, DM_D2); return; } - rD = tmp; - r[(FLD_D + 1) & 0x1f] = tmp2; + rD = data1; + rD2 = data2; r[0] = 0; } @@ -381,78 +432,89 @@ OP_DEF(ld_d_imm) OP_DEF(ld_w_imm) { uint32 addr; - uint64 tmp; + uint64 data; addr = rS1 + IMM16; LDST_ALIGN(4); + AddCycle(3); // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_32(addr); - if ((int64)tmp < 0) { - ReadDataException(addr, DM_W); + data = cmmu[1].load_32(addr); + if ((int64)data < 0) { + ReadDataException32(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); + AddCycle(3); // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_16(addr); - if ((int64)tmp < 0) { - ReadDataException(addr, DM_H); + data = cmmu[1].load_16(addr); + if ((int64)data < 0) { + ReadDataException32(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); + AddCycle(3); // Table.7-3 + lastaddr = addr; - tmp = cmmu[1].load_8(addr); - if ((int64)tmp < 0) { - ReadDataException(addr, DM_B); + data = cmmu[1].load_8(addr); + if ((int64)data < 0) { + ReadDataException32(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); + AddCycle(1 + 4); // Table.7-3 + lastaddr = addr; - if ((int64)cmmu[1].store_32(addr, rD) < 0) { - WriteDataException(addr, rD, DM_D1); + rv = cmmu[1].store_32(addr, rD); + if ((int64)rv < 0) { + WriteDataException64(addr, 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) { + WriteDataException64(addr, DM_D2); return; } } @@ -461,13 +523,17 @@ OP_DEF(st_d_imm) OP_DEF(st_w_imm) { uint32 addr; + uint64 rv; addr = rS1 + IMM16; LDST_ALIGN(4); + AddCycle(3); // Table.7-3 + lastaddr = addr; - if ((int64)cmmu[1].store_32(addr, rD) < 0) { - WriteDataException(addr, rD, DM_W); + rv = cmmu[1].store_32(addr, rD); + if ((int64)rv < 0) { + WriteDataException32(addr, DM_W); return; } } @@ -476,13 +542,17 @@ OP_DEF(st_w_imm) OP_DEF(st_h_imm) { uint32 addr; + uint64 rv; addr = rS1 + IMM16; LDST_ALIGN(2); + AddCycle(3); // Table.7-3 + lastaddr = addr; - if ((int64)cmmu[1].store_16(addr, rD) < 0) { - WriteDataException(addr, rD, DM_H); + rv = cmmu[1].store_16(addr, rD & 0xffff); + if ((int64)rv < 0) { + WriteDataException32(addr, DM_H); return; } } @@ -491,13 +561,17 @@ OP_DEF(st_h_imm) OP_DEF(st_b_imm) { uint32 addr; + uint64 rv; addr = rS1 + IMM16; LDST_ALIGN(1); + AddCycle(3); // Table.7-3 + lastaddr = addr; - if ((int64)cmmu[1].store_8(addr, rD) < 0) { - WriteDataException(addr, rD, DM_B); + rv = cmmu[1].store_8(addr, rD & 0xff); + if ((int64)rv < 0) { + WriteDataException32(addr, DM_B); return; } } @@ -621,6 +695,9 @@ OP_DEF(divu_imm) fpu_unimpl(); return; } + + AddCycle(38); // Table.7-6 (ただしざっくり) + if (IMM16 == 0) { Exception(M88K_EXCEP_INT_DIV); return; @@ -637,6 +714,9 @@ OP_DEF(mul_imm) fpu_unimpl(); return; } + + AddCycle(4); // Table.7-6 (ただしざっくり) + if (FLD_D == 0) return; rD = rS1 * IMM16; @@ -677,6 +757,9 @@ OP_DEF(div_imm) fpu_unimpl(); return; } + + AddCycle(38); // Table.7-6 (ただしざっくり) + if ((int32)rS1 < 0 || IMM16 == 0) { Exception(M88K_EXCEP_INT_DIV); return; @@ -702,8 +785,9 @@ OP_DEF(ldcr) return; } if (FLD_CR > countof(cr)) { - // 88100 undocumented, - // 88110 compatible + // 制御レジスタ番号が範囲外の時は + // 88100 では undocumented、 + // 88110 では Unimplemented Opcode 例外。 Exception(M88K_EXCEP_UNIMPL_OP); return; } @@ -717,30 +801,31 @@ OP_DEF(fldcr) { int n = FLD_CR; - if (IsUser()) { - // fcr62, 63 はユーザモードでアクセスできる - // それ以外はスーパバイザ用。 - if (n < 62) { - Exception(M88K_EXCEP_PRIV); - return; - } - } - - if (8 < n && n < 62) { - // 未実装 FCR - // 88100 undocumented, - // 88110 compatible - Exception(M88K_EXCEP_UNIMPL_OP); - return; - } + // 88100 88110(Only for reference) + // Super User Super User + // ----- ----- ----- ----- + // FCR0 read FPRV read FPRV + // FCR1..8 read FPRV FUNIMP FPRV + // FCR9..61 read0 FPRV FUNIMP FPRV + // FCR62,63 read read read read - // fcr は 62,63 の配置に細工がしてある if (n >= 62) { n = n - 62 + 9; + } else { + if (IsUser()) { + FPPreciseException(FPECR_FPRV); + return; + } + if (n > 8) { + rD = 0; + return; + } } if (FLD_D == 0) return; - rD = fcr[n]; + + // XXX 内部がバグってなければ読み出す時にマスクする必要はないが、一応 + rD = fcr[n] & fcr_mask[n]; } // 100000_zzzzzSSSSS_10000n_nnnnnsssss @@ -751,14 +836,15 @@ OP_DEF(stcr) return; } if (FLD_CR > countof(cr)) { - // 88100 undocumented, - // 88110 unimpl_op + // 制御レジスタ番号が範囲外の時は + // 88100 では undocumented、 + // 88110 では Unimplemented Opcode 例外。 Exception(M88K_EXCEP_UNIMPL_OP); return; } cr[FLD_CR] = rS1; if (FLD_CR == 1) { - SetPSR(psr); + SetPSR(); } } @@ -767,27 +853,34 @@ OP_DEF(fstcr) { int n = FLD_CR; - if (IsUser()) { - // fcr62, 63 はユーザモードでアクセスできる - // それ以外はスーパバイザ用。 - if (n < 62) { - Exception(M88K_EXCEP_PRIV); + // 88100 88110(Only for reference) + // Super User Super User + // ----- ----- ----- ----- + // FCR0 write FPRV write FPRV + // FCR1..8 noop FPRV FUNIMP FPRV + // FCR9..61 noop FPRV FUNIMP FPRV + // FCR62,63 write write write write + + if (n >= 62) { + n = n - 62 + 9; + } else { + if (IsUser()) { + FPPreciseException(FPECR_FPRV); + return; + } + // FCR1..8(読み込み専用レジスタ)、9..61(未実装レジスタ) ともに noop + if (n > 0) { return; } } - if (8 < n && n < 62) { - // 未実装 FCR - // 88100 null op - // 88110 fpu-unimpl - return; - } + fcr[n] = rS1 & fcr_mask[n]; - // fcr は 62,63 の配置に細工がしてある - if (n >= 62) { - n = n - 62 + 9; + // FPCR が変更されたら、ホストの丸めモードも更新 + if (n == 10) { + uint32 rm = (fpcr >> 14) & 3; + std::fesetround(rm_to_round[rm]); } - fcr[n] = rS1; } // 100000_DDDDDSSSSS_11000n_nnnnnsssss @@ -800,8 +893,9 @@ OP_DEF(xcr) return; } if (FLD_CR > countof(cr)) { - // 88100 undocumented, - // 88110 unimpl_op + // 制御レジスタ番号が範囲外の時は + // 88100 では undocumented、 + // 88110 では Unimplemented Opcode 例外。 Exception(M88K_EXCEP_UNIMPL_OP); return; } @@ -809,7 +903,7 @@ OP_DEF(xcr) rD = cr[FLD_CR]; cr[FLD_CR] = tmp; if (FLD_CR == 1) { - SetPSR(psr); + SetPSR(); } r[0] = 0; } @@ -817,110 +911,256 @@ OP_DEF(xcr) // 100000_DDDDDSSSSS_11001n_nnnnnsssss OP_DEF(fxcr) { - uint32 tmp; - int n = FLD_CR; + uint n = FLD_CR; - if (IsUser()) { - // fcr62, 63 はユーザモードでアクセスできる - // それ以外はスーパバイザ用。 - if (n < 62) { - Exception(M88K_EXCEP_PRIV); - return; - } - } + // 88100 88110(Only for reference) + // Super User Super User + // ----- ----- ----- ----- + // FCR0 xchg FPRV xchg FPRV + // FCR1..8 read FPRV FUNIMP FPRV + // FCR9..61 read0 FPRV FUNIMP FPRV + // FCR62,63 xchg xchg xchg xchg - if (8 < n && n < 62) { - // 未実装 FCR - // 88100 null op - // 88110 fpu-unimpl + if (IsUser() && n < 62) { + FPPreciseException(FPECR_FPRV); return; } - // fcr は 62,63 の配置に細工がしてある - if (n >= 62) { + if (n == 0) { + goto exchange; + } else if (__predict_false(n < 9)) { + // FCR1..8 は読み込み専用レジスタなので、読み出す側のみ + rD = fcr[n] & fcr_mask[n]; + r[0] = 0; + return; + } else if (__predict_false(n < 62)) { + // FCR9..61 は未実装レジスタなので、0 が読み出せるのみ + rD = 0; + return; + } else { n = n - 62 + 9; } - tmp = rS1; - rD = fcr[n]; - fcr[n] = tmp; + + exchange: + uint32 tmp = rS1; + rD = fcr[n] & fcr_mask[n]; + fcr[n] = tmp & fcr_mask[n]; + r[0] = 0; } // 100001_DDDDDSSSSS_00000n_nnnnnsssss OP_DEF(fmul) { - OP_FUNC(unimpl); + // 暫定 + double s1; + double s2; + + if (FLD_D == 0) { + FPPreciseException(FPECR_FUNIMP); + return; + } + + FP_GET(s1, FP_T1, FLD_S1); + FP_GET(s2, FP_T2, FLD_S2); + FP_SET(FP_TD, FLD_D, s1 * s2); } // 100001_DDDDD00000_001000_000nnsssss OP_DEF(flt) { - OP_FUNC(unimpl); + // 暫定 + if (FLD_D == 0) { + FPPreciseException(FPECR_FUNIMP); + return; + } + + FP_SET(FP_TD, FLD_D, (double)(int32)rS2); } + // 100001_DDDDDSSSSS_00101n_nnnnnsssss OP_DEF(fadd) { - OP_FUNC(unimpl); + // 暫定 + double s1; + double s2; + + if (FLD_D == 0) { + FPPreciseException(FPECR_FUNIMP); + return; + } + + FP_GET(s1, FP_T1, FLD_S1); + FP_GET(s2, FP_T2, FLD_S2); + FP_SET(FP_TD, FLD_D, s1 + s2); } // 100001_DDDDDSSSSS_00110n_nnnnnsssss OP_DEF(fsub) { - OP_FUNC(unimpl); + // 暫定 + double s1; + double s2; + + if (FLD_D == 0) { + FPPreciseException(FPECR_FUNIMP); + return; + } + + FP_GET(s1, FP_T1, FLD_S1); + FP_GET(s2, FP_T2, FLD_S2); + FP_SET(FP_TD, FLD_D, s1 - s2); } // 100001_DDDDDSSSSS_00111n_nnnnnsssss OP_DEF(fcmp) { - OP_FUNC(unimpl); + double s1; + double s2; + + if (FLD_D == 0) { + FPPreciseException(FPECR_FUNIMP); + return; + } + + FP_GET(s1, FP_T1, FLD_S1); + FP_GET(s2, FP_T2, FLD_S2); + + // XXX テストが出来てないので Inf と非正規化数は未対応 + // 非正規化数もここのはず + if (std::isnan(s1) || std::isnan(s2) || + std::isinf(s1) || std::isinf(s2) ) + { + FPPreciseException(FPECR_FROP, s1, s2); + return; + } + + // + // 31 .. 12 11 10 9 8 7 6 5 4 3 2 1 0 + // +---- ---+----+----+----+----+----+----+----+----+----+----+----+----+ + // | 0 | ob | in | ib | ou | ge | lt | le | gt | ne | eq | cp | nc | + // +---- ---+----+----+----+----+----+----+----+----+----+----+----+----+ + + uint32 res = 0; + if (s2 >= 0) { + if (s1 <= 0 || s2 <= s1) res |= 0x0800; // ob + if (0 < s1 && s1 < s2) res |= 0x0400; // in + if (0 <= s1 && s1 <= s2) res |= 0x0200; // ib + if (s1 < 0 || s2 < s1) res |= 0x0100; // ou + } + if (s1 >= s2) res |= 0x0080; // ge + if (s1 < s2) res |= 0x0040; // lt + if (s1 <= s2) res |= 0x0020; // le + if (s1 > s2) res |= 0x0010; // gt + if (s1 != s2) res |= 0x0008; // ne + if (s1 == s2) res |= 0x0004; // eq + // cp; if and only if the two operands are comparable + if (!std::isnan(s1) && !std::isnan(s2)) { + res |= 0x0002; + } + // nc; if, and only if, the two operands are not comparable + if (std::isnan(s1) || std::isnan(s2)) { + res |= 0x0001; + } + + rD = res; +} + +// int, nint, trnc 命令の共通部 +void +m88kcpu::ops_int(int rndmode) +{ + double src; + double res; + int exponent; + + if (FLD_D == 0) { + FPPreciseException(FPECR_FUNIMP); + return; + } + + if (FP_T2 == 0) { + float f; + FP_GET(f, FP_T2, FLD_S2); + src = f; + } else { + FP_GET(src, FP_T2, FLD_S2); + } + + int saved_rndmode = std::fegetround(); + + std::fesetround(rndmode); + res = std::nearbyint(src); + std::fesetround(saved_rndmode); + + exponent = 0; + std::frexp(res, &exponent); + if (std::isinf(res) || std::isnan(res) || exponent >= 30) { + // 整数で表現出来ない(かも知れない)ので例外 + FPPreciseException(FPECR_FIOV, src); + return; + } + rD = (uint32)(int32)res; } // 100001_DDDDD00000_010010_0nn00sssss OP_DEF(int) { - OP_FUNC(unimpl); + // 現在のモードのまま (ちょっと無駄だけど…) + ops_int(std::fegetround()); } // 100001_DDDDD00000_010100_0nn00sssss OP_DEF(nint) { - OP_FUNC(unimpl); + ops_int(FE_TONEAREST); } // 100001_DDDDD00000_010110_0nn00sssss OP_DEF(trnc) { - OP_FUNC(unimpl); + ops_int(FE_TOWARDZERO); } // 100001_DDDDDSSSSS_01110n_nnnnnsssss OP_DEF(fdiv) { - OP_FUNC(unimpl); + // 暫定 + double s1; + double s2; + + if (FLD_D == 0) { + FPPreciseException(FPECR_FUNIMP); + return; + } + + FP_GET(s1, FP_T1, FLD_S1); + FP_GET(s2, FP_T2, FLD_S2); + FP_SET(FP_TD, FLD_D, s1 / s2); } // 110000_nnnnnnnnnn_nnnnnn_nnnnnnnnnn OP_DEF(br) { - EnterBranch(); - DoBranch(xip + D26); + AddCycle(1); // Table.7-5 + EnterBranch(xip + D26); ExitBranch(); } // 110001_nnnnnnnnnn_nnnnnn_nnnnnnnnnn OP_DEF(br_n) { - EnterBranch(); - DoBranch(xip + D26); + AddCycle(0); // Table.7-5 + EnterBranch(xip + D26); ExitDelayBranch(); } // 110010_nnnnnnnnnn_nnnnnn_nnnnnnnnnn OP_DEF(bsr) { - EnterBranch(); - DoBranch(xip + D26); + AddCycle(1); // Table.7-5 + EnterBranch(xip + D26); r[1] = nip; ExitBranch(); } @@ -928,8 +1168,8 @@ OP_DEF(bsr) // 110011_nnnnnnnnnn_nnnnnn_nnnnnnnnnn OP_DEF(bsr_n) { - EnterBranch(); - DoBranch(xip + D26); + AddCycle(0); // Table.7-5 + EnterBranch(xip + D26); r[1] = nip + 4; ExitDelayBranch(); } @@ -938,8 +1178,8 @@ OP_DEF(bsr_n) OP_DEF(bb0) { if ((rS1 & (1 << B5)) == 0) { - EnterBranch(); - DoBranch(xip + D16); + AddCycle(1); // Table.7-5 + EnterBranch(xip + D16); ExitBranch(); } } @@ -948,8 +1188,7 @@ OP_DEF(bb0) OP_DEF(bb0_n) { if ((rS1 & (1 << B5)) == 0) { - EnterBranch(); - DoBranch(xip + D16); + EnterBranch(xip + D16); ExitDelayBranch(); } } @@ -958,8 +1197,8 @@ OP_DEF(bb0_n) OP_DEF(bb1) { if ((rS1 & (1 << B5)) != 0) { - EnterBranch(); - DoBranch(xip + D16); + AddCycle(1); // Table.7-5 + EnterBranch(xip + D16); ExitBranch(); } } @@ -968,8 +1207,7 @@ OP_DEF(bb1) OP_DEF(bb1_n) { if ((rS1 & (1 << B5)) != 0) { - EnterBranch(); - DoBranch(xip + D16); + EnterBranch(xip + D16); ExitDelayBranch(); } } @@ -977,9 +1215,9 @@ OP_DEF(bb1_n) // 111010_MMMMMSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(bcnd) { - if (acc_cnd(rS1) & M5) { - EnterBranch(); - DoBranch(xip + D16); + if ((acc_cnd(rS1) & M5) != 0) { + AddCycle(1); // Table.7-5 + EnterBranch(xip + D16); ExitBranch(); } } @@ -987,9 +1225,8 @@ OP_DEF(bcnd) // 111011_MMMMMSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(bcnd_n) { - if (acc_cnd(rS1) & M5) { - EnterBranch(); - DoBranch(xip + D16); + if ((acc_cnd(rS1) & M5) != 0) { + EnterBranch(xip + D16); ExitDelayBranch(); } } @@ -1059,7 +1296,7 @@ OP_DEF(rot_1) { if (FLD_D == 0) return; - rD = ROR(rS1, O5); + rD = ROR32(rS1, O5); } // 111100_bbbbbSSSSS_110100_0vvvvvvvvv @@ -1083,6 +1320,31 @@ OP_DEF(tb1) } if ((rS1 & (1 << B5)) != 0) { ExceptionCore(VEC9, ExceptionKind::TRAP); + } else if (pseudo_stop_enable) { + + // OpenBSD の STOP エミュレーション。 + // OpenBSD 6.6 では + // L1: tb1 #1 r0, #0xff + // ld r13, r21, #0x168 + // bcnd.n eq0, r13, L1 + // or r2, r25, #0x5ea0 + // の命令列が使用されている。 + // tb1 は volatile 変数をアクセスするための同期命令として + // gcc により出力されている。スーパバイザが volatile を + // 観測するために同じアドレスの tb1 に直接ブランチで 3 回飛び込んで + // きたら、それは割り込みか別プロセッサか I/O による変更を待っている + // はずなので STOP と判断してみる。 + // o. 3回、という数字に確たる理由はない。 + // o. 念の為、16バイト(4命令)後方以内からのブランチに限定しておく。 + + if (IsSuper() && VEC9 == 0xff) { + const auto& e = brhist.entry[brhist.top]; + if (e.count == 3 + && e.to == xip + && e.from - e.to < 16) { + ChangeState(CPU_STATE_STOP); + } + } } } @@ -1093,7 +1355,7 @@ OP_DEF(tcnd) Exception(M88K_EXCEP_PRIV); return; } - if (acc_cnd(rS1) & M5) { + if ((acc_cnd(rS1) & M5) != 0) { ExceptionCore(VEC9, ExceptionKind::TRAP); } } @@ -1107,14 +1369,16 @@ OP_DEF(xmem_bu) LDST_USR_SCALE_ALIGN(1); + AddCycle(4); // Table.7-3 + lastaddr = addr; // 例外安全のために都度 S/U ビットを制御 if (__predict_false(usr)) cmmu[1].SetSuper(false); - tmp = cmmu[1].xmem_8(addr, rD); + tmp = cmmu[1].xmem_8(addr, rD & 0xff); if (__predict_false(usr)) cmmu[1].SetSuper(true); if ((int64)tmp < 0) { - XmemDataException(addr, rD, DM_BU | DM_LOCK | usr); + XmemDataException(addr, DM_BU | DM_LOCK | usr); return; } if (FLD_D == 0) @@ -1131,6 +1395,8 @@ OP_DEF(xmem_w) LDST_USR_SCALE_ALIGN(4); + AddCycle(4); // Table.7-3 + lastaddr = addr; // 例外安全のために都度 S/U ビットを制御 @@ -1138,7 +1404,7 @@ OP_DEF(xmem_w) tmp = cmmu[1].xmem_32(addr, rD); if (__predict_false(usr)) cmmu[1].SetSuper(true); if ((int64)tmp < 0) { - XmemDataException(addr, rD, DM_W | DM_LOCK | usr); + XmemDataException(addr, DM_W | DM_LOCK | usr); return; } @@ -1150,186 +1416,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); + AddCycle(3); // Table.7-3 + lastaddr = addr; if (__predict_false(usr)) cmmu[1].SetSuper(false); - tmp = cmmu[1].load_16(addr); + data = cmmu[1].load_16(addr); if (__predict_false(usr)) cmmu[1].SetSuper(true); - if ((int64)tmp < 0) { - ReadDataException(addr, DM_HU | usr); + if ((int64)data < 0) { + ReadDataException32(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); + AddCycle(3); // Table.7-3 + lastaddr = addr; if (__predict_false(usr)) cmmu[1].SetSuper(false); - tmp = cmmu[1].load_8(addr); + data = cmmu[1].load_8(addr); if (__predict_false(usr)) cmmu[1].SetSuper(true); - if ((int64)tmp < 0) { - ReadDataException(addr, DM_BU | usr); + if ((int64)data < 0) { + ReadDataException32(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); + AddCycle(4); // Table.7-3 + lastaddr = addr; // 例外安全のために都度 S/U ビットを制御 if (__predict_false(usr)) cmmu[1].SetSuper(false); - tmp = cmmu[1].load_32(addr); + data1 = cmmu[1].load_32(addr); if (__predict_false(usr)) cmmu[1].SetSuper(true); - if ((int64)tmp < 0) { - ReadDataException(addr, DM_D1 | usr); + if ((int64)data1 < 0) { + ReadDataException64(addr, DM_D1 | usr); return; } addr += 4; if (__predict_false(usr)) cmmu[1].SetSuper(false); - tmp2 = cmmu[1].load_32(addr); + data2 = cmmu[1].load_32(addr); if (__predict_false(usr)) cmmu[1].SetSuper(true); - if ((int64)tmp < 0) { - ReadDataException(addr, DM_D2 | usr); + if ((int64)data2 < 0) { + ReadDataException64(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); + AddCycle(3); // Table.7-3 + lastaddr = addr; if (__predict_false(usr)) cmmu[1].SetSuper(false); - tmp = cmmu[1].load_32(addr); + data = cmmu[1].load_32(addr); if (__predict_false(usr)) cmmu[1].SetSuper(true); - if ((int64)tmp < 0) { - ReadDataException(addr, DM_W | usr); + if ((int64)data < 0) { + ReadDataException32(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); + AddCycle(3); // Table.7-3 + lastaddr = addr; if (__predict_false(usr)) cmmu[1].SetSuper(false); - tmp = cmmu[1].load_16(addr); + data = cmmu[1].load_16(addr); if (__predict_false(usr)) cmmu[1].SetSuper(true); - if ((int64)tmp < 0) { - ReadDataException(addr, DM_H | usr); + if ((int64)data < 0) { + ReadDataException32(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); + AddCycle(3); // Table.7-3 + lastaddr = addr; if (__predict_false(usr)) cmmu[1].SetSuper(false); - tmp = cmmu[1].load_8(addr); + data = cmmu[1].load_8(addr); if (__predict_false(usr)) cmmu[1].SetSuper(true); - if ((int64)tmp < 0) { - ReadDataException(addr, DM_B | usr); + if ((int64)data < 0) { + ReadDataException32(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 tmp2; + uint64 rv; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(8); + AddCycle(1 + 4); // Table.7-3 + lastaddr = addr; if (__predict_false(usr)) cmmu[1].SetSuper(false); - tmp2 = cmmu[1].store_32(addr, rD); + rv = cmmu[1].store_32(addr, rD); if (__predict_false(usr)) cmmu[1].SetSuper(true); - if ((int64)tmp2 < 0) { - WriteDataException(addr, rD, DM_D1 | usr); + if ((int64)rv < 0) { + WriteDataException64(addr, DM_D1 | usr); return; } addr += 4; if (__predict_false(usr)) cmmu[1].SetSuper(false); - tmp2 = cmmu[1].store_32(addr, r[(FLD_D + 1) & 0x1f]); + rv = cmmu[1].store_32(addr, rD2); if (__predict_false(usr)) cmmu[1].SetSuper(true); - if ((int64)tmp2 < 0) { - WriteDataException(addr, r[(FLD_D + 1) & 0x1f], DM_D2 | usr); + if ((int64)rv < 0) { + WriteDataException64(addr, DM_D2 | usr); return; } } @@ -1337,20 +1617,22 @@ OP_DEF(st_d) // 111101_DDDDDSSSSS_001001_xU000sssss OP_DEF(st_w) { - uint64 tmp2; + uint64 rv; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(4); + AddCycle(3); // Table.7-3 + lastaddr = addr; if (__predict_false(usr)) cmmu[1].SetSuper(false); - tmp2 = cmmu[1].store_32(addr, rD); + rv = cmmu[1].store_32(addr, rD); if (__predict_false(usr)) cmmu[1].SetSuper(true); - if ((int64)tmp2 < 0) { - WriteDataException(addr, rD, DM_W | usr); + if ((int64)rv < 0) { + WriteDataException32(addr, DM_W | usr); return; } } @@ -1358,20 +1640,22 @@ OP_DEF(st_w) // 111101_DDDDDSSSSS_001010_xU000sssss OP_DEF(st_h) { - uint64 tmp2; + uint64 rv; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(2); + AddCycle(3); // Table.7-3 + lastaddr = addr; if (__predict_false(usr)) cmmu[1].SetSuper(false); - tmp2 = cmmu[1].store_16(addr, rD); + rv = cmmu[1].store_16(addr, rD & 0xffff); if (__predict_false(usr)) cmmu[1].SetSuper(true); - if ((int64)tmp2 < 0) { - WriteDataException(addr, rD, DM_H | usr); + if ((int64)rv < 0) { + WriteDataException32(addr, DM_H | usr); return; } } @@ -1379,20 +1663,22 @@ OP_DEF(st_h) // 111101_DDDDDSSSSS_001011_xU000sssss OP_DEF(st_b) { - uint64 tmp2; + uint64 rv; uint32 addr; uint32 usr; LDST_USR_SCALE_ALIGN(1); + AddCycle(3); // Table.7-3 + lastaddr = addr; if (__predict_false(usr)) cmmu[1].SetSuper(false); - tmp2 = cmmu[1].store_8(addr, rD); + rv = cmmu[1].store_8(addr, rD & 0xff); if (__predict_false(usr)) cmmu[1].SetSuper(true); - if ((int64)tmp2 < 0) { - WriteDataException(addr, rD, DM_B | usr); + if ((int64)rv < 0) { + WriteDataException32(addr, DM_B | usr); return; } } @@ -1507,7 +1793,7 @@ OP_DEF(addu) res += GetCY(); } if (IsCO) { - SetCY((res >> 32) & 1); + SetCY(res >> 32); } if (FLD_D == 0) return; @@ -1517,8 +1803,7 @@ OP_DEF(addu) // 111101_DDDDDSSSSS_011001_nn000sssss OP_DEF(subu) { - // sub 命令では CY ビットは Z80 とかと逆で、0 ならボロー発生。 - // 88100 のマニュアルには書いてない。88110 のマニュアルにある。 + // sub* 命令の CY ビットはボローが発生*していない*時セット。 // 1 の補数をとって足す。 uint64 res = (uint64)rS1 + (~rS2); @@ -1528,7 +1813,7 @@ OP_DEF(subu) res += 1; } if (IsCO) { - SetCY((res >> 32) & 1); + SetCY(res >> 32); } if (FLD_D == 0) return; @@ -1542,6 +1827,9 @@ OP_DEF(divu) fpu_unimpl(); return; } + + AddCycle(38); // Table.7-6 (ただしざっくり) + if (rS2 == 0) { Exception(M88K_EXCEP_INT_DIV); return; @@ -1558,6 +1846,9 @@ OP_DEF(mul) fpu_unimpl(); return; } + + AddCycle(4); // Table.7-6 (ただしざっくり) + if (FLD_D == 0) return; rD = rS1 * rS2; @@ -1570,15 +1861,15 @@ OP_DEF(add) if (IsCI) { res += GetCY(); } - // XXX: オーバーフローが起きたときにキャリーが更新されるかどうか - // ドキュメントされてない気がする - if (IsCO) { - SetCY((res >> 32) & 1); - } if (isovf_add(rS1, rS2, res)) { Exception(M88K_EXCEP_INT_OVF); return; } + + // rD, Cy の更新は例外が起きなかった時だけ (p.6-19) + if (IsCO) { + SetCY(res >> 32); + } if (FLD_D == 0) return; rD = res; @@ -1587,8 +1878,7 @@ OP_DEF(add) // 111101_DDDDDSSSSS_011101_nn000sssss OP_DEF(sub) { - // sub 命令では CY ビットは Z80 とかと逆で、0 ならボロー発生。 - // 88100 のマニュアルには書いてない。88110 のマニュアルにある。 + // sub* 命令の CY ビットはボローが発生*していない*時セット。 // 1 の補数をとって足す。 uint64 res = (uint64)rS1 + (~rS2); @@ -1597,15 +1887,15 @@ OP_DEF(sub) } else { res += 1; } - // XXX: オーバーフローが起きたときにキャリーが更新されるかどうか - // ドキュメントされてない気がする - if (IsCO) { - SetCY((res >> 32) & 1); - } if (isovf_sub(rS1, rS2, res)) { Exception(M88K_EXCEP_INT_OVF); return; } + + // rD, Cy の更新は例外が起きなかった時だけ (p.6-19) + if (IsCO) { + SetCY(res >> 32); + } if (FLD_D == 0) return; rD = res; @@ -1618,6 +1908,9 @@ OP_DEF(div) fpu_unimpl(); return; } + + AddCycle(38); // Table.7-6 (ただしざっくり) + if ((int32)rS1 < 0 || (int32)rS2 <= 0) { Exception(M88K_EXCEP_INT_DIV); return; @@ -1703,30 +1996,30 @@ OP_DEF(rot_2) if (FLD_D == 0) return; int o = (rS2 ) & 0x1f; - rD = ROR(rS1, o); + rD = ROR32(rS1, o); } // 111101_zzzzzzzzzz_110000_00000sssss OP_DEF(jmp) { - EnterBranch(); - DoBranch(rS2); + AddCycle(1); // Table.7-5 + EnterBranch(rS2); ExitBranch(); } // 111101_zzzzzzzzzz_110001_00000sssss OP_DEF(jmp_n) { - EnterBranch(); - DoBranch(rS2); + AddCycle(0); // Table.7-5 + EnterBranch(rS2); ExitDelayBranch(); } // 111101_zzzzzzzzzz_110010_00000sssss OP_DEF(jsr) { - EnterBranch(); - DoBranch(rS2); + AddCycle(1); // Table.7-5 + EnterBranch(rS2); r[1] = nip; ExitBranch(); } @@ -1734,8 +2027,8 @@ OP_DEF(jsr) // 111101_zzzzzzzzzz_110011_00000sssss OP_DEF(jsr_n) { - EnterBranch(); - DoBranch(rS2); + AddCycle(0); // Table.7-5 + EnterBranch(rS2); r[1] = nip + 4; ExitDelayBranch(); } @@ -1786,6 +2079,8 @@ OP_DEF(ff0) // 111101_zzzzzzzzzz_111111_0000000000 OP_DEF(rte) { + AddCycle(2); // Table.7-5 + SetPSR(epsr); // LUNA88K の ROM1.2 は sxip, snip, sfip を制御してくる。 @@ -1797,20 +2092,20 @@ OP_DEF(rte) // CPU は sxip は評価しない。snip は 0 なので、無効として nop 扱い。 // (実際はクロックが消費されるかも?) // sfip が VALID なので、そこから実行を再開する。 - if (snip & SIP_V) { - DoBranch(snip & SIP_MASK); - fetch(); + if ((snip & SIP_V)) { + EnterBranch(snip & SIP_MASK); + ExitBranch(); fip = sfip & SIP_MASK; } else { - DoBranch(sfip & SIP_MASK); - fetch(); + EnterBranch(sfip & SIP_MASK); + ExitBranch(); } // ディレイスロットにいた命令かどうかをやんわり復元する。 // 88110 ではこの情報が E?IP に保持されているが // 88100 にはない。 if (nip + 4 != fip) { - opF = OpSetDelay(opF); + ExitDelayBranch(); } } @@ -1830,17 +2125,28 @@ OP_DEF(tbnd_2) } } -// 111111_zzzzzzzzzz_000011_zzzzzzzzzz -OP_DEF(getc) +// 111111_DDDDDSSSSS_000001_nnnnnnnnnn +OP_DEF(doscall) { - printf("getc inst\n"); - int c = getchar(); - r[2] = c; -} + // Human68k の DOS call エミュレーションのような何か。 + // 独自ニーモニックでは doscall rD, rS1, #nn と表記することにする。 + // + // Human68k の DOS コール #nn 番を実行し、戻り値を rD に書き込む。 + // 戻り値のない DOS コールでは rD に 0 を指定すること。 + // 引数は rS1 以降に一つずつ置く。m68k でのワードやバイト値の場合は + // 下位詰めするだけで上位ビットのクリア等は不要。 + // 処理するこちら側が必要なビット幅だけを取り出すこと。 + // + // また、ここでのコンソールは VM 内のコンソールではなくホストの標準入出力。 + // + // コールバックがセットされていて特権状態の時のみ発動する。 + // それ以外では現物同様不当命令扱い。 -// 111111_zzzzzzzzzz_000100_zzzzzzzzzz -OP_DEF(putc) -{ - // r3 を表示 - putchar(r[3]); + if (fline_callback && IsSuper()) { + if (fline_callback(this, fline_arg)) { + return; + } + } + + OP_FUNC(illegal); }