--- nono/m680x0/m680x0fpu.cpp 2026/04/29 17:05:31 1.1 +++ nono/m680x0/m680x0fpu.cpp 2026/04/29 17:05:35 1.1.1.2 @@ -166,7 +166,7 @@ MPU680x0Device::ResetFPU(bool hard) // 事前に弾くこと。いずれも消費クロック数は 0 なので問題ない。 // またそれ以外のすべてのアドレッシングモードで反応するため、命令ごとに // 対応していないアドレッシングモードは事前に弾くこと。 -inline uint32 +uint32 MPU680x0Device::cea_fpu(int bytes) { int n; @@ -226,6 +226,24 @@ MPU680x0Device::cea_fpu(int bytes) throw M68K::EXCEP_ILLEGAL; } +// こっちは LC040 用。 +// +// Dn、An、#imm に対しては 0 を返す (MC68040UM.pdf, p.A-6)。 +// それ以外のすべてのアドレッシングモードで反応するため、命令ごとに +// 対応していないアドレッシングモードは事前に弾くこと。 +// 不当パターンなら EXCEP_ILLEGAL をスローする。 +uint32 +MPU680x0Device::cea_fpulc(int bytes) +{ + uint mr = ir & 077; + + if (__predict_false(mr < 16 || mr == 074)) { + return 0; + } else { + return cea_fpu(bytes); + } +} + // メモリから 64ビット値を読み込む void MPU680x0Device::read_8(uint32 addr, uint32 *data) @@ -260,13 +278,80 @@ MPU680x0Device::write_12(uint32 addr, co write_4(addr + 8, data[2]); } -// ソースを data[] に読み込む。 -// 不当命令パターンなら false を返す。 -// size_id は SIZE_*。 -bool -MPU680x0Device::readSRC(int size_id, uint32 *data) +// FPU 不当命令 (1ワード目で確定) +void +MPU680x0Device::fpu_op_illg() +{ + putlog(1, "FPU illegal instruction %04X", ir); + Exception(M68K::EXCEP_FLINE); +} + +// FPU 不当命令 (2ワード目で確定) +void +MPU680x0Device::fpu_op_illg2() +{ + putlog(1, "FPU illegal instruction %04X %04X", ir, ir2); + Exception(M68K::EXCEP_FLINE); +} + +// R/M=0 (レジスタ-レジスタ演算)、opclass=%000 +void +MPU680x0Device::fpu_op_fgen_reg() +{ + uint srcnum = (ir2 >> 10) & 7; + + fgen(&RegFP(srcnum), SIZE_X); +} + +// R/M=1 (メモリ-レジスタ演算)、opclass=%010 +void +MPU680x0Device::fpu_op_fgen_mem() { uint mr = ir & 0x3f; + uint size_id = (ir2 >> 10) & 7; + uint32 ea = 0; + uint32 data[3]; + + // source specifier はオペランドサイズ + if (size_id == 7) { + // こんなところに FMOVECR + fpu_op_fmovecr(); + return; + } + + if ((mr >> 3) == 1) { // An + fpu_op_illg2(); + return; + } + + uint bytes; + switch (size_id) { + case SIZE_B: + bytes = 1; + break; + case SIZE_W: + bytes = 2; + break; + case SIZE_L: + case SIZE_S: + bytes = 4; + break; + case SIZE_D: + bytes = 8; + break; + case SIZE_X: + case SIZE_P: + bytes = 12; + break; + default: + __unreachable(); + } + + if (__predict_false(GetFPUType().Is4060LC())) { + ea = cea_fpulc(bytes); + ExceptionFPLC(ea); + return; + } // 68881 本(p6-13) にはクロック数はこう書いてあるので // Inst | src=FPn | memory source or dst operand format @@ -287,130 +372,76 @@ MPU680x0Device::readSRC(int size_id, uin // どうしたもんか。 if (mr < 8) { // Dn - switch (size_id) { - case SIZE_B: + switch (bytes) { + case 1: data[0] = (int32)(int8)reg.D[mr]; break; - case SIZE_W: + case 2: data[0] = (int32)(int16)reg.D[mr]; break; - case SIZE_L: - case SIZE_S: + case 4: data[0] = reg.D[mr]; break; default: - return false; + fpu_op_illg2(); + return; } - // XXX EA はどうなる? + // 参照されない EA がどうなるか分からないが、LC040 からの類推。 + fpu40.ea = 0; // ソースが Dn の場合は 5クロック減 CYCLE(-5); - } else if (mr == 0x3c) { // #imm - switch (size_id) { - case SIZE_B: + } else if (mr == 074) { // #imm + switch (bytes) { + case 1: data[0] = fetch_2() & 0xff; break; - case SIZE_W: + case 2: data[0] = fetch_2(); break; - case SIZE_L: - case SIZE_S: + case 4: data[0] = fetch_4(); break; - case SIZE_D: + case 8: data[0] = fetch_4(); data[1] = fetch_4(); break; - case SIZE_X: - case SIZE_P: + case 12: data[0] = fetch_4(); data[1] = fetch_4(); data[2] = fetch_4(); break; default: - return false; + __unreachable(); } - // XXX EA はどうなる? + // 参照されない EA がどうなるか分からないが、LC040 からの類推。 + fpu40.ea = 0; } else { - // それ以外は EA を求めてからメモリから読み込む - uint32 ea; - switch (size_id) { - case SIZE_B: - ea = cea_fpu(1); + // それ以外は EA を求めてからメモリから読み込む。 + ea = cea_fpu(bytes); + + switch (bytes) { + case 1: data[0] = (int32)(int8)read_1(ea); break; - case SIZE_W: - ea = cea_fpu(2); + case 2: data[0] = (int32)(int16)read_2(ea); break; - case SIZE_L: - case SIZE_S: - ea = cea_fpu(4); + case 4: data[0] = read_4(ea); break; - case SIZE_D: - ea = cea_fpu(8); + case 8: read_8(ea, data); break; - case SIZE_X: - case SIZE_P: - ea = cea_fpu(12); + case 12: read_12(ea, data); break; default: - return false; + __unreachable(); } fpu40.ea = ea; } - return true; -} - -// FPU 不当命令 (1ワード目で確定) -void -MPU680x0Device::fpu_op_illg() -{ - putlog(1, "FPU illegal instruction %04X", ir); - Exception(M68K::EXCEP_FLINE); -} - -// FPU 不当命令 (2ワード目で確定) -void -MPU680x0Device::fpu_op_illg2() -{ - putlog(1, "FPU illegal instruction %04X %04X", ir, ir2); - Exception(M68K::EXCEP_FLINE); -} - -// R/M=0 (レジスタ-レジスタ演算)、opclass=%000 -void -MPU680x0Device::fpu_op_fgen_reg() -{ - uint srcnum = (ir2 >> 10) & 7; - - fgen(&RegFP(srcnum), SIZE_X); -} - -// R/M=1 (メモリ-レジスタ演算)、opclass=%010 -void -MPU680x0Device::fpu_op_fgen_mem() -{ - uint size_id = (ir2 >> 10) & 7; - uint32 data[3]; - - // source specifier はオペランドサイズ - if (size_id == 7) { - // こんなところに FMOVECR - fpu_op_fmovecr(); - return; - } - - // 第1ワードの EA がソース EA - if (!readSRC(size_id, data)) { - fpu_op_illg2(); - return; - } - fgen(data, size_id); } @@ -916,8 +947,12 @@ MPU680x0Device::fgen040pre(uint opmode, void MPU680x0Device::fpu_op_fmovecr() { - if (GetFPUType().Is4060FPU()) { - ExceptionFP(M68K::EXCEP_FP_UNIMPL); + if (__predict_false(GetFPUType().Is6888x() == false)) { + if (GetFPUType().Is4060LC()) { + ExceptionFPLC(0); + } else { + ExceptionFP(M68K::EXCEP_FP_UNIMPL); + } return; } @@ -974,6 +1009,16 @@ MPU680x0Device::fpu_op_fmove_b_mem() uint32 ea; uint32 data; + if (__predict_false((mr >> 3) == 1 || mr >= 072)) { // An, PC, #imm + fpu_op_illg2(); + return; + } + if (__predict_false(GetFPUType().Is4060LC())) { + ea = cea_fpulc(1); + ExceptionFPLC(ea); + return; + } + fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum)); fpu_implode(&fe, &fe.fe_f3, FTYPE_LNG, &data); // 入力値は long か、long で表現できなければ 0x80000000 が返ってくるが、 @@ -1004,6 +1049,16 @@ MPU680x0Device::fpu_op_fmove_w_mem() uint32 ea; uint32 data; + if (__predict_false((mr >> 3) == 1 || mr >= 072)) { // An, PC, #imm + fpu_op_illg2(); + return; + } + if (__predict_false(GetFPUType().Is4060LC())) { + ea = cea_fpulc(2); + ExceptionFPLC(ea); + return; + } + fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum)); fpu_implode(&fe, &fe.fe_f3, FTYPE_LNG, &data); // 入力値は long か、long で表現できなければ 0x80000000 が返ってくるが、 @@ -1034,6 +1089,16 @@ MPU680x0Device::fpu_op_fmove_l_mem() uint32 ea; uint32 data; + if (__predict_false((mr >> 3) == 1 || mr >= 072)) { // An, PC, #imm + fpu_op_illg2(); + return; + } + if (__predict_false(GetFPUType().Is4060LC())) { + ea = cea_fpulc(4); + ExceptionFPLC(ea); + return; + } + fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum)); fpu_implode(&fe, &fe.fe_f3, FTYPE_LNG, &data); // XXX オーバーフローチェック @@ -1058,6 +1123,16 @@ MPU680x0Device::fpu_op_fmove_s_mem() uint32 ea; uint32 data; + if (__predict_false((mr >> 3) == 1 || mr >= 072)) { // An, PC, #imm + fpu_op_illg2(); + return; + } + if (__predict_false(GetFPUType().Is4060LC())) { + ea = cea_fpulc(4); + ExceptionFPLC(ea); + return; + } + fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum)); fpu_implode(&fe, &fe.fe_f3, FTYPE_SNG, &data); @@ -1082,12 +1157,23 @@ MPU680x0Device::fpu_op_fmove_s_mem() void MPU680x0Device::fpu_op_fmove_d_mem() { - CYCLE(86); - + uint mr = ir & 077; uint srcnum = (ir2 >> 7) & 7; uint32 ea; uint32 data[2]; + if (__predict_false(mr <= 017 || mr >= 072)) { // Dn, An, PC, #imm + fpu_op_illg2(); + return; + } + if (__predict_false(GetFPUType().Is4060LC())) { + ea = cea_fpulc(8); + ExceptionFPLC(ea); + return; + } + + CYCLE(86); + fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum)); fpu_implode(&fe, &fe.fe_f3, FTYPE_DBL, data); @@ -1106,12 +1192,23 @@ MPU680x0Device::fpu_op_fmove_d_mem() void MPU680x0Device::fpu_op_fmove_x_mem() { - CYCLE(72); - + uint mr = ir & 077; uint srcnum = (ir2 >> 7) & 7; uint32 ea; uint32 data[3]; + if (__predict_false(mr <= 017 || mr >= 072)) { // Dn, An, PC, #imm + fpu_op_illg2(); + return; + } + if (__predict_false(GetFPUType().Is4060LC())) { + ea = cea_fpulc(12); + ExceptionFPLC(ea); + return; + } + + CYCLE(72); + fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum)); fpu_implode(&fe, &fe.fe_f3, FTYPE_EXT, data); @@ -1130,13 +1227,23 @@ MPU680x0Device::fpu_op_fmove_x_mem() void MPU680x0Device::fpu_op_fmove_p_mem() { + uint mr = ir & 077; uint srcnum = (ir2 >> 7) & 7; uint32 ea; uint32 data[3]; uint32 k; - if (GetFPUType().Is4060FPU()) { - ExceptionFP(M68K::EXCEP_FP_UNIMPL); + if (__predict_false(mr <= 017 || mr >= 072)) { // Dn, An, PC, #imm + fpu_op_illg2(); + return; + } + if (__predict_false(GetFPUType().Is6888x() == false)) { + if (GetFPUType().Is4060LC()) { + ea = cea_fpulc(12); + ExceptionFPLC(ea); + } else { + ExceptionFP(M68K::EXCEP_FP_UNIMPL); + } return; } @@ -1199,6 +1306,10 @@ MPU680x0Device::fpu_op_fmovem_ea2ctl() fpu_op_illg2(); return; } + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } src[0] = reg.R[mr]; } else if (mr < 16) { // An @@ -1208,6 +1319,10 @@ MPU680x0Device::fpu_op_fmovem_ea2ctl() fpu_op_illg2(); return; } + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } src[0] = reg.R[mr]; } else if (mr < 0x3c) { // 対メモリ @@ -1219,12 +1334,23 @@ MPU680x0Device::fpu_op_fmovem_ea2ctl() // 読み込んでいくので、ロジックは全部同じになる。 // (68000PRM.pdf p393) ea = cea_fpu(count * 4); + + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(ea); + return; + } + for (i = 0; i < count; i++) { src[i] = read_4(ea); ea += 4; } } else if (mr == 0x3c) { // #imm + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } + CYCLE2(25, 29); CYCLE(count * 6); @@ -1280,6 +1406,10 @@ MPU680x0Device::fpu_op_fmovem_ctl2ea() fpu_op_illg2(); return; } + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } if ((reglist & 4)) { reg.R[mr] = RegFPCR; @@ -1299,6 +1429,10 @@ MPU680x0Device::fpu_op_fmovem_ctl2ea() fpu_op_illg2(); return; } + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } reg.R[mr] = RegFPIAR; return; } @@ -1308,11 +1442,16 @@ MPU680x0Device::fpu_op_fmovem_ctl2ea() } // それ以外、対メモリの場合。 + // EA を取得して... + ea = cea_fpu(count * 4); + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(ea); + return; + } + CYCLE2(27, 30); CYCLE(count * 6); - // EA を取得して... - ea = cea_fpu(count * 4); // 該当レジスタをメモリへ書き出し if ((reglist & 4)) { write_4(ea, RegFPCR); @@ -1336,7 +1475,7 @@ void MPU680x0Device::fpu_op_fmovem_ea2reg() { uint mr = ir & 0x3f; - bool predec = !(ir2 & 0x1000); + bool predec = !(ir2 & 0x1000); // 第2ワードの mode が predec か bool dynamic = (ir2 & 0x0800); uint8 reglist = ir2 & 0xff; int i; @@ -1346,7 +1485,7 @@ MPU680x0Device::fpu_op_fmovem_ea2reg() // FMOVEM.X , 35+23n 38+23n // FMOVEM.X ,Dn 49+23n 52+23n - // 不当パターンを弾く + // 不当パターンを弾く。 switch (mr) { case 000 ... 007: // Dn case 010 ... 017: // An @@ -1372,6 +1511,11 @@ MPU680x0Device::fpu_op_fmovem_ea2reg() int count = __builtin_popcount(reglist); ea = cea_fpu(12 * count); + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(ea); + return; + } + if (predec) { // predec ならビット7から FP7..FP0 で、bit7 側から順に転送。 for (i = 7; reglist; reglist <<=1, i--) { @@ -1400,7 +1544,7 @@ void MPU680x0Device::fpu_op_fmovem_reg2ea() { uint mr = ir & 0x3f; - bool anpd = false; // 第1ワード EA が -(An) か + bool anpd = false; // 第1ワード EA が -(An) か bool predec = !(ir2 & 0x1000); // 第2ワードの mode が predec か bool dynamic = (ir2 & 0x0800); uint8 reglist = ir2 & 0xff; @@ -1411,8 +1555,8 @@ MPU680x0Device::fpu_op_fmovem_reg2ea() // FMOVEM.X , 37+25n 40+25n // FMOVEM.X Dn, 51+25n 54+25n - // 不当パターンを弾く - // ついでに -(An) だけ覚えておく + // 不当パターンを弾く。 + // ついでに -(An) だけ覚えておく。 switch (mr) { case 000 ... 007: // Dn case 010 ... 017: // An @@ -1456,6 +1600,14 @@ MPU680x0Device::fpu_op_fmovem_reg2ea() uint n = eanum(ir); save_reg_pd(n); + // XXX この場合 ea はどこを指すのか? + if (__predict_false(GetFPUType().Is4060LC())) { + // Dn,An,#imm は弾いてあるので cea_fpu() と等価になる。 + ea = cea_fpu(12 * count); + ExceptionFPLC(ea); + return; + } + if (predec) { // predec ならビット7から FP7..FP0 で、bit7 側から順に転送。 for (i = 7; reglist; reglist <<= 1, i--) { @@ -1477,6 +1629,11 @@ MPU680x0Device::fpu_op_fmovem_reg2ea() // -(An) 以外は EA を最初に一度計算しておく方法が使える ea = cea_fpu(12 * count); + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(ea); + return; + } + if (predec) { // predec ならビット7から FP7..FP0 で、bit7 側から順に転送。 for (i = 7; reglist; reglist <<= 1, i--) { @@ -1510,6 +1667,13 @@ MPU680x0Device::fpu_op_fscc() uint32 data; uint32 ea; + // XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。 + if (__predict_false(GetFPUType().Is4060LC())) { + ea = cea_fpulc(1); + ExceptionFPLC(ea); + return; + } + // cache worst // FScc.B Dn 18 21 // FScc.B (An)+/-(An) 22 25 ; condition is true @@ -1561,6 +1725,12 @@ MPU680x0Device::fpu_op_fscc() void MPU680x0Device::fpu_op_fdbcc() { + // XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。 + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } + uint rr = ir & 7; uint cond = ir2 & 0x3f; @@ -1596,6 +1766,12 @@ MPU680x0Device::fpu_op_fdbcc() void MPU680x0Device::fpu_op_ftrapcc_w() { + // XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。 + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } + uint cond = ir2 & 0x3f; if (cond > 0x1f) { @@ -1618,6 +1794,12 @@ MPU680x0Device::fpu_op_ftrapcc_w() void MPU680x0Device::fpu_op_ftrapcc_l() { + // XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。 + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } + uint cond = ir2 & 0x3f; if (cond > 0x1f) { @@ -1640,6 +1822,12 @@ MPU680x0Device::fpu_op_ftrapcc_l() void MPU680x0Device::fpu_op_ftrapcc() { + // XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。 + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } + uint cond = ir2 & 0x3f; if (cond > 0x1f) { @@ -1666,6 +1854,12 @@ MPU680x0Device::fpu_op_ftrapcc() void MPU680x0Device::fpu_op_fbcc_w() { + // XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。 + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } + uint cond = ir & 0x3f; // TODO: Illegal condition の戻り値を処理すること。他の test_cc() も同様。 @@ -1687,6 +1881,12 @@ MPU680x0Device::fpu_op_fbcc_w() void MPU680x0Device::fpu_op_fbcc_l() { + // XXX 条件部に1ビット不当パターンがあるがどっちの判定が先か。 + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } + uint cond = ir & 0x3f; if (test_cc(&fe, cond) == -1) { @@ -1778,6 +1978,13 @@ MPU680x0Device::fpu_op_fsave() return; } + // XXX 実機がどう動くか分からないが、とりあえずここでは + // LC なら fpu_state は NULL から変化しないのでフレーム長 4 として動く。 + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(ea); + return; + } + // -(An) もそれ以外もどちらも、まず先頭フレームを書き出す。 // この時点で ea は先頭フレームの位置を指している。 write_4(ea, frame[0]); @@ -1844,6 +2051,11 @@ MPU680x0Device::fpu_op_frestore() return; } + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(ea); + return; + } + // 先頭の1ロングワードを読んでみる。 // これがフォーマットワード $VVLL'xxxx で、VV がバージョン(8bit)、 // LL が(自身を含まないこれ以降の) バイト数 (8bit) を示している。