diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp index 6b394976..b0e30c11 100644 --- a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp @@ -225,6 +225,10 @@ public: // Set syscall callback void set_syscall_callback(syscall_fn fn) { execute_do_syscall = fn; } + // Caches invalidation + void invalidate_cache(); + void invalidate_cache_range(uintptr start, uintptr end); + protected: // Init decoder with one instruction info @@ -254,9 +258,6 @@ private: block_info::decode_info * decode_cache_p; block_info::decode_info * decode_cache_end_p; - void invalidate_cache(); - void invalidate_cache_range(uintptr start, uintptr end); - // Instruction handlers void execute_nop(uint32 opcode); void execute_illegal(uint32 opcode); diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-decode.cpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-decode.cpp index 28c845c8..d42d7047 100644 --- a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-decode.cpp +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-decode.cpp @@ -732,7 +732,7 @@ const powerpc_cpu::instr_info_t powerpc_cpu::powerpc_ii_table[] = { SC_form, 17, 0, CFLOW_NORMAL }, { "slw", - EXECUTE_SHIFT(shll, RA, RS, RB, andi<0x1f>, CA_BIT_0, RC_BIT_G), + EXECUTE_SHIFT(shll, RA, RS, RB, andi<0x3f>, CA_BIT_0, RC_BIT_G), NULL, X_form, 31, 24, CFLOW_NORMAL }, @@ -742,12 +742,12 @@ const powerpc_cpu::instr_info_t powerpc_cpu::powerpc_ii_table[] = { X_form, 31, 792, CFLOW_NORMAL }, { "srawi", - EXECUTE_SHIFT(shra, RA, RS, SH, nop, CA_BIT_1, RC_BIT_G), + EXECUTE_SHIFT(shra, RA, RS, SH, andi<0x1f>, CA_BIT_1, RC_BIT_G), NULL, X_form, 31, 824, CFLOW_NORMAL }, { "srw", - EXECUTE_SHIFT(shrl, RA, RS, RB, andi<0x1f>, CA_BIT_0, RC_BIT_G), + EXECUTE_SHIFT(shrl, RA, RS, RB, andi<0x3f>, CA_BIT_0, RC_BIT_G), NULL, X_form, 31, 536, CFLOW_NORMAL }, diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-execute.cpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-execute.cpp index 8c40a5e6..22ceb087 100644 --- a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-execute.cpp +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-execute.cpp @@ -247,12 +247,29 @@ void powerpc_cpu::execute_rlwimi(uint32 opcode) * Rc Predicate to record CR0 **/ +template< class OP > +struct invalid_shift { + static inline uint32 value(uint32) { + return 0; + } +}; + +template<> +struct invalid_shift { + static inline uint32 value(uint32 r) { + return 0 - (r >> 31); + } +}; + template< class OP, class RD, class RA, class SH, class SO, class CA, class Rc > void powerpc_cpu::execute_shift(uint32 opcode) { const uint32 n = SO::apply(SH::get(this, opcode)); const uint32 r = RA::get(this, opcode); - uint32 d = OP::apply(r, n); + + // Shift operation is valid only if rB[26] = 0 + // TODO: optimize srw case where shift operation would zero result + uint32 d = (n & 0x20) ? invalid_shift::value(r) : OP::apply(r, n); // Set XER (CA) if instruction is algebraic variant if (CA::test(opcode)) { @@ -415,9 +432,11 @@ void powerpc_cpu::execute_multiply(uint32 opcode) const uint32 b = operand_RB::get(this, opcode); uint64 d = SB ? (int64)(int32)a * (int64)(int32)b : (uint64)a * (uint64)b; - // Set XER (OV, SO) if instruction has OE set - if (OE::test(opcode)) - xer().set_ov((d >> 32) != 0); + // Overflow if the product cannot be represented in 32 bits + if (OE::test(opcode)) { + const uint32 upper = d >> 32; + xer().set_ov(upper != 0 && upper != 0xffffffff); + } // Only keep high word if multiply high instruction if (HI) diff --git a/SheepShaver/src/kpx_cpu/src/test/test-powerpc.cpp b/SheepShaver/src/kpx_cpu/src/test/test-powerpc.cpp index 5283fd80..90393b05 100644 --- a/SheepShaver/src/kpx_cpu/src/test/test-powerpc.cpp +++ b/SheepShaver/src/kpx_cpu/src/test/test-powerpc.cpp @@ -116,6 +116,7 @@ void powerpc_test_cpu::execute(uint32 opcode) uint32 code[] = { opcode, 0x18000000 }; try { + invalidate_cache(); pc() = (uintptr)code; powerpc_cpu::execute(); } @@ -145,6 +146,7 @@ bool powerpc_test_cpu::test(void) uint32 init_xer = native_get_xer() & ~(XER_OV_field::mask() | XER_CA_field::mask()); // Emulated registers IDs + const int R_ = -1; const int RD = 10; const int RA = 11; const int RB = 12; @@ -164,53 +166,76 @@ bool powerpc_test_cpu::test(void) #define TEST_ASM_RRR_(OP,RD,RA,RB,RC) asm volatile (OP " %0,%1,%2" : "=r" (RD) : "r" (RA), "r" (RB) : "cc") #define TEST_ASM_RRIT(OP,RD,RA,SH,MK) asm volatile (OP " %0,%1,%2,%3" : "=r" (RD) : "r" (RA), "i" (SH), "T" (MK)) -#define TEST_INSTRUCTION(FORMAT, NATIVE_OP, EMUL_OP) do { \ - printf("Testing " NATIVE_OP "\n"); \ - const uint32 opcode = EMUL_OP; \ - for (uint32 i = 0; i < 8; i++) { \ - const uint32 ra = i << 29; \ - for (uint32 j = 0; j < 8; j++) { \ - const uint32 rb = j << 29; \ - \ - uint32 native_rd, native_cr, native_xer; \ - native_set_xer(init_xer); \ - native_set_cr(init_cr); \ - TEST_ASM_##FORMAT(NATIVE_OP, native_rd, ra, rb, 0); \ - native_xer = native_get_xer(); \ - native_cr = native_get_cr(); \ - \ - gpr(RA) = ra; \ - gpr(RB) = rb; \ - emul_set_xer(init_xer); \ - emul_set_cr(init_cr); \ - execute(opcode); \ - uint32 emul_rd, emul_cr, emul_xer; \ - emul_xer = emul_get_xer(); \ - emul_cr = emul_get_cr(); \ - emul_rd = gpr(RD); \ - \ - bool ok = (native_rd == emul_rd) \ - && (native_xer == emul_xer) \ - && (native_cr == emul_cr); \ - \ - if (!ok) { \ - printf("FAIL: " NATIVE_OP " [%08x]\n", opcode); \ - errors++; \ - } \ - \ - if (!ok || verbose) { \ - printf(" %08x, %08x => %08x [", ra, rb, native_rd); \ - print_flags(native_cr, native_xer); \ - printf("]\n"); \ - printf(" %08x, %08x => %08x [", ra, rb, emul_rd); \ - print_flags(emul_cr, emul_xer); \ - printf("]\n"); \ - } \ - } \ - } \ +#define TEST_ASM(FORMAT, OP, RD, CR, XER, A0, A1, A2) do { \ + native_set_xer(init_xer); \ + native_set_cr(init_cr); \ + TEST_ASM_##FORMAT(OP, RD, A0, A1, A2); \ + XER = native_get_xer(); \ + CR = native_get_cr(); \ } while (0) +#define TEST_EMU_R0(OP,PRE,POST) do { PRE; execute(OP); POST; } while (0) +#define TEST_EMU_RD(OP,RD,VD,PRE) TEST_EMU_R0(OP,PRE,VD=gpr(RD)) +#define TEST_EMU_____(OP,RD,VD,R0,A0,R1,A1,R2,A2) TEST_EMU_R0(/**/,/**/) +#define TEST_EMU_R___(OP,RD,VD,R0,A0,R1,A1,R2,A2) TEST_EMU_RD(OP,RD,VD,gpr(R0)=A0) +#define TEST_EMU_RR__(OP,RD,VD,R0,A0,R1,A1,R2,A2) TEST_EMU_RD(OP,RD,VD,gpr(R0)=A0;gpr(R1)=A1) +#define TEST_EMU_RRR_(OP,RD,VD,R0,A0,R1,A1,R2,A2) TEST_EMU_RD(OP,RD,VD,gpr(R0)=A0;gpr(R1)=A1;gpr(R2)=A2) + +#define TEST_EMU(FORMAT, OP, RD, VD, CR, XER, R0, A0, R1, A1, R2, A2) do { \ + emul_set_xer(init_xer); \ + emul_set_cr(init_cr); \ + TEST_EMU_##FORMAT(OP, RD, VD, R0, A0, R1, A1, R2, A2); \ + XER = emul_get_xer(); \ + CR = emul_get_cr(); \ + } while (0) + +#define TEST_ONE(FORMAT, NATIVE_OP, EMUL_OP, RD, R0, A0, R1, A1, R2, A2) do { \ + uint32 native_rd = 0, native_xer, native_cr; \ + TEST_ASM(FORMAT, NATIVE_OP, native_rd, native_cr, native_xer, A0, A1, A2); \ + uint32 emul_rd = 0, emul_xer, emul_cr; \ + TEST_EMU(FORMAT, EMUL_OP, RD, emul_rd, emul_cr, emul_xer, R0, A0, R1, A1, R2, A2); \ + \ + bool ok = native_rd == emul_rd \ + && native_xer == emul_xer \ + && native_cr == emul_cr; \ + \ + if (!ok) { \ + printf("FAIL: " NATIVE_OP " [%08x]\n", opcode); \ + errors++; \ + } \ + \ + if (!ok || verbose) { \ + printf(" %08x, %08x => %08x [", ra, rb, native_rd); \ + print_flags(native_cr, native_xer); \ + printf("]\n"); \ + printf(" %08x, %08x => %08x [", ra, rb, emul_rd); \ + print_flags(emul_cr, emul_xer); \ + printf("]\n"); \ + } \ + } while (0) + +#define TEST_INSTRUCTION(FORMAT, NATIVE_OP, EMUL_OP) do { \ + printf("Testing " NATIVE_OP "\n"); \ + const uint32 opcode = EMUL_OP; \ + for (uint32 i = 0; i < 8; i++) { \ + const uint32 ra = i << 29; \ + for (uint32 j = 0; j < 8; j++) { \ + const uint32 rb = j << 29; \ + TEST_ONE(FORMAT, NATIVE_OP, EMUL_OP, RD, RA, ra, RB, rb, R_, 0); \ + } \ + } \ + for (int32 i = -2; i < 2; i++) { \ + const uint32 ra = i; \ + for (int32 j = -2; j < 2; j++) { \ + const uint32 rb = j; \ + TEST_ONE(FORMAT, NATIVE_OP, EMUL_OP, RD, RA, ra, RB, rb, R_, 0); \ + } \ + } \ + } while (0) + + TEST_INSTRUCTION(RRR_,"add", _XO(31,RD,RA,RB,0,266,0)); TEST_INSTRUCTION(RRR_,"add.", _XO(31,RD,RA,RB,0,266,1)); + TEST_INSTRUCTION(RRR_,"addo", _XO(31,RD,RA,RB,1,266,0)); TEST_INSTRUCTION(RRR_,"addo." , _XO(31,RD,RA,RB,1,266,1)); TEST_INSTRUCTION(RRR_,"addc.", _XO(31,RD,RA,RB,0, 10,1)); TEST_INSTRUCTION(RRR_,"addco.", _XO(31,RD,RA,RB,1, 10,1));