--- nono/m88xx0/m88100ops.cpp 2026/04/29 17:05:01 1.1.1.10 +++ nono/m88xx0/m88100ops.cpp 2026/04/29 17:05:05 1.1.1.11 @@ -40,51 +40,15 @@ #define FP_T2 m88100opf_FP_T2(opX) #define FP_TD m88100opf_FP_TD(opX) -// XXX: 暫定 -static float -u2f(uint32 u) -{ - float v; - memcpy(&v, &u, 4); - return v; -} - -static double -u2d(uint64 u) -{ - double v; - memcpy(&v, &u, 8); - return v; -} - -static uint32 -f2u(float v) -{ - uint32 u; - memcpy(&u, &v, 4); - return u; -} - -static uint64 -d2u(double v) -{ - uint64 u; - memcpy(&u, &v, 8); - return u; -} - +// XXX t(サイズ)が原因で Reserved Operand になった時の S1, S2 はどうなる? #define FP_GET(v, t, n) \ do { \ if (t == 0) { \ v = u2f(r[n]); \ } else if (t == 1) { \ - if (n == 31) { \ - FPException(M88K_FPEXCEP_FUNIMP); \ - return; \ - } \ - v = u2d(((uint64)r[n] << 32) | r[n + 1]); \ + v = u2d(((uint64)r[n] << 32) | r[(n + 1) & 31]); \ } else { \ - FPException(M88K_FPEXCEP_FROP); \ + FPPreciseException(FPECR_FROP); \ return; \ } \ } while (0) @@ -94,15 +58,12 @@ do { \ if (t == 0) { \ r[n] = f2u(v); \ } else if (t == 1) { \ - if (n == 31) { \ - FPException(M88K_FPEXCEP_FUNIMP); \ - return; \ - } \ uint64 tmp = d2u(v); \ r[n] = tmp >> 32; \ - r[n + 1] = (uint32)tmp; \ + if (__predict_true(n != 31)) \ + r[n + 1] = (uint32)tmp; \ } else { \ - FPException(M88K_FPEXCEP_FROP); \ + FPPreciseException(FPECR_FROP); \ return; \ } \ } while (0) @@ -351,6 +312,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 @@ -827,8 +798,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; } @@ -842,30 +814,31 @@ OP_DEF(fldcr) { int 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 read FPRV read FPRV + // FCR1..8 read FPRV FUNIMP FPRV + // FCR9..61 read0 FPRV FUNIMP FPRV + // FCR62,63 read read read read - if (8 < n && n < 62) { - // 未実装 FCR - // 88100 undocumented, - // 88110 compatible - Exception(M88K_EXCEP_UNIMPL_OP); - return; - } - - // 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 @@ -876,8 +849,9 @@ 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; } @@ -892,27 +866,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 @@ -925,8 +906,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; } @@ -942,32 +924,41 @@ 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; } @@ -979,7 +970,7 @@ OP_DEF(fmul) double s2; if (FLD_D == 0) { - FPException(M88K_FPEXCEP_FUNIMP); + FPPreciseException(FPECR_FUNIMP); return; } @@ -993,7 +984,7 @@ OP_DEF(flt) { // 暫定 if (FLD_D == 0) { - FPException(M88K_FPEXCEP_FUNIMP); + FPPreciseException(FPECR_FUNIMP); return; } @@ -1009,7 +1000,7 @@ OP_DEF(fadd) double s2; if (FLD_D == 0) { - FPException(M88K_FPEXCEP_FUNIMP); + FPPreciseException(FPECR_FUNIMP); return; } @@ -1026,7 +1017,7 @@ OP_DEF(fsub) double s2; if (FLD_D == 0) { - FPException(M88K_FPEXCEP_FUNIMP); + FPPreciseException(FPECR_FUNIMP); return; } @@ -1038,80 +1029,111 @@ OP_DEF(fsub) // 100001_DDDDDSSSSS_00111n_nnnnnsssss OP_DEF(fcmp) { - // 暫定 double s1; double s2; if (FLD_D == 0) { - FPException(M88K_FPEXCEP_FUNIMP); + FPPreciseException(FPECR_FUNIMP); return; } FP_GET(s1, FP_T1, FLD_S1); FP_GET(s2, FP_T2, FLD_S2); - rD = acc_fcmp(s1, 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; } -// 100001_DDDDD00000_010010_0nn00sssss -OP_DEF(int) -{ - // 暫定 - double s2; +// int, nint, trnc 命令の共通部 +void +m88kcpu::ops_int(int rndmode) +{ + double src; + double res; + int exponent; if (FLD_D == 0) { - FPException(M88K_FPEXCEP_FUNIMP); + FPPreciseException(FPECR_FUNIMP); return; } - FP_GET(s2, FP_T2, FLD_S2); + 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(); - int mode = (fpcr >> 14) & 3; - switch (mode) { - case 0: - std::fesetround(FE_TONEAREST); - rD = (uint32)(int32)std::nearbyint(s2); - break; - case 1: - rD = (uint32)(int32)std::trunc(s2); - break; - case 2: - rD = (uint32)(int32)std::floor(s2); - break; - case 3: - rD = (uint32)(int32)std::ceil(s2); - break; + 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) +{ + // 現在のモードのまま (ちょっと無駄だけど…) + ops_int(std::fegetround()); } // 100001_DDDDD00000_010100_0nn00sssss OP_DEF(nint) { - // 暫定 - double s2; - - if (FLD_D == 0) { - FPException(M88K_FPEXCEP_FUNIMP); - return; - } - - FP_GET(s2, FP_T2, FLD_S2); - std::fesetround(FE_TONEAREST); - rD = (uint32)(int32)std::nearbyint(s2); + ops_int(FE_TONEAREST); } // 100001_DDDDD00000_010110_0nn00sssss OP_DEF(trnc) { - // 暫定 - double s2; - - if (FLD_D == 0) { - FPException(M88K_FPEXCEP_FUNIMP); - return; - } - - FP_GET(s2, FP_T2, FLD_S2); - rD = (uint32)(int32)std::trunc(s2); + ops_int(FE_TOWARDZERO); } // 100001_DDDDDSSSSS_01110n_nnnnnsssss @@ -1122,7 +1144,7 @@ OP_DEF(fdiv) double s2; if (FLD_D == 0) { - FPException(M88K_FPEXCEP_FUNIMP); + FPPreciseException(FPECR_FUNIMP); return; }