--- nono/m680x0/m680x0fpu.cpp 2026/04/29 17:05:35 1.1.1.2 +++ 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) @@ -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) { @@ -390,7 +464,7 @@ MPU680x0Device::fpu_op_fgen_mem() fpu40.ea = 0; // ソースが Dn の場合は 5クロック減 - CYCLE(-5); + CYCLE3(fpu_src_dn); } else if (mr == 074) { // #imm switch (bytes) { case 1: @@ -470,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; @@ -489,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; @@ -565,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); @@ -577,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; @@ -606,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; @@ -617,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 @@ -643,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; @@ -665,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; @@ -821,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 { @@ -835,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; @@ -849,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; @@ -864,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; @@ -1029,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); } @@ -1069,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); } @@ -1104,10 +1180,10 @@ MPU680x0Device::fpu_op_fmove_l_mem() // 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); } @@ -1143,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); } @@ -1172,7 +1248,7 @@ MPU680x0Device::fpu_op_fmove_d_mem() return; } - CYCLE(86); + CYCLE3(fmove_d_fp_ea); fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum)); fpu_implode(&fe, &fe.fe_f3, FTYPE_DBL, data); @@ -1207,7 +1283,7 @@ MPU680x0Device::fpu_op_fmove_x_mem() return; } - CYCLE(72); + CYCLE3(fmove_x_fp_ea); fpu_explode(&fe, &fe.fe_f3, FTYPE_EXT, &RegFP(srcnum)); fpu_implode(&fe, &fe.fe_f3, FTYPE_EXT, data); @@ -1292,6 +1368,7 @@ MPU680x0Device::fpu_op_fmovem_ea2ctl() // // FMOVE #imm と FMOVEM #imm (n==1) は cache case が 1クロックだけ違う // ようだけどもう無視する。 + // 040 で複数レジスタのケースの記述が見当たらない。 // 転送するレジスタ数。 // nofpc の場合は現在の 68881 の実装では FPIAR が選択される。 @@ -1300,7 +1377,7 @@ 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(); @@ -1313,7 +1390,7 @@ MPU680x0Device::fpu_op_fmovem_ea2ctl() src[0] = reg.R[mr]; } else if (mr < 16) { // An - CYCLE2(28, 31); + CYCLE3(fmovem_rn_ctl); // 対アドレスレジスタは FPIAR のみ有効 if (reglist != 1) { fpu_op_illg2(); @@ -1326,7 +1403,7 @@ MPU680x0Device::fpu_op_fmovem_ea2ctl() src[0] = reg.R[mr]; } else if (mr < 0x3c) { // 対メモリ - CYCLE2(28, 30); + CYCLE3(fmovem_mem_ctl); CYCLE(count * 6); // EA を取得。 @@ -1351,7 +1428,7 @@ MPU680x0Device::fpu_op_fmovem_ea2ctl() return; } - CYCLE2(25, 29); + CYCLE3(fmovem_imm_ctl); CYCLE(count * 6); for (i = 0; i < count; i++) { @@ -1392,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 が選択される。 @@ -1400,7 +1479,7 @@ 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(); @@ -1423,7 +1502,7 @@ MPU680x0Device::fpu_op_fmovem_ctl2ea() return; } if (mr < 16) { // An - CYCLE2(31, 34); + CYCLE3(fmovem_ctl_rn); // 対アドレスレジスタは FPIAR のみ有効 if (reglist != 1) { fpu_op_illg2(); @@ -1449,7 +1528,7 @@ MPU680x0Device::fpu_op_fmovem_ctl2ea() return; } - CYCLE2(27, 30); + CYCLE3(fmovem_ctl_mem); CYCLE(count * 6); // 該当レジスタをメモリへ書き出し @@ -1502,9 +1581,9 @@ 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 は最初に一度取得しておいて... @@ -1533,7 +1612,7 @@ MPU680x0Device::fpu_op_fmovem_ea2reg() } } } - CYCLE(23 * count); + CYCLE3(fmovem_ea2reg_n) * count; // 副作用上等! } // FMOVEM.X , @@ -1578,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); @@ -1596,7 +1675,7 @@ 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); @@ -1652,7 +1731,7 @@ MPU680x0Device::fpu_op_fmovem_reg2ea() } } } - CYCLE(25 * count); + CYCLE3(fmovem_reg2ea_n) * count; // 副作用上等! } // FScc.B @@ -1689,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; @@ -1699,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: @@ -1712,7 +1791,7 @@ MPU680x0Device::fpu_op_fscc() } FALLTHROUGH; default: - CYCLE2(20, 23); + CYCLE3(fscc_mem); break; } @@ -1741,7 +1820,7 @@ MPU680x0Device::fpu_op_fdbcc() if (test_cc(&fe, cond) == -1) { // 真なら何もしない。disp を読み飛ばして次へ - CYCLE2(20, 24); + CYCLE3(fdbcc_true); fetch_2(); } else { // 偽の場合 @@ -1750,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); @@ -1783,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); } } @@ -1811,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); } } @@ -1838,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); } } @@ -1864,13 +1943,13 @@ MPU680x0Device::fpu_op_fbcc_w() // 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(); } } @@ -1890,13 +1969,13 @@ MPU680x0Device::fpu_op_fbcc_l() 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(); } } @@ -1939,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); @@ -2008,6 +2087,7 @@ MPU680x0Device::fpu_op_frestore() uint32 ea; uint32 fmtword; uint32 ver_len; + uint32 ver; uint32 len; bool postinc; @@ -2079,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) { // 浮動小数点未実装命令フレーム。 @@ -2113,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) { @@ -2208,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 からコマンド名を返す。 @@ -2280,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)