--- nono/m680x0/m680x0fpu.cpp 2026/04/29 17:05:31 1.1 +++ nono/m680x0/m680x0fpu.cpp 2026/04/29 17:05:57 1.1.1.4 @@ -41,8 +41,82 @@ // o: ハードウェアサポート // x: ソフトウェアサポート +// 68040 の BUSY フレーム: +// MC68040UM.pdf (p.9-40)、fpsp/fpsp.h より。 +// +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---------------+---------------+---------------+---------------+ +//$00 | VERSION=$41 | $60 | | +// +---------------+---------------+---------------+---------------+ +//$04 | | +// +---------------+---------------+---------------+---------------+ +//$08(LV-92)| CU_SAVEPC : |(FPR_DIRTY_BITS) | +// +---------------+---------------+---------------+---------------+ +//$0c(LV-88)| | +//$10(LV-84)| | +//$14(LV-80)| | +// +---------------+---------------+---------------+---------------+ +//$18(LV-76)| |<- WBTS WBTE[14:0] | (WBTEMP_SGN) | | +// +---------------+---------------+---------------+---------------+ +// (WBTEMP_SGN) のバイトは fpsp がワークとして使っているようだ。 +// +---------------+---------------+---------------+---------------+ +//$1c(LV-72)| WBTM[63-32] (資料によって [65-34][33-02] 表記もあるが | +//$20(LV-68)| WBTM[31-00] ここでは仮数部のビット位置で数える) | +// +---------------+---------------+---------------+---------------+ +//$24(LV-64)| (FPSR_SHADOW) | +// +---------------+---------------+---------------+---------------+ +//$28(LV-60)| FPIARCU | +// +---------------+---------------+---------------+---------------+ +//$2c(LV-56)| | +// +---------------+---------------+---------------+---------------+ +//$30(LV-52)| (CMDREG2B.W?/.L?) | +// +---------+-----+---------------+---------------+---------------+ +//$34(LV-48)| | CMDREG3B | | +// +---------+-----+---------------+---------------+---------------+ +//$38(LV-44)| (NMNEXC) | (NMCEXC) | | +// | N N|N N N C C C C C| | +// | U N|O V D U S O V D| | +// +---------------+---------------+---------------+---------------+ +// NMN Unsupported --+ | | | | | | | | +-- NMC Underflow +// NMN SNAN ----+ | | | | | | +---- NMC Overflow +// NMN OPErr ------+ | | | | +------ NMC OPErr +// NMN Overflow --------+ | | +-------- NMC SNAN +// NMN Underflow ----------+ +---------- NMC Unsupported +// +---------------+---------------+---------------+---------------+ +//$3c(LV-40)|STAG |E| |V|G|R|S| | | +// +---------------+---------------+---------------+---------------+ +// (ETEMP15) --+ | | | +-- SBIT (WBTEMP Sticky) +// | | +---- WBTM0 (WBTEMP Round) +// | +------ WBTM1 (WBTEMP Guard) +// +-------- WBTM66 (WBTEMP Overflow) +// +---------------+---------------+---------------+---------------+ +//$40(LV-36)| CMDREG1B | | +// +-------+-------+-------+-------+---------------+---------------+ +//$44(LV-32)|DTAG |F|. . . .:. . .|W|. . . .| | +// +-------+-------+-------+-------+---------------+---------------+ +// +-- (FPTEMP15) +-- (WBTEMP15) +// +---------------+---------------+---------------+---------------+ +//$48(LV-28)|. . . . .|E|E|S|X|.|U|T|. . . .| | +// | |1|3|F|F| |F|F| | | +// +---------------+---------------+---------------+---------------+ +// (SFLAG) --+ | | +-- TFLAG +// (XFLAG) ----+ +---- (UFLAG) +// +---------------+---------------+---------------+---------------+ +//$4c(LV-24)| |<-FPTS FPTE | | +// +---------------+---------------+---------------+---------------+ +//$50(LV-20)| FPTM[63-32] | +//$54(LV-16)| FPTM[31-00] | +// +---------------+---------------+---------------+---------------+ +//$58(LV-12)| |<-ETS ETE | | +// +---------------+---------------+---------------+---------------+ +//$5c(LV-8) | ETM[63-32] | +//$60(LV-4) | ETM[31-00] | +// +---------------+---------------+---------------+---------------+ +//$64(LV-0) + #include "mpu680x0.h" #include "m680x0disasm.h" +#include "m680x0cycle.h" #include "textscreen.h" #define SIZE_L (0) @@ -166,7 +240,7 @@ MPU680x0Device::ResetFPU(bool hard) // 事前に弾くこと。いずれも消費クロック数は 0 なので問題ない。 // またそれ以外のすべてのアドレッシングモードで反応するため、命令ごとに // 対応していないアドレッシングモードは事前に弾くこと。 -inline uint32 +uint32 MPU680x0Device::cea_fpu(int bytes) { int n; @@ -178,7 +252,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 +264,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 +300,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 +352,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 +446,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 +544,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 +563,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 +639,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 +651,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 +680,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 +691,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 +717,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 +739,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 +895,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 +911,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 +925,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 +940,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 +1023,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 +1085,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 +1105,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 +1125,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 +1145,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 +1165,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 +1199,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 +1219,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 +1233,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 +1268,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 +1303,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 +1368,7 @@ MPU680x0Device::fpu_op_fmovem_ea2ctl() // // FMOVE #imm と FMOVEM #imm (n==1) は cache case が 1クロックだけ違う // ようだけどもう無視する。 + // 040 で複数レジスタのケースの記述が見当たらない。 // 転送するレジスタ数。 // nofpc の場合は現在の 68881 の実装では FPIAR が選択される。 @@ -1193,25 +1377,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 +1411,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 +1469,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 +1479,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 +1502,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 +1521,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 +1554,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 +1564,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 +1581,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 +1612,7 @@ MPU680x0Device::fpu_op_fmovem_ea2reg() } } } - CYCLE(23 * count); + CYCLE3(fmovem_ea2reg_n) * count; // 副作用上等! } // FMOVEM.X , @@ -1400,7 +1623,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 +1634,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 +1657,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 +1675,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 +1708,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 +1731,7 @@ MPU680x0Device::fpu_op_fmovem_reg2ea() } } } - CYCLE(25 * count); + CYCLE3(fmovem_reg2ea_n) * count; // 副作用上等! } // FScc.B @@ -1510,6 +1746,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 +1768,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 +1778,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 +1791,7 @@ MPU680x0Device::fpu_op_fscc() } FALLTHROUGH; default: - CYCLE2(20, 23); + CYCLE3(fscc_mem); break; } @@ -1561,6 +1804,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 +1820,7 @@ MPU680x0Device::fpu_op_fdbcc() if (test_cc(&fe, cond) == -1) { // 真なら何もしない。disp を読み飛ばして次へ - CYCLE2(20, 24); + CYCLE3(fdbcc_true); fetch_2(); } else { // 偽の場合 @@ -1580,11 +1829,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 +1845,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 +1862,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 +1873,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 +1890,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 +1901,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 +1917,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 +1933,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 +1960,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 +2018,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 +2057,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]); @@ -1801,6 +2087,7 @@ MPU680x0Device::fpu_op_frestore() uint32 ea; uint32 fmtword; uint32 ver_len; + uint32 ver; uint32 len; bool postinc; @@ -1844,6 +2131,11 @@ MPU680x0Device::fpu_op_frestore() return; } + if (__predict_false(GetFPUType().Is4060LC())) { + ExceptionFPLC(ea); + return; + } + // 先頭の1ロングワードを読んでみる。 // これがフォーマットワード $VVLL'xxxx で、VV がバージョン(8bit)、 // LL が(自身を含まないこれ以降の) バイト数 (8bit) を示している。 @@ -1867,20 +2159,22 @@ MPU680x0Device::fpu_op_frestore() ea += 4; ver_len = fmtword >> 16; + ver = ver_len >> 8; len = ver_len & 0xff; if (GetFPUType().Is4060FPU()) { - if (ver_len == 0) { + if (ver == 0) { // NULL フレーム。 // NULL フレームのリストアは FPU の状態をリセットする。 - CYCLE2(21, 22); + CYCLE3(frestore_null); ResetFPU(false); + goto done; } else if (ver_len == 0x4100) { // IDLE フレーム。 // 現状何も実装していないので、状態を戻して読み捨てるだけ。 - CYCLE2(57, 58); + CYCLE3(frestore_idle); fpu_state = FPU_STATE_IDLE; } else if (ver_len == 0x4130) { // 浮動小数点未実装命令フレーム。 @@ -1901,17 +2195,19 @@ MPU680x0Device::fpu_op_frestore() goto done; } } else /* 68881 */ { - if (ver_len == 0) { + if (ver == 0) { // NULL フレーム。 + // 長さフィールドは見ない。(MC68881UM.pdf 6.4.2.1) // NULL フレームのリストアは FPU の状態をリセットする。 - CYCLE2(21, 22); + CYCLE3(frestore_null); ResetFPU(false); + goto done; } else if (ver_len == 0x1f18) { // IDLE フレーム。 // 現状何も実装していないので、状態を戻して読み捨てるだけ。 - CYCLE2(57, 58); + CYCLE3(frestore_idle); fpu_state = FPU_STATE_IDLE; } else { if (ver_len == 0x1fb4) { @@ -1996,8 +2292,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 からコマンド名を返す。 @@ -2068,7 +2364,7 @@ MPU68040Device::GetFPUTagName(int32 tag) // モニター更新 (040 内部情報) void -MPU68040Device::MonitorUpdateFPU40(TextScreen& screen, int y, const FPU40& tmp) +MPU68040Device::MonitorScreenFPU40(TextScreen& screen, int y, const FPU40& tmp) { // CMDREG1B:$xxxx OPCLASS=0 Rs=7 Rd=7 cmd=00(FTWOTOX) Flags: E3 E1 T // ETEMP :$01234567_01234567_01234567 STAG:1(Unnormal)