--- nono/m680x0/m680x0fpu.cpp 2026/04/29 17:05:31 1.1 +++ nono/m680x0/m680x0fpu.cpp 2026/04/29 17:05:40 1.1.1.3 @@ -43,6 +43,7 @@ #include "mpu680x0.h" #include "m680x0disasm.h" +#include "m680x0cycle.h" #include "textscreen.h" #define SIZE_L (0) @@ -166,7 +167,7 @@ MPU680x0Device::ResetFPU(bool hard) // 事前に弾くこと。いずれも消費クロック数は 0 なので問題ない。 // またそれ以外のすべてのアドレッシングモードで反応するため、命令ごとに // 対応していないアドレッシングモードは事前に弾くこと。 -inline uint32 +uint32 MPU680x0Device::cea_fpu(int bytes) { int n; @@ -178,7 +179,7 @@ MPU680x0Device::cea_fpu(int bytes) case 2: // (An) return cea_anin(eanum(ir)); case 3: // (An)+ - CYCLE(6); + CYCLE3(cea_fpu_anpi); n = eanum(ir); save_reg_pi(n); if (bytes == 1) { @@ -190,7 +191,7 @@ MPU680x0Device::cea_fpu(int bytes) } break; case 4: // -(An) - CYCLE(6); + CYCLE3(cea_fpu_anpd); n = eanum(ir); save_reg_pd(n); if (bytes == 1) { @@ -226,6 +227,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 +279,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 +373,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: + CYCLE3(fpu_src_dn); + } 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); } @@ -439,7 +471,7 @@ MPU680x0Device::fgen(const uint32 *srcda switch (opmode) { case 0x00: // FMOVE fmove: - CYCLE(33); + CYCLE3(fmove); res = &fe.fe_f2; fpu_round_prec(&fe, res); break; @@ -458,7 +490,7 @@ MPU680x0Device::fgen(const uint32 *srcda break; case 0x04: // FSQRT fsqrt: - CYCLE(107); + CYCLE3(fsqrt); res = fpu_sqrt(&fe); fpu_round_prec(&fe, res); break; @@ -534,7 +566,7 @@ MPU680x0Device::fgen(const uint32 *srcda break; case 0x18: // FABS fabs: - CYCLE(35); + CYCLE3(fabs); fe.fe_f2.fp_sign = 0; res = &fe.fe_f2; fpu_round_prec(&fe, res); @@ -546,7 +578,7 @@ MPU680x0Device::fgen(const uint32 *srcda break; case 0x1a: // FNEG fneg: - CYCLE(35); + CYCLE3(fneg); res = &fe.fe_f2; if (!ISNAN(res)) { res->fp_sign = !res->fp_sign; @@ -575,7 +607,7 @@ MPU680x0Device::fgen(const uint32 *srcda break; case 0x20: // FDIV fdiv: - CYCLE(103); + CYCLE3(fdiv); res = fpu_div(&fe); fpu_round_prec(&fe, res); break; @@ -586,18 +618,18 @@ MPU680x0Device::fgen(const uint32 *srcda break; case 0x22: // FADD fadd: - CYCLE(51); + CYCLE3(fadd); res = fpu_add(&fe); fpu_round_prec(&fe, res); break; case 0x23: // FMUL fmul: - CYCLE(71); + CYCLE3(fmul); res = fpu_mul(&fe); fpu_round_prec(&fe, res); break; case 0x24: // FSGLDIV - CYCLE(69); + CYCLE3(fsgldiv); res = fpu_sgldiv(&fe); break; case 0x25: // FREM @@ -612,12 +644,12 @@ MPU680x0Device::fgen(const uint32 *srcda fpu_emul_fscale(&fe, ir2); return; case 0x27: // FSGLMUL - CYCLE(59); + CYCLE3(fsglmul); res = fpu_sglmul(&fe); break; case 0x28: // FSUB fsub: - CYCLE(51); + CYCLE3(fsub); res = fpu_sub(&fe); fpu_round_prec(&fe, res); break; @@ -634,12 +666,12 @@ MPU680x0Device::fgen(const uint32 *srcda fpu_round_prec(&fe, res); break; case 0x38: // FCMP - CYCLE(33); + CYCLE3(fcmp); res = fpu_cmp(&fe); discard_result = 1; break; case 0x3a: // FTST - CYCLE(33); + CYCLE3(ftst); res = &fe.fe_f2; discard_result = 1; break; @@ -790,12 +822,14 @@ MPU680x0Device::fgen040pre(uint opmode, // Denorm - etemp,DENO etemp,UNNO etemp,UNNO // Unnorm - - etemp,UNNO etemp,UNNO + // サイクル数は 68040UM.pdf p10-30 の表から適当に逆算。 + int32 stag = FPU40::TAG_NONE; switch (srcsize_id) { case SIZE_B: case SIZE_W: case SIZE_L: - CYCLE(+27); + CYCLE(+0); if (srcdata[0] == 0) { stag = FPU40::TAG_ZERO; } else { @@ -804,7 +838,7 @@ MPU680x0Device::fgen040pre(uint opmode, fpu_explode(&fe, &fe.fe_f2, FTYPE_LNG, srcdata); break; case SIZE_S: - CYCLE(+19); + CYCLE(+0); if (is_denormal_s(srcdata[0])) { // 68040UM.pdf, p9-24. Figure 9-9 (a) uint32 s = srcdata[0] & 0x8000'0000; @@ -818,7 +852,7 @@ MPU680x0Device::fgen040pre(uint opmode, fpu_explode(&fe, &fe.fe_f2, FTYPE_SNG, srcdata); break; case SIZE_D: - CYCLE(+25); + CYCLE(+0); if (is_denormal_d(srcdata)) { // 68040UM.pdf, p9-24. Figure 9-9 (b) uint32 s = srcdata[0] & 0x8000'0000; @@ -833,7 +867,7 @@ MPU680x0Device::fgen040pre(uint opmode, fpu_explode(&fe, &fe.fe_f2, FTYPE_DBL, srcdata); break; case SIZE_X: - CYCLE(+23); + CYCLE(+1); if (is_denormal_x(srcdata)) { memcpy(fpu40.etemp, srcdata, 12); stag = FPU40::TAG_DENORMAL_X; @@ -916,8 +950,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 +1012,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 が返ってくるが、 @@ -984,11 +1032,11 @@ MPU680x0Device::fpu_op_fmove_b_mem() } if (mr < 8) { // Dn - CYCLE(100-2); + CYCLE3(fmove_i_fp_dn); reg.D[mr] &= 0xffffff00; reg.D[mr] |= (uint8)data; } else { - CYCLE(100); + CYCLE3(fmove_i_fp_mem); ea = cea_fpu(1); write_1(ea, (uint8)data); } @@ -1004,6 +1052,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 が返ってくるが、 @@ -1014,11 +1072,11 @@ MPU680x0Device::fpu_op_fmove_w_mem() } if (mr < 8) { // Dn - CYCLE(100-2); + CYCLE3(fmove_i_fp_dn); reg.D[mr] &= 0xffff0000; reg.D[mr] |= (uint16)data; } else { - CYCLE(100); + CYCLE3(fmove_i_fp_mem); ea = cea_fpu(2); write_2(ea, (uint16)data); } @@ -1034,15 +1092,25 @@ 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 オーバーフローチェック if (mr < 8) { // Dn - CYCLE(100-2); + CYCLE3(fmove_i_fp_dn); reg.D[mr] = data; } else { - CYCLE(100); + CYCLE3(fmove_i_fp_mem); ea = cea_fpu(4); write_4(ea, data); } @@ -1058,6 +1126,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); @@ -1068,10 +1146,10 @@ MPU680x0Device::fpu_op_fmove_s_mem() fpu_upd_excp(&fe); if (mr < 8) { // Dn - CYCLE(80-2); + CYCLE3(fmove_s_fp_dn); reg.D[mr] = data; } else { - CYCLE(80); + CYCLE3(fmove_s_fp_mem); ea = cea_fpu(4); write_4(ea, data); } @@ -1082,12 +1160,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; + } + + CYCLE3(fmove_d_fp_ea); + fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum)); fpu_implode(&fe, &fe.fe_f3, FTYPE_DBL, data); @@ -1106,12 +1195,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; + } + + CYCLE3(fmove_x_fp_ea); + fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum)); fpu_implode(&fe, &fe.fe_f3, FTYPE_EXT, data); @@ -1130,13 +1230,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; } @@ -1185,6 +1295,7 @@ MPU680x0Device::fpu_op_fmovem_ea2ctl() // // FMOVE #imm と FMOVEM #imm (n==1) は cache case が 1クロックだけ違う // ようだけどもう無視する。 + // 040 で複数レジスタのケースの記述が見当たらない。 // 転送するレジスタ数。 // nofpc の場合は現在の 68881 の実装では FPIAR が選択される。 @@ -1193,25 +1304,33 @@ MPU680x0Device::fpu_op_fmovem_ea2ctl() count = __builtin_popcount(reglist); if (mr < 8) { // Dn - CYCLE2(28, 31); + CYCLE3(fmovem_rn_ctl); // 対データレジスタは転送数 1 のみ有効 if (count > 1) { fpu_op_illg2(); return; } + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } src[0] = reg.R[mr]; } else if (mr < 16) { // An - CYCLE2(28, 31); + CYCLE3(fmovem_rn_ctl); // 対アドレスレジスタは FPIAR のみ有効 if (reglist != 1) { fpu_op_illg2(); return; } + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } src[0] = reg.R[mr]; } else if (mr < 0x3c) { // 対メモリ - CYCLE2(28, 30); + CYCLE3(fmovem_mem_ctl); CYCLE(count * 6); // EA を取得。 @@ -1219,13 +1338,24 @@ 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 - CYCLE2(25, 29); + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } + + CYCLE3(fmovem_imm_ctl); CYCLE(count * 6); for (i = 0; i < count; i++) { @@ -1266,6 +1396,8 @@ MPU680x0Device::fpu_op_fmovem_ctl2ea() // FMOVE FPc,Rn 31 34 // FMOVE FPc, 33 36 // FMOVEM FPcs, 27+6n 30+6n + // + // 040 で複数レジスタのケースの記述が見当たらない。 // 転送するレジスタ数。 // nofpc の場合は現在の 68881 の実装では FPIAR が選択される。 @@ -1274,12 +1406,16 @@ MPU680x0Device::fpu_op_fmovem_ctl2ea() count = __builtin_popcount(reglist); if (mr < 8) { // Dn - CYCLE2(31, 34); + CYCLE3(fmovem_ctl_rn); // 対データレジスタは転送数は 1 のみ有効 if (count > 1) { fpu_op_illg2(); return; } + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } if ((reglist & 4)) { reg.R[mr] = RegFPCR; @@ -1293,12 +1429,16 @@ MPU680x0Device::fpu_op_fmovem_ctl2ea() return; } if (mr < 16) { // An - CYCLE2(31, 34); + CYCLE3(fmovem_ctl_rn); // 対アドレスレジスタは FPIAR のみ有効 if (reglist != 1) { fpu_op_illg2(); return; } + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(0); + return; + } reg.R[mr] = RegFPIAR; return; } @@ -1308,11 +1448,16 @@ MPU680x0Device::fpu_op_fmovem_ctl2ea() } // それ以外、対メモリの場合。 - CYCLE2(27, 30); - CYCLE(count * 6); - // EA を取得して... ea = cea_fpu(count * 4); + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(ea); + return; + } + + CYCLE3(fmovem_ctl_mem); + CYCLE(count * 6); + // 該当レジスタをメモリへ書き出し if ((reglist & 4)) { write_4(ea, RegFPCR); @@ -1336,7 +1481,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 +1491,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 @@ -1363,15 +1508,20 @@ MPU680x0Device::fpu_op_fmovem_ea2reg() // ついでにここでベースサイクル数を加算。 if (dynamic) { reglist = reg.D[(reglist >> 4) & 7]; - CYCLE2(49, 52); + CYCLE3(fmovem_ea2reg_dyn); } else { - CYCLE2(35, 38); + CYCLE3(fmovem_ea2reg_sta); } // EA は最初に一度取得しておいて... 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--) { @@ -1389,7 +1539,7 @@ MPU680x0Device::fpu_op_fmovem_ea2reg() } } } - CYCLE(23 * count); + CYCLE3(fmovem_ea2reg_n) * count; // 副作用上等! } // FMOVEM.X , @@ -1400,7 +1550,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 +1561,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 @@ -1434,9 +1584,9 @@ MPU680x0Device::fpu_op_fmovem_reg2ea() // ついでにここでベースクロックを記録 if (dynamic) { reglist = reg.D[(reglist >> 4) & 7]; - CYCLE2(51, 54); + CYCLE3(fmovem_reg2ea_dyn); } else { - CYCLE2(37, 40); + CYCLE3(fmovem_reg2ea_sta); } int count = __builtin_popcount(reglist); @@ -1452,10 +1602,18 @@ MPU680x0Device::fpu_op_fmovem_reg2ea() // cea_fpu() は1命令中で EA の取得が1回である前提のあくまで便利関数 // なので、例外的である FMOVEM は仕方がない。 if (anpd) { - CYCLE(6); + CYCLE3(fmovem_reg2ea_anpd); 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 +1635,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--) { @@ -1495,7 +1658,7 @@ MPU680x0Device::fpu_op_fmovem_reg2ea() } } } - CYCLE(25 * count); + CYCLE3(fmovem_reg2ea_n) * count; // 副作用上等! } // FScc.B @@ -1510,6 +1673,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 @@ -1525,7 +1695,7 @@ MPU680x0Device::fpu_op_fscc() switch (mm) { case 0: // Dn - CYCLE2(18, 21); + CYCLE3(fscc_dn); reg.D[rr] &= 0xffffff00; reg.D[rr] |= data; return; @@ -1535,9 +1705,9 @@ MPU680x0Device::fpu_op_fscc() // ここだけ他のメモリ系 EA とはクロックが違うし、 // 条件の成立可否でも違う。 if (data == 0x00) { - CYCLE2(21, 24); + CYCLE3(fscc_anp_z); } else { - CYCLE2(22, 25); + CYCLE3(fscc_anp_nz); } break; case 7: @@ -1548,7 +1718,7 @@ MPU680x0Device::fpu_op_fscc() } FALLTHROUGH; default: - CYCLE2(20, 23); + CYCLE3(fscc_mem); break; } @@ -1561,6 +1731,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; @@ -1571,7 +1747,7 @@ MPU680x0Device::fpu_op_fdbcc() if (test_cc(&fe, cond) == -1) { // 真なら何もしない。disp を読み飛ばして次へ - CYCLE2(20, 24); + CYCLE3(fdbcc_true); fetch_2(); } else { // 偽の場合 @@ -1580,11 +1756,11 @@ MPU680x0Device::fpu_op_fdbcc() if (data == 0xffff) { // ループ終了。disp を読み飛ばす - CYCLE2(24, 32); + CYCLE3(fdbcc_done); fetch_2(); } else { // 計算に使う PC はディスプレースメントのあるアドレス。 - CYCLE2(20, 26); + CYCLE3(fdbcc_jump); uint32 origin = reg.pc; int32 disp = (int32)(int16)fetch_2(); Jump(origin + disp); @@ -1596,6 +1772,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) { @@ -1607,10 +1789,10 @@ MPU680x0Device::fpu_op_ftrapcc_w() fetch_2(); if (test_cc(&fe, cond) == -1) { - CYCLE2(41, 45); + CYCLE3(ftrapccw_excep); Exception(M68K::EXCEP_TRAPV); } else { - CYCLE2(20, 23); + CYCLE3(ftrapccw_nop); } } @@ -1618,6 +1800,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) { @@ -1629,10 +1817,10 @@ MPU680x0Device::fpu_op_ftrapcc_l() fetch_4(); if (test_cc(&fe, cond) == -1) { - CYCLE2(43, 52); + CYCLE3(ftrapccl_excep); Exception(M68K::EXCEP_TRAPV); } else { - CYCLE2(22, 27); + CYCLE3(ftrapccl_nop); } } @@ -1640,6 +1828,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) { @@ -1650,10 +1844,10 @@ MPU680x0Device::fpu_op_ftrapcc() // 即値なし。 if (test_cc(&fe, cond) == -1) { - CYCLE2(39, 47); + CYCLE3(ftrapcc_excep); Exception(M68K::EXCEP_TRAPV); } else { - CYCLE2(18, 22); + CYCLE3(ftrapcc_nop); } } @@ -1666,17 +1860,23 @@ 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() も同様。 if (test_cc(&fe, cond) == -1) { - CYCLE2(20, 23); + CYCLE3(fbcc_jump); uint32 origin = reg.pc; int32 disp = (int32)(int16)fetch_2(); Jump(origin + disp); } else { // disp を読み飛ばす - CYCLE2(18, 19); + CYCLE3(fbccw_nop); fetch_2(); } } @@ -1687,16 +1887,22 @@ 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) { - CYCLE2(20, 23); + CYCLE3(fbcc_jump); uint32 origin = reg.pc; int32 disp = (int32)fetch_4(); Jump(origin + disp); } else { // disp を読み飛ばす - CYCLE2(18, 21); + CYCLE3(fbccl_nop); fetch_4(); } } @@ -1739,14 +1945,14 @@ MPU680x0Device::fpu_op_fsave() switch (fpu_state) { case FPU_STATE_NULL: // NULL フレーム - CYCLE2(16, 18); + CYCLE3(fsave_null); frame = fsave_frame_null; framelen = countof(fsave_frame_null); break; case FPU_STATE_IDLE: case FPU_STATE_BUSY: // 実装してないけどとりあえず。 // IDLE フレーム - CYCLE2(52, 54); // XXX これは最小値らしいけど + CYCLE3(fsave_idle); // XXX これは最小値らしいけど if (fpu_type.Is4060FPU()) { frame = fsave_frame_idle_68040; framelen = countof(fsave_frame_idle_68040); @@ -1778,6 +1984,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 +2057,11 @@ MPU680x0Device::fpu_op_frestore() return; } + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(ea); + return; + } + // 先頭の1ロングワードを読んでみる。 // これがフォーマットワード $VVLL'xxxx で、VV がバージョン(8bit)、 // LL が(自身を含まないこれ以降の) バイト数 (8bit) を示している。 @@ -1874,13 +2092,13 @@ MPU680x0Device::fpu_op_frestore() // NULL フレーム。 // NULL フレームのリストアは FPU の状態をリセットする。 - CYCLE2(21, 22); + CYCLE3(frestore_null); ResetFPU(false); } else if (ver_len == 0x4100) { // IDLE フレーム。 // 現状何も実装していないので、状態を戻して読み捨てるだけ。 - CYCLE2(57, 58); + CYCLE3(frestore_idle); fpu_state = FPU_STATE_IDLE; } else if (ver_len == 0x4130) { // 浮動小数点未実装命令フレーム。 @@ -1905,13 +2123,13 @@ MPU680x0Device::fpu_op_frestore() // NULL フレーム。 // NULL フレームのリストアは FPU の状態をリセットする。 - CYCLE2(21, 22); + CYCLE3(frestore_null); ResetFPU(false); } else if (ver_len == 0x1f18) { // IDLE フレーム。 // 現状何も実装していないので、状態を戻して読み捨てるだけ。 - CYCLE2(57, 58); + CYCLE3(frestore_idle); fpu_state = FPU_STATE_IDLE; } else { if (ver_len == 0x1fb4) { @@ -1996,8 +2214,8 @@ MPU680x0Device::ExceptionFP(uint vector) // EXCEP_FP_UNIMPL (未実装命令例外) はベクタが F ラインと同じで、 // フレームフォーマットが違うので、定数値に細工がしてある。 - const uint32 inst = BranchHistory_m680x0::Exception | (vector & 0xff); - ExceptionGeneric(vector, inst); + const uint32 info = BranchHistory_m680x0::InfoException(vector & 0xff); + ExceptionGeneric(vector, info); } // CMDREG1B からコマンド名を返す。