--- nono/m88xx0/m88100ops.cpp 2026/04/29 17:05:26 1.1.1.15 +++ nono/m88xx0/m88100ops.cpp 2026/04/29 17:05:56 1.1.1.17 @@ -4,6 +4,45 @@ // Licensed under nono-license.txt // +// 疑似 STOP について。 +// m88k には、m68k の STOP 状態に相当するものは存在せず、同じところを +// ぐるぐる回って時間を潰す方法が取られる。実機であれば (電力が無駄な +// こと以外は) これでも特段困らないが、エミュレータ、特に高速モードでは +// ホストの CPU がぶん回る状況になるため、これを検出して等速モードに +// 落としたい。 +// この無限ループの命令列は OS/コンパイラによってバリエーションがあるので +// 勝手に分類してそれぞれ対応する。 +// +// STOP#1) +// or r0,rS1,rS2 命令のみで構成されたループの場合は、or 命令を +// カウントして DoBranch() で検出している。 +// XXX どこで使われてたっけ +// +// STOP#2) +// OpenBSD 6.6(?) 〜 7.7 では以下の命令列が使用されている。 +// L1: tb1 #1, r0, #0xff +// ld r13, r21, #0x168 +// bcnd.n eq0, r13, L1 +// or r2, r25, #0x5ea0 +// tb1 は volatile 変数をアクセスするための同期命令として +// gcc 3.3.6 により出力されている(*1)。スーパバイザ状態において、 +// 同じ位置の tb1 命令に後方から直接 3 回ジャンプしてきたら、 +// それは volatile 変数を観測しながら変化を待っているループだと +// 思われるので、STOP と判断する。 +// これは tb1 命令内で検出している。 +// 3回という数字に確たる理由はない。また念の為、4命令後方以内 +// からのブランチに限定しておく。 +// *1: https://misskey.io/notes/aervliztx3l308r2 参照 +// +// STOP#3) +// OpenBSD 7.8 以降は tb1 命令の代わりに fldcr r0, fpcr 命令が +// 出力されるようになったが基本構造は STOP#2 と同じ。 +// fldcr r0, fpcr 命令内で対応。 +// L1: fldcr r0, fpcr +// ld r13, r24, #0x0204 +// bcnd.n eq0, r13, L1 +// or r2, r21, #0x7b18 + #include "m88100acc.h" #include "bitops.h" #include @@ -20,13 +59,13 @@ #define O5 m88100opf_O5(reg.opX) #define IMM16 m88100opf_IMM16(reg.opX) #define VEC9 m88100opf_VEC9(reg.opX) +#define IsCO ((reg.opX & 0x100)) +#define IsCI ((reg.opX & 0x200)) // 符号拡張・シフト済みのディスプレースメント // XXX: (負数の右シフト)GCC only #define D16 (((int32)(int16)reg.opX) << 2) #define D26 (((int32)(reg.opX << 6) >> 4)) -#define IsCO ((reg.opX >> 8) & 1) -#define IsCI ((reg.opX >> 9) & 1) // レジスタアクセス #define rD (reg.r[FLD_D]) @@ -47,13 +86,17 @@ do { \ if (t == 0) { \ v = u2f(reg.r[n]); \ } else if (t == 1) { \ - v = u2d(((uint64)reg.r[n] << 32) | reg.r[(n + 1) & 31]); \ + uint64 tmp = (uint64)reg.r[n] << 32; \ + if (__predict_true(n != 31)) \ + tmp |= reg.r[n + 1]; \ + v = u2d(tmp); \ } else { \ FPPreciseException(FPECR_FROP); \ return; \ } \ } while (0) +// n==0 は事前に弾いてある。 #define FP_SET(t, n, v) \ do { \ if (t == 0) { \ @@ -69,6 +112,13 @@ do { \ } \ } while (0) +#define CHECK_FPU do { \ + if (IsFPUEnable() == false) { \ + FPPreciseException(FPECR_FUNIMP); \ + return; \ + } \ +} while (0) + // ブランチ命令 void MPU88xx0Device::DoBranch(uint32 toaddr, bool next_exec) @@ -80,33 +130,20 @@ MPU88xx0Device::DoBranch(uint32 toaddr, auto e = brhist.AddEntry(reg.xip | (IsSuper() ? 1 : 0), toaddr, reg.opX); reg.fip = toaddr; - // STOP 検出。 - // m88k には STOP 状態 (何もせず割り込みだけ待つ状態。m68k の STOP 命令 - // による STOP 状態に相当するもの) は存在せず、割り込みが起きるまで NOP - // 相当の命令を無限ループで実行し続ける方法が一般的にとられる。 - // 実機であれば (電力が無駄なこと以外は) これで特段困らないかも知れないが - // エミュレータ (特に高速モード) ではこのような無限ループはホストの CPU - // がぶん回る状況になるため避けたい。そのためこの無限ループを検出したい。 - // ただし特定の(または定番の)命令列があるわけではなく、任意の無限ループが - // 用いられているため、汎用的に自動検出する必要がある。 - - // ループの 1 回目のブランチで初期化して、 - // 2 回目のブランチで STOP だったかどうかをチェックする。 - // 3 回目以降の場合は STOP でなかったので、もうチェックする必要はない。 - - // m68k の STOP 命令が特権命令なので、それに倣ってここでも特権状態のみを - // 対象とする。 - // 通常のブランチで来る回数がはるかに多いと考えられるので、 // 先にカウント数をチェック対象かどうか調べる。 + // また m68k の STOP 命令に倣ってここでも特権状態のみを対象とする。 if (e.count < 3 && IsSuper()) { if (e.count == 1) { // 新規ブランチ // 初期化 nop_counter = 0; } else if (pseudo_stop_enable) { - // 疑似 STOP 状態有効のときに、 - // e.count == 2 のときだけ STOP の検証をすれば良い。 + // STOP#1 の検出。 + // ループ内がすべて NOP (or r0, rS1, rS2) だったら STOP 状態。 + // そのためループ2周目に入ったら nop_counter を初期化し、 + // ループ3周目に入ったところで命令数を数える。 + // ブランチ間の命令がすべて NOP なら STOP ということにする。 // d はブランチ間の命令数 uint32 d = (reg.xip - toaddr) / sizeof(uint32); @@ -114,18 +151,9 @@ MPU88xx0Device::DoBranch(uint32 toaddr, if (next_exec) { d++; } - // ブランチ間の命令がすべて NOP なら STOP ということにする。 if (nop_counter == d) { ChangeState(CPU_STATE_STOP); } - - // OpenBSD 6.6 では - // L1: tb1 #1 r0, #0xff - // ld r13, r21, #0x168 - // bcnd.n eq0, r13, L1 - // or r2, r25, #0x5ea0 - // の命令列が使用されている。 - // これについては tb1 で実装してある。 } // STOP ではなかったら、あとは放置していい } @@ -150,7 +178,7 @@ MPU88xx0Device::DoBranch(uint32 toaddr, inline uint32 MPU88xx0Device::ldst_scale(uint32 size) { - if ((reg.opX & (1 << 9)) != 0) { + if ((reg.opX & (1U << 9)) != 0) { return rS1 + rS2 * size; } else { return rS1 + rS2; @@ -163,7 +191,7 @@ MPU88xx0Device::ldst_scale(uint32 size) #define LDST_USR_SCALE(size) do { \ usr = (reg.opX & (1U << 8)) ? DM_USR : 0; \ if (IsUser() && usr) { \ - Exception(EXCEP_PRIV); \ + Exception(EXCPRI_PRIV); \ return; \ } \ addr = ldst_scale(size); \ @@ -177,7 +205,7 @@ MPU88xx0Device::ldst_scale(uint32 size) if (IsMXM()) { \ addr &= ~(size - 1); \ } else { \ - Exception(EXCEP_MISALIGNED); \ + InternalException(EXCVEC_MISALIGNED); \ return; \ } \ } \ @@ -275,9 +303,9 @@ acc_bf(int w, int ofs) { uint32 m; if (w == 0) { - m = 0xFFFFFFFFU; + m = 0xffffffffU; } else { - m = ~(0xFFFFFFFFU << w); + m = ~(0xffffffffU << w); } return m << ofs; } @@ -298,7 +326,7 @@ static int rm_to_round[4] = { OP_DEF(xmem_bu_imm) { uint32 addr; - uint64 tmp; + busdata data; addr = rS1 + IMM16; LDST_ALIGN(1); @@ -306,21 +334,21 @@ OP_DEF(xmem_bu_imm) AddCycle(4); // Table.7-3 lastaddr = addr; - tmp = cmmu[1]->xmem_1(addr, rD & 0xff); - if ((int64)tmp < 0) { - XmemDataException(addr, DM_BU | DM_LOCK); + data = cmmu[1]->xmem_1(addr, rD & 0xff); + if (__predict_false(data.IsBusErr())) { + XmemDataException(addr, DM_BU); return; } if (FLD_D == 0) return; - rD = tmp; + rD = data.Data(); } // 000001_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(xmem_w_imm) { uint32 addr; - uint64 tmp; + busdata data; addr = rS1 + IMM16; LDST_ALIGN(4); @@ -328,21 +356,21 @@ OP_DEF(xmem_w_imm) AddCycle(4); // Table.7-3 lastaddr = addr; - tmp = cmmu[1]->xmem_4(addr, rD); - if ((int64)tmp < 0) { - XmemDataException(addr, DM_W | DM_LOCK); + data = cmmu[1]->xmem_4(addr, rD); + if (__predict_false(data.IsBusErr())) { + XmemDataException(addr, DM_W); return; } if (FLD_D == 0) return; - rD = tmp; + rD = data.Data(); } // 000010_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(ld_hu_imm) { uint32 addr; - uint64 data; + busdata data; addr = rS1 + IMM16; LDST_ALIGN(2); @@ -351,20 +379,20 @@ OP_DEF(ld_hu_imm) lastaddr = addr; data = cmmu[1]->load_2(addr); - if ((int64)data < 0) { + if (__predict_false(data.IsBusErr())) { ReadDataException32(addr, DM_HU); return; } if (FLD_D == 0) return; - rD = data; + rD = data.Data(); } // 000011_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(ld_bu_imm) { uint32 addr; - uint64 data; + busdata data; addr = rS1 + IMM16; LDST_ALIGN(1); @@ -373,20 +401,20 @@ OP_DEF(ld_bu_imm) lastaddr = addr; data = cmmu[1]->load_1(addr); - if ((int64)data < 0) { + if (__predict_false(data.IsBusErr())) { ReadDataException32(addr, DM_BU); return; } if (FLD_D == 0) return; - rD = data; + rD = data.Data(); } // 000100_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(ld_d_imm) { - uint32 addr; - uint64 data1, data2; + uint32 addr; // ReadDataException64() に渡すのは常に先頭アドレス。 + busdata data; addr = rS1 + IMM16; LDST_ALIGN(8); @@ -394,28 +422,32 @@ OP_DEF(ld_d_imm) AddCycle(4); // Table.7-3 lastaddr = addr; - data1 = cmmu[1]->load_4(addr); - if ((int64)data1 < 0) { + + data = cmmu[1]->load_4(addr); + if (__predict_false(data.IsBusErr())) { ReadDataException64(addr, DM_D1); return; } - addr += 4; - data2 = cmmu[1]->load_4(addr); - if ((int64)data2 < 0) { + if (__predict_true(FLD_D != 0)) { + rD = data.Data(); + } + + data = cmmu[1]->load_4(addr + 4); + if (__predict_false(data.IsBusErr())) { ReadDataException64(addr, DM_D2); return; } - rD = data1; - rD2 = data2; - reg.r[0] = 0; + if (__predict_true(FLD_D2 != 0)) { + rD2 = data.Data(); + } } // 000101_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(ld_w_imm) { uint32 addr; - uint64 data; + busdata data; addr = rS1 + IMM16; LDST_ALIGN(4); @@ -424,20 +456,20 @@ OP_DEF(ld_w_imm) lastaddr = addr; data = cmmu[1]->load_4(addr); - if ((int64)data < 0) { + if (__predict_false(data.IsBusErr())) { ReadDataException32(addr, DM_W); return; } if (FLD_D == 0) return; - rD = data; + rD = data.Data(); } // 000110_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(ld_h_imm) { uint32 addr; - uint64 data; + busdata data; addr = rS1 + IMM16; LDST_ALIGN(2); @@ -446,20 +478,20 @@ OP_DEF(ld_h_imm) lastaddr = addr; data = cmmu[1]->load_2(addr); - if ((int64)data < 0) { + if (__predict_false(data.IsBusErr())) { ReadDataException32(addr, DM_H); return; } if (FLD_D == 0) return; - rD = (uint32)(int32)(int16)data; + rD = (uint32)(int32)(int16)data.Data(); } // 000111_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(ld_b_imm) { uint32 addr; - uint64 data; + busdata data; addr = rS1 + IMM16; LDST_ALIGN(1); @@ -468,20 +500,20 @@ OP_DEF(ld_b_imm) lastaddr = addr; data = cmmu[1]->load_1(addr); - if ((int64)data < 0) { + if (__predict_false(data.IsBusErr())) { ReadDataException32(addr, DM_B); return; } if (FLD_D == 0) return; - rD = (uint32)(int32)(int8)data; + rD = (uint32)(int32)(int8)data.Data(); } // 001000_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(st_d_imm) { - uint32 addr; - uint64 rv; + uint32 addr; // WriteDataException64() に渡すのは常に先頭アドレス。 + busdata r; addr = rS1 + IMM16; LDST_ALIGN(8); @@ -489,14 +521,15 @@ OP_DEF(st_d_imm) AddCycle(1 + 4); // Table.7-3 lastaddr = addr; - rv = cmmu[1]->store_4(addr, rD); - if ((int64)rv < 0) { + + r = cmmu[1]->store_4(addr, rD); + if (__predict_false(r.IsBusErr())) { WriteDataException64(addr, DM_D1); return; } - addr += 4; - rv = cmmu[1]->store_4(addr, rD2); - if ((int64)rv < 0) { + + r = cmmu[1]->store_4(addr + 4, rD2); + if (__predict_false(r.IsBusErr())) { WriteDataException64(addr, DM_D2); return; } @@ -506,7 +539,7 @@ OP_DEF(st_d_imm) OP_DEF(st_w_imm) { uint32 addr; - uint64 rv; + busdata r; addr = rS1 + IMM16; LDST_ALIGN(4); @@ -514,8 +547,8 @@ OP_DEF(st_w_imm) AddCycle(3); // Table.7-3 lastaddr = addr; - rv = cmmu[1]->store_4(addr, rD); - if ((int64)rv < 0) { + r = cmmu[1]->store_4(addr, rD); + if (__predict_false(r.IsBusErr())) { WriteDataException32(addr, DM_W); return; } @@ -525,7 +558,7 @@ OP_DEF(st_w_imm) OP_DEF(st_h_imm) { uint32 addr; - uint64 rv; + busdata r; addr = rS1 + IMM16; LDST_ALIGN(2); @@ -533,8 +566,8 @@ OP_DEF(st_h_imm) AddCycle(3); // Table.7-3 lastaddr = addr; - rv = cmmu[1]->store_2(addr, rD & 0xffff); - if ((int64)rv < 0) { + r = cmmu[1]->store_2(addr, rD & 0xffff); + if (__predict_false(r.IsBusErr())) { WriteDataException32(addr, DM_H); return; } @@ -544,7 +577,7 @@ OP_DEF(st_h_imm) OP_DEF(st_b_imm) { uint32 addr; - uint64 rv; + busdata r; addr = rS1 + IMM16; LDST_ALIGN(1); @@ -552,8 +585,8 @@ OP_DEF(st_b_imm) AddCycle(3); // Table.7-3 lastaddr = addr; - rv = cmmu[1]->store_1(addr, rD & 0xff); - if ((int64)rv < 0) { + r = cmmu[1]->store_1(addr, rD & 0xff); + if (__predict_false(r.IsBusErr())) { WriteDataException32(addr, DM_B); return; } @@ -674,15 +707,12 @@ OP_DEF(subu_imm) // 011010_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(divu_imm) { - if (!IsFPUEnable()) { - fpu_unimpl(); - return; - } + CHECK_FPU; AddCycle(38); // Table.7-6 (ただしざっくり) if (IMM16 == 0) { - Exception(EXCEP_INT_DIV); + InternalException(EXCVEC_INT_DIV); return; } if (FLD_D == 0) @@ -693,10 +723,7 @@ OP_DEF(divu_imm) // 011011_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(mul_imm) { - if (!IsFPUEnable()) { - fpu_unimpl(); - return; - } + CHECK_FPU; AddCycle(4); // Table.7-6 (ただしざっくり) @@ -711,7 +738,7 @@ OP_DEF(add_imm) uint32 imm = IMM16; uint32 res = rS1 + imm; if (isovf_add(rS1, imm, res)) { - Exception(EXCEP_INT_OVF); + InternalException(EXCVEC_INT_OVF); return; } if (FLD_D == 0) @@ -725,7 +752,7 @@ OP_DEF(sub_imm) uint32 imm = IMM16; uint32 res = rS1 - imm; if (isovf_sub(rS1, imm, res)) { - Exception(EXCEP_INT_OVF); + InternalException(EXCVEC_INT_OVF); return; } if (FLD_D == 0) @@ -736,15 +763,12 @@ OP_DEF(sub_imm) // 011110_DDDDDSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(div_imm) { - if (!IsFPUEnable()) { - fpu_unimpl(); - return; - } + CHECK_FPU; AddCycle(38); // Table.7-6 (ただしざっくり) if ((int32)rS1 < 0 || IMM16 == 0) { - Exception(EXCEP_INT_DIV); + InternalException(EXCVEC_INT_DIV); return; } if (FLD_D == 0) @@ -763,15 +787,15 @@ OP_DEF(cmp_imm) // 100000_DDDDDzzzzz_01000n_nnnnnzzzzz OP_DEF(ldcr) { - if (IsUser()) { - Exception(EXCEP_PRIV); + if (__predict_false(IsUser())) { + Exception(EXCPRI_PRIV); return; } if (FLD_CR > countof(reg.cr)) { // 制御レジスタ番号が範囲外の時は // 88100 では undocumented、 // 88110 では Unimplemented Opcode 例外。 - Exception(EXCEP_UNIMPL_OP); + Exception(EXCPRI_UNIMPL_OP); return; } if (FLD_D == 0) @@ -795,7 +819,7 @@ OP_DEF(fldcr) if (n >= 62) { n = n - 62 + 9; } else { - if (IsUser()) { + if (__predict_false(IsUser())) { FPPreciseException(FPECR_FPRV); return; } @@ -804,8 +828,20 @@ OP_DEF(fldcr) return; } } - if (FLD_D == 0) + if (FLD_D == 0) { + // STOP#3。OpenBSD 7.8 以降の STOP エミュレーション。 + // 4命令以内の後方から fldcr r0, fpcr (何もしない命令) への分岐が + // 3回連続すれば STOP 状態とする。 + if (__predict_true(pseudo_stop_enable)) { + if (__predict_true(IsSuper())) { + const auto& e = brhist.entry[brhist.top]; + if (e.count == 3 && e.to == reg.xip && e.from - e.to < 16) { + ChangeState(CPU_STATE_STOP); + } + } + } return; + } // XXX 内部がバグってなければ読み出す時にマスクする必要はないが、一応 rD = reg.fcr[n] & reg.fcr_mask[n]; @@ -814,19 +850,24 @@ OP_DEF(fldcr) // 100000_zzzzzSSSSS_10000n_nnnnnsssss OP_DEF(stcr) { - if (IsUser()) { - Exception(EXCEP_PRIV); + if (__predict_false(IsUser())) { + Exception(EXCPRI_PRIV); return; } - if (FLD_CR > countof(reg.cr)) { + + uint n = FLD_CR; + if (n > countof(reg.cr)) { // 制御レジスタ番号が範囲外の時は // 88100 では undocumented、 // 88110 では Unimplemented Opcode 例外。 - Exception(EXCEP_UNIMPL_OP); + Exception(EXCPRI_UNIMPL_OP); return; } - reg.cr[FLD_CR] = rS1; - if (FLD_CR == 1) { + // PID, SXIP は Read Only。 + if (__predict_true(n != 0 && n != 4)) { + reg.cr[n] = rS1; + } + if (n == 1) { SetPSR(); } } @@ -847,7 +888,7 @@ OP_DEF(fstcr) if (n >= 62) { n = n - 62 + 9; } else { - if (IsUser()) { + if (__predict_false(IsUser())) { FPPreciseException(FPECR_FPRV); return; } @@ -871,24 +912,29 @@ OP_DEF(xcr) { uint32 tmp; - if (IsUser()) { - Exception(EXCEP_PRIV); + if (__predict_false(IsUser())) { + Exception(EXCPRI_PRIV); return; } - if (FLD_CR > countof(reg.cr)) { + + uint n = FLD_CR; + if (n > countof(reg.cr)) { // 制御レジスタ番号が範囲外の時は // 88100 では undocumented、 // 88110 では Unimplemented Opcode 例外。 - Exception(EXCEP_UNIMPL_OP); + Exception(EXCPRI_UNIMPL_OP); return; } tmp = rS1; - rD = reg.cr[FLD_CR]; - reg.cr[FLD_CR] = tmp; - if (FLD_CR == 1) { - SetPSR(); + rD = reg.cr[n]; + // PID, SXIP は Read Only。 + if (__predict_true(n != 0 && n != 4)) { + reg.cr[n] = tmp; } reg.r[0] = 0; + if (n == 1) { + SetPSR(); + } } // 100000_DDDDDSSSSS_11001n_nnnnnsssss @@ -904,7 +950,7 @@ OP_DEF(fxcr) // FCR9..61 read0 FPRV FUNIMP FPRV // FCR62,63 xchg xchg xchg xchg - if (IsUser() && n < 62) { + if (__predict_false(IsUser()) && n < 62) { FPPreciseException(FPECR_FPRV); return; } @@ -935,10 +981,10 @@ OP_DEF(fxcr) // 100001_DDDDDSSSSS_00000n_nnnnnsssss OP_DEF(fmul) { - // 暫定 double s1; double s2; + CHECK_FPU; if (FLD_D == 0) { FPPreciseException(FPECR_FUNIMP); return; @@ -952,7 +998,7 @@ OP_DEF(fmul) // 100001_DDDDD00000_001000_000nnsssss OP_DEF(flt) { - // 暫定 + CHECK_FPU; if (FLD_D == 0) { FPPreciseException(FPECR_FUNIMP); return; @@ -965,10 +1011,10 @@ OP_DEF(flt) // 100001_DDDDDSSSSS_00101n_nnnnnsssss OP_DEF(fadd) { - // 暫定 double s1; double s2; + CHECK_FPU; if (FLD_D == 0) { FPPreciseException(FPECR_FUNIMP); return; @@ -982,10 +1028,10 @@ OP_DEF(fadd) // 100001_DDDDDSSSSS_00110n_nnnnnsssss OP_DEF(fsub) { - // 暫定 double s1; double s2; + CHECK_FPU; if (FLD_D == 0) { FPPreciseException(FPECR_FUNIMP); return; @@ -1002,6 +1048,7 @@ OP_DEF(fcmp) double s1; double s2; + CHECK_FPU; if (FLD_D == 0) { FPPreciseException(FPECR_FUNIMP); return; @@ -1027,10 +1074,10 @@ OP_DEF(fcmp) uint32 res = 0; if (s2 >= 0) { - if (s1 <= 0 || s2 <= s1) res |= 0x0800; // ob + if (s1 <= 0 || s1 >= s2) 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 < 0 || s1 > s2) res |= 0x0100; // ou } if (s1 >= s2) res |= 0x0080; // ge if (s1 < s2) res |= 0x0040; // lt @@ -1058,6 +1105,7 @@ MPU88xx0Device::ops_int(int rndmode) double res; int exponent; + CHECK_FPU; if (FLD_D == 0) { FPPreciseException(FPECR_FUNIMP); return; @@ -1109,10 +1157,10 @@ OP_DEF(trnc) // 100001_DDDDDSSSSS_01110n_nnnnnsssss OP_DEF(fdiv) { - // 暫定 double s1; double s2; + CHECK_FPU; if (FLD_D == 0) { FPPreciseException(FPECR_FUNIMP); return; @@ -1156,7 +1204,7 @@ OP_DEF(bsr_n) // 110100_nnnnnSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(bb0) { - if ((rS1 & (1 << B5)) == 0) { + if ((rS1 & (1U << B5)) == 0) { AddCycle(1); // Table.7-5 DoBranch(reg.xip + D16, false); } @@ -1165,7 +1213,7 @@ OP_DEF(bb0) // 110101_nnnnnSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(bb0_n) { - if ((rS1 & (1 << B5)) == 0) { + if ((rS1 & (1U << B5)) == 0) { DoBranch(reg.xip + D16, true); } } @@ -1173,7 +1221,7 @@ OP_DEF(bb0_n) // 110110_nnnnnSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(bb1) { - if ((rS1 & (1 << B5)) != 0) { + if ((rS1 & (1U << B5)) != 0) { AddCycle(1); // Table.7-5 DoBranch(reg.xip + D16, false); } @@ -1182,7 +1230,7 @@ OP_DEF(bb1) // 110111_nnnnnSSSSS_nnnnnn_nnnnnnnnnn OP_DEF(bb1_n) { - if ((rS1 & (1 << B5)) != 0) { + if ((rS1 & (1U << B5)) != 0) { DoBranch(reg.xip + D16, true); } } @@ -1224,7 +1272,7 @@ OP_DEF(set_1) OP_DEF(ext_1) { int w, o, left; - uint32 tmp; + uint32 res; if (FLD_D == 0) return; @@ -1233,16 +1281,16 @@ OP_DEF(ext_1) w = 32; o = O5; left = 32 - w - o; - tmp = ASL(rS1, left); - tmp = ASR(tmp, left); - rD = ASR(tmp, o); + res = ASL(rS1, left); + res = ASR(res, left); + rD = ASR(res, o); } // 111100_DDDDDSSSSS_100110_wwwwwooooo OP_DEF(extu_1) { int w, o, left; - uint32 tmp; + uint32 res; if (FLD_D == 0) return; @@ -1251,9 +1299,9 @@ OP_DEF(extu_1) w = 32; o = O5; left = 32 - w - o; - tmp = ASL(rS1, left); - tmp = LSR(tmp, left); - rD = tmp >> o; + res = ASL(rS1, left); + res = LSR(res, left); + rD = res >> o; } // 111100_DDDDDSSSSS_101000_wwwwwooooo @@ -1276,11 +1324,11 @@ OP_DEF(rot_1) OP_DEF(tb0) { if (IsUser() && VEC9 < 128) { - Exception(EXCEP_PRIV); + Exception(EXCPRI_PRIV); return; } - if ((rS1 & (1 << B5)) == 0) { - ExceptionCore(VEC9, ExceptionKind::TRAP); + if ((rS1 & (1U << B5)) == 0) { + TrapException(VEC9); } } @@ -1288,28 +1336,15 @@ OP_DEF(tb0) OP_DEF(tb1) { if (IsUser() && VEC9 < 128) { - Exception(EXCEP_PRIV); + Exception(EXCPRI_PRIV); return; } - if ((rS1 & (1 << B5)) != 0) { - ExceptionCore(VEC9, ExceptionKind::TRAP); + if ((rS1 & (1U << B5)) != 0) { + TrapException(VEC9); } else if (pseudo_stop_enable) { - - // OpenBSD の STOP エミュレーション。 - // OpenBSD 6.6 では - // L1: tb1 #1 r0, #0xff - // ld r13, r21, #0x168 - // bcnd.n eq0, r13, L1 - // or r2, r25, #0x5ea0 - // の命令列が使用されている。 - // tb1 は volatile 変数をアクセスするための同期命令として - // gcc により出力されている。スーパバイザが volatile を - // 観測するために同じアドレスの tb1 に直接ブランチで 3 回飛び込んで - // きたら、それは割り込みか別プロセッサか I/O による変更を待っている - // はずなので STOP と判断してみる。 - // o. 3回、という数字に確たる理由はない。 - // o. 念の為、16バイト(4命令)後方以内からのブランチに限定しておく。 - + // STOP#2。OpenBSD 6.6 〜 7.7 の STOP エミュレーション。 + // 4命令以内の後方から tb1 false, 0xff 命令への分岐が 3回連続すれば + // STOP 状態とする。 if (IsSuper() && VEC9 == 0xff) { const auto& e = brhist.entry[brhist.top]; if (e.count == 3 @@ -1325,18 +1360,18 @@ OP_DEF(tb1) OP_DEF(tcnd) { if (IsUser() && VEC9 < 128) { - Exception(EXCEP_PRIV); + Exception(EXCPRI_PRIV); return; } if ((acc_cnd(rS1) & M5) != 0) { - ExceptionCore(VEC9, ExceptionKind::TRAP); + TrapException(VEC9); } } // 111101_DDDDDSSSSS_000000_xU000sssss OP_DEF(xmem_bu) { - uint64 tmp; + busdata data; uint32 addr; uint32 usr; @@ -1348,22 +1383,22 @@ OP_DEF(xmem_bu) // 例外安全のために都度 S/U ビットを制御 ENTER_USR(); - tmp = cmmu[1]->xmem_1(addr, rD & 0xff); + data = cmmu[1]->xmem_1(addr, rD & 0xff); LEAVE_USR(); - if ((int64)tmp < 0) { - XmemDataException(addr, DM_BU | DM_LOCK | usr); + if (__predict_false(data.IsBusErr())) { + XmemDataException(addr, DM_BU | usr); return; } if (FLD_D == 0) return; - rD = tmp; + rD = data.Data(); } // 111101_DDDDDSSSSS_000001_xU000sssss OP_DEF(xmem_w) { - uint64 tmp; + busdata data; uint32 addr; uint32 usr; @@ -1375,23 +1410,23 @@ OP_DEF(xmem_w) // 例外安全のために都度 S/U ビットを制御 ENTER_USR(); - tmp = cmmu[1]->xmem_4(addr, rD); + data = cmmu[1]->xmem_4(addr, rD); LEAVE_USR(); - if ((int64)tmp < 0) { - XmemDataException(addr, DM_W | DM_LOCK | usr); + if (__predict_false(data.IsBusErr())) { + XmemDataException(addr, DM_W | usr); return; } if (FLD_D == 0) return; - rD = tmp; + rD = data.Data(); } // 111101_DDDDDSSSSS_000010_xU000sssss OP_DEF(ld_hu) { - uint64 data; + busdata data; uint32 addr; uint32 usr; @@ -1405,20 +1440,20 @@ OP_DEF(ld_hu) data = cmmu[1]->load_2(addr); LEAVE_USR(); - if ((int64)data < 0) { + if (__predict_false(data.IsBusErr())) { ReadDataException32(addr, DM_HU | usr); return; } if (FLD_D == 0) return; - rD = data; + rD = data.Data(); } // 111101_DDDDDSSSSS_000011_xU000sssss OP_DEF(ld_bu) { - uint64 data; uint32 addr; + busdata data; uint32 usr; LDST_USR_SCALE_ALIGN(1); @@ -1431,20 +1466,20 @@ OP_DEF(ld_bu) data = cmmu[1]->load_1(addr); LEAVE_USR(); - if ((int64)data < 0) { + if (__predict_false(data.IsBusErr())) { ReadDataException32(addr, DM_BU | usr); return; } if (FLD_D == 0) return; - rD = data; + rD = data.Data(); } // 111101_DDDDDSSSSS_000100_xU000sssss OP_DEF(ld_d) { - uint64 data1, data2; - uint32 addr; + uint32 addr; // ReadDataException64() に渡すのは常に先頭アドレス。 + busdata data; uint32 usr; LDST_USR_SCALE_ALIGN(8); @@ -1455,33 +1490,35 @@ OP_DEF(ld_d) // 例外安全のために都度 S/U ビットを制御 ENTER_USR(); - data1 = cmmu[1]->load_4(addr); + data = cmmu[1]->load_4(addr); LEAVE_USR(); - if ((int64)data1 < 0) { + if (__predict_false(data.IsBusErr())) { ReadDataException64(addr, DM_D1 | usr); return; } - addr += 4; + if (__predict_true(FLD_D != 0)) { + rD = data.Data(); + } ENTER_USR(); - data2 = cmmu[1]->load_4(addr); + data = cmmu[1]->load_4(addr + 4); LEAVE_USR(); - if ((int64)data2 < 0) { + if (__predict_false(data.IsBusErr())) { ReadDataException64(addr, DM_D2 | usr); return; } - rD = data1; - rD2 = data2; - reg.r[0] = 0; + if (__predict_true(FLD_D2 != 0)) { + rD2 = data.Data(); + } } // 111101_DDDDDSSSSS_000101_xU000sssss OP_DEF(ld_w) { - uint64 data; uint32 addr; + busdata data; uint32 usr; LDST_USR_SCALE_ALIGN(4); @@ -1506,8 +1543,8 @@ OP_DEF(ld_w) // 111101_DDDDDSSSSS_000110_xU000sssss OP_DEF(ld_h) { - uint64 data; uint32 addr; + busdata data; uint32 usr; LDST_USR_SCALE_ALIGN(2); @@ -1520,20 +1557,20 @@ OP_DEF(ld_h) data = cmmu[1]->load_2(addr); LEAVE_USR(); - if ((int64)data < 0) { + if (__predict_false(data.IsBusErr())) { ReadDataException32(addr, DM_H | usr); return; } if (FLD_D == 0) return; - rD = (uint32)(int32)(int16)data; + rD = (uint32)(int32)(int16)data.Data(); } // 111101_DDDDDSSSSS_000111_xU000sssss OP_DEF(ld_b) { - uint64 data; uint32 addr; + busdata data; uint32 usr; LDST_USR_SCALE_ALIGN(1); @@ -1546,20 +1583,20 @@ OP_DEF(ld_b) data = cmmu[1]->load_1(addr); LEAVE_USR(); - if ((int64)data < 0) { + if (__predict_false(data.IsBusErr())) { ReadDataException32(addr, DM_B | usr); return; } if (FLD_D == 0) return; - rD = (uint32)(int32)(int8)data; + rD = (uint32)(int32)(int8)data.Data(); } // 111101_DDDDDSSSSS_001000_xU000sssss OP_DEF(st_d) { - uint64 rv; - uint32 addr; + uint32 addr; // WriteDataException64() に渡すのは常に先頭アドレス。 + busdata r; uint32 usr; LDST_USR_SCALE_ALIGN(8); @@ -1569,21 +1606,19 @@ OP_DEF(st_d) lastaddr = addr; ENTER_USR(); - rv = cmmu[1]->store_4(addr, rD); + r = cmmu[1]->store_4(addr, rD); LEAVE_USR(); - if ((int64)rv < 0) { + if (__predict_false(r.IsBusErr())) { WriteDataException64(addr, DM_D1 | usr); return; } - addr += 4; - ENTER_USR(); - rv = cmmu[1]->store_4(addr, rD2); + r = cmmu[1]->store_4(addr + 4, rD2); LEAVE_USR(); - if ((int64)rv < 0) { + if (__predict_false(r.IsBusErr())) { WriteDataException64(addr, DM_D2 | usr); return; } @@ -1592,8 +1627,8 @@ OP_DEF(st_d) // 111101_DDDDDSSSSS_001001_xU000sssss OP_DEF(st_w) { - uint64 rv; uint32 addr; + busdata r; uint32 usr; LDST_USR_SCALE_ALIGN(4); @@ -1603,10 +1638,10 @@ OP_DEF(st_w) lastaddr = addr; ENTER_USR(); - rv = cmmu[1]->store_4(addr, rD); + r = cmmu[1]->store_4(addr, rD); LEAVE_USR(); - if ((int64)rv < 0) { + if (__predict_false(r.IsBusErr())) { WriteDataException32(addr, DM_W | usr); return; } @@ -1615,8 +1650,8 @@ OP_DEF(st_w) // 111101_DDDDDSSSSS_001010_xU000sssss OP_DEF(st_h) { - uint64 rv; uint32 addr; + busdata r; uint32 usr; LDST_USR_SCALE_ALIGN(2); @@ -1626,10 +1661,10 @@ OP_DEF(st_h) lastaddr = addr; ENTER_USR(); - rv = cmmu[1]->store_2(addr, rD & 0xffff); + r = cmmu[1]->store_2(addr, rD & 0xffff); LEAVE_USR(); - if ((int64)rv < 0) { + if (__predict_false(r.IsBusErr())) { WriteDataException32(addr, DM_H | usr); return; } @@ -1638,8 +1673,8 @@ OP_DEF(st_h) // 111101_DDDDDSSSSS_001011_xU000sssss OP_DEF(st_b) { - uint64 rv; uint32 addr; + busdata r; uint32 usr; LDST_USR_SCALE_ALIGN(1); @@ -1649,10 +1684,10 @@ OP_DEF(st_b) lastaddr = addr; ENTER_USR(); - rv = cmmu[1]->store_1(addr, rD & 0xff); + r = cmmu[1]->store_1(addr, rD & 0xff); LEAVE_USR(); - if ((int64)rv < 0) { + if (__predict_false(r.IsBusErr())) { WriteDataException32(addr, DM_B | usr); return; } @@ -1798,15 +1833,12 @@ OP_DEF(subu) // 111101_DDDDDSSSSS_011010_zz000sssss OP_DEF(divu) { - if (!IsFPUEnable()) { - fpu_unimpl(); - return; - } + CHECK_FPU; AddCycle(38); // Table.7-6 (ただしざっくり) if (rS2 == 0) { - Exception(EXCEP_INT_DIV); + InternalException(EXCVEC_INT_DIV); return; } if (FLD_D == 0) @@ -1817,10 +1849,7 @@ OP_DEF(divu) // 111101_DDDDDSSSSS_011011_zz000sssss OP_DEF(mul) { - if (!IsFPUEnable()) { - fpu_unimpl(); - return; - } + CHECK_FPU; AddCycle(4); // Table.7-6 (ただしざっくり) @@ -1837,7 +1866,7 @@ OP_DEF(add) res += GetCY(); } if (isovf_add(rS1, rS2, res)) { - Exception(EXCEP_INT_OVF); + InternalException(EXCVEC_INT_OVF); return; } @@ -1863,7 +1892,7 @@ OP_DEF(sub) res += 1; } if (isovf_sub(rS1, rS2, res)) { - Exception(EXCEP_INT_OVF); + InternalException(EXCVEC_INT_OVF); return; } @@ -1879,15 +1908,12 @@ OP_DEF(sub) // 111101_DDDDDSSSSS_011110_zz000sssss OP_DEF(div) { - if (!IsFPUEnable()) { - fpu_unimpl(); - return; - } + CHECK_FPU; AddCycle(38); // Table.7-6 (ただしざっくり) if ((int32)rS1 < 0 || (int32)rS2 <= 0) { - Exception(EXCEP_INT_DIV); + InternalException(EXCVEC_INT_DIV); return; } if (FLD_D == 0) @@ -1923,7 +1949,7 @@ OP_DEF(set_2) OP_DEF(ext_2) { int w, o, left; - uint32 tmp; + uint32 res; if (FLD_D == 0) return; @@ -1932,16 +1958,16 @@ OP_DEF(ext_2) w = 32; o = (rS2 ) & 0x1f; left = 32 - w - o; - tmp = ASL(rS1, left); - tmp = ASR(tmp, left); - rD = ASR(tmp, o); + res = ASL(rS1, left); + res = ASR(res, left); + rD = ASR(res, o); } // 111101_DDDDDSSSSS_100110_00000sssss OP_DEF(extu_2) { int w, o, left; - uint32 tmp; + uint32 res; if (FLD_D == 0) return; @@ -1950,9 +1976,9 @@ OP_DEF(extu_2) w = 32; o = (rS2 ) & 0x1f; left = 32 - w - o; - tmp = ASL(rS1, left); - tmp = LSR(tmp, left); - rD = tmp >> o; + res = ASL(rS1, left); + res = LSR(res, left); + rD = res >> o; } // 111101_DDDDDSSSSS_101000_00000sssss @@ -2025,7 +2051,6 @@ OP_DEF(ff1) } rD = i; } - } // 111101_DDDDDzzzzz_111011_00000sssss @@ -2052,23 +2077,28 @@ OP_DEF(ff0) // 111101_zzzzzzzzzz_111111_0000000000 OP_DEF(rte) { + if (__predict_false(IsUser())) { + Exception(EXCPRI_PRIV); + return; + } + AddCycle(2); // Table.7-5 SetPSR(reg.epsr); - // LUNA-88K の PROM 1.20 は sxip, snip, sfip を制御してくる。 - // PROM 1.20 の DAE ハンドラは、 - // sxip = INVALID - // snip = 0 (INVALID) - // sfip = original sxip - // で rte してくる。 - // CPU は sxip は評価しない。snip は 0 なので、無効として nop 扱い。 - // (実際はクロックが消費されるかも?) - // sfip が VALID なので、そこから実行を再開する。 + // 本当は有効な SxIP から順次パイプラインを埋めるだけで、 + // こんな条件分けになるわけではないのだが、分岐履歴とかのため。 + + if (__predict_false((reg.sfip & SIP_V) == 0)) { + // どうなる? + putlog(0, "rte: SFIP.V==0 (NOT IMPLEMENTED)"); + } + if ((reg.snip & SIP_V)) { DoBranch(reg.snip & SIP_MASK, false); reg.fip = reg.sfip & SIP_MASK; } else { + // PROM 1.20 の DAE ハンドラが snip の VALID を落として rte を呼ぶ。 DoBranch(reg.sfip & SIP_MASK, false); } } @@ -2077,7 +2107,7 @@ OP_DEF(rte) OP_DEF(tbnd_1) { if (rS1 > rS2) { - Exception(EXCEP_BOUNDS); + InternalException(EXCVEC_BOUNDS); } } @@ -2085,7 +2115,7 @@ OP_DEF(tbnd_1) OP_DEF(tbnd_2) { if (rS1 > IMM16) { - Exception(EXCEP_BOUNDS); + InternalException(EXCVEC_BOUNDS); } }