/* DingusPPC - The Experimental PowerPC Macintosh emulator Copyright (C) 2018-20 divingkatae and maximum (theweirdo) spatium (Contact divingkatae#1017 or powermax#2286 on Discord for more info) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ // General opcodes for the processor - ppcopcodes.cpp #include "ppcemu.h" #include "ppcmmu.h" #include #include #include #include #include #include #include #include #include uint32_t crf_d; uint32_t crf_s; uint32_t reg_s; uint32_t reg_d; uint32_t reg_a; uint32_t reg_b; uint32_t reg_c; // used only for floating point multiplication operations uint32_t xercon; uint32_t cmp_c; uint32_t crm; uint32_t uimm; uint32_t grab_sr; uint32_t grab_inb; // This is for grabbing the number of immediate bytes for loading and storing uint32_t ppc_to; int32_t simm; int32_t adr_li; // Used for GP calcs uint32_t ppc_result_a = 0; uint32_t ppc_result_b = 0; uint32_t ppc_result_c = 0; uint32_t ppc_result_d = 0; uint32_t strwrd_replace_value; /** Extract the registers desired and the values of the registers This also takes the MSR into account, mainly to determine what endian the numbers are to be stored in. **/ // Storage and register retrieval functions for the integer functions. void ppc_store_result_regd() { ppc_state.gpr[reg_d] = ppc_result_d; } void ppc_store_result_rega() { ppc_state.gpr[reg_a] = ppc_result_a; } void ppc_grab_regsdasimm() { reg_d = (ppc_cur_instruction >> 21) & 31; reg_a = (ppc_cur_instruction >> 16) & 31; simm = (int32_t)((int16_t)((ppc_cur_instruction)&65535)); ppc_result_a = ppc_state.gpr[reg_a]; } void ppc_grab_regsdauimm() { reg_d = (ppc_cur_instruction >> 21) & 31; reg_a = (ppc_cur_instruction >> 16) & 31; uimm = (uint32_t)((uint16_t)((ppc_cur_instruction)&65535)); ppc_result_a = ppc_state.gpr[reg_a]; } void ppc_grab_regsasimm() { reg_a = (ppc_cur_instruction >> 16) & 31; simm = (int32_t)((int16_t)(ppc_cur_instruction & 65535)); ppc_result_a = ppc_state.gpr[reg_a]; } void ppc_grab_regssauimm() { reg_s = (ppc_cur_instruction >> 21) & 31; reg_a = (ppc_cur_instruction >> 16) & 31; uimm = (uint32_t)((uint16_t)((ppc_cur_instruction)&65535)); ppc_result_d = ppc_state.gpr[reg_s]; ppc_result_a = ppc_state.gpr[reg_a]; } void ppc_grab_regsdab() { reg_d = (ppc_cur_instruction >> 21) & 31; reg_a = (ppc_cur_instruction >> 16) & 31; reg_b = (ppc_cur_instruction >> 11) & 31; ppc_result_a = ppc_state.gpr[reg_a]; ppc_result_b = ppc_state.gpr[reg_b]; } void ppc_grab_regssab() { reg_s = (ppc_cur_instruction >> 21) & 31; reg_a = (ppc_cur_instruction >> 16) & 31; reg_b = (ppc_cur_instruction >> 11) & 31; ppc_result_d = ppc_state.gpr[reg_s]; ppc_result_a = ppc_state.gpr[reg_a]; ppc_result_b = ppc_state.gpr[reg_b]; } void ppc_grab_regssa() { reg_s = (ppc_cur_instruction >> 21) & 31; reg_a = (ppc_cur_instruction >> 16) & 31; ppc_result_d = ppc_state.gpr[reg_s]; ppc_result_a = ppc_state.gpr[reg_a]; } void ppc_grab_regssb() { reg_s = (ppc_cur_instruction >> 21) & 31; reg_b = (ppc_cur_instruction >> 11) & 31; ppc_result_d = ppc_state.gpr[reg_s]; ppc_result_b = ppc_state.gpr[reg_b]; } void ppc_grab_regsda() { reg_d = (ppc_cur_instruction >> 21) & 31; reg_a = (ppc_cur_instruction >> 16) & 31; ppc_result_a = ppc_state.gpr[reg_a]; } void ppc_grab_regsdb() { reg_d = (ppc_cur_instruction >> 21) & 31; reg_b = (ppc_cur_instruction >> 11) & 31; ppc_result_b = ppc_state.gpr[reg_b]; } // Affects CR Field 0 - For integer operations void ppc_changecrf0(uint32_t set_result) { ppc_state.cr &= 0x0FFFFFFFUL; if (set_result == 0) { ppc_state.cr |= 0x20000000UL; } else { if (set_result & 0x80000000) { ppc_state.cr |= 0x80000000UL; } else { ppc_state.cr |= 0x40000000UL; } } /* copy XER[SO] into CR0[SO]. */ ppc_state.cr |= (ppc_state.spr[SPR::XER] >> 3) & 0x10000000UL; } // Affects the XER register's Carry Bit inline void ppc_carry(uint32_t a, uint32_t b) { if (b < a) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } } inline void ppc_carry_sub(uint32_t a, uint32_t b) { if (b >= a) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } } // Affects the XER register's SO and OV Bits inline void ppc_setsoov(uint32_t a, uint32_t b, uint32_t d) { if ((a ^ b) & (a ^ d) & 0x80000000UL) { ppc_state.spr[SPR::XER] |= 0xC0000000UL; } else { ppc_state.spr[SPR::XER] &= 0xBFFFFFFFUL; } } /** The core functionality of this PPC emulation is within all of these void functions. This is where the opcode tables in the ppcemumain.h come into play - reducing the number of comparisons needed. This means loads of functions, but less CPU cycles needed to determine the function (theoretically). **/ void ppc_addi() { ppc_grab_regsdasimm(); ppc_result_d = (reg_a == 0) ? simm : (ppc_result_a + simm); ppc_store_result_regd(); } void ppc_addic() { ppc_grab_regsdasimm(); ppc_result_d = (ppc_result_a + simm); ppc_carry(ppc_result_a, ppc_result_d); ppc_store_result_regd(); } void ppc_addicdot() { ppc_grab_regsdasimm(); ppc_result_d = (ppc_result_a + simm); ppc_changecrf0(ppc_result_d); ppc_carry(ppc_result_a, ppc_result_d); ppc_store_result_regd(); } void ppc_addis() { ppc_grab_regsdasimm(); ppc_result_d = (reg_a == 0) ? (simm << 16) : (ppc_result_a + (simm << 16)); ppc_store_result_regd(); } void ppc_add() { ppc_grab_regsdab(); ppc_result_d = ppc_result_a + ppc_result_b; ppc_store_result_regd(); } void ppc_adddot() { ppc_grab_regsdab(); ppc_result_d = ppc_result_a + ppc_result_b; ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } // addo + addodot void ppc_addo() { ppc_grab_regsdab(); ppc_result_d = ppc_result_a + ppc_result_b; ppc_setsoov(ppc_result_a, ~ppc_result_b, ppc_result_d); ppc_store_result_regd(); } void ppc_addodot() { ppc_grab_regsdab(); ppc_result_d = ppc_result_a + ppc_result_b; ppc_setsoov(ppc_result_a, ~ppc_result_b, ppc_result_d); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_addc() { ppc_grab_regsdab(); ppc_result_d = ppc_result_a + ppc_result_b; ppc_carry(ppc_result_a, ppc_result_d); ppc_store_result_regd(); } void ppc_addcdot() { ppc_grab_regsdab(); ppc_result_d = ppc_result_a + ppc_result_b; ppc_carry(ppc_result_a, ppc_result_d); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_addco() { ppc_grab_regsdab(); ppc_result_d = ppc_result_a + ppc_result_b; ppc_setsoov(ppc_result_a, ~ppc_result_b, ppc_result_d); ppc_carry(ppc_result_a, ppc_result_d); ppc_store_result_regd(); } void ppc_addcodot() { ppc_grab_regsdab(); ppc_result_d = ppc_result_a + ppc_result_b; ppc_setsoov(ppc_result_a, ~ppc_result_b, ppc_result_d); ppc_carry(ppc_result_a, ppc_result_d); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_adde() { ppc_grab_regsdab(); uint32_t xer_ca = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ppc_result_a + ppc_result_b + xer_ca; if ((ppc_result_d < ppc_result_a) || (xer_ca && (ppc_result_d == ppc_result_a))) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_store_result_regd(); } void ppc_addedot() { ppc_grab_regsdab(); uint32_t xer_ca = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ppc_result_a + ppc_result_b + xer_ca; if ((ppc_result_d < ppc_result_a) || (xer_ca && (ppc_result_d == ppc_result_a))) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_addeo() { ppc_grab_regsdab(); uint32_t xer_ca = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ppc_result_a + ppc_result_b + xer_ca; if ((ppc_result_d < ppc_result_a) || (xer_ca && (ppc_result_d == ppc_result_a))) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_setsoov(ppc_result_a, ~ppc_result_b, ppc_result_d); ppc_store_result_regd(); } void ppc_addeodot() { ppc_grab_regsdab(); uint32_t xer_ca = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ppc_result_a + ppc_result_b + xer_ca; if ((ppc_result_d < ppc_result_a) || (xer_ca && (ppc_result_d == ppc_result_a))) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_setsoov(ppc_result_a, ~ppc_result_b, ppc_result_d); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_addme() { ppc_grab_regsda(); uint32_t xer_ca = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ppc_result_a + xer_ca - 1; if (((xer_ca - 1) < 0xFFFFFFFFUL) | (ppc_result_d < ppc_result_a)) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_store_result_regd(); } void ppc_addmedot() { ppc_grab_regsda(); uint32_t xer_ca = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ppc_result_a + xer_ca - 1; if (((xer_ca - 1) < 0xFFFFFFFFUL) | (ppc_result_d < ppc_result_a)) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_addmeo() { ppc_grab_regsda(); uint32_t xer_ca = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ppc_result_a + xer_ca - 1; ppc_setsoov(ppc_result_a, 0, ppc_result_d); if (((xer_ca - 1) < 0xFFFFFFFFUL) | (ppc_result_d < ppc_result_a)) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_store_result_regd(); } void ppc_addmeodot() { ppc_grab_regsda(); uint32_t xer_ca = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ppc_result_a + xer_ca - 1; ppc_setsoov(ppc_result_a, 0, ppc_result_d); ppc_changecrf0(ppc_result_d); if (((xer_ca - 1) < 0xFFFFFFFFUL) | (ppc_result_d < ppc_result_a)) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_store_result_regd(); } void ppc_addze() { ppc_grab_regsda(); uint32_t grab_xer = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ppc_result_a + grab_xer; if (ppc_result_d < ppc_result_a) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_store_result_regd(); } void ppc_addzedot() { ppc_grab_regsda(); uint32_t grab_xer = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ppc_result_a + grab_xer; if (ppc_result_d < ppc_result_a) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_addzeo() { ppc_grab_regsda(); uint32_t grab_xer = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ppc_result_a + grab_xer; ppc_setsoov(ppc_result_a, 0xFFFFFFFFUL, ppc_result_d); if (ppc_result_d < ppc_result_a) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_store_result_regd(); } void ppc_addzeodot() { ppc_grab_regsda(); uint32_t grab_xer = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ppc_result_a + grab_xer; ppc_setsoov(ppc_result_a, 0xFFFFFFFFUL, ppc_result_d); if (ppc_result_d < ppc_result_a) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_subf() { ppc_grab_regsdab(); ppc_result_d = ppc_result_b - ppc_result_a; ppc_store_result_regd(); } void ppc_subfdot() { ppc_grab_regsdab(); ppc_result_d = ppc_result_b - ppc_result_a; ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_subfo() { ppc_grab_regsdab(); ppc_result_d = ppc_result_b - ppc_result_a; ppc_setsoov(ppc_result_b, ppc_result_a, ppc_result_d); ppc_store_result_regd(); } void ppc_subfodot() { ppc_grab_regsdab(); ppc_result_d = ppc_result_b - ppc_result_a; ppc_setsoov(ppc_result_b, ppc_result_a, ppc_result_d); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_subfc() { ppc_grab_regsdab(); ppc_result_d = ppc_result_b - ppc_result_a; ppc_carry_sub(ppc_result_a, ppc_result_b); ppc_store_result_regd(); } void ppc_subfcdot() { ppc_grab_regsdab(); ppc_result_d = ppc_result_b - ppc_result_a; ppc_carry_sub(ppc_result_a, ppc_result_b); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_subfco() { ppc_grab_regsdab(); ppc_result_d = ppc_result_b - ppc_result_a; ppc_setsoov(ppc_result_b, ppc_result_a, ppc_result_d); ppc_carry_sub(ppc_result_a, ppc_result_b); ppc_store_result_regd(); } void ppc_subfcodot() { ppc_grab_regsdab(); ppc_result_d = ppc_result_b - ppc_result_a; ppc_setsoov(ppc_result_b, ppc_result_a, ppc_result_d); ppc_carry_sub(ppc_result_a, ppc_result_b); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_subfic() { ppc_grab_regsdasimm(); ppc_result_d = simm - ppc_result_a; ppc_carry(~ppc_result_a, ppc_result_d); ppc_store_result_regd(); } void ppc_subfe() { ppc_grab_regsdab(); uint32_t grab_xer = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ~ppc_result_a + ppc_result_b + grab_xer; ppc_carry(~ppc_result_a, ppc_result_d); ppc_store_result_regd(); } void ppc_subfedot() { ppc_grab_regsdab(); uint32_t grab_xer = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ~ppc_result_a + ppc_result_b + grab_xer; ppc_carry(~ppc_result_a, ppc_result_d); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_subfeo() { ppc_grab_regsdab(); uint32_t grab_xer = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ~ppc_result_a + ppc_result_b + grab_xer; ppc_setsoov(ppc_result_b, ppc_result_a, ppc_result_d); ppc_carry(~ppc_result_a, ppc_result_d); ppc_store_result_regd(); } void ppc_subfeodot() { ppc_grab_regsdab(); uint32_t grab_xer = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ~ppc_result_a + ppc_result_b + grab_xer; ppc_setsoov(ppc_result_b, ppc_result_a, ppc_result_d); ppc_carry(~ppc_result_a, ppc_result_d); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_subfme() { ppc_grab_regsda(); uint32_t grab_xer = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ~ppc_result_a + grab_xer - 1; ppc_carry(~ppc_result_a, ppc_result_d); ppc_store_result_regd(); } void ppc_subfmedot() { ppc_grab_regsda(); uint32_t grab_xer = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ~ppc_result_a + grab_xer - 1; ppc_carry(~ppc_result_a, ppc_result_d); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_subfmeo() { ppc_grab_regsda(); uint32_t grab_xer = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ~ppc_result_a + grab_xer - 1; ppc_setsoov(ppc_result_b, ppc_result_a, ppc_result_d); ppc_carry(~ppc_result_a, ppc_result_d); ppc_store_result_regd(); } void ppc_subfmeodot() { ppc_grab_regsda(); uint32_t grab_xer = !!(ppc_state.spr[SPR::XER] & 0x20000000); ppc_result_d = ~ppc_result_a + grab_xer - 1; ppc_setsoov(ppc_result_b, ppc_result_a, ppc_result_d); ppc_carry(~ppc_result_a, ppc_result_d); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_subfze() { ppc_grab_regsda(); ppc_result_d = ~ppc_result_a + (ppc_state.spr[SPR::XER] & 0x20000000); ppc_carry(~ppc_result_a, ppc_result_d); ppc_store_result_regd(); } void ppc_subfzedot() { ppc_grab_regsda(); ppc_result_d = ~ppc_result_a + (ppc_state.spr[SPR::XER] & 0x20000000); ppc_carry(~ppc_result_a, ppc_result_d); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_subfzeo() { ppc_grab_regsda(); ppc_result_d = ~ppc_result_a + (ppc_state.spr[SPR::XER] & 0x20000000); ppc_setsoov(ppc_result_b, ppc_result_a, ppc_result_d); ppc_carry(~ppc_result_a, ppc_result_d); ppc_store_result_regd(); } void ppc_subfzeodot() { ppc_grab_regsda(); ppc_result_d = ~ppc_result_a + (ppc_state.spr[SPR::XER] & 0x20000000); ppc_setsoov(ppc_result_b, ppc_result_a, ppc_result_d); ppc_carry(~ppc_result_a, ppc_result_d); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_and() { ppc_grab_regssab(); ppc_result_a = ppc_result_d & ppc_result_b; ppc_store_result_rega(); } void ppc_anddot() { ppc_grab_regssab(); ppc_result_a = ppc_result_d & ppc_result_b; ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_andc() { ppc_grab_regssab(); ppc_result_a = ppc_result_d & ~(ppc_result_b); ppc_store_result_rega(); } void ppc_andcdot() { ppc_grab_regssab(); ppc_result_a = ppc_result_d & ~(ppc_result_b); ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_andidot() { ppc_grab_regssauimm(); ppc_result_a = ppc_result_d & uimm; ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_andisdot() { ppc_grab_regssauimm(); ppc_result_a = ppc_result_d & (uimm << 16); ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_nand() { ppc_grab_regssab(); ppc_result_a = ~(ppc_result_d & ppc_result_b); ppc_store_result_rega(); } void ppc_nanddot() { ppc_grab_regssab(); ppc_result_a = ~(ppc_result_d & ppc_result_b); ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_or() { ppc_grab_regssab(); ppc_result_a = ppc_result_d | ppc_result_b; ppc_store_result_rega(); } void ppc_ordot() { ppc_grab_regssab(); ppc_result_a = ppc_result_d | ppc_result_b; ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_orc() { ppc_grab_regssab(); ppc_result_a = ppc_result_d | ~(ppc_result_b); ppc_store_result_rega(); } void ppc_orcdot() { ppc_grab_regssab(); ppc_result_a = ppc_result_d | ~(ppc_result_b); ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_ori() { ppc_grab_regssauimm(); ppc_result_a = ppc_result_d | uimm; ppc_store_result_rega(); } void ppc_oris() { ppc_grab_regssauimm(); ppc_result_a = (uimm << 16) | ppc_result_d; ppc_store_result_rega(); } void ppc_eqv() { ppc_grab_regssab(); ppc_result_a = ~(ppc_result_d ^ ppc_result_b); ppc_store_result_rega(); } void ppc_eqvdot() { ppc_grab_regssab(); ppc_result_a = ~(ppc_result_d ^ ppc_result_b); ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_nor() { ppc_grab_regssab(); ppc_result_a = ~(ppc_result_d | ppc_result_b); ppc_store_result_rega(); } void ppc_nordot() { ppc_grab_regssab(); ppc_result_a = ~(ppc_result_d | ppc_result_b); ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_xor() { ppc_grab_regssab(); ppc_result_a = ppc_result_d ^ ppc_result_b; ppc_store_result_rega(); } void ppc_xordot() { ppc_grab_regssab(); ppc_result_a = ppc_result_d ^ ppc_result_b; ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_xori() { ppc_grab_regssauimm(); ppc_result_a = ppc_result_d ^ uimm; ppc_store_result_rega(); } void ppc_xoris() { ppc_grab_regssauimm(); ppc_result_a = ppc_result_d ^ (uimm << 16); ppc_store_result_rega(); } void ppc_neg() { ppc_grab_regsda(); ppc_result_d = ~(ppc_result_a) + 1; ppc_store_result_regd(); } void ppc_negdot() { ppc_grab_regsda(); ppc_result_d = ~(ppc_result_a) + 1; ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_nego() { ppc_grab_regsda(); ppc_result_d = ~(ppc_result_a) + 1; if (ppc_result_a == 0x80000000) ppc_state.spr[SPR::XER] |= 0xc0000000; else ppc_state.spr[SPR::XER] &= 0xBFFFFFFF; ppc_store_result_regd(); } void ppc_negodot() { ppc_grab_regsda(); ppc_result_d = ~(ppc_result_a) + 1; if (ppc_result_a == 0x80000000) ppc_state.spr[SPR::XER] |= 0xc0000000; else ppc_state.spr[SPR::XER] &= 0xBFFFFFFF; ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_cntlzw() { ppc_grab_regssa(); uint32_t lead = 0; uint32_t bit_check = ppc_result_d; #ifdef USE_GCC_BUILTINS lead = __builtin_clz(bit_check); #elif defined USE_VS_BUILTINS lead = __lzcnt(bit_check); #else for (uint32_t mask = 0x80000000UL; mask; lead++, mask >>= 1) { if (bit_check & mask) break; } #endif ppc_result_a = lead; ppc_store_result_rega(); } void ppc_cntlzwdot() { ppc_grab_regssa(); uint32_t lead = 0; uint32_t bit_check = ppc_result_d; #ifdef USE_GCC_BUILTINS lead = __builtin_clz(bit_check); #elif defined USE_VS_BUILTINS lead = __lzcnt(bit_check); #else for (uint32_t mask = 0x80000000UL; mask; lead++, mask >>= 1) { if (bit_check & mask) break; } #endif ppc_result_a = lead; ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_mulhwu() { ppc_grab_regsdab(); uint64_t product = (uint64_t)ppc_result_a * (uint64_t)ppc_result_b; ppc_result_d = (uint32_t)(product >> 32); ppc_store_result_regd(); } void ppc_mulhwudot() { ppc_grab_regsdab(); uint64_t product = (uint64_t)ppc_result_a * (uint64_t)ppc_result_b; ppc_result_d = (uint32_t)(product >> 32); ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_mulhw() { ppc_grab_regsdab(); int64_t product = (int64_t)(int32_t)ppc_result_a * (int64_t)(int32_t)ppc_result_b; ppc_result_d = product >> 32; ppc_store_result_regd(); } void ppc_mulhwdot() { ppc_grab_regsdab(); int64_t product = (int64_t)(int32_t)ppc_result_a * (int64_t)(int32_t)ppc_result_b; ppc_result_d = product >> 32; ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_mullw() { ppc_grab_regsdab(); int64_t product = (int64_t)(int32_t)ppc_result_a * (int64_t)(int32_t)ppc_result_b; ppc_result_d = (uint32_t)product; ppc_store_result_regd(); } void ppc_mullwdot() { ppc_grab_regsdab(); int64_t product = (int64_t)(int32_t)ppc_result_a * (int64_t)(int32_t)ppc_result_b; ppc_result_d = (uint32_t)product; ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_mullwo() { ppc_grab_regsdab(); int64_t product = (int64_t)(int32_t)ppc_result_a * (int64_t)(int32_t)ppc_result_b; if (product != (int64_t)(int32_t)product) { ppc_state.spr[SPR::XER] |= 0xC0000000; } else { ppc_state.spr[SPR::XER] &= 0xBFFFFFFFUL; } ppc_result_d = (uint32_t)product; ppc_store_result_regd(); } void ppc_mullwodot() { ppc_grab_regsdab(); int64_t product = (int64_t)(int32_t)ppc_result_a * (int64_t)(int32_t)ppc_result_b; if (product != (int64_t)(int32_t)product) { ppc_state.spr[SPR::XER] |= 0xC0000000; } else { ppc_state.spr[SPR::XER] &= 0xBFFFFFFFUL; } ppc_result_d = (uint32_t)product; ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_mulli() { ppc_grab_regsdasimm(); int64_t product = (int64_t)(int32_t)ppc_result_a * (int64_t)(int32_t)simm; ppc_result_d = (uint32_t)product; ppc_store_result_regd(); } void ppc_divw() { ppc_grab_regsdab(); if (!ppc_result_b) { /* handle the "anything / 0" case */ ppc_result_d = (ppc_result_a & 0x80000000) ? -1 : 0; /* UNDOCUMENTED! */ } else if (ppc_result_a == 0x80000000UL && ppc_result_b == 0xFFFFFFFFUL) { ppc_result_d = 0xFFFFFFFF; } else { /* normal signed devision */ ppc_result_d = (int32_t)ppc_result_a / (int32_t)ppc_result_b; } ppc_store_result_regd(); } void ppc_divwdot() { ppc_grab_regsdab(); if (!ppc_result_b) { /* handle the "anything / 0" case */ ppc_result_d = (ppc_result_a & 0x80000000) ? -1 : 0; /* UNDOCUMENTED! */ } else if (ppc_result_a == 0x80000000UL && ppc_result_b == 0xFFFFFFFFUL) { ppc_result_d = 0xFFFFFFFF; } else { /* normal signed devision */ ppc_result_d = (int32_t)ppc_result_a / (int32_t)ppc_result_b; } ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_divwo() { ppc_grab_regsdab(); if (!ppc_result_b) { /* handle the "anything / 0" case */ ppc_result_d = (ppc_result_a & 0x80000000) ? -1 : 0; /* UNDOCUMENTED! */ ppc_state.spr[SPR::XER] |= 0xC0000000; } else if (ppc_result_a == 0x80000000UL && ppc_result_b == 0xFFFFFFFFUL) { ppc_result_d = 0xFFFFFFFF; ppc_state.spr[SPR::XER] |= 0xC0000000; } else { /* normal signed devision */ ppc_result_d = (int32_t)ppc_result_a / (int32_t)ppc_result_b; ppc_state.spr[SPR::XER] &= 0xBFFFFFFFUL; } ppc_store_result_regd(); } void ppc_divwodot() { ppc_grab_regsdab(); if (!ppc_result_b) { /* handle the "anything / 0" case */ ppc_result_d = (ppc_result_a & 0x80000000) ? -1 : 0; /* UNDOCUMENTED! */ ppc_state.spr[SPR::XER] |= 0xC0000000; } else if (ppc_result_a == 0x80000000UL && ppc_result_b == 0xFFFFFFFFUL) { ppc_result_d = 0xFFFFFFFF; ppc_state.spr[SPR::XER] |= 0xC0000000; } else { /* normal signed devision */ ppc_result_d = (int32_t)ppc_result_a / (int32_t)ppc_result_b; ppc_state.spr[SPR::XER] &= 0xBFFFFFFFUL; } ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } void ppc_divwu() { ppc_grab_regsdab(); if (!ppc_result_b) { /* division by zero */ ppc_result_d = 0; } else { ppc_result_d = ppc_result_a / ppc_result_b; } ppc_store_result_regd(); } void ppc_divwudot() { ppc_grab_regsdab(); if (!ppc_result_b) { /* division by zero */ ppc_result_d = 0; ppc_state.cr |= 0x20000000; } else { ppc_result_d = ppc_result_a / ppc_result_b; ppc_changecrf0(ppc_result_d); } ppc_store_result_regd(); } void ppc_divwuo() { ppc_grab_regsdab(); if (!ppc_result_b) { /* division by zero */ ppc_result_d = 0; ppc_state.spr[SPR::XER] |= 0xC0000000; } else { ppc_result_d = ppc_result_a / ppc_result_b; ppc_state.spr[SPR::XER] &= 0xBFFFFFFFUL; } ppc_store_result_regd(); } void ppc_divwuodot() { ppc_grab_regsdab(); if (!ppc_result_b) { /* division by zero */ ppc_result_d = 0; ppc_state.spr[SPR::XER] |= 0xC0000000; } else { ppc_result_d = ppc_result_a / ppc_result_b; ppc_state.spr[SPR::XER] &= 0xBFFFFFFFUL; } ppc_changecrf0(ppc_result_d); ppc_store_result_regd(); } // Value shifting void ppc_slw() { ppc_grab_regssab(); if (ppc_result_b & 0x20) { ppc_result_a = 0; } else { ppc_result_a = ppc_result_d << (ppc_result_b & 0x1F); } ppc_store_result_rega(); } void ppc_slwdot() { ppc_grab_regssab(); if (ppc_result_b & 0x20) { ppc_result_a = 0; } else { ppc_result_a = ppc_result_d << (ppc_result_b & 0x1F); } ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_srw() { ppc_grab_regssab(); if (ppc_result_b & 0x20) { ppc_result_a = 0; } else { ppc_result_a = ppc_result_d >> (ppc_result_b & 0x1F); } ppc_store_result_rega(); } void ppc_srwdot() { ppc_grab_regssab(); if (ppc_result_b & 0x20) { ppc_result_a = 0; } else { ppc_result_a = ppc_result_d >> (ppc_result_b & 0x1F); } ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_sraw() { ppc_grab_regssab(); if (ppc_result_b & 0x20) { ppc_result_a = (int32_t)ppc_result_d >> 31; ppc_state.spr[SPR::XER] |= (ppc_result_a & 1) << 29; } else { uint32_t shift = ppc_result_b & 0x1F; uint32_t mask = (1 << shift) - 1; ppc_result_a = (int32_t)ppc_result_d >> shift; if ((ppc_result_d & 0x80000000UL) && (ppc_result_d & mask)) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } } ppc_store_result_rega(); } void ppc_srawdot() { ppc_grab_regssab(); if (ppc_result_b & 0x20) { ppc_result_a = (int32_t)ppc_result_d >> 31; ppc_state.spr[SPR::XER] |= (ppc_result_a & 1) << 29; } else { uint32_t shift = ppc_result_b & 0x1F; uint32_t mask = (1 << shift) - 1; ppc_result_a = (int32_t)ppc_result_d >> shift; if ((ppc_result_d & 0x80000000UL) && (ppc_result_d & mask)) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } } ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_srawi() { ppc_grab_regssa(); unsigned shift = (ppc_cur_instruction >> 11) & 0x1F; uint32_t mask = (1 << shift) - 1; ppc_result_a = (int32_t)ppc_result_d >> shift; if ((ppc_result_d & 0x80000000UL) && (ppc_result_d & mask)) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_store_result_rega(); } void ppc_srawidot() { ppc_grab_regssa(); unsigned shift = (ppc_cur_instruction >> 11) & 0x1F; uint32_t mask = (1 << shift) - 1; ppc_result_a = (int32_t)ppc_result_d >> shift; if ((ppc_result_d & 0x80000000UL) && (ppc_result_d & mask)) { ppc_state.spr[SPR::XER] |= 0x20000000UL; } else { ppc_state.spr[SPR::XER] &= 0xDFFFFFFFUL; } ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } /** mask generator for rotate and shift instructions (ยง 4.2.1.4 PowerpC PEM) */ static inline uint32_t rot_mask(unsigned rot_mb, unsigned rot_me) { uint32_t m1 = 0xFFFFFFFFUL >> rot_mb; uint32_t m2 = 0xFFFFFFFFUL << (31 - rot_me); return ((rot_mb <= rot_me) ? m2 & m1 : m1 | m2); } void ppc_rlwimi() { ppc_grab_regssa(); unsigned rot_sh = (ppc_cur_instruction >> 11) & 31; unsigned rot_mb = (ppc_cur_instruction >> 6) & 31; unsigned rot_me = (ppc_cur_instruction >> 1) & 31; uint32_t mask = rot_mask(rot_mb, rot_me); uint32_t r = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh))); ppc_result_a = (ppc_result_a & ~mask) | (r & mask); if ((ppc_cur_instruction & 0x01) == 1) { ppc_changecrf0(ppc_result_a); } ppc_store_result_rega(); } void ppc_rlwinm() { ppc_grab_regssa(); unsigned rot_sh = (ppc_cur_instruction >> 11) & 31; unsigned rot_mb = (ppc_cur_instruction >> 6) & 31; unsigned rot_me = (ppc_cur_instruction >> 1) & 31; uint32_t mask = rot_mask(rot_mb, rot_me); uint32_t r = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh))); ppc_result_a = r & mask; if ((ppc_cur_instruction & 0x01) == 1) { ppc_changecrf0(ppc_result_a); } ppc_store_result_rega(); } void ppc_rlwnm() { ppc_grab_regssab(); unsigned rot_mb = (ppc_cur_instruction >> 6) & 31; unsigned rot_me = (ppc_cur_instruction >> 1) & 31; uint32_t mask = rot_mask(rot_mb, rot_me); uint32_t r = ((ppc_result_d << ppc_result_b) | (ppc_result_d >> (32 - ppc_result_b))); ppc_result_a = r & mask; if ((ppc_cur_instruction & 0x01) == 1) { ppc_changecrf0(ppc_result_a); } ppc_store_result_rega(); } void ppc_mfcr() { reg_d = (ppc_cur_instruction >> 21) & 31; ppc_state.gpr[reg_d] = ppc_state.cr; } void ppc_mtsr() { #ifdef PROFILER supervisor_inst_num++; #endif if ((ppc_state.msr & 0x4000) == 0) { reg_s = (ppc_cur_instruction >> 21) & 31; grab_sr = (ppc_cur_instruction >> 16) & 15; ppc_state.sr[grab_sr] = ppc_state.gpr[reg_s]; } } void ppc_mtsrin() { #ifdef PROFILER supervisor_inst_num++; #endif if ((ppc_state.msr & 0x4000) == 0) { ppc_grab_regssb(); grab_sr = ppc_result_b >> 28; ppc_state.sr[grab_sr] = ppc_result_d; } } void ppc_mfsr() { #ifdef PROFILER supervisor_inst_num++; #endif if ((ppc_state.msr & 0x4000) == 0) { reg_d = (ppc_cur_instruction >> 21) & 31; grab_sr = (ppc_cur_instruction >> 16) & 15; ppc_state.gpr[reg_d] = ppc_state.sr[grab_sr]; } } void ppc_mfsrin() { #ifdef PROFILER supervisor_inst_num++; #endif if ((ppc_state.msr & 0x4000) == 0) { ppc_grab_regsdb(); grab_sr = ppc_result_b >> 28; ppc_state.gpr[reg_d] = ppc_state.sr[grab_sr]; } } void ppc_mfmsr() { #ifdef PROFILER supervisor_inst_num++; #endif if (ppc_state.msr & 0x4000) { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x00040000); } reg_d = (ppc_cur_instruction >> 21) & 31; ppc_state.gpr[reg_d] = ppc_state.msr; } void ppc_mtmsr() { #ifdef PROFILER supervisor_inst_num++; #endif if (ppc_state.msr & 0x4000) { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x00040000); } reg_s = (ppc_cur_instruction >> 21) & 31; ppc_state.msr = ppc_state.gpr[reg_s]; } void ppc_mfspr() { uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 31) << 5) | ((ppc_cur_instruction >> 16) & 31); #ifdef PROFILER if (ref_spr > 31) { supervisor_inst_num++; } #endif reg_d = (ppc_cur_instruction >> 21) & 31; ppc_state.gpr[reg_d] = ppc_state.spr[ref_spr]; } void ppc_mtspr() { uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 31) << 5) | ((ppc_cur_instruction >> 16) & 31); reg_s = (ppc_cur_instruction >> 21) & 31; #ifdef PROFILER if (ref_spr > 31) { supervisor_inst_num++; } #endif if (ref_spr != 287) { ppc_state.spr[ref_spr] = ppc_state.gpr[reg_s]; } switch (ref_spr) { // Mirror the TBRs in the SPR range to the user-mode TBRs. case 284: timebase_counter = (timebase_counter & 0xFFFFFFFF00000000ULL) + ppc_state.gpr[reg_s]; break; case 285: timebase_counter = (timebase_counter & 0x00000000FFFFFFFFULL) + (((uint64_t)(ppc_state.gpr[reg_s])) << 32); break; case 528: case 529: case 530: case 531: case 532: case 533: case 534: case 535: ibat_update(ref_spr); break; case 536: case 537: case 538: case 539: case 540: case 541: case 542: case 543: dbat_update(ref_spr); } } void ppc_mftb() { uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 31) << 5) | ((ppc_cur_instruction >> 16) & 31); reg_d = (ppc_cur_instruction >> 21) & 31; switch (ref_spr) { case 268: ppc_state.gpr[reg_d] = timebase_counter & 0xFFFFFFFFUL; break; case 269: ppc_state.gpr[reg_d] = (timebase_counter >> 32) & 0xFFFFFFFFUL; break; default: std::cout << "Invalid TBR access attempted!" << std::endl; } } void ppc_mtcrf() { uint32_t cr_mask = 0; ppc_grab_regssa(); crm = ((ppc_cur_instruction >> 12) & 255); // check this cr_mask += (crm & 128) ? 0xF0000000 : 0x00000000; cr_mask += (crm & 64) ? 0x0F000000 : 0x00000000; cr_mask += (crm & 32) ? 0x00F00000 : 0x00000000; cr_mask += (crm & 16) ? 0x000F0000 : 0x00000000; cr_mask += (crm & 8) ? 0x0000F000 : 0x00000000; cr_mask += (crm & 4) ? 0x00000F00 : 0x00000000; cr_mask += (crm & 2) ? 0x000000F0 : 0x00000000; cr_mask += (crm & 1) ? 0x0000000F : 0x00000000; ppc_state.cr = (ppc_result_d & cr_mask) | (ppc_state.cr & ~(cr_mask)); } void ppc_mcrxr() { crf_d = (ppc_cur_instruction >> 23) & 7; crf_d = crf_d << 2; ppc_state.cr = (ppc_state.cr & ~(0xF0000000UL >> crf_d)) | ((ppc_state.spr[SPR::XER] & 0xF0000000UL) >> crf_d); ppc_state.spr[SPR::XER] &= 0x0FFFFFFF; } void ppc_extsb() { ppc_grab_regssa(); ppc_result_d = ppc_result_d & 0xFF; ppc_result_a = (ppc_result_d < 0x80) ? (ppc_result_d & 0x000000FF) : (0xFFFFFF00UL | (ppc_result_d & 0x000000FF)); ppc_store_result_rega(); } void ppc_extsbdot() { ppc_grab_regssa(); ppc_result_d = ppc_result_d & 0xFF; ppc_result_a = (ppc_result_d < 0x80) ? (ppc_result_d & 0x000000FF) : (0xFFFFFF00UL | (ppc_result_d & 0x000000FF)); ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } void ppc_extsh() { ppc_grab_regssa(); ppc_result_d = ppc_result_d & 0xFFFF; ppc_result_a = (ppc_result_d < 0x8000) ? (ppc_result_d & 0x0000FFFF) : (0xFFFF0000UL | (ppc_result_d & 0x0000FFFF)); ppc_store_result_rega(); } void ppc_extshdot() { ppc_grab_regssa(); ppc_result_d = ppc_result_d & 0xFFFF; ppc_result_a = (ppc_result_d < 0x8000) ? (ppc_result_d & 0x0000FFFF) : (0xFFFF0000UL | (ppc_result_d & 0x0000FFFF)); ppc_changecrf0(ppc_result_a); ppc_store_result_rega(); } // Branching Instructions // The last two bytes of the instruction are used for determining how the branch happens. // The middle 24 bytes are the 24-bit address to use for branching to. void ppc_b() { uint32_t quick_test = (ppc_cur_instruction & 0x03FFFFFC); adr_li = (quick_test < 0x2000000) ? quick_test : (0xFC000000UL + quick_test); ppc_next_instruction_address = (uint32_t)(ppc_state.pc + adr_li); grab_branch = 1; bb_kind = BB_end_kind::BB_BRANCH; } void ppc_bl() { uint32_t quick_test = (ppc_cur_instruction & 0x03FFFFFC); adr_li = (quick_test < 0x2000000) ? quick_test : (0xFC000000UL + quick_test); ppc_next_instruction_address = (uint32_t)(ppc_state.pc + adr_li); ppc_state.spr[SPR::LR] = (uint32_t)(ppc_state.pc + 4); grab_branch = 1; bb_kind = BB_end_kind::BB_BRANCH; } void ppc_ba() { uint32_t quick_test = (ppc_cur_instruction & 0x03FFFFFC); adr_li = (quick_test < 0x2000000) ? quick_test : (0xFC000000UL + quick_test); ppc_next_instruction_address = adr_li; grab_branch = 1; bb_kind = BB_end_kind::BB_BRANCH; } void ppc_bla() { uint32_t quick_test = (ppc_cur_instruction & 0x03FFFFFC); adr_li = (quick_test < 0x2000000) ? quick_test : (0xFC000000UL + quick_test); ppc_next_instruction_address = adr_li; ppc_state.spr[SPR::LR] = ppc_state.pc + 4; grab_branch = 1; bb_kind = BB_end_kind::BB_BRANCH; } void ppc_bc() { uint32_t ctr_ok; uint32_t cnd_ok; uint32_t br_bo = (ppc_cur_instruction >> 21) & 31; uint32_t br_bi = (ppc_cur_instruction >> 16) & 31; int32_t br_bd = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFC)); if (!(br_bo & 0x04)) { (ppc_state.spr[SPR::CTR])--; /* decrement CTR */ } ctr_ok = (br_bo & 0x04) || ((ppc_state.spr[SPR::CTR] != 0) == !(br_bo & 0x02)); cnd_ok = (br_bo & 0x10) || (!(ppc_state.cr & (0x80000000UL >> br_bi)) == !(br_bo & 0x08)); if (ctr_ok && cnd_ok) { ppc_next_instruction_address = (ppc_state.pc + br_bd); grab_branch = 1; bb_kind = BB_end_kind::BB_BRANCH; } } void ppc_bca() { uint32_t ctr_ok; uint32_t cnd_ok; uint32_t br_bo = (ppc_cur_instruction >> 21) & 31; uint32_t br_bi = (ppc_cur_instruction >> 16) & 31; int32_t br_bd = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFC)); if (!(br_bo & 0x04)) { (ppc_state.spr[SPR::CTR])--; /* decrement CTR */ } ctr_ok = (br_bo & 0x04) || ((ppc_state.spr[SPR::CTR] != 0) == !(br_bo & 0x02)); cnd_ok = (br_bo & 0x10) || (!(ppc_state.cr & (0x80000000UL >> br_bi)) == !(br_bo & 0x08)); if (ctr_ok && cnd_ok) { ppc_next_instruction_address = br_bd; grab_branch = 1; bb_kind = BB_end_kind::BB_BRANCH; } } void ppc_bcl() { uint32_t ctr_ok; uint32_t cnd_ok; uint32_t br_bo = (ppc_cur_instruction >> 21) & 31; uint32_t br_bi = (ppc_cur_instruction >> 16) & 31; int32_t br_bd = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFC)); if (!(br_bo & 0x04)) { (ppc_state.spr[SPR::CTR])--; /* decrement CTR */ } ctr_ok = (br_bo & 0x04) || ((ppc_state.spr[SPR::CTR] != 0) == !(br_bo & 0x02)); cnd_ok = (br_bo & 0x10) || (!(ppc_state.cr & (0x80000000UL >> br_bi)) == !(br_bo & 0x08)); if (ctr_ok && cnd_ok) { ppc_next_instruction_address = (ppc_state.pc + br_bd); grab_branch = 1; bb_kind = BB_end_kind::BB_BRANCH; } ppc_state.spr[SPR::LR] = ppc_state.pc + 4; } void ppc_bcla() { uint32_t ctr_ok; uint32_t cnd_ok; uint32_t br_bo = (ppc_cur_instruction >> 21) & 31; uint32_t br_bi = (ppc_cur_instruction >> 16) & 31; int32_t br_bd = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFC)); if (!(br_bo & 0x04)) { (ppc_state.spr[SPR::CTR])--; /* decrement CTR */ } ctr_ok = (br_bo & 0x04) || ((ppc_state.spr[SPR::CTR] != 0) == !(br_bo & 0x02)); cnd_ok = (br_bo & 0x10) || (!(ppc_state.cr & (0x80000000UL >> br_bi)) == !(br_bo & 0x08)); if (ctr_ok && cnd_ok) { ppc_next_instruction_address = br_bd; grab_branch = 1; bb_kind = BB_end_kind::BB_BRANCH; } ppc_state.spr[SPR::LR] = ppc_state.pc + 4; } void ppc_bcctr() { uint32_t br_bo = (ppc_cur_instruction >> 21) & 31; uint32_t br_bi = (ppc_cur_instruction >> 16) & 31; uint32_t cnd_ok = (br_bo & 0x10) || (!(ppc_state.cr & (0x80000000UL >> br_bi)) == !(br_bo & 0x08)); if (cnd_ok) { ppc_next_instruction_address = (ppc_state.spr[SPR::CTR] & 0xFFFFFFFCUL); grab_branch = 1; bb_kind = BB_end_kind::BB_BRANCH; } } void ppc_bcctrl() { uint32_t br_bo = (ppc_cur_instruction >> 21) & 31; uint32_t br_bi = (ppc_cur_instruction >> 16) & 31; uint32_t cnd_ok = (br_bo & 0x10) || (!(ppc_state.cr & (0x80000000UL >> br_bi)) == !(br_bo & 0x08)); if (cnd_ok) { ppc_next_instruction_address = (ppc_state.spr[SPR::CTR] & 0xFFFFFFFCUL); grab_branch = 1; bb_kind = BB_end_kind::BB_BRANCH; } ppc_state.spr[SPR::LR] = ppc_state.pc + 4; } void ppc_bclr() { uint32_t br_bo = (ppc_cur_instruction >> 21) & 31; uint32_t br_bi = (ppc_cur_instruction >> 16) & 31; uint32_t ctr_ok; uint32_t cnd_ok; if (!(br_bo & 0x04)) { (ppc_state.spr[SPR::CTR])--; /* decrement CTR */ } ctr_ok = (br_bo & 0x04) || ((ppc_state.spr[SPR::CTR] != 0) == !(br_bo & 0x02)); cnd_ok = (br_bo & 0x10) || (!(ppc_state.cr & (0x80000000UL >> br_bi)) == !(br_bo & 0x08)); if (ctr_ok && cnd_ok) { ppc_next_instruction_address = (ppc_state.spr[SPR::LR] & 0xFFFFFFFCUL); grab_branch = 1; bb_kind = BB_end_kind::BB_BRANCH; } } void ppc_bclrl() { uint32_t br_bo = (ppc_cur_instruction >> 21) & 31; uint32_t br_bi = (ppc_cur_instruction >> 16) & 31; uint32_t ctr_ok; uint32_t cnd_ok; if (!(br_bo & 0x04)) { (ppc_state.spr[SPR::CTR])--; /* decrement CTR */ } ctr_ok = (br_bo & 0x04) || ((ppc_state.spr[SPR::CTR] != 0) == !(br_bo & 0x02)); cnd_ok = (br_bo & 0x10) || (!(ppc_state.cr & (0x80000000UL >> br_bi)) == !(br_bo & 0x08)); if (ctr_ok && cnd_ok) { ppc_next_instruction_address = (ppc_state.spr[SPR::LR] & 0xFFFFFFFCUL); grab_branch = 1; bb_kind = BB_end_kind::BB_BRANCH; } ppc_state.spr[SPR::LR] = ppc_state.pc + 4; } // Compare Instructions void ppc_cmp() { #ifdef CHECK_INVALID if (ppc_cur_instruction & 0x200000) { LOG_F(WARNING, "Invalid CMP instruction form (L=1)!\n"); return; } #endif crf_d = (ppc_cur_instruction >> 23) & 7; crf_d = crf_d << 2; ppc_grab_regssab(); xercon = (ppc_state.spr[SPR::XER] & 0x80000000UL) >> 3; cmp_c = (((int32_t)ppc_result_a) == ((int32_t)ppc_result_b)) ? 0x20000000UL : (((int32_t)ppc_result_a) > ((int32_t)ppc_result_b)) ? 0x40000000UL : 0x80000000UL; ppc_state.cr = ((ppc_state.cr & ~(0xf0000000UL >> crf_d)) | ((cmp_c + xercon) >> crf_d)); } void ppc_cmpi() { #ifdef CHECK_INVALID if (ppc_cur_instruction & 0x200000) { LOG_F(WARNING, "Invalid CMPI instruction form (L=1)!\n"); return; } #endif crf_d = (ppc_cur_instruction >> 23) & 7; crf_d = crf_d << 2; ppc_grab_regsasimm(); xercon = (ppc_state.spr[SPR::XER] & 0x80000000UL) >> 3; cmp_c = (((int32_t)ppc_result_a) == simm) ? 0x20000000UL : (((int32_t)ppc_result_a) > simm) ? 0x40000000UL : 0x80000000UL; ppc_state.cr = ((ppc_state.cr & ~(0xf0000000UL >> crf_d)) | ((cmp_c + xercon) >> crf_d)); } void ppc_cmpl() { #ifdef CHECK_INVALID if (ppc_cur_instruction & 0x200000) { LOG_F(WARNING, "Invalid CMPL instruction form (L=1)!\n"); return; } #endif crf_d = (ppc_cur_instruction >> 23) & 7; crf_d = crf_d << 2; ppc_grab_regssab(); xercon = (ppc_state.spr[SPR::XER] & 0x80000000UL) >> 3; cmp_c = (ppc_result_a == ppc_result_b) ? 0x20000000UL : (ppc_result_a > ppc_result_b) ? 0x40000000UL : 0x80000000UL; ppc_state.cr = ((ppc_state.cr & ~(0xf0000000UL >> crf_d)) | ((cmp_c + xercon) >> crf_d)); } void ppc_cmpli() { #ifdef CHECK_INVALID if (ppc_cur_instruction & 0x200000) { LOG_F(WARNING, "Invalid CMPLI instruction form (L=1)!\n"); return; } #endif crf_d = (ppc_cur_instruction >> 23) & 7; crf_d = crf_d << 2; ppc_grab_regssauimm(); xercon = (ppc_state.spr[SPR::XER] & 0x80000000UL) >> 3; cmp_c = (ppc_result_a == uimm) ? 0x20000000UL : (ppc_result_a > uimm) ? 0x40000000UL : 0x80000000UL; ppc_state.cr = ((ppc_state.cr & ~(0xf0000000UL >> crf_d)) | ((cmp_c + xercon) >> crf_d)); } // Condition Register Changes void ppc_crand() { ppc_grab_regsdab(); if ((ppc_state.cr & (0x80000000UL >> reg_a)) && (ppc_state.cr & (0x80000000UL >> reg_b))) { ppc_state.cr |= (0x80000000UL >> reg_d); } else { ppc_state.cr &= ~(0x80000000UL >> reg_d); } } void ppc_crandc() { ppc_grab_regsdab(); if ((ppc_state.cr & (0x80000000UL >> reg_a)) && !(ppc_state.cr & (0x80000000UL >> reg_b))) { ppc_state.cr |= (0x80000000UL >> reg_d); } else { ppc_state.cr &= ~(0x80000000UL >> reg_d); } } void ppc_creqv() { ppc_grab_regsdab(); if (!((ppc_state.cr & (0x80000000UL >> reg_a)) ^ (ppc_state.cr & (0x80000000UL >> reg_b)))) { ppc_state.cr |= (0x80000000UL >> reg_d); } else { ppc_state.cr &= ~(0x80000000UL >> reg_d); } } void ppc_crnand() { ppc_grab_regsdab(); if (!((ppc_state.cr & (0x80000000UL >> reg_a)) && (ppc_state.cr & (0x80000000UL >> reg_b)))) { ppc_state.cr |= (0x80000000UL >> reg_d); } else { ppc_state.cr &= ~(0x80000000UL >> reg_d); } } void ppc_crnor() { ppc_grab_regsdab(); if (!((ppc_state.cr & (0x80000000UL >> reg_a)) || (ppc_state.cr & (0x80000000UL >> reg_b)))) { ppc_state.cr |= (0x80000000UL >> reg_d); } else { ppc_state.cr &= ~(0x80000000UL >> reg_d); } } void ppc_cror() { ppc_grab_regsdab(); if ((ppc_state.cr & (0x80000000UL >> reg_a)) || (ppc_state.cr & (0x80000000UL >> reg_b))) { ppc_state.cr |= (0x80000000UL >> reg_d); } else { ppc_state.cr &= ~(0x80000000UL >> reg_d); } } void ppc_crorc() { ppc_grab_regsdab(); if ((ppc_state.cr & (0x80000000UL >> reg_a)) || !(ppc_state.cr & (0x80000000UL >> reg_b))) { ppc_state.cr |= (0x80000000UL >> reg_d); } else { ppc_state.cr &= ~(0x80000000UL >> reg_d); } } void ppc_crxor() { ppc_grab_regsdab(); if ((ppc_state.cr & (0x80000000UL >> reg_a)) ^ (ppc_state.cr & (0x80000000UL >> reg_b))) { ppc_state.cr |= (0x80000000UL >> reg_d); } else { ppc_state.cr &= ~(0x80000000UL >> reg_d); } } // Processor MGMT Fns. void ppc_rfi() { #ifdef PROFILER supervisor_inst_num++; #endif uint32_t new_srr1_val = (ppc_state.spr[SPR::SRR1] & 0x87C0FF73UL); uint32_t new_msr_val = (ppc_state.msr & ~(0x87C0FF73UL)); ppc_state.msr = (new_msr_val | new_srr1_val) & 0xFFFBFFFFUL; ppc_next_instruction_address = ppc_state.spr[SPR::SRR0] & 0xFFFFFFFCUL; grab_return = true; bb_kind = BB_end_kind::BB_RFI; } void ppc_sc() { ppc_exception_handler(Except_Type::EXC_SYSCALL, 0x20000); } void ppc_tw() { reg_a = (ppc_cur_instruction >> 11) & 31; reg_b = (ppc_cur_instruction >> 16) & 31; ppc_to = (ppc_cur_instruction >> 21) & 31; if ((((int32_t)ppc_state.gpr[reg_a] < (int32_t)ppc_state.gpr[reg_b]) & (ppc_to & 0x10)) || (((int32_t)ppc_state.gpr[reg_a] > (int32_t)ppc_state.gpr[reg_b]) & (ppc_to & 0x08)) || (((int32_t)ppc_state.gpr[reg_a] == (int32_t)ppc_state.gpr[reg_b]) & (ppc_to & 0x04)) || ((ppc_state.gpr[reg_a] < ppc_state.gpr[reg_b]) & (ppc_to & 0x02)) || ((ppc_state.gpr[reg_a] > ppc_state.gpr[reg_b]) & (ppc_to & 0x01))) { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_twi() { simm = (int32_t)((int16_t)((ppc_cur_instruction)&0xFFFF)); reg_a = (ppc_cur_instruction >> 16) & 0x1F; ppc_to = (ppc_cur_instruction >> 21) & 0x1F; if ((((int32_t)ppc_state.gpr[reg_a] < simm) && (ppc_to & 0x10)) || (((int32_t)ppc_state.gpr[reg_a] > simm) && (ppc_to & 0x08)) || (((int32_t)ppc_state.gpr[reg_a] == simm) && (ppc_to & 0x04)) || ((ppc_state.gpr[reg_a] < (uint32_t)simm) && (ppc_to & 0x02)) || ((ppc_state.gpr[reg_a] > (uint32_t)simm) && (ppc_to & 0x01))) { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_eieio() { /* placeholder */ } void ppc_isync() { /* placeholder */ } void ppc_sync() { /* placeholder */ } void ppc_icbi() { /* placeholder */ } void ppc_dcbf() { /* placeholder */ } void ppc_dcbi() { #ifdef PROFILER supervisor_inst_num++; #endif /* placeholder */ } void ppc_dcbst() { /* placeholder */ } void ppc_dcbt() { // Not needed, the HDI reg is touched to no-op this instruction. return; } void ppc_dcbtst() { // Not needed, the HDI reg is touched to no-op this instruction. return; } void ppc_dcbz() { ppc_grab_regsdab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); if (!(ppc_state.pc & 32) && (ppc_state.pc < 0xFFFFFFE0UL)) { ppc_grab_regsdab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); mem_write_qword(ppc_effective_address, 0); mem_write_qword((ppc_effective_address + 8), 0); mem_write_qword((ppc_effective_address + 16), 0); mem_write_qword((ppc_effective_address + 24), 0); } else { ppc_exception_handler(Except_Type::EXC_ALIGNMENT, 0x00000); } } // Integer Load and Store Functions void ppc_stb() { ppc_grab_regssa(); ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += (reg_a > 0) ? ppc_result_a : 0; mem_write_byte(ppc_effective_address, ppc_result_d); } void ppc_stbx() { ppc_grab_regssab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); mem_write_byte(ppc_effective_address, ppc_result_d); } void ppc_stbu() { ppc_grab_regssa(); if (reg_a != 0) { ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += ppc_result_a; mem_write_byte(ppc_effective_address, ppc_result_d); ppc_state.gpr[reg_a] = ppc_effective_address; } else { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_stbux() { ppc_grab_regssab(); if (reg_a != 0) { ppc_effective_address = ppc_result_a + ppc_result_b; mem_write_byte(ppc_effective_address, ppc_result_d); ppc_state.gpr[reg_a] = ppc_effective_address; } else { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_sth() { ppc_grab_regssa(); ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += (reg_a > 0) ? ppc_result_a : 0; mem_write_word(ppc_effective_address, ppc_result_d); } void ppc_sthu() { ppc_grab_regssa(); if (reg_a != 0) { ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += ppc_result_a; mem_write_word(ppc_effective_address, ppc_result_d); ppc_state.gpr[reg_a] = ppc_effective_address; } else { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_sthux() { ppc_grab_regssab(); if (reg_a != 0) { ppc_effective_address = ppc_result_a + ppc_result_b; mem_write_word(ppc_effective_address, ppc_result_d); ppc_state.gpr[reg_a] = ppc_effective_address; } else { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_sthx() { ppc_grab_regssab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); mem_write_word(ppc_effective_address, ppc_result_d); } void ppc_sthbrx() { ppc_grab_regssab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); ppc_result_d = (uint32_t)(BYTESWAP_16((uint16_t)ppc_result_d)); mem_write_word(ppc_effective_address, ppc_result_d); } void ppc_stw() { ppc_grab_regssa(); ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += (reg_a > 0) ? ppc_result_a : 0; mem_write_dword(ppc_effective_address, ppc_result_d); } void ppc_stwx() { ppc_grab_regssab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); mem_write_dword(ppc_effective_address, ppc_result_d); } void ppc_stwcx() { // PLACEHOLDER CODE FOR STWCX - We need to check for reserve memory ppc_grab_regssab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); if (ppc_state.reserve) { mem_write_dword(ppc_effective_address, ppc_result_d); ppc_state.cr |= (ppc_state.spr[SPR::XER] & 0x80000000) ? 0x30000000 : 0x20000000; ppc_state.reserve = false; } else { ppc_state.cr |= (ppc_state.spr[SPR::XER] & 0x80000000) ? 0x10000000 : 0; } } void ppc_stwu() { ppc_grab_regssa(); if (reg_a != 0) { ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += ppc_result_a; mem_write_dword(ppc_effective_address, ppc_result_d); ppc_state.gpr[reg_a] = ppc_effective_address; } else { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_stwux() { ppc_grab_regssab(); if (reg_a != 0) { ppc_effective_address = ppc_result_a + ppc_result_b; mem_write_dword(ppc_effective_address, ppc_result_d); ppc_state.gpr[reg_a] = ppc_effective_address; } else { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_stwbrx() { ppc_grab_regssab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); ppc_result_d = BYTESWAP_32(ppc_result_d); mem_write_dword(ppc_effective_address, ppc_result_d); } void ppc_stmw() { ppc_grab_regssa(); ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += (reg_a > 0) ? ppc_result_a : 0; /* what should we do if EA is unaligned? */ if (ppc_effective_address & 3) { ppc_exception_handler(Except_Type::EXC_ALIGNMENT, 0x00000); } for (; reg_s <= 31; reg_s++) { mem_write_dword(ppc_effective_address, ppc_state.gpr[reg_s]); ppc_effective_address += 4; } } void ppc_lbz() { ppc_grab_regsda(); ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += (reg_a > 0) ? ppc_result_a : 0; ppc_result_d = mem_grab_byte(ppc_effective_address); ppc_store_result_regd(); } void ppc_lbzu() { ppc_grab_regsda(); ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); if ((reg_a != reg_d) || reg_a != 0) { ppc_effective_address += ppc_result_a; ppc_result_d = mem_grab_byte(ppc_effective_address); ppc_result_a = ppc_effective_address; ppc_store_result_regd(); ppc_store_result_rega(); } else { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_lbzx() { ppc_grab_regsdab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); ppc_result_d = mem_grab_byte(ppc_effective_address); ppc_store_result_regd(); } void ppc_lbzux() { ppc_grab_regsdab(); if ((reg_a != reg_d) || reg_a != 0) { ppc_effective_address = ppc_result_a + ppc_result_b; ppc_result_d = mem_grab_byte(ppc_effective_address); ppc_result_a = ppc_effective_address; ppc_store_result_regd(); ppc_store_result_rega(); } else { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_lhz() { ppc_grab_regsda(); ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += (reg_a > 0) ? ppc_result_a : 0; ppc_result_d = mem_grab_word(ppc_effective_address); ppc_store_result_regd(); } void ppc_lhzu() { ppc_grab_regsda(); if ((reg_a != reg_d) || reg_a != 0) { ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += ppc_result_a; ppc_result_d = mem_grab_word(ppc_effective_address); ppc_result_a = ppc_effective_address; ppc_store_result_regd(); ppc_store_result_rega(); } else { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_lhzx() { ppc_grab_regsdab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); ppc_result_d = mem_grab_word(ppc_effective_address); ppc_store_result_regd(); } void ppc_lhzux() { ppc_grab_regsdab(); if ((reg_a != reg_d) || reg_a != 0) { ppc_effective_address = ppc_result_a + ppc_result_b; ppc_result_d = mem_grab_word(ppc_effective_address); ppc_result_a = ppc_effective_address; ppc_store_result_regd(); ppc_store_result_rega(); } else { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_lha() { ppc_grab_regsda(); ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += (reg_a > 0) ? ppc_result_a : 0; uint16_t val = mem_grab_word(ppc_effective_address); if (val & 0x8000) { ppc_result_d = 0xFFFF0000UL | (uint32_t)val; } else { ppc_result_d = (uint32_t)val; } ppc_store_result_regd(); } void ppc_lhau() { ppc_grab_regsda(); if ((reg_a != reg_d) || reg_a != 0) { ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += ppc_result_a; uint16_t val = mem_grab_word(ppc_effective_address); if (val & 0x8000) { ppc_result_d = 0xFFFF0000UL | (uint32_t)val; } else { ppc_result_d = (uint32_t)val; } ppc_store_result_regd(); ppc_result_a = ppc_effective_address; ppc_store_result_rega(); } else { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_lhaux() { ppc_grab_regsdab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); uint16_t val = mem_grab_word(ppc_effective_address); if (val & 0x8000) { ppc_result_d = 0xFFFF0000UL | (uint32_t)val; } else { ppc_result_d = (uint32_t)val; } ppc_store_result_regd(); ppc_result_a = ppc_effective_address; ppc_store_result_rega(); } void ppc_lhax() { ppc_grab_regsdab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); uint16_t val = mem_grab_word(ppc_effective_address); if (val & 0x8000) { ppc_result_d = 0xFFFF0000UL | (uint32_t)val; } else { ppc_result_d = (uint32_t)val; } ppc_store_result_regd(); } void ppc_lhbrx() { ppc_grab_regsdab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); ppc_result_d = (uint32_t)(BYTESWAP_16(mem_grab_word(ppc_effective_address))); ppc_store_result_regd(); } void ppc_lwz() { ppc_grab_regsda(); ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += (reg_a > 0) ? ppc_result_a : 0; ppc_result_d = mem_grab_dword(ppc_effective_address); ppc_store_result_regd(); } void ppc_lwbrx() { ppc_grab_regsdab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); ppc_result_d = BYTESWAP_32(mem_grab_dword(ppc_effective_address)); ppc_store_result_regd(); } void ppc_lwzu() { ppc_grab_regsda(); ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); if ((reg_a != reg_d) || reg_a != 0) { ppc_effective_address += ppc_result_a; ppc_result_d = mem_grab_dword(ppc_effective_address); ppc_store_result_regd(); ppc_result_a = ppc_effective_address; ppc_store_result_rega(); } else { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } } void ppc_lwzx() { ppc_grab_regsdab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); ppc_result_d = mem_grab_dword(ppc_effective_address); ppc_store_result_regd(); } void ppc_lwzux() { ppc_grab_regsdab(); if ((reg_a != reg_d) || reg_a != 0) { ppc_effective_address = ppc_result_a + ppc_result_b; } else { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x20000); } ppc_result_d = mem_grab_dword(ppc_effective_address); ppc_result_a = ppc_effective_address; ppc_store_result_regd(); ppc_store_result_rega(); } void ppc_lwarx() { // Placeholder - Get the reservation of memory implemented! ppc_grab_regsdab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); ppc_state.reserve = true; ppc_result_d = mem_grab_dword(ppc_effective_address); ppc_store_result_regd(); } void ppc_lmw() { ppc_grab_regsda(); ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF)); ppc_effective_address += (reg_a > 0) ? ppc_result_a : 0; // How many words to load in memory - using a do-while for this do { ppc_state.gpr[reg_d] = mem_grab_dword(ppc_effective_address); ppc_effective_address += 4; reg_d++; } while (reg_d < 32); } void ppc_lswi() { ppc_grab_regsda(); ppc_effective_address = ppc_result_a; grab_inb = (ppc_cur_instruction >> 11) & 31; grab_inb = (grab_inb & 32) == 0 ? 32 : grab_inb; uint32_t shift_times = 0; while (grab_inb > 0) { switch (shift_times) { case 0: ppc_state.gpr[reg_d] = (ppc_result_d & 0x00FFFFFFUL) | (mem_grab_byte(ppc_effective_address) << 24); ppc_store_result_regd(); break; case 1: ppc_result_d = (ppc_result_d & 0xFF00FFFFUL) | (mem_grab_byte(ppc_effective_address) << 16); ppc_store_result_regd(); break; case 2: ppc_result_d = (ppc_result_d & 0xFFFF00FFUL) | (mem_grab_byte(ppc_effective_address) << 8); ppc_store_result_regd(); break; case 3: ppc_result_d = (ppc_result_d & 0xFFFFFF00UL) | mem_grab_byte(ppc_effective_address); ppc_store_result_regd(); break; default: LOG_F(ERROR, "Something really horrible happened with lswi. \n"); } if (shift_times == 3) { shift_times = 0; reg_d = (reg_d + 1) & 0x1F; } else { shift_times++; } ppc_effective_address++; grab_inb--; } } void ppc_lswx() { ppc_grab_regsdab(); // Invalid instruction forms if ((ppc_result_d == 0) && (ppc_result_a == 0)) { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x100000); } if ((ppc_result_d == ppc_result_a) || (ppc_result_a == ppc_result_b)) { ppc_exception_handler(Except_Type::EXC_PROGRAM, 0x100000); } ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); grab_inb = ppc_state.spr[SPR::XER] & 127; uint32_t shift_times = 0; while (grab_inb > 0) { switch (shift_times) { case 0: ppc_result_d = (ppc_result_d & 0x00FFFFFFUL) | (mem_grab_byte(ppc_effective_address) << 24); ppc_store_result_regd(); break; case 1: ppc_result_d = (ppc_result_d & 0xFF00FFFFUL) | (mem_grab_byte(ppc_effective_address) << 16); ppc_store_result_regd(); break; case 2: ppc_result_d = (ppc_result_d & 0xFFFF00FFUL) | (mem_grab_byte(ppc_effective_address) << 8); ppc_store_result_regd(); break; case 3: ppc_result_d = (ppc_result_d & 0xFFFFFF00UL) | mem_grab_byte(ppc_effective_address); ppc_store_result_regd(); break; default: LOG_F(ERROR, "Something really horrible happened with lswx. \n"); } if (shift_times == 3) { shift_times = 0; reg_d = (reg_d + 1) & 0x1F; } else { shift_times++; } ppc_effective_address++; grab_inb--; } } void ppc_stswi() { ppc_grab_regssa(); ppc_effective_address = (reg_a == 0) ? 0 : ppc_result_a; grab_inb = (ppc_cur_instruction >> 11) & 31; grab_inb = (grab_inb & 32) == 0 ? 32 : grab_inb; uint32_t shift_times = 0; while (grab_inb > 0) { switch (shift_times) { case 0: strwrd_replace_value = (ppc_result_d >> 24); mem_write_byte(ppc_effective_address, strwrd_replace_value); break; case 1: strwrd_replace_value = (ppc_result_d >> 16); mem_write_byte(ppc_effective_address, strwrd_replace_value); break; case 2: strwrd_replace_value = (ppc_result_d >> 8); mem_write_byte(ppc_effective_address, strwrd_replace_value); break; case 3: strwrd_replace_value = (ppc_result_d); mem_write_byte(ppc_effective_address, strwrd_replace_value); break; default: LOG_F(ERROR, "Something really horrible happened with stswi. \n"); } if (shift_times == 3) { shift_times = 0; reg_s = (reg_s + 1) & 0x1F; } else { shift_times++; } ppc_effective_address++; grab_inb--; } } void ppc_stswx() { ppc_grab_regssab(); ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b); grab_inb = ppc_state.spr[SPR::XER] & 127; uint32_t shift_times = 0; while (grab_inb > 0) { switch (shift_times) { case 0: strwrd_replace_value = (ppc_result_d >> 24); mem_write_byte(ppc_effective_address, strwrd_replace_value); break; case 1: strwrd_replace_value = (ppc_result_d >> 16); mem_write_byte(ppc_effective_address, strwrd_replace_value); break; case 2: strwrd_replace_value = (ppc_result_d >> 8); mem_write_byte(ppc_effective_address, strwrd_replace_value); break; case 3: strwrd_replace_value = (ppc_result_d); mem_write_byte(ppc_effective_address, strwrd_replace_value); break; default: LOG_F(ERROR, "Something really horrible happened with stswx. \n"); } if (shift_times == 3) { shift_times = 0; reg_s = (reg_s + 1) & 0x1F; } else { shift_times++; } ppc_effective_address++; grab_inb--; } } // TLB Instructions void ppc_tlbie() { #ifdef PROFILER supervisor_inst_num++; #endif /* placeholder */ } void ppc_tlbia() { #ifdef PROFILER supervisor_inst_num++; #endif /* placeholder */ } void ppc_tlbld() { #ifdef PROFILER supervisor_inst_num++; #endif /* placeholder */ } void ppc_tlbli() { #ifdef PROFILER supervisor_inst_num++; #endif /* placeholder */ } void ppc_tlbsync() { #ifdef PROFILER supervisor_inst_num++; #endif /* placeholder */ }