/* DingusPPC - The Experimental PowerPC Macintosh emulator Copyright (C) 2018-24 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 . */ // The Power-specific opcodes for the processor - ppcopcodes.cpp // Any shared opcodes are in ppcopcodes.cpp #include "ppcemu.h" #include "ppcmacros.h" #include "ppcmmu.h" #include /** mask generator for rotate and shift instructions (ยง 4.2.1.4 PowerpC PEM) */ static inline uint32_t power_rot_mask(unsigned rot_mb, unsigned rot_me) { uint32_t m1 = 0xFFFFFFFFU >> rot_mb; uint32_t m2 = 0xFFFFFFFFU << (31 - rot_me); return ((rot_mb <= rot_me) ? m2 & m1 : m1 | m2); } template void dppc_interpreter::power_abs() { uint32_t ppc_result_d; ppc_grab_regsda(ppc_cur_instruction); if (ppc_result_a == 0x80000000) { ppc_result_d = ppc_result_a; if (ov) ppc_state.spr[SPR::XER] |= XER::SO | XER::OV; } else { ppc_result_d = (int32_t(ppc_result_a) < 0) ? -ppc_result_a : ppc_result_a; if (ov) ppc_state.spr[SPR::XER] &= ~XER::OV; } if (rec) ppc_changecrf0(ppc_result_d); ppc_store_iresult_reg(reg_d, ppc_result_d); } template void dppc_interpreter::power_abs(); template void dppc_interpreter::power_abs(); template void dppc_interpreter::power_abs(); template void dppc_interpreter::power_abs(); void dppc_interpreter::power_clcs() { uint32_t ppc_result_d; ppc_grab_da(ppc_cur_instruction); switch (reg_a) { case 12: //instruction cache line size case 13: //data cache line size case 14: //minimum line size case 15: //maximum line size default: ppc_result_d = is_601 ? 64 : 32; break; case 7: case 23: ppc_result_d = is_601 ? 64 : 0; break; case 8: case 9: case 24: case 25: ppc_result_d = is_601 ? 64 : 4; break; case 10: case 11: case 26: case 27: ppc_result_d = is_601 ? 64 : 0x4000; break; } ppc_store_iresult_reg(reg_d, ppc_result_d); } template void dppc_interpreter::power_div() { uint32_t ppc_result_d; ppc_grab_regsdab(ppc_cur_instruction); int64_t dividend = (uint64_t(ppc_result_a) << 32) | ppc_state.spr[SPR::MQ]; int32_t divisor = ppc_result_b; int64_t quotient; int32_t remainder; if (dividend == -0x80000000 && divisor == -1) { remainder = 0; ppc_result_d = 0x80000000U; // -2^31 aka INT32_MIN if (ov) ppc_state.spr[SPR::XER] |= XER::SO | XER::OV; } else if (!divisor) { remainder = 0; ppc_result_d = 0x80000000U; // -2^31 aka INT32_MIN if (ov) ppc_state.spr[SPR::XER] |= XER::SO | XER::OV; } else { quotient = dividend / divisor; remainder = dividend % divisor; ppc_result_d = uint32_t(quotient); if (ov) { if (((quotient >> 31) + 1) & ~1) { ppc_state.spr[SPR::XER] |= XER::SO | XER::OV; } else { ppc_state.spr[SPR::XER] &= ~XER::OV; } } } if (rec) ppc_changecrf0(remainder); ppc_store_iresult_reg(reg_d, ppc_result_d); ppc_state.spr[SPR::MQ] = remainder; } template void dppc_interpreter::power_div(); template void dppc_interpreter::power_div(); template void dppc_interpreter::power_div(); template void dppc_interpreter::power_div(); template void dppc_interpreter::power_divs() { uint32_t ppc_result_d; int32_t remainder; ppc_grab_regsdab(ppc_cur_instruction); if (!ppc_result_b) { // handle the "anything / 0" case ppc_result_d = -1; remainder = ppc_result_a; if (ov) ppc_state.spr[SPR::XER] |= XER::SO | XER::OV; } else if (ppc_result_a == 0x80000000U && ppc_result_b == 0xFFFFFFFFU) { ppc_result_d = 0x80000000U; remainder = 0; if (ov) ppc_state.spr[SPR::XER] |= XER::SO | XER::OV; } else { // normal signed devision ppc_result_d = int32_t(ppc_result_a) / int32_t(ppc_result_b); remainder = (int32_t(ppc_result_a) % int32_t(ppc_result_b)); if (ov) ppc_state.spr[SPR::XER] &= ~XER::OV; } if (rec) ppc_changecrf0(remainder); ppc_store_iresult_reg(reg_d, ppc_result_d); ppc_state.spr[SPR::MQ] = remainder; } template void dppc_interpreter::power_divs(); template void dppc_interpreter::power_divs(); template void dppc_interpreter::power_divs(); template void dppc_interpreter::power_divs(); template void dppc_interpreter::power_doz() { ppc_grab_regsdab(ppc_cur_instruction); uint32_t ppc_result_d = (int32_t(ppc_result_a) < int32_t(ppc_result_b)) ? ppc_result_b - ppc_result_a : 0; if (ov) { if (int32_t(ppc_result_d) < 0) { ppc_state.spr[SPR::XER] |= XER::SO | XER::OV; } else { ppc_state.spr[SPR::XER] &= ~XER::OV; } } if (rec) ppc_changecrf0(ppc_result_d); ppc_store_iresult_reg(reg_d, ppc_result_d); } template void dppc_interpreter::power_doz(); template void dppc_interpreter::power_doz(); template void dppc_interpreter::power_doz(); template void dppc_interpreter::power_doz(); void dppc_interpreter::power_dozi() { uint32_t ppc_result_d; ppc_grab_regsdasimm(ppc_cur_instruction); if (((int32_t)ppc_result_a) > simm) { ppc_result_d = 0; } else { ppc_result_d = simm - ppc_result_a; } ppc_store_iresult_reg(reg_d, ppc_result_d); } template void dppc_interpreter::power_lscbx() { ppc_grab_regsdab(ppc_cur_instruction); ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0); uint32_t bytes_to_load = (ppc_state.spr[SPR::XER] & 0x7F); uint32_t bytes_remaining = bytes_to_load; uint8_t matching_byte = (uint8_t)(ppc_state.spr[SPR::XER] >> 8); uint32_t ppc_result_d = 0; bool is_match = false; // for storing each byte uint8_t shift_amount = 24; while (bytes_remaining > 0) { uint8_t return_value = mmu_read_vmem(ppc_effective_address); ppc_result_d |= return_value << shift_amount; if (!shift_amount) { if (reg_d != reg_a && reg_d != reg_b) ppc_store_iresult_reg(reg_d, ppc_result_d); reg_d = (reg_d + 1) & 0x1F; ppc_result_d = 0; shift_amount = 24; } else { shift_amount -= 8; } ppc_effective_address++; bytes_remaining--; if (return_value == matching_byte) { is_match = true; break; } } // store partially loaded register if any if (shift_amount != 24 && reg_d != reg_a && reg_d != reg_b) ppc_store_iresult_reg(reg_d, ppc_result_d); ppc_state.spr[SPR::XER] = (ppc_state.spr[SPR::XER] & ~0x7F) | (bytes_to_load - bytes_remaining); if (rec) { ppc_state.cr = (ppc_state.cr & 0x0FFFFFFFUL) | (is_match ? CRx_bit::CR_EQ : 0) | ((ppc_state.spr[SPR::XER] & XER::SO) >> 3); } } template void dppc_interpreter::power_lscbx(); template void dppc_interpreter::power_lscbx(); template void dppc_interpreter::power_maskg() { ppc_grab_regssab(ppc_cur_instruction); uint32_t mask_start = ppc_result_d & 0x1F; uint32_t mask_end = ppc_result_b & 0x1F; uint32_t insert_mask = 0; if (mask_start < (mask_end + 1)) { insert_mask = power_rot_mask(mask_start, mask_end); } else if (mask_start == (mask_end + 1)) { insert_mask = 0xFFFFFFFF; } else { insert_mask = ~(power_rot_mask(mask_end + 1, mask_start - 1)); } ppc_result_a = insert_mask; if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_maskg(); template void dppc_interpreter::power_maskg(); template void dppc_interpreter::power_maskir() { ppc_grab_regssab(ppc_cur_instruction); ppc_result_a = (ppc_result_a & ~ppc_result_b) | (ppc_result_d & ppc_result_b); if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_maskir(); template void dppc_interpreter::power_maskir(); template void dppc_interpreter::power_mul() { ppc_grab_regsdab(ppc_cur_instruction); int64_t product = int64_t(int32_t(ppc_result_a)) * int32_t(ppc_result_b); uint32_t ppc_result_d = uint32_t(uint64_t(product) >> 32); ppc_state.spr[SPR::MQ] = uint32_t(product); if (ov) { if (uint64_t(product >> 31) + 1 & ~1) { ppc_state.spr[SPR::XER] |= XER::SO | XER::OV; } else { ppc_state.spr[SPR::XER] &= ~XER::OV; } } if (rec) ppc_changecrf0(uint32_t(product)); ppc_store_iresult_reg(reg_d, ppc_result_d); } template void dppc_interpreter::power_mul(); template void dppc_interpreter::power_mul(); template void dppc_interpreter::power_mul(); template void dppc_interpreter::power_mul(); template void dppc_interpreter::power_nabs() { ppc_grab_regsda(ppc_cur_instruction); uint32_t ppc_result_d = (int32_t(ppc_result_a) < 0) ? ppc_result_a : -ppc_result_a; if (ov) ppc_state.spr[SPR::XER] &= ~XER::OV; if (rec) ppc_changecrf0(ppc_result_d); ppc_store_iresult_reg(reg_d, ppc_result_d); } template void dppc_interpreter::power_nabs(); template void dppc_interpreter::power_nabs(); template void dppc_interpreter::power_nabs(); template void dppc_interpreter::power_nabs(); void dppc_interpreter::power_rlmi() { ppc_grab_regssab(ppc_cur_instruction); unsigned rot_mb = (ppc_cur_instruction >> 6) & 0x1F; unsigned rot_me = (ppc_cur_instruction >> 1) & 0x1F; unsigned rot_sh = ppc_result_b & 0x1F; uint32_t r = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh))); uint32_t mask = power_rot_mask(rot_mb, rot_me); ppc_result_a = ((r & mask) | (ppc_result_a & ~mask)); if ((ppc_cur_instruction & 0x01) == 1) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_rrib() { ppc_grab_regssab(ppc_cur_instruction); unsigned rot_sh = ppc_result_b & 0x1F; if (int32_t(ppc_result_d) < 0) { ppc_result_a |= (0x80000000U >> rot_sh); } else { ppc_result_a &= ~(0x80000000U >> rot_sh); } if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_rrib(); template void dppc_interpreter::power_rrib(); template void dppc_interpreter::power_sle() { ppc_grab_regssab(ppc_cur_instruction); unsigned rot_sh = ppc_result_b & 0x1F; ppc_result_a = ppc_result_d << rot_sh; ppc_state.spr[SPR::MQ] = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh))); ppc_store_iresult_reg(reg_a, ppc_result_a); if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_sle(); template void dppc_interpreter::power_sle(); template void dppc_interpreter::power_sleq() { ppc_grab_regssab(ppc_cur_instruction); unsigned rot_sh = ppc_result_b & 0x1F; uint32_t r = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh))); uint32_t mask = power_rot_mask(0, 31 - rot_sh); ppc_result_a = ((r & mask) | (ppc_state.spr[SPR::MQ] & ~mask)); ppc_state.spr[SPR::MQ] = r; if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_sleq(); template void dppc_interpreter::power_sleq(); template void dppc_interpreter::power_sliq() { ppc_grab_regssash(ppc_cur_instruction); ppc_result_a = ppc_result_d << rot_sh; ppc_state.spr[SPR::MQ] = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh))); if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_sliq(); template void dppc_interpreter::power_sliq(); template void dppc_interpreter::power_slliq() { ppc_grab_regssash(ppc_cur_instruction); uint32_t r = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh))); uint32_t mask = power_rot_mask(0, 31 - rot_sh); ppc_result_a = ((r & mask) | (ppc_state.spr[SPR::MQ] & ~mask)); ppc_state.spr[SPR::MQ] = r; if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_slliq(); template void dppc_interpreter::power_slliq(); template void dppc_interpreter::power_sllq() { ppc_grab_regssab(ppc_cur_instruction); unsigned rot_sh = ppc_result_b & 0x1F; if (ppc_result_b & 0x20) { ppc_result_a = ppc_state.spr[SPR::MQ] & (-1U << rot_sh); } else { ppc_result_a = ((ppc_result_d << rot_sh) | (ppc_state.spr[SPR::MQ] & ((1 << rot_sh) - 1))); } if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_sllq(); template void dppc_interpreter::power_sllq(); template void dppc_interpreter::power_slq() { ppc_grab_regssab(ppc_cur_instruction); unsigned rot_sh = ppc_result_b & 0x1F; if (ppc_result_b & 0x20) { ppc_result_a = 0; } else { ppc_result_a = ppc_result_d << rot_sh; } if (rec) ppc_changecrf0(ppc_result_a); ppc_state.spr[SPR::MQ] = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh))); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_slq(); template void dppc_interpreter::power_slq(); template void dppc_interpreter::power_sraiq() { ppc_grab_regssash(ppc_cur_instruction); uint32_t mask = (1 << rot_sh) - 1; ppc_result_a = (int32_t)ppc_result_d >> rot_sh; ppc_state.spr[SPR::MQ] = (ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh)); if ((int32_t(ppc_result_d) < 0) && (ppc_result_d & mask)) { ppc_state.spr[SPR::XER] |= XER::CA; } else { ppc_state.spr[SPR::XER] &= ~XER::CA; } if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_sraiq(); template void dppc_interpreter::power_sraiq(); template void dppc_interpreter::power_sraq() { ppc_grab_regssab(ppc_cur_instruction); unsigned rot_sh = ppc_result_b & 0x1F; uint32_t mask = (ppc_result_b & 0x20) ? -1 : (1 << rot_sh) - 1; ppc_result_a = (int32_t)ppc_result_d >> ((ppc_result_b & 0x20) ? 31 : rot_sh); ppc_state.spr[SPR::MQ] = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh))); if ((int32_t(ppc_result_d) < 0) && (ppc_result_d & mask)) { ppc_state.spr[SPR::XER] |= XER::CA; } else { ppc_state.spr[SPR::XER] &= ~XER::CA; } ppc_state.spr[SPR::MQ] = (ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh)); if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_sraq(); template void dppc_interpreter::power_sraq(); template void dppc_interpreter::power_sre() { ppc_grab_regssab(ppc_cur_instruction); unsigned rot_sh = ppc_result_b & 0x1F; ppc_result_a = ppc_result_d >> rot_sh; ppc_state.spr[SPR::MQ] = (ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh)); if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_sre(); template void dppc_interpreter::power_sre(); template void dppc_interpreter::power_srea() { ppc_grab_regssab(ppc_cur_instruction); unsigned rot_sh = ppc_result_b & 0x1F; ppc_result_a = (int32_t)ppc_result_d >> rot_sh; uint32_t r = ((ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh))); uint32_t mask = -1U >> rot_sh; if ((int32_t(ppc_result_d) < 0) && (r & ~mask)) { ppc_state.spr[SPR::XER] |= XER::CA; } else { ppc_state.spr[SPR::XER] &= ~XER::CA; } if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); ppc_state.spr[SPR::MQ] = r; } template void dppc_interpreter::power_srea(); template void dppc_interpreter::power_srea(); template void dppc_interpreter::power_sreq() { ppc_grab_regssab(ppc_cur_instruction); unsigned rot_sh = ppc_result_b & 0x1F; uint32_t mask = -1U >> rot_sh; ppc_result_a = (ppc_result_d >> rot_sh) | (ppc_state.spr[SPR::MQ] & ~mask); ppc_state.spr[SPR::MQ] = (ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh)); if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_sreq(); template void dppc_interpreter::power_sreq(); template void dppc_interpreter::power_sriq() { ppc_grab_regssash(ppc_cur_instruction); ppc_result_a = ppc_result_d >> rot_sh; ppc_state.spr[SPR::MQ] = (ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh)); if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_sriq(); template void dppc_interpreter::power_sriq(); template void dppc_interpreter::power_srliq() { ppc_grab_regssash(ppc_cur_instruction); uint32_t r = (ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh)); unsigned mask = power_rot_mask(rot_sh, 31); ppc_result_a = ((r & mask) | (ppc_state.spr[SPR::MQ] & ~mask)); ppc_state.spr[SPR::MQ] = r; if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_srliq(); template void dppc_interpreter::power_srliq(); template void dppc_interpreter::power_srlq() { ppc_grab_regssab(ppc_cur_instruction); unsigned rot_sh = ppc_result_b & 0x1F; uint32_t r = (ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh)); unsigned mask = power_rot_mask(rot_sh, 31); if (ppc_result_b & 0x20) { ppc_result_a = (ppc_state.spr[SPR::MQ] & mask); } else { ppc_result_a = ((r & mask) | (ppc_state.spr[SPR::MQ] & ~mask)); } if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_srlq(); template void dppc_interpreter::power_srlq(); template void dppc_interpreter::power_srq() { ppc_grab_regssab(ppc_cur_instruction); unsigned rot_sh = ppc_result_b & 0x1F; if (ppc_result_b & 0x20) { ppc_result_a = 0; } else { ppc_result_a = ppc_result_d >> rot_sh; } ppc_state.spr[SPR::MQ] = (ppc_result_d >> rot_sh) | (ppc_result_d << (32 - rot_sh)); if (rec) ppc_changecrf0(ppc_result_a); ppc_store_iresult_reg(reg_a, ppc_result_a); } template void dppc_interpreter::power_srq(); template void dppc_interpreter::power_srq();