/*
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 .
*/
// General opcodes for the processor - ppcopcodes.cpp
#include
#include
#include "ppcemu.h"
#include "ppcmacros.h"
#include "ppcmmu.h"
#include
#include
//Extract the registers desired and the values of the registers.
// 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] |= XER::CA;
} else {
ppc_state.spr[SPR::XER] &= ~XER::CA;
}
}
inline void ppc_carry_sub(uint32_t a, uint32_t b) {
if (b >= a) {
ppc_state.spr[SPR::XER] |= XER::CA;
} else {
ppc_state.spr[SPR::XER] &= ~XER::CA;
}
}
// 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] |= XER::SO | XER::OV;
} else {
ppc_state.spr[SPR::XER] &= ~XER::OV;
}
}
typedef std::function CtxSyncCallback;
std::vector gCtxSyncCallbacks;
// perform context synchronization by executing registered actions if any
void do_ctx_sync() {
while (!gCtxSyncCallbacks.empty()) {
gCtxSyncCallbacks.back()();
gCtxSyncCallbacks.pop_back();
}
}
void add_ctx_sync_action(const CtxSyncCallback &cb) {
gCtxSyncCallbacks.push_back(cb);
}
/**
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).
**/
template
void dppc_interpreter::ppc_addi() {
ppc_grab_regsdasimm(ppc_cur_instruction);
if (shift)
ppc_state.gpr[reg_d] = (reg_a == 0) ? (simm << 16) : (ppc_result_a + (simm << 16));
else
ppc_state.gpr[reg_d] = (reg_a == 0) ? simm : (ppc_result_a + simm);
}
template void dppc_interpreter::ppc_addi();
template void dppc_interpreter::ppc_addi();
template
void dppc_interpreter::ppc_addic() {
ppc_grab_regsdasimm(ppc_cur_instruction);
uint32_t ppc_result_d = (ppc_result_a + simm);
ppc_carry(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::ppc_addic();
template void dppc_interpreter::ppc_addic();;
template
void dppc_interpreter::ppc_add() {
ppc_grab_regsdab(ppc_cur_instruction);
uint32_t ppc_result_d = ppc_result_a + ppc_result_b;
if (carry)
ppc_carry(ppc_result_a, ppc_result_d);
if (ov)
ppc_setsoov(ppc_result_a, ~ppc_result_b, ppc_result_d);
if (rec)
ppc_changecrf0(ppc_result_d);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
template void dppc_interpreter::ppc_add();
template void dppc_interpreter::ppc_add();
template void dppc_interpreter::ppc_add();
template void dppc_interpreter::ppc_add();
template void dppc_interpreter::ppc_add();
template void dppc_interpreter::ppc_add();
template void dppc_interpreter::ppc_add();
template void dppc_interpreter::ppc_add();
template
void dppc_interpreter::ppc_adde() {
ppc_grab_regsdab(ppc_cur_instruction);
uint32_t xer_ca = !!(ppc_state.spr[SPR::XER] & XER::CA);
uint32_t 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] |= XER::CA;
} else {
ppc_state.spr[SPR::XER] &= ~XER::CA;
}
if (ov)
ppc_setsoov(ppc_result_a, ~ppc_result_b, ppc_result_d);
if (rec)
ppc_changecrf0(ppc_result_d);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
template void dppc_interpreter::ppc_adde();
template void dppc_interpreter::ppc_adde();
template void dppc_interpreter::ppc_adde();
template void dppc_interpreter::ppc_adde();
template
void dppc_interpreter::ppc_addme() {
ppc_grab_regsda(ppc_cur_instruction);
uint32_t xer_ca = !!(ppc_state.spr[SPR::XER] & XER::CA);
uint32_t 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] |= XER::CA;
} else {
ppc_state.spr[SPR::XER] &= ~XER::CA;
}
if (ov)
ppc_setsoov(ppc_result_a, 0, ppc_result_d);
if (rec)
ppc_changecrf0(ppc_result_d);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
template void dppc_interpreter::ppc_addme();
template void dppc_interpreter::ppc_addme();
template void dppc_interpreter::ppc_addme();
template void dppc_interpreter::ppc_addme();
template
void dppc_interpreter::ppc_addze() {
ppc_grab_regsda(ppc_cur_instruction);
uint32_t grab_xer = !!(ppc_state.spr[SPR::XER] & XER::CA);
uint32_t ppc_result_d = ppc_result_a + grab_xer;
if (ppc_result_d < ppc_result_a) {
ppc_state.spr[SPR::XER] |= XER::CA;
} else {
ppc_state.spr[SPR::XER] &= ~XER::CA;
}
if (ov)
ppc_setsoov(ppc_result_a, 0xFFFFFFFFUL, ppc_result_d);
if (rec)
ppc_changecrf0(ppc_result_d);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
template void dppc_interpreter::ppc_addze();
template void dppc_interpreter::ppc_addze();
template void dppc_interpreter::ppc_addze();
template void dppc_interpreter::ppc_addze();
void dppc_interpreter::ppc_subfic() {
ppc_grab_regsdasimm(ppc_cur_instruction);
uint32_t ppc_result_d = simm - ppc_result_a;
if (simm == -1)
ppc_state.spr[SPR::XER] |= XER::CA;
else
ppc_carry(~ppc_result_a, ppc_result_d);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
template
void dppc_interpreter::ppc_subf() {
ppc_grab_regsdab(ppc_cur_instruction);
uint32_t ppc_result_d = ppc_result_b - ppc_result_a;
if (carry)
ppc_carry_sub(ppc_result_a, ppc_result_b);
if (ov)
ppc_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::ppc_subf();
template void dppc_interpreter::ppc_subf();
template void dppc_interpreter::ppc_subf();
template void dppc_interpreter::ppc_subf();
template void dppc_interpreter::ppc_subf();
template void dppc_interpreter::ppc_subf();
template void dppc_interpreter::ppc_subf();
template void dppc_interpreter::ppc_subf();
template
void dppc_interpreter::ppc_subfe() {
ppc_grab_regsdab(ppc_cur_instruction);
uint32_t grab_ca = !!(ppc_state.spr[SPR::XER] & XER::CA);
uint32_t ppc_result_d = ~ppc_result_a + ppc_result_b + grab_ca;
if (grab_ca && ppc_result_b == 0xFFFFFFFFUL)
ppc_state.spr[SPR::XER] |= XER::CA;
else
ppc_carry(~ppc_result_a, ppc_result_d);
if (ov)
ppc_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::ppc_subfe();
template void dppc_interpreter::ppc_subfe();
template void dppc_interpreter::ppc_subfe();
template void dppc_interpreter::ppc_subfe();
template
void dppc_interpreter::ppc_subfme() {
ppc_grab_regsda(ppc_cur_instruction);
uint32_t grab_ca = !!(ppc_state.spr[SPR::XER] & XER::CA);
uint32_t ppc_result_d = ~ppc_result_a + grab_ca - 1;
if (ppc_result_a == 0xFFFFFFFFUL && !grab_ca)
ppc_state.spr[SPR::XER] &= ~XER::CA;
else
ppc_state.spr[SPR::XER] |= XER::CA;
if (ov) {
if (ppc_result_d == ppc_result_a && 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::ppc_subfme();
template void dppc_interpreter::ppc_subfme();
template void dppc_interpreter::ppc_subfme();
template void dppc_interpreter::ppc_subfme();
template
void dppc_interpreter::ppc_subfze() {
ppc_grab_regsda(ppc_cur_instruction);
uint32_t grab_ca = !!(ppc_state.spr[SPR::XER] & XER::CA);
uint32_t ppc_result_d = ~ppc_result_a + grab_ca;
if (!ppc_result_d && grab_ca) // special case: ppc_result_d = 0 and CA=1
ppc_state.spr[SPR::XER] |= XER::CA;
else
ppc_state.spr[SPR::XER] &= ~XER::CA;
if (ov) {
if (ppc_result_d && ppc_result_d == ppc_result_a)
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::ppc_subfze();
template void dppc_interpreter::ppc_subfze();
template void dppc_interpreter::ppc_subfze();
template void dppc_interpreter::ppc_subfze();
template
void dppc_interpreter::ppc_andirc() {
ppc_grab_regssauimm(ppc_cur_instruction);
ppc_result_a = shift ? (ppc_result_d & (uimm << 16)) : (ppc_result_d & uimm);
ppc_changecrf0(ppc_result_a);
ppc_store_iresult_reg(reg_a, ppc_result_a);
}
template void dppc_interpreter::ppc_andirc();
template void dppc_interpreter::ppc_andirc();
template
void dppc_interpreter::ppc_ori() {
ppc_grab_regssauimm(ppc_cur_instruction);
ppc_result_a = shift ? (ppc_result_d | (uimm << 16)) : (ppc_result_d | uimm);
ppc_store_iresult_reg(reg_a, ppc_result_a);
}
template void dppc_interpreter::ppc_ori();
template void dppc_interpreter::ppc_ori();
template
void dppc_interpreter::ppc_xori() {
ppc_grab_regssauimm(ppc_cur_instruction);
ppc_result_a = shift ? (ppc_result_d ^ (uimm << 16)) : (ppc_result_d ^ uimm);
ppc_store_iresult_reg(reg_a, ppc_result_a);
}
template void dppc_interpreter::ppc_xori();
template void dppc_interpreter::ppc_xori();
template
void dppc_interpreter::ppc_logical() {
ppc_grab_regssab(ppc_cur_instruction);
if (logical_op == logical_fun::ppc_and)
ppc_result_a = ppc_result_d & ppc_result_b;
else if (logical_op == logical_fun::ppc_andc)
ppc_result_a = ppc_result_d & ~(ppc_result_b);
else if (logical_op == logical_fun::ppc_eqv)
ppc_result_a = ~(ppc_result_d ^ ppc_result_b);
else if (logical_op == logical_fun::ppc_nand)
ppc_result_a = ~(ppc_result_d & ppc_result_b);
else if (logical_op == logical_fun::ppc_nor)
ppc_result_a = ~(ppc_result_d | ppc_result_b);
else if (logical_op == logical_fun::ppc_or)
ppc_result_a = ppc_result_d | ppc_result_b;
else if (logical_op == logical_fun::ppc_orc)
ppc_result_a = ppc_result_d | ~(ppc_result_b);
else if (logical_op == logical_fun::ppc_xor)
ppc_result_a = 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::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template void dppc_interpreter::ppc_logical();
template
void dppc_interpreter::ppc_neg() {
ppc_grab_regsda(ppc_cur_instruction);
uint32_t ppc_result_d = ~(ppc_result_a) + 1;
if (ov) {
if (ppc_result_a == 0x80000000)
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::ppc_neg();;
template void dppc_interpreter::ppc_neg();
template void dppc_interpreter::ppc_neg();
template void dppc_interpreter::ppc_neg();
template
void dppc_interpreter::ppc_cntlzw() {
ppc_grab_regssa(ppc_cur_instruction);
uint32_t bit_check = ppc_result_d;
#ifdef __builtin_clz //for GCC and Clang users
uint32_t lead = !bit_check ? 32 : __builtin_clz(bit_check);
#elif defined __lzcnt //for Visual C++ users
uint32_t lead = __lzcnt(bit_check);
#else
uint32_t lead, mask;
for (mask = 0x80000000UL, lead = 0; mask != 0; lead++, mask >>= 1) {
if (bit_check & mask)
break;
}
#endif
ppc_result_a = lead;
if (rec) {
ppc_changecrf0(ppc_result_a);
}
ppc_store_iresult_reg(reg_a, ppc_result_a);
}
template void dppc_interpreter::ppc_cntlzw();
template void dppc_interpreter::ppc_cntlzw();;
template
void dppc_interpreter::ppc_mulhwu() {
ppc_grab_regsdab(ppc_cur_instruction);
uint64_t product = uint64_t(ppc_result_a) * uint64_t(ppc_result_b);
uint32_t ppc_result_d = uint32_t(product >> 32);
if (rec)
ppc_changecrf0(ppc_result_d);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
template void dppc_interpreter::ppc_mulhwu();
template void dppc_interpreter::ppc_mulhwu();;
template
void dppc_interpreter::ppc_mulhw() {
ppc_grab_regsdab(ppc_cur_instruction);
int64_t product = int64_t(int32_t(ppc_result_a)) * int64_t(int32_t(ppc_result_b));
uint32_t ppc_result_d = product >> 32;
if (rec)
ppc_changecrf0(ppc_result_d);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
template void dppc_interpreter::ppc_mulhw();
template void dppc_interpreter::ppc_mulhw();;
template
void dppc_interpreter::ppc_mullw() {
ppc_grab_regsdab(ppc_cur_instruction);
int64_t product = int64_t(int32_t(ppc_result_a)) * int64_t(int32_t(ppc_result_b));
if (ov) {
if (product != int64_t(int32_t(product))) {
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
} else {
ppc_state.spr[SPR::XER] &= ~XER::OV;
}
}
uint32_t ppc_result_d = (uint32_t)product;
if (rec)
ppc_changecrf0(ppc_result_d);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
template void dppc_interpreter::ppc_mullw();
template void dppc_interpreter::ppc_mullw();
template void dppc_interpreter::ppc_mullw();
template void dppc_interpreter::ppc_mullw();
void dppc_interpreter::ppc_mulli() {
ppc_grab_regsdasimm(ppc_cur_instruction);
int64_t product = int64_t(int32_t(ppc_result_a)) * int64_t(int32_t(simm));
uint32_t ppc_result_d = uint32_t(product);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
template
void dppc_interpreter::ppc_divw() {
uint32_t ppc_result_d;
ppc_grab_regsdab(ppc_cur_instruction);
if (!ppc_result_b) { // handle the "anything / 0" case
ppc_result_d = 0; // tested on G4 in Mac OS X 10.4 and Open Firmware.
// ppc_result_d = (ppc_result_a & 0x80000000) ? -1 : 0; /* UNDOCUMENTED! */
if (ov)
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
} else if (ppc_result_a == 0x80000000UL && ppc_result_b == 0xFFFFFFFFUL) {
ppc_result_d = 0; // tested on G4 in Mac OS X 10.4 and Open Firmware.
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);
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::ppc_divw();;
template void dppc_interpreter::ppc_divw();
template void dppc_interpreter::ppc_divw();
template void dppc_interpreter::ppc_divw();
template
void dppc_interpreter::ppc_divwu() {
uint32_t ppc_result_d;
ppc_grab_regsdab(ppc_cur_instruction);
if (!ppc_result_b) { // division by zero
ppc_result_d = 0;
if (ov)
ppc_state.spr[SPR::XER] |= XER::SO | XER::OV;
if (rec)
ppc_state.cr |= 0x20000000;
} else {
ppc_result_d = ppc_result_a / ppc_result_b;
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::ppc_divwu();;
template void dppc_interpreter::ppc_divwu();
template void dppc_interpreter::ppc_divwu();
template void dppc_interpreter::ppc_divwu();
// Value shifting
template
void dppc_interpreter::ppc_shift() {
ppc_grab_regssab(ppc_cur_instruction);
if (ppc_result_b & 0x20) {
ppc_result_a = 0;
}
else {
ppc_result_a = isleft ? (ppc_result_d << (ppc_result_b & 0x1F))
: (ppc_result_d >> (ppc_result_b & 0x1F));
}
if (rec)
ppc_changecrf0(ppc_result_a);
ppc_store_iresult_reg(reg_a, ppc_result_a);
}
template void dppc_interpreter::ppc_shift();
template void dppc_interpreter::ppc_shift();
template void dppc_interpreter::ppc_shift();
template void dppc_interpreter::ppc_shift();
template
void dppc_interpreter::ppc_sraw() {
ppc_grab_regssab(ppc_cur_instruction);
// clear XER[CA] by default
ppc_state.spr[SPR::XER] &= ~XER::CA;
if (ppc_result_b & 0x20) {
// fill rA with the sign bit of rS
ppc_result_a = int32_t(ppc_result_d) >> 31;
if (ppc_result_a) // if rA is negative
ppc_state.spr[SPR::XER] |= XER::CA;
} else {
uint32_t shift = ppc_result_b & 0x1F;
ppc_result_a = int32_t(ppc_result_d) >> shift;
if ((ppc_result_d & 0x80000000UL) && (ppc_result_d & ((1U << shift) - 1)))
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::ppc_sraw();
template void dppc_interpreter::ppc_sraw();;
template
void dppc_interpreter::ppc_srawi() {
ppc_grab_regssa(ppc_cur_instruction);
uint32_t shift = (ppc_cur_instruction >> 11) & 0x1F;
// clear XER[CA] by default
ppc_state.spr[SPR::XER] &= ~XER::CA;
if ((ppc_result_d & 0x80000000UL) && (ppc_result_d & ((1U << shift) - 1)))
ppc_state.spr[SPR::XER] |= XER::CA;
ppc_result_a = int32_t(ppc_result_d) >> shift;
if (rec)
ppc_changecrf0(ppc_result_a);
ppc_store_iresult_reg(reg_a, ppc_result_a);
}
template void dppc_interpreter::ppc_srawi();
template void dppc_interpreter::ppc_srawi();;
/** 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 = (uint32_t)(0xFFFFFFFFUL << (31 - rot_me));
return ((rot_mb <= rot_me) ? m2 & m1 : m1 | m2);
}
void dppc_interpreter::ppc_rlwimi() {
ppc_grab_regssa(ppc_cur_instruction);
unsigned rot_sh = (ppc_cur_instruction >> 11) & 0x1F;
unsigned rot_mb = (ppc_cur_instruction >> 6) & 0x1F;
unsigned rot_me = (ppc_cur_instruction >> 1) & 0x1F;
uint32_t mask = rot_mask(rot_mb, rot_me);
uint32_t r = rot_sh ? ((ppc_result_d << rot_sh) |
(ppc_result_d >> (32 - rot_sh))) : ppc_result_d;
ppc_result_a = (ppc_result_a & ~mask) | (r & mask);
if ((ppc_cur_instruction & 0x01) == 1) {
ppc_changecrf0(ppc_result_a);
}
ppc_store_iresult_reg(reg_a, ppc_result_a);
}
void dppc_interpreter::ppc_rlwinm() {
ppc_grab_regssa(ppc_cur_instruction);
unsigned rot_sh = (ppc_cur_instruction >> 11) & 0x1F;
unsigned rot_mb = (ppc_cur_instruction >> 6) & 0x1F;
unsigned rot_me = (ppc_cur_instruction >> 1) & 0x1F;
uint32_t mask = rot_mask(rot_mb, rot_me);
uint32_t r = rot_sh ? ((ppc_result_d << rot_sh) |
(ppc_result_d >> (32 - rot_sh))) : ppc_result_d;
ppc_result_a = r & mask;
if ((ppc_cur_instruction & 0x01) == 1) {
ppc_changecrf0(ppc_result_a);
}
ppc_store_iresult_reg(reg_a, ppc_result_a);
}
void dppc_interpreter::ppc_rlwnm() {
ppc_grab_regssab(ppc_cur_instruction);
ppc_result_b &= 0x1F;
unsigned rot_mb = (ppc_cur_instruction >> 6) & 0x1F;
unsigned rot_me = (ppc_cur_instruction >> 1) & 0x1F;
uint32_t mask = rot_mask(rot_mb, rot_me);
uint32_t rot = ppc_result_b & 0x1F;
uint32_t r = rot ? ((ppc_result_d << rot) |
(ppc_result_d >> (32 - rot))) : ppc_result_d;
ppc_result_a = r & mask;
if ((ppc_cur_instruction & 0x01) == 1) {
ppc_changecrf0(ppc_result_a);
}
ppc_store_iresult_reg(reg_a, ppc_result_a);
}
void dppc_interpreter::ppc_mfcr() {
int reg_d = (ppc_cur_instruction >> 21) & 0x1F;
ppc_state.gpr[reg_d] = ppc_state.cr;
}
void dppc_interpreter::ppc_mtsr() {
#ifdef CPU_PROFILING
num_supervisor_instrs++;
#endif
if (ppc_state.msr & MSR::PR) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::NOT_ALLOWED);
}
int reg_s = (ppc_cur_instruction >> 21) & 0x1F;
uint32_t grab_sr = (ppc_cur_instruction >> 16) & 0x0F;
ppc_state.sr[grab_sr] = ppc_state.gpr[reg_s];
mmu_pat_ctx_changed();
}
void dppc_interpreter::ppc_mtsrin() {
#ifdef CPU_PROFILING
num_supervisor_instrs++;
#endif
if (ppc_state.msr & MSR::PR) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::NOT_ALLOWED);
}
ppc_grab_regssb(ppc_cur_instruction);
uint32_t grab_sr = ppc_result_b >> 28;
ppc_state.sr[grab_sr] = ppc_result_d;
mmu_pat_ctx_changed();
}
void dppc_interpreter::ppc_mfsr() {
#ifdef CPU_PROFILING
num_supervisor_instrs++;
#endif
if (ppc_state.msr & MSR::PR) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::NOT_ALLOWED);
}
int reg_d = (ppc_cur_instruction >> 21) & 0x1F;
uint32_t grab_sr = (ppc_cur_instruction >> 16) & 0x0F;
ppc_state.gpr[reg_d] = ppc_state.sr[grab_sr];
}
void dppc_interpreter::ppc_mfsrin() {
#ifdef CPU_PROFILING
num_supervisor_instrs++;
#endif
if (ppc_state.msr & MSR::PR) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::NOT_ALLOWED);
}
ppc_grab_regsdb(ppc_cur_instruction);
uint32_t grab_sr = ppc_result_b >> 28;
ppc_state.gpr[reg_d] = ppc_state.sr[grab_sr];
}
void dppc_interpreter::ppc_mfmsr() {
#ifdef CPU_PROFILING
num_supervisor_instrs++;
#endif
if (ppc_state.msr & MSR::PR) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::NOT_ALLOWED);
}
uint32_t reg_d = (ppc_cur_instruction >> 21) & 0x1F;
ppc_state.gpr[reg_d] = ppc_state.msr;
}
void dppc_interpreter::ppc_mtmsr() {
#ifdef CPU_PROFILING
num_supervisor_instrs++;
#endif
if (ppc_state.msr & MSR::PR) {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::NOT_ALLOWED);
}
uint32_t reg_s = (ppc_cur_instruction >> 21) & 0x1F;
ppc_state.msr = ppc_state.gpr[reg_s];
// generate External Interrupt Exception
// if CPU interrupt line is asserted
if (ppc_state.msr & MSR::EE && int_pin) {
//LOG_F(WARNING, "MTMSR: CPU INT pending, generate CPU exception");
ppc_exception_handler(Except_Type::EXC_EXT_INT, 0);
} else if ((ppc_state.msr & MSR::EE) && dec_exception_pending) {
dec_exception_pending = false;
//LOG_F(WARNING, "MTMSR: decrementer exception triggered");
ppc_exception_handler(Except_Type::EXC_DECR, 0);
} else {
mmu_change_mode();
}
}
static inline void calc_rtcl_value()
{
uint64_t new_ts = get_virt_time_ns();
uint64_t rtc_l = new_ts - rtc_timestamp + rtc_lo;
if (rtc_l >= ONE_BILLION_NS) { // check RTCL overflow
rtc_hi += (uint32_t)(rtc_l / ONE_BILLION_NS);
rtc_lo = rtc_l % ONE_BILLION_NS;
}
else {
rtc_lo = (uint32_t)rtc_l;
}
rtc_timestamp = new_ts;
}
static inline uint64_t calc_tbr_value()
{
uint64_t tbr_inc;
uint32_t tbr_inc_lo;
uint64_t diff = get_virt_time_ns() - tbr_wr_timestamp;
_u32xu64(tbr_freq_ghz, diff, tbr_inc, tbr_inc_lo);
return (tbr_wr_value + tbr_inc);
}
static inline uint32_t calc_dec_value() {
uint64_t dec_adj;
uint32_t dec_adj_lo;
uint64_t diff = get_virt_time_ns() - dec_wr_timestamp;
_u32xu64(tbr_freq_ghz, diff, dec_adj, dec_adj_lo);
return (dec_wr_value - static_cast(dec_adj));
}
static void update_timebase(uint64_t mask, uint64_t new_val)
{
uint64_t tbr_value = calc_tbr_value();
tbr_wr_value = (tbr_value & mask) | new_val;
tbr_wr_timestamp = get_virt_time_ns();
}
static uint32_t decrementer_timer_id = 0;
static void trigger_decrementer_exception() {
decrementer_timer_id = 0;
dec_wr_value = -1;
dec_wr_timestamp = get_virt_time_ns();
if (ppc_state.msr & MSR::EE) {
dec_exception_pending = false;
//LOG_F(WARNING, "decrementer exception triggered");
ppc_exception_handler(Except_Type::EXC_DECR, 0);
}
else {
//LOG_F(WARNING, "decrementer exception pending");
dec_exception_pending = true;
}
}
static void update_decrementer(uint32_t val) {
dec_wr_value = val;
dec_wr_timestamp = get_virt_time_ns();
dec_exception_pending = false;
if (is_601)
return;
if (decrementer_timer_id) {
//LOG_F(WARNING, "decrementer cancel timer");
TimerManager::get_instance()->cancel_timer(decrementer_timer_id);
}
uint64_t time_out;
uint32_t time_out_lo;
_u32xu64(val, tbr_period_ns, time_out, time_out_lo);
//LOG_F(WARNING, "decrementer:0x%08X ns:%llu", val, time_out);
decrementer_timer_id = TimerManager::get_instance()->add_oneshot_timer(
time_out,
trigger_decrementer_exception
);
}
void dppc_interpreter::ppc_mfspr() {
uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 0x1F) << 5) |
((ppc_cur_instruction >> 16) & 0x1F);
#ifdef CPU_PROFILING
if (ref_spr > 31) {
num_supervisor_instrs++;
}
#endif
switch (ref_spr) {
case SPR::RTCL_U:
calc_rtcl_value();
ppc_state.spr[SPR::RTCL_U] = rtc_lo & 0x3FFFFF80UL;
break;
case SPR::RTCU_U:
calc_rtcl_value();
ppc_state.spr[SPR::RTCU_U] = rtc_hi;
break;
case SPR::DEC:
ppc_state.spr[SPR::DEC] = calc_dec_value();
break;
}
ppc_state.gpr[(ppc_cur_instruction >> 21) & 0x1F] = ppc_state.spr[ref_spr];
}
void dppc_interpreter::ppc_mtspr() {
uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 0x1F) << 5) |
((ppc_cur_instruction >> 16) & 0x1F);
#ifdef CPU_PROFILING
if (ref_spr > 31) {
num_supervisor_instrs++;
}
#endif
if (ref_spr == SPR::PVR || (
ref_spr == SPR::MQ && !is_601
)) { // prevent writes to the read-only registers
return;
}
uint32_t val = ppc_state.gpr[(ppc_cur_instruction >> 21) & 0x1F];
ppc_state.spr[ref_spr] = val;
switch (ref_spr) {
case SPR::XER:
ppc_state.spr[ref_spr] = val & 0xe000ff7f;
break;
case SPR::SDR1:
mmu_pat_ctx_changed(); // adapt to SDR1 changes
break;
case SPR::RTCL_S:
calc_rtcl_value();
rtc_lo = val & 0x3FFFFF80UL;
break;
case SPR::RTCU_S:
calc_rtcl_value();
rtc_hi = val;
break;
case SPR::DEC:
update_decrementer(val);
break;
case SPR::TBL_S:
update_timebase(0xFFFFFFFF00000000ULL, val);
break;
case SPR::TBU_S:
update_timebase(0x00000000FFFFFFFFULL, uint64_t(val) << 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 dppc_interpreter::ppc_mftb() {
uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 0x1F) << 5) |
((ppc_cur_instruction >> 16) & 0x1F);
int reg_d = (ppc_cur_instruction >> 21) & 0x1F;
uint64_t tbr_value = calc_tbr_value();
switch (ref_spr) {
case SPR::TBL_U:
ppc_state.gpr[reg_d] = uint32_t(tbr_value);
break;
case SPR::TBU_U:
ppc_state.gpr[reg_d] = uint32_t(tbr_value >> 32);
break;
default:
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
void dppc_interpreter::ppc_mtcrf() {
ppc_grab_regssa(ppc_cur_instruction);
uint8_t crm = (ppc_cur_instruction >> 12) & 0xFFU;
uint32_t cr_mask = 0;
if (crm == 0xFFU) // the fast case
cr_mask = 0xFFFFFFFFUL;
else { // the slow case
if (crm & 0x80) cr_mask |= 0xF0000000UL;
if (crm & 0x40) cr_mask |= 0x0F000000UL;
if (crm & 0x20) cr_mask |= 0x00F00000UL;
if (crm & 0x10) cr_mask |= 0x000F0000UL;
if (crm & 0x08) cr_mask |= 0x0000F000UL;
if (crm & 0x04) cr_mask |= 0x00000F00UL;
if (crm & 0x02) cr_mask |= 0x000000F0UL;
if (crm & 0x01) cr_mask |= 0x0000000FUL;
}
ppc_state.cr = (ppc_state.cr & ~cr_mask) | (ppc_result_d & cr_mask);
}
void dppc_interpreter::ppc_mcrxr() {
int crf_d = (ppc_cur_instruction >> 21) & 0x1C;
ppc_state.cr = (ppc_state.cr & ~(0xF0000000UL >> crf_d)) |
((ppc_state.spr[SPR::XER] & 0xF0000000UL) >> crf_d);
ppc_state.spr[SPR::XER] &= 0x0FFFFFFF;
}
template
void dppc_interpreter::ppc_exts() {
ppc_grab_regssa(ppc_cur_instruction);
ppc_result_a = int32_t(T(ppc_result_d));
if (rec)
ppc_changecrf0(ppc_result_a);
ppc_store_iresult_reg(reg_a, ppc_result_a);
}
template void dppc_interpreter::ppc_exts();
template void dppc_interpreter::ppc_exts();
template void dppc_interpreter::ppc_exts();
template void dppc_interpreter::ppc_exts();
// Branching Instructions
template
void dppc_interpreter::ppc_b() {
int32_t adr_li = int32_t((ppc_cur_instruction & ~3UL) << 6) >> 6;
if (a)
ppc_next_instruction_address = adr_li;
else
ppc_next_instruction_address = uint32_t(ppc_state.pc + adr_li);
if (l)
ppc_state.spr[SPR::LR] = uint32_t(ppc_state.pc + 4);
exec_flags = EXEF_BRANCH;
}
template void dppc_interpreter::ppc_b();
template void dppc_interpreter::ppc_b();
template void dppc_interpreter::ppc_b();
template void dppc_interpreter::ppc_b();
template
void dppc_interpreter::ppc_bc() {
uint32_t ctr_ok;
uint32_t cnd_ok;
uint32_t br_bo = (ppc_cur_instruction >> 21) & 0x1F;
uint32_t br_bi = (ppc_cur_instruction >> 16) & 0x1F;
int32_t br_bd = int32_t(int16_t(ppc_cur_instruction & ~3UL));
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) {
if (a)
ppc_next_instruction_address = br_bd;
else
ppc_next_instruction_address = uint32_t(ppc_state.pc + br_bd);
exec_flags = EXEF_BRANCH;
}
if (l)
ppc_state.spr[SPR::LR] = ppc_state.pc + 4;
}
template void dppc_interpreter::ppc_bc();
template void dppc_interpreter::ppc_bc();
template void dppc_interpreter::ppc_bc();
template void dppc_interpreter::ppc_bc();
template
void dppc_interpreter::ppc_bcctr() {
uint32_t ctr_ok;
uint32_t cnd_ok;
uint32_t br_bo = (ppc_cur_instruction >> 21) & 0x1F;
uint32_t br_bi = (ppc_cur_instruction >> 16) & 0x1F;
uint32_t ctr = ppc_state.spr[SPR::CTR];
uint32_t new_ctr;
if (for601) {
new_ctr = ctr - 1;
if (!(br_bo & 0x04)) {
ppc_state.spr[SPR::CTR] = new_ctr; /* decrement CTR */
}
}
else {
new_ctr = ctr;
}
ctr_ok = (br_bo & 0x04) | ((new_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 = (ctr & ~3UL);
exec_flags = EXEF_BRANCH;
}
if (l)
ppc_state.spr[SPR::LR] = ppc_state.pc + 4;
}
template void dppc_interpreter::ppc_bcctr();
template void dppc_interpreter::ppc_bcctr();
template void dppc_interpreter::ppc_bcctr();
template void dppc_interpreter::ppc_bcctr();
template
void dppc_interpreter::ppc_bclr() {
uint32_t br_bo = (ppc_cur_instruction >> 21) & 0x1F;
uint32_t br_bi = (ppc_cur_instruction >> 16) & 0x1F;
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] & ~3UL);
exec_flags = EXEF_BRANCH;
}
if (l)
ppc_state.spr[SPR::LR] = ppc_state.pc + 4;
}
template void dppc_interpreter::ppc_bclr();
template void dppc_interpreter::ppc_bclr();
// Compare Instructions
void dppc_interpreter::ppc_cmp() {
#ifdef CHECK_INVALID
if (ppc_cur_instruction & 0x200000) {
LOG_F(WARNING, "Invalid CMP instruction form (L=1)!");
return;
}
#endif
int crf_d = (ppc_cur_instruction >> 21) & 0x1C;
ppc_grab_regssab(ppc_cur_instruction);
uint32_t xercon = (ppc_state.spr[SPR::XER] & XER::SO) >> 3;
uint32_t 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 dppc_interpreter::ppc_cmpi() {
#ifdef CHECK_INVALID
if (ppc_cur_instruction & 0x200000) {
LOG_F(WARNING, "Invalid CMPI instruction form (L=1)!");
return;
}
#endif
int crf_d = (ppc_cur_instruction >> 21) & 0x1C;
ppc_grab_regsasimm(ppc_cur_instruction);
uint32_t xercon = (ppc_state.spr[SPR::XER] & XER::SO) >> 3;
uint32_t 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 dppc_interpreter::ppc_cmpl() {
#ifdef CHECK_INVALID
if (ppc_cur_instruction & 0x200000) {
LOG_F(WARNING, "Invalid CMPL instruction form (L=1)!");
return;
}
#endif
int crf_d = (ppc_cur_instruction >> 21) & 0x1C;
ppc_grab_regssab(ppc_cur_instruction);
uint32_t xercon = (ppc_state.spr[SPR::XER] & XER::SO) >> 3;
uint32_t 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 dppc_interpreter::ppc_cmpli() {
#ifdef CHECK_INVALID
if (ppc_cur_instruction & 0x200000) {
LOG_F(WARNING, "Invalid CMPLI instruction form (L=1)!");
return;
}
#endif
int crf_d = (ppc_cur_instruction >> 21) & 0x1C;
ppc_grab_regssauimm(ppc_cur_instruction);
uint32_t xercon = (ppc_state.spr[SPR::XER] & XER::SO) >> 3;
uint32_t 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 dppc_interpreter::ppc_mcrf() {
int crf_d = (ppc_cur_instruction >> 21) & 0x1C;
int crf_s = (ppc_cur_instruction >> 16) & 0x1C;
// extract and right justify source flags field
uint32_t grab_s = (ppc_state.cr >> (28 - crf_s)) & 0xF;
ppc_state.cr = (ppc_state.cr & ~(0xf0000000UL >> crf_d)) | (grab_s << (28 - crf_d));
}
void dppc_interpreter::ppc_crand() {
ppc_grab_dab(ppc_cur_instruction);
uint8_t ir = (ppc_state.cr >> (31 - reg_a)) & (ppc_state.cr >> (31 - reg_b));
if (ir & 1) {
ppc_state.cr |= (0x80000000UL >> reg_d);
} else {
ppc_state.cr &= ~(0x80000000UL >> reg_d);
}
}
void dppc_interpreter::ppc_crandc() {
ppc_grab_dab(ppc_cur_instruction);
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 dppc_interpreter::ppc_creqv() {
ppc_grab_dab(ppc_cur_instruction);
uint8_t ir = (ppc_state.cr >> (31 - reg_a)) ^ (ppc_state.cr >> (31 - reg_b));
if (ir & 1) { // compliment is implemented by swapping the following if/else bodies
ppc_state.cr &= ~(0x80000000UL >> reg_d);
} else {
ppc_state.cr |= (0x80000000UL >> reg_d);
}
}
void dppc_interpreter::ppc_crnand() {
ppc_grab_dab(ppc_cur_instruction);
uint8_t ir = (ppc_state.cr >> (31 - reg_a)) & (ppc_state.cr >> (31 - reg_b));
if (ir & 1) {
ppc_state.cr &= ~(0x80000000UL >> reg_d);
} else {
ppc_state.cr |= (0x80000000UL >> reg_d);
}
}
void dppc_interpreter::ppc_crnor() {
ppc_grab_dab(ppc_cur_instruction);
uint8_t ir = (ppc_state.cr >> (31 - reg_a)) | (ppc_state.cr >> (31 - reg_b));
if (ir & 1) {
ppc_state.cr &= ~(0x80000000UL >> reg_d);
} else {
ppc_state.cr |= (0x80000000UL >> reg_d);
}
}
void dppc_interpreter::ppc_cror() {
ppc_grab_dab(ppc_cur_instruction);
uint8_t ir = (ppc_state.cr >> (31 - reg_a)) | (ppc_state.cr >> (31 - reg_b));
if (ir & 1) {
ppc_state.cr |= (0x80000000UL >> reg_d);
} else {
ppc_state.cr &= ~(0x80000000UL >> reg_d);
}
}
void dppc_interpreter::ppc_crorc() {
ppc_grab_dab(ppc_cur_instruction);
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 dppc_interpreter::ppc_crxor() {
ppc_grab_dab(ppc_cur_instruction);
uint8_t ir = (ppc_state.cr >> (31 - reg_a)) ^ (ppc_state.cr >> (31 - reg_b));
if (ir & 1) {
ppc_state.cr |= (0x80000000UL >> reg_d);
} else {
ppc_state.cr &= ~(0x80000000UL >> reg_d);
}
}
// Processor MGMT Fns.
void dppc_interpreter::ppc_rfi() {
#ifdef CPU_PROFILING
num_supervisor_instrs++;
#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;
// generate External Interrupt Exception
// if CPU interrupt line is still asserted
if (ppc_state.msr & MSR::EE && int_pin) {
uint32_t save_srr0 = ppc_state.spr[SPR::SRR0] & ~3UL;
ppc_exception_handler(Except_Type::EXC_EXT_INT, 0);
ppc_state.spr[SPR::SRR0] = save_srr0;
return;
}
if ((ppc_state.msr & MSR::EE) && dec_exception_pending) {
dec_exception_pending = false;
//LOG_F(WARNING, "decrementer exception from rfi msr:0x%X", ppc_state.msr);
uint32_t save_srr0 = ppc_state.spr[SPR::SRR0] & ~3UL;
ppc_exception_handler(Except_Type::EXC_DECR, 0);
ppc_state.spr[SPR::SRR0] = save_srr0;
return;
}
ppc_next_instruction_address = ppc_state.spr[SPR::SRR0] & ~3UL;
do_ctx_sync(); // RFI is context synchronizing
mmu_change_mode();
grab_return = true;
exec_flags = EXEF_RFI;
}
void dppc_interpreter::ppc_sc() {
do_ctx_sync(); // SC is context synchronizing!
ppc_exception_handler(Except_Type::EXC_SYSCALL, 0x20000);
}
void dppc_interpreter::ppc_tw() {
uint32_t reg_a = (ppc_cur_instruction >> 11) & 0x1F;
uint32_t reg_b = (ppc_cur_instruction >> 16) & 0x1F;
uint32_t ppc_to = (ppc_cur_instruction >> 21) & 0x1F;
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, Exc_Cause::TRAP);
}
}
void dppc_interpreter::ppc_twi() {
int32_t simm = int32_t(int16_t(ppc_cur_instruction));
uint32_t reg_a = (ppc_cur_instruction >> 16) & 0x1F;
uint32_t 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, Exc_Cause::TRAP);
}
}
void dppc_interpreter::ppc_eieio() {
/* placeholder */
}
void dppc_interpreter::ppc_isync() {
do_ctx_sync();
}
void dppc_interpreter::ppc_sync() {
/* placeholder */
}
void dppc_interpreter::ppc_icbi() {
/* placeholder */
}
void dppc_interpreter::ppc_dcbf() {
/* placeholder */
}
void dppc_interpreter::ppc_dcbi() {
#ifdef CPU_PROFILING
num_supervisor_instrs++;
#endif
/* placeholder */
}
void dppc_interpreter::ppc_dcbst() {
/* placeholder */
}
void dppc_interpreter::ppc_dcbt() {
// Not needed, the HDI reg is touched to no-op this instruction.
return;
}
void dppc_interpreter::ppc_dcbtst() {
// Not needed, the HDI reg is touched to no-op this instruction.
return;
}
void dppc_interpreter::ppc_dcbz() {
ppc_grab_regsdab(ppc_cur_instruction);
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
ppc_effective_address &= 0xFFFFFFE0UL; // align EA on a 32-byte boundary
// the following is not especially efficient but necessary
// to make BlockZero under Mac OS 8.x and later to work
mmu_write_vmem(ppc_effective_address + 0, 0);
mmu_write_vmem(ppc_effective_address + 8, 0);
mmu_write_vmem(ppc_effective_address + 16, 0);
mmu_write_vmem(ppc_effective_address + 24, 0);
}
// Integer Load and Store Functions
template
void dppc_interpreter::ppc_st() {
#ifdef CPU_PROFILING
num_int_stores++;
#endif
ppc_grab_regssa(ppc_cur_instruction);
ppc_effective_address = int32_t(int16_t(ppc_cur_instruction));
ppc_effective_address += reg_a ? ppc_result_a : 0;
mmu_write_vmem(ppc_effective_address, ppc_result_d);
}
template void dppc_interpreter::ppc_st();
template void dppc_interpreter::ppc_st();
template void dppc_interpreter::ppc_st();
template
void dppc_interpreter::ppc_stx() {
#ifdef CPU_PROFILING
num_int_stores++;
#endif
ppc_grab_regssab(ppc_cur_instruction);
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
mmu_write_vmem(ppc_effective_address, ppc_result_d);
}
template void dppc_interpreter::ppc_stx();
template void dppc_interpreter::ppc_stx();
template void dppc_interpreter::ppc_stx();
template
void dppc_interpreter::ppc_stu() {
#ifdef CPU_PROFILING
num_int_stores++;
#endif
ppc_grab_regssa(ppc_cur_instruction);
if (reg_a != 0) {
ppc_effective_address = int32_t(int16_t(ppc_cur_instruction));
ppc_effective_address += ppc_result_a;
mmu_write_vmem(ppc_effective_address, ppc_result_d);
ppc_state.gpr[reg_a] = ppc_effective_address;
} else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
template void dppc_interpreter::ppc_stu();
template void dppc_interpreter::ppc_stu();
template void dppc_interpreter::ppc_stu();
template
void dppc_interpreter::ppc_stux() {
#ifdef CPU_PROFILING
num_int_stores++;
#endif
ppc_grab_regssab(ppc_cur_instruction);
if (reg_a != 0) {
ppc_effective_address = ppc_result_a + ppc_result_b;
mmu_write_vmem(ppc_effective_address, ppc_result_d);
ppc_state.gpr[reg_a] = ppc_effective_address;
} else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
template void dppc_interpreter::ppc_stux();
template void dppc_interpreter::ppc_stux();
template void dppc_interpreter::ppc_stux();
void dppc_interpreter::ppc_sthbrx() {
#ifdef CPU_PROFILING
num_int_stores++;
#endif
ppc_grab_regssab(ppc_cur_instruction);
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
ppc_result_d = uint32_t(BYTESWAP_16(uint16_t(ppc_result_d)));
mmu_write_vmem(ppc_effective_address, ppc_result_d);
}
void dppc_interpreter::ppc_stwcx() {
#ifdef CPU_PROFILING
num_int_stores++;
#endif
ppc_grab_regssab(ppc_cur_instruction);
ppc_effective_address = (reg_a == 0) ? ppc_result_b : (ppc_result_a + ppc_result_b);
ppc_state.cr &= 0x0FFFFFFFUL; // clear CR0
ppc_state.cr |= (ppc_state.spr[SPR::XER] & XER::SO) >> 3; // copy XER[SO] to CR0[SO]
if (ppc_state.reserve) {
mmu_write_vmem(ppc_effective_address, ppc_result_d);
ppc_state.reserve = false;
ppc_state.cr |= 0x20000000UL; // set CR0[EQ]
}
}
void dppc_interpreter::ppc_stwbrx() {
#ifdef CPU_PROFILING
num_int_stores++;
#endif
ppc_grab_regssab(ppc_cur_instruction);
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
ppc_result_d = BYTESWAP_32(ppc_result_d);
mmu_write_vmem(ppc_effective_address, ppc_result_d);
}
void dppc_interpreter::ppc_stmw() {
#ifdef CPU_PROFILING
num_int_stores++;
#endif
ppc_grab_regssa(ppc_cur_instruction);
ppc_effective_address = int32_t(int16_t(ppc_cur_instruction));
ppc_effective_address += reg_a ? ppc_result_a : 0;
/* what should we do if EA is unaligned? */
if (ppc_effective_address & 3) {
ppc_alignment_exception(ppc_effective_address);
}
for (; reg_s <= 31; reg_s++) {
mmu_write_vmem(ppc_effective_address, ppc_state.gpr[reg_s]);
ppc_effective_address += 4;
}
}
template
void dppc_interpreter::ppc_lz() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
ppc_grab_regsda(ppc_cur_instruction);
ppc_effective_address = int32_t(int16_t(ppc_cur_instruction));
ppc_effective_address += reg_a ? ppc_result_a : 0;
uint32_t ppc_result_d = mmu_read_vmem(ppc_effective_address);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
template void dppc_interpreter::ppc_lz();
template void dppc_interpreter::ppc_lz();
template void dppc_interpreter::ppc_lz();
template
void dppc_interpreter::ppc_lzu() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
ppc_grab_regsda(ppc_cur_instruction);
ppc_effective_address = int32_t(int16_t(ppc_cur_instruction));
if ((reg_a != reg_d) && reg_a != 0) {
ppc_effective_address += ppc_result_a;
uint32_t ppc_result_d = mmu_read_vmem(ppc_effective_address);
uint32_t ppc_result_a = ppc_effective_address;
ppc_store_iresult_reg(reg_d, ppc_result_d);
ppc_store_iresult_reg(reg_a, ppc_result_a);
} else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
template void dppc_interpreter::ppc_lzu();
template void dppc_interpreter::ppc_lzu();
template void dppc_interpreter::ppc_lzu();
template
void dppc_interpreter::ppc_lzx() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
ppc_grab_regsdab(ppc_cur_instruction);
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
uint32_t ppc_result_d = mmu_read_vmem(ppc_effective_address);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
template void dppc_interpreter::ppc_lzx(void);
template void dppc_interpreter::ppc_lzx(void);
template void dppc_interpreter::ppc_lzx(void);
template
void dppc_interpreter::ppc_lzux() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
ppc_grab_regsdab(ppc_cur_instruction);
if ((reg_a != reg_d) && reg_a != 0) {
ppc_effective_address = ppc_result_a + ppc_result_b;
uint32_t ppc_result_d = mmu_read_vmem(ppc_effective_address);
ppc_result_a = ppc_effective_address;
ppc_store_iresult_reg(reg_d, ppc_result_d);
ppc_store_iresult_reg(reg_a, ppc_result_a);
} else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
template void dppc_interpreter::ppc_lzux(void);
template void dppc_interpreter::ppc_lzux(void);
template void dppc_interpreter::ppc_lzux(void);
void dppc_interpreter::ppc_lha() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
ppc_grab_regsda(ppc_cur_instruction);
ppc_effective_address = int32_t(int16_t(ppc_cur_instruction));
ppc_effective_address += (reg_a ? ppc_result_a : 0);
int16_t val = mmu_read_vmem(ppc_effective_address);
ppc_store_iresult_reg(reg_d, int32_t(val));
}
void dppc_interpreter::ppc_lhau() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
ppc_grab_regsda(ppc_cur_instruction);
if ((reg_a != reg_d) && reg_a != 0) {
ppc_effective_address = int32_t(int16_t(ppc_cur_instruction));
ppc_effective_address += ppc_result_a;
int16_t val = mmu_read_vmem(ppc_effective_address);
ppc_store_iresult_reg(reg_d, int32_t(val));
uint32_t ppc_result_a = ppc_effective_address;
ppc_store_iresult_reg(reg_a, ppc_result_a);
} else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
void dppc_interpreter::ppc_lhaux() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
ppc_grab_regsdab(ppc_cur_instruction);
if ((reg_a != reg_d) && reg_a != 0) {
ppc_effective_address = ppc_result_a + ppc_result_b;
int16_t val = mmu_read_vmem(ppc_effective_address);
ppc_store_iresult_reg(reg_d, int32_t(val));
uint32_t ppc_result_a = ppc_effective_address;
ppc_store_iresult_reg(reg_a, ppc_result_a);
}
else {
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
}
void dppc_interpreter::ppc_lhax() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
ppc_grab_regsdab(ppc_cur_instruction);
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
int16_t val = mmu_read_vmem(ppc_effective_address);
ppc_store_iresult_reg(reg_d, int32_t(val));
}
void dppc_interpreter::ppc_lhbrx() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
ppc_grab_regsdab(ppc_cur_instruction);
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
uint32_t ppc_result_d = uint32_t(BYTESWAP_16(mmu_read_vmem(ppc_effective_address)));
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
void dppc_interpreter::ppc_lwbrx() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
ppc_grab_regsdab(ppc_cur_instruction);
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
uint32_t ppc_result_d = BYTESWAP_32(mmu_read_vmem(ppc_effective_address));
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
void dppc_interpreter::ppc_lwarx() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
// Placeholder - Get the reservation of memory implemented!
ppc_grab_regsdab(ppc_cur_instruction);
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
ppc_state.reserve = true;
uint32_t ppc_result_d = mmu_read_vmem(ppc_effective_address);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
void dppc_interpreter::ppc_lmw() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
ppc_grab_regsda(ppc_cur_instruction);
ppc_effective_address = int32_t(int16_t(ppc_cur_instruction));
ppc_effective_address += (reg_a ? ppc_result_a : 0);
// How many words to load in memory - using a do-while for this
do {
ppc_state.gpr[reg_d] = mmu_read_vmem(ppc_effective_address);
ppc_effective_address += 4;
reg_d++;
} while (reg_d < 32);
}
void dppc_interpreter::ppc_lswi() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
ppc_grab_regsda(ppc_cur_instruction);
ppc_effective_address = reg_a ? ppc_result_a : 0;
uint32_t grab_inb = (ppc_cur_instruction >> 11) & 0x1F;
grab_inb = grab_inb ? grab_inb : 32;
while (grab_inb >= 4) {
ppc_state.gpr[reg_d] = mmu_read_vmem(ppc_effective_address);
reg_d++;
if (reg_d >= 32) { // wrap around through GPR0
reg_d = 0;
}
ppc_effective_address += 4;
grab_inb -= 4;
}
// handle remaining bytes
switch (grab_inb) {
case 1:
ppc_state.gpr[reg_d] = mmu_read_vmem(ppc_effective_address) << 24;
break;
case 2:
ppc_state.gpr[reg_d] = mmu_read_vmem(ppc_effective_address) << 16;
break;
case 3:
ppc_state.gpr[reg_d] = mmu_read_vmem(ppc_effective_address) << 16;
ppc_state.gpr[reg_d] += mmu_read_vmem(ppc_effective_address + 2) << 8;
break;
default:
break;
}
}
void dppc_interpreter::ppc_lswx() {
#ifdef CPU_PROFILING
num_int_loads++;
#endif
ppc_grab_regsdab(ppc_cur_instruction);
/*
// Invalid instruction forms
if ((reg_d == 0 && reg_a == 0) || (reg_d == reg_a) || (reg_d == reg_b)) {
// UNTESTED! Does invalid form really cause exception?
// G4 doesn't do exception
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
}
*/
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
uint32_t grab_inb = ppc_state.spr[SPR::XER] & 0x7F;
for (;;) {
if (is_601 && (reg_d == reg_b || (reg_a != 0 && reg_d == reg_a))) {
// UNTESTED! MPC601 manual is inconsistant on whether reg_b is skipped or not
reg_d = (reg_d + 1) & 0x1F; // wrap around through GPR0
}
switch (grab_inb) {
case 0:
return;
case 1:
ppc_state.gpr[reg_d] = mmu_read_vmem(ppc_effective_address) << 24;
return;
case 2:
ppc_state.gpr[reg_d] = mmu_read_vmem(ppc_effective_address) << 16;
return;
case 3:
ppc_state.gpr[reg_d] = (mmu_read_vmem(ppc_effective_address) << 16)
| (mmu_read_vmem(ppc_effective_address + 2) << 8);
return;
}
ppc_state.gpr[reg_d] = mmu_read_vmem(ppc_effective_address);
reg_d = (reg_d + 1) & 0x1F; // wrap around through GPR0
ppc_effective_address += 4;
grab_inb -= 4;
}
}
void dppc_interpreter::ppc_stswi() {
#ifdef CPU_PROFILING
num_int_stores++;
#endif
ppc_grab_regssa(ppc_cur_instruction);
ppc_effective_address = reg_a ? ppc_result_a : 0;
uint32_t grab_inb = (ppc_cur_instruction >> 11) & 0x1F;
grab_inb = grab_inb ? grab_inb : 32;
while (grab_inb >= 4) {
mmu_write_vmem(ppc_effective_address, ppc_state.gpr[reg_s]);
reg_s++;
if (reg_s >= 32) { // wrap around through GPR0
reg_s = 0;
}
ppc_effective_address += 4;
grab_inb -= 4;
}
// handle remaining bytes
switch (grab_inb) {
case 1:
mmu_write_vmem(ppc_effective_address, ppc_state.gpr[reg_s] >> 24);
break;
case 2:
mmu_write_vmem(ppc_effective_address, ppc_state.gpr[reg_s] >> 16);
break;
case 3:
mmu_write_vmem(ppc_effective_address, ppc_state.gpr[reg_s] >> 16);
mmu_write_vmem(ppc_effective_address + 2, (ppc_state.gpr[reg_s] >> 8) & 0xFF);
break;
default:
break;
}
}
void dppc_interpreter::ppc_stswx() {
#ifdef CPU_PROFILING
num_int_stores++;
#endif
ppc_grab_regssab(ppc_cur_instruction);
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
uint32_t grab_inb = ppc_state.spr[SPR::XER] & 127;
while (grab_inb >= 4) {
mmu_write_vmem(ppc_effective_address, ppc_state.gpr[reg_s]);
reg_s++;
if (reg_s >= 32) { // wrap around through GPR0
reg_s = 0;
}
ppc_effective_address += 4;
grab_inb -= 4;
}
// handle remaining bytes
switch (grab_inb) {
case 1:
mmu_write_vmem(ppc_effective_address, ppc_state.gpr[reg_s] >> 24);
break;
case 2:
mmu_write_vmem(ppc_effective_address, ppc_state.gpr[reg_s] >> 16);
break;
case 3:
mmu_write_vmem(ppc_effective_address, ppc_state.gpr[reg_s] >> 16);
mmu_write_vmem(ppc_effective_address + 2, (ppc_state.gpr[reg_s] >> 8) & 0xFF);
break;
default:
break;
}
}
void dppc_interpreter::ppc_eciwx() {
uint32_t ear_enable = 0x80000000;
// error if EAR[E] != 1
if (!(ppc_state.spr[282] && ear_enable)) {
ppc_exception_handler(Except_Type::EXC_DSI, 0x0);
}
ppc_grab_regsdab(ppc_cur_instruction);
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
if (ppc_effective_address & 0x3) {
ppc_alignment_exception(ppc_effective_address);
}
uint32_t ppc_result_d = mmu_read_vmem(ppc_effective_address);
ppc_store_iresult_reg(reg_d, ppc_result_d);
}
void dppc_interpreter::ppc_ecowx() {
uint32_t ear_enable = 0x80000000;
// error if EAR[E] != 1
if (!(ppc_state.spr[282] && ear_enable)) {
ppc_exception_handler(Except_Type::EXC_DSI, 0x0);
}
ppc_grab_regssab(ppc_cur_instruction);
ppc_effective_address = ppc_result_b + (reg_a ? ppc_result_a : 0);
if (ppc_effective_address & 0x3) {
ppc_alignment_exception(ppc_effective_address);
}
mmu_write_vmem(ppc_effective_address, ppc_result_d);
}
// TLB Instructions
void dppc_interpreter::ppc_tlbie() {
#ifdef CPU_PROFILING
num_supervisor_instrs++;
#endif
tlb_flush_entry(ppc_state.gpr[(ppc_cur_instruction >> 11) & 0x1F]);
}
void dppc_interpreter::ppc_tlbia() {
#ifdef CPU_PROFILING
num_supervisor_instrs++;
#endif
/* placeholder */
}
void dppc_interpreter::ppc_tlbld() {
#ifdef CPU_PROFILING
num_supervisor_instrs++;
#endif
/* placeholder */
}
void dppc_interpreter::ppc_tlbli() {
#ifdef CPU_PROFILING
num_supervisor_instrs++;
#endif
/* placeholder */
}
void dppc_interpreter::ppc_tlbsync() {
#ifdef CPU_PROFILING
num_supervisor_instrs++;
#endif
/* placeholder */
}