/* 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 // Affects the XER register's SO and OV Bits inline void power_setsoov(uint32_t a, uint32_t b, uint32_t d) { if ((a ^ b) & (a ^ d) & 0x80000000UL) { ppc_state.spr[SPR::XER] |= XER::SO | XER::OV; } else { ppc_state.spr[SPR::XER] &= ~XER::OV; } } /** 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 = 0xFFFFFFFFUL >> rot_mb; uint32_t m2 = uint32_t(0xFFFFFFFFUL << (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 = ppc_result_a & 0x7FFFFFFF; } 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_regsda(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 ppc_result_d = 64; break; default: ppc_result_d = 0; } 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); uint64_t dividend = ((uint64_t)ppc_result_a << 32) | ppc_state.spr[SPR::MQ]; int32_t divisor = ppc_result_b; if ((ppc_result_a == 0x80000000UL && divisor == -1) || !divisor) { ppc_state.spr[SPR::MQ] = 0; ppc_result_d = 0x80000000UL; // -2^31 aka INT32_MIN } else { ppc_result_d = uint32_t(dividend / divisor); ppc_state.spr[SPR::MQ] = dividend % divisor; } if (ov) power_setsoov(ppc_result_b, ppc_result_a, ppc_result_d); if (rec) ppc_changecrf0(ppc_result_d); ppc_store_iresult_reg(reg_d, ppc_result_d); } 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() { ppc_grab_regsdab(ppc_cur_instruction); uint32_t ppc_result_d = ppc_result_a / ppc_result_b; ppc_state.spr[SPR::MQ] = (ppc_result_a % ppc_result_b); if (ov) power_setsoov(ppc_result_b, ppc_result_a, ppc_result_d); if (rec) ppc_changecrf0(ppc_result_d); ppc_store_iresult_reg(reg_d, ppc_result_d); } 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)) ? 0 : ppc_result_b - ppc_result_a; if (rec) ppc_changecrf0(ppc_result_d); if (ov) power_setsoov(ppc_result_a, ppc_result_b, 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); uint8_t return_value = 0; uint32_t bytes_to_load = (ppc_state.spr[SPR::XER] & 0x7F); uint32_t bytes_copied = 0; uint8_t matching_byte = (uint8_t)(ppc_state.spr[SPR::XER] >> 8); uint32_t ppc_result_d = 0; // for storing each byte uint32_t bitmask = 0xFF000000; uint8_t shift_amount = 24; while (bytes_to_load > 0) { return_value = mmu_read_vmem(ppc_effective_address); ppc_result_d = (ppc_result_d & ~bitmask) | (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; bitmask = 0xFF000000; shift_amount = 24; } else { bitmask >>= 8; shift_amount -= 8; } ppc_effective_address++; bytes_copied++; bytes_to_load--; if (return_value == matching_byte) break; } // store partiallly 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_copied; if (rec) ppc_changecrf0(ppc_result_d); } 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_d); 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); uint64_t product; product = ((uint64_t)ppc_result_a) * ((uint64_t)ppc_result_b); uint32_t ppc_result_d = ((uint32_t)(product >> 32)); ppc_state.spr[SPR::MQ] = ((uint32_t)(product)); if (rec) ppc_changecrf0(ppc_result_d); if (ov) power_setsoov(ppc_result_a, ppc_result_b, ppc_result_d); 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 (rec) ppc_changecrf0(ppc_result_d); if (ov) ppc_state.spr[SPR::XER] &= ~XER::OV; 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); if (int32_t(ppc_result_d) < 0) { ppc_result_a |= ((ppc_result_d & 0x80000000) >> ppc_result_b); } else { ppc_result_a &= ~((ppc_result_d & 0x80000000) >> ppc_result_b); } 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; uint32_t r = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh))); uint32_t mask = power_rot_mask(0, 31 - rot_sh); 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_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 = ppc_result_d << rot_sh; } else { ppc_result_a = 0; } if (rec) ppc_changecrf0(ppc_result_a); ppc_state.spr[SPR::MQ] = ((ppc_result_d << rot_sh) | (ppc_result_d >> (32 - rot_sh))); } 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 = (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; } 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; 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 & rot_sh)) { 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_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; unsigned mask = power_rot_mask(rot_sh, 31); ppc_result_a = ((rot_sh & mask) | (ppc_state.spr[SPR::MQ] & ~mask)); ppc_state.spr[SPR::MQ] = 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();