mirror of
https://github.com/dingusdev/dingusppc.git
synced 2025-02-15 15:31:31 +00:00
Implement SoftTLB for writes.
This commit is contained in:
parent
4f3dd797be
commit
089645e830
@ -148,22 +148,26 @@ void dppc_interpreter::power_lscbx() {
|
||||
if (match_found == false) {
|
||||
switch (shift_amount) {
|
||||
case 0:
|
||||
return_value = mem_grab_byte(ppc_effective_address);
|
||||
return_value = mmu_read_vmem<uint8_t>(ppc_effective_address);
|
||||
//return_value = mem_grab_byte(ppc_effective_address);
|
||||
ppc_result_d = (ppc_result_d & 0x00FFFFFF) | (return_value << 24);
|
||||
ppc_store_result_regd();
|
||||
break;
|
||||
case 1:
|
||||
return_value = mem_grab_byte(ppc_effective_address);
|
||||
return_value = mmu_read_vmem<uint8_t>(ppc_effective_address);
|
||||
//return_value = mem_grab_byte(ppc_effective_address);
|
||||
ppc_result_d = (ppc_result_d & 0xFF00FFFF) | (return_value << 16);
|
||||
ppc_store_result_regd();
|
||||
break;
|
||||
case 2:
|
||||
return_value = mem_grab_byte(ppc_effective_address);
|
||||
return_value = mmu_read_vmem<uint8_t>(ppc_effective_address);
|
||||
//return_value = mem_grab_byte(ppc_effective_address);
|
||||
ppc_result_d = (ppc_result_d & 0xFFFF00FF) | (return_value << 8);
|
||||
ppc_store_result_regd();
|
||||
break;
|
||||
case 3:
|
||||
return_value = mem_grab_byte(ppc_effective_address);
|
||||
return_value = mmu_read_vmem<uint8_t>(ppc_effective_address);
|
||||
//return_value = mem_grab_byte(ppc_effective_address);
|
||||
ppc_result_d = (ppc_result_d & 0xFFFFFF00) | return_value;
|
||||
ppc_store_result_regd();
|
||||
break;
|
||||
@ -494,4 +498,4 @@ void dppc_interpreter::power_srlq() {
|
||||
|
||||
void dppc_interpreter::power_srq() {
|
||||
LOG_F(WARNING, "OOPS! Placeholder for srq!!! \n");
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include "devices/memctrlbase.h"
|
||||
#include "endianswap.h"
|
||||
#include <cinttypes>
|
||||
#include <functional>
|
||||
#include <setjmp.h>
|
||||
#include <string>
|
||||
|
||||
@ -301,6 +302,9 @@ void ppc_fp_changecrf1();
|
||||
// MEMORY DECLARATIONS
|
||||
extern MemCtrlBase* mem_ctrl_instance;
|
||||
|
||||
//typedef std::function<void()> CtxSyncCallback;
|
||||
extern void add_ctx_sync_action(const std::function<void()> &);
|
||||
|
||||
// The functions used by the PowerPC processor
|
||||
namespace dppc_interpreter {
|
||||
extern void ppc_bcctr();
|
||||
|
@ -674,11 +674,11 @@ void dppc_interpreter::ppc_fctiw() {
|
||||
if (std::isnan(val_reg_b)) {
|
||||
ppc_state.fpr[reg_d].int64_r = 0x80000000;
|
||||
ppc_state.fpscr |= 0x1000100;
|
||||
}
|
||||
}
|
||||
else if (val_reg_b > static_cast<double>(0x7fffffff)) {
|
||||
ppc_state.fpr[reg_d].int64_r = 0x7fffffff;
|
||||
ppc_state.fpscr |= 0x100;
|
||||
}
|
||||
}
|
||||
else if (val_reg_b < -static_cast<double>(0x80000000)) {
|
||||
ppc_state.fpr[reg_d].int64_r = 0x80000000;
|
||||
ppc_state.fpscr |= 0x100;
|
||||
@ -696,7 +696,7 @@ void dppc_interpreter::ppc_fctiw() {
|
||||
}
|
||||
|
||||
ppc_store_dfpresult_int(reg_d);
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (rc_flag)
|
||||
@ -710,15 +710,15 @@ void dppc_interpreter::ppc_fctiwz() {
|
||||
if (std::isnan(val_reg_b)) {
|
||||
ppc_state.fpr[reg_d].int64_r = 0x80000000;
|
||||
ppc_state.fpscr |= 0x1000100;
|
||||
}
|
||||
}
|
||||
else if (val_reg_b > static_cast<double>(0x7fffffff)) {
|
||||
ppc_state.fpr[reg_d].int64_r = 0x7fffffff;
|
||||
ppc_state.fpscr |= 0x100;
|
||||
}
|
||||
}
|
||||
else if (val_reg_b < -static_cast<double>(0x80000000)) {
|
||||
ppc_state.fpr[reg_d].int64_r = 0x80000000;
|
||||
ppc_state.fpscr |= 0x100;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ppc_result64_d = round_to_zero(val_reg_b);
|
||||
|
||||
@ -735,7 +735,8 @@ void dppc_interpreter::ppc_lfs() {
|
||||
ppc_grab_regsfpdia();
|
||||
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
||||
ppc_effective_address += (reg_a) ? val_reg_a : 0;
|
||||
ppc_result64_d = mem_grab_dword(ppc_effective_address);
|
||||
ppc_result64_d = mmu_read_vmem<uint32_t>(ppc_effective_address);
|
||||
//ppc_result64_d = mem_grab_dword(ppc_effective_address);
|
||||
ppc_store_sfpresult_int(reg_d);
|
||||
}
|
||||
|
||||
@ -745,7 +746,8 @@ void dppc_interpreter::ppc_lfsu() {
|
||||
if (reg_a) {
|
||||
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
||||
ppc_effective_address += (reg_a) ? val_reg_a : 0;
|
||||
ppc_result64_d = mem_grab_dword(ppc_effective_address);
|
||||
ppc_result64_d = mmu_read_vmem<uint32_t>(ppc_effective_address);
|
||||
//ppc_result64_d = mem_grab_dword(ppc_effective_address);
|
||||
ppc_store_sfpresult_int(reg_d);
|
||||
ppc_state.gpr[reg_a] = ppc_effective_address;
|
||||
} else {
|
||||
@ -756,7 +758,8 @@ void dppc_interpreter::ppc_lfsu() {
|
||||
void dppc_interpreter::ppc_lfsx() {
|
||||
ppc_grab_regsfpdiab();
|
||||
ppc_effective_address = (reg_a) ? val_reg_a + val_reg_b : val_reg_b;
|
||||
ppc_result64_d = mem_grab_dword(ppc_effective_address);
|
||||
ppc_result64_d = mmu_read_vmem<uint32_t>(ppc_effective_address);
|
||||
//ppc_result64_d = mem_grab_dword(ppc_effective_address);
|
||||
ppc_store_sfpresult_int(reg_d);
|
||||
}
|
||||
|
||||
@ -764,7 +767,8 @@ void dppc_interpreter::ppc_lfsux() {
|
||||
ppc_grab_regsfpdiab();
|
||||
if (reg_a) {
|
||||
ppc_effective_address = val_reg_a + val_reg_b;
|
||||
ppc_result64_d = mem_grab_dword(ppc_effective_address);
|
||||
ppc_result64_d = mmu_read_vmem<uint32_t>(ppc_effective_address);
|
||||
//ppc_result64_d = mem_grab_dword(ppc_effective_address);
|
||||
ppc_store_sfpresult_int(reg_d);
|
||||
ppc_state.gpr[reg_a] = ppc_effective_address;
|
||||
} else {
|
||||
@ -820,7 +824,8 @@ void dppc_interpreter::ppc_stfs() {
|
||||
ppc_grab_regsfpsia();
|
||||
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
||||
ppc_effective_address += (reg_a) ? val_reg_a : 0;
|
||||
mem_write_dword(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
||||
mmu_write_vmem<uint32_t>(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
||||
//mem_write_dword(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_stfsu() {
|
||||
@ -828,7 +833,8 @@ void dppc_interpreter::ppc_stfsu() {
|
||||
if (reg_a != 0) {
|
||||
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
||||
ppc_effective_address += val_reg_a;
|
||||
mem_write_dword(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
||||
mmu_write_vmem<uint32_t>(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
||||
//mem_write_dword(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
||||
ppc_state.gpr[reg_a] = ppc_effective_address;
|
||||
} else {
|
||||
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
|
||||
@ -838,14 +844,16 @@ void dppc_interpreter::ppc_stfsu() {
|
||||
void dppc_interpreter::ppc_stfsx() {
|
||||
ppc_grab_regsfpsiab();
|
||||
ppc_effective_address = (reg_a) ? val_reg_a + val_reg_b : val_reg_b;
|
||||
mem_write_dword(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
||||
mmu_write_vmem<uint32_t>(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
||||
//mem_write_dword(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_stfsux() {
|
||||
ppc_grab_regsfpsiab();
|
||||
if (reg_a) {
|
||||
ppc_effective_address = val_reg_a + val_reg_b;
|
||||
mem_write_dword(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
||||
mmu_write_vmem<uint32_t>(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
||||
//mem_write_dword(ppc_effective_address, uint32_t(ppc_state.fpr[reg_s].int64_r));
|
||||
ppc_state.gpr[reg_a] = ppc_effective_address;
|
||||
} else {
|
||||
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
|
||||
@ -856,7 +864,8 @@ void dppc_interpreter::ppc_stfd() {
|
||||
ppc_grab_regsfpsia();
|
||||
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
||||
ppc_effective_address += (reg_a) ? val_reg_a : 0;
|
||||
mem_write_qword(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
||||
mmu_write_vmem<uint64_t>(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
||||
//mem_write_qword(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_stfdu() {
|
||||
@ -864,7 +873,8 @@ void dppc_interpreter::ppc_stfdu() {
|
||||
if (reg_a != 0) {
|
||||
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
||||
ppc_effective_address += val_reg_a;
|
||||
mem_write_qword(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
||||
mmu_write_vmem<uint64_t>(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
||||
//mem_write_qword(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
||||
ppc_state.gpr[reg_a] = ppc_effective_address;
|
||||
} else {
|
||||
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
|
||||
@ -874,14 +884,16 @@ void dppc_interpreter::ppc_stfdu() {
|
||||
void dppc_interpreter::ppc_stfdx() {
|
||||
ppc_grab_regsfpsiab();
|
||||
ppc_effective_address = (reg_a) ? val_reg_a + val_reg_b : val_reg_b;
|
||||
mem_write_qword(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
||||
mmu_write_vmem<uint64_t>(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
||||
//mem_write_qword(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_stfdux() {
|
||||
ppc_grab_regsfpsiab();
|
||||
if (reg_a != 0) {
|
||||
ppc_effective_address = val_reg_a + val_reg_b;
|
||||
mem_write_qword(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
||||
mmu_write_vmem<uint64_t>(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
||||
//mem_write_qword(ppc_effective_address, ppc_state.fpr[reg_s].int64_r);
|
||||
ppc_state.gpr[reg_a] = ppc_effective_address;
|
||||
} else {
|
||||
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
|
||||
@ -891,7 +903,8 @@ void dppc_interpreter::ppc_stfdux() {
|
||||
void dppc_interpreter::ppc_stfiwx() {
|
||||
ppc_grab_regsfpsiab();
|
||||
ppc_effective_address = (reg_a) ? val_reg_a + val_reg_b : val_reg_b;
|
||||
mem_write_dword(ppc_effective_address, (uint32_t)(ppc_state.fpr[reg_s].int64_r));
|
||||
mmu_write_vmem<uint32_t>(ppc_effective_address, (uint32_t)(ppc_state.fpr[reg_s].int64_r));
|
||||
//mem_write_dword(ppc_effective_address, (uint32_t)(ppc_state.fpr[reg_s].int64_r));
|
||||
}
|
||||
|
||||
// Floating Point Register Transfer
|
||||
|
@ -137,6 +137,7 @@ public:
|
||||
|
||||
/** Temporary TLB test variables. */
|
||||
bool MemAccessType; // true - memory, false - I/O
|
||||
bool Unaligned_crosspage = false;
|
||||
uint64_t MemAddr = 0;
|
||||
MMIODevice *Device = 0;
|
||||
uint32_t DevOffset = 0;
|
||||
@ -239,6 +240,21 @@ static inline void write_phys_mem(AddressMapEntry *mru_rgn, uint32_t addr, T val
|
||||
#ifdef MMU_PROFILING
|
||||
dmem_writes_total++;
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
if (!MemAccessType) {
|
||||
LOG_F(ERROR, "TLB real memory access expected!");
|
||||
}
|
||||
|
||||
if (!is_aligned && Unaligned_crosspage) {
|
||||
LOG_F(WARNING, "Unaligned cross-page access ignored!");
|
||||
} else if ((mru_rgn->mem_ptr + (addr - mru_rgn->start)) != (uint8_t *)MemAddr) {
|
||||
LOG_F(ERROR, "TLB address mismatch! Expected: 0x%llx, got: 0x%llx",
|
||||
(uint64_t)(mru_rgn->mem_ptr + (addr - mru_rgn->start)),
|
||||
(uint64_t)MemAddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
switch(sizeof(T)) {
|
||||
case 1:
|
||||
*(mru_rgn->mem_ptr + (addr - mru_rgn->start)) = value;
|
||||
@ -270,6 +286,18 @@ static inline void write_phys_mem(AddressMapEntry *mru_rgn, uint32_t addr, T val
|
||||
#ifdef MMU_PROFILING
|
||||
iomem_writes_total++;
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
if (MemAccessType) {
|
||||
LOG_F(ERROR, "TLB I/O memory access expected!");
|
||||
}
|
||||
|
||||
if (mru_rgn->devobj != Device || (addr - mru_rgn->start) != DevOffset) {
|
||||
LOG_F(ERROR, "TLB MMIO access mismatch! Expected: 0x%X, got: 0x%X",
|
||||
addr - mru_rgn->start, DevOffset);
|
||||
}
|
||||
#endif
|
||||
|
||||
mru_rgn->devobj->write(mru_rgn->start, addr - mru_rgn->start, value,
|
||||
sizeof(T));
|
||||
} else {
|
||||
@ -298,6 +326,9 @@ void ppc_set_cur_instruction(const uint8_t* ptr) {
|
||||
ppc_cur_instruction = READ_DWORD_BE_A(ptr);
|
||||
}
|
||||
|
||||
bool gTLBFlushBatEntries = false;
|
||||
bool gTLBFlushPatEntries = false;
|
||||
|
||||
void ibat_update(uint32_t bat_reg) {
|
||||
int upper_reg_num;
|
||||
uint32_t bl, hi_mask;
|
||||
@ -335,6 +366,19 @@ void dbat_update(uint32_t bat_reg) {
|
||||
bat_entry->hi_mask = hi_mask;
|
||||
bat_entry->phys_hi = ppc_state.spr[upper_reg_num + 1] & hi_mask;
|
||||
bat_entry->bepi = ppc_state.spr[upper_reg_num] & hi_mask;
|
||||
|
||||
if (!gTLBFlushBatEntries) {
|
||||
gTLBFlushBatEntries = true;
|
||||
add_ctx_sync_action(&tlb_flush_bat_entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mmu_pat_ctx_changed()
|
||||
{
|
||||
if (!gTLBFlushPatEntries) {
|
||||
gTLBFlushPatEntries = true;
|
||||
add_ctx_sync_action(&tlb_flush_pat_entries);
|
||||
}
|
||||
}
|
||||
|
||||
@ -437,7 +481,9 @@ static bool search_pteg(
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint32_t page_address_translate(uint32_t la, bool is_instr_fetch, unsigned msr_pr, int is_write) {
|
||||
static PATResult page_address_translate(uint32_t la, bool is_instr_fetch,
|
||||
unsigned msr_pr, int is_write)
|
||||
{
|
||||
uint32_t sr_val, page_index, pteg_hash1, vsid, pte_word2;
|
||||
unsigned key, pp;
|
||||
uint8_t* pte_addr;
|
||||
@ -497,8 +543,12 @@ static uint32_t page_address_translate(uint32_t la, bool is_instr_fetch, unsigne
|
||||
pte_addr[7] |= 0x80;
|
||||
}
|
||||
|
||||
/* return physical address */
|
||||
return ((pte_word2 & 0xFFFFF000) | (la & 0x00000FFF));
|
||||
/* return physical address, access protection and C status */
|
||||
return PATResult{
|
||||
((pte_word2 & 0xFFFFF000) | (la & 0x00000FFF)),
|
||||
static_cast<uint8_t>((key << 2) | pp),
|
||||
static_cast<uint8_t>(pte_word2 & 0x80)
|
||||
};
|
||||
}
|
||||
|
||||
/** PowerPC-style MMU instruction address translation. */
|
||||
@ -535,7 +585,8 @@ static uint32_t ppc_mmu_instr_translate(uint32_t la) {
|
||||
|
||||
/* page address translation */
|
||||
if (!bat_hit) {
|
||||
pa = page_address_translate(la, true, msr_pr, 0);
|
||||
PATResult pat_res = page_address_translate(la, true, msr_pr, 0);
|
||||
pa = pat_res.phys;
|
||||
|
||||
#ifdef MMU_PROFILING
|
||||
ptab_transl_total++;
|
||||
@ -581,7 +632,8 @@ static uint32_t ppc_mmu_addr_translate(uint32_t la, int is_write) {
|
||||
|
||||
/* page address translation */
|
||||
if (!bat_hit) {
|
||||
pa = page_address_translate(la, false, msr_pr, is_write);
|
||||
PATResult pat_res = page_address_translate(la, false, msr_pr, is_write);
|
||||
pa = pat_res.phys;
|
||||
|
||||
#ifdef MMU_PROFILING
|
||||
ptab_transl_total++;
|
||||
@ -635,73 +687,6 @@ static void mem_write_unaligned(uint32_t addr, uint32_t value, uint32_t size) {
|
||||
}
|
||||
}
|
||||
|
||||
void mem_write_byte(uint32_t addr, uint8_t value) {
|
||||
/* data address translation if enabled */
|
||||
if (ppc_state.msr & 0x10) {
|
||||
addr = ppc_mmu_addr_translate(addr, 1);
|
||||
}
|
||||
|
||||
write_phys_mem<uint8_t, true>(&last_write_area, addr, value);
|
||||
}
|
||||
|
||||
void mem_write_word(uint32_t addr, uint16_t value) {
|
||||
if (addr & 1) {
|
||||
mem_write_unaligned(addr, value, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
/* data address translation if enabled */
|
||||
if (ppc_state.msr & 0x10) {
|
||||
addr = ppc_mmu_addr_translate(addr, 1);
|
||||
}
|
||||
|
||||
write_phys_mem<uint16_t, true>(&last_write_area, addr, value);
|
||||
}
|
||||
|
||||
void mem_write_dword(uint32_t addr, uint32_t value) {
|
||||
if (addr & 3) {
|
||||
mem_write_unaligned(addr, value, 4);
|
||||
return;
|
||||
}
|
||||
|
||||
/* data address translation if enabled */
|
||||
if (ppc_state.msr & 0x10) {
|
||||
addr = ppc_mmu_addr_translate(addr, 1);
|
||||
}
|
||||
|
||||
write_phys_mem<uint32_t, true>(&last_write_area, addr, value);
|
||||
}
|
||||
|
||||
void mem_write_qword(uint32_t addr, uint64_t value) {
|
||||
if (addr & 7) {
|
||||
LOG_F(ERROR, "SOS! Attempt to write unaligned QWORD to 0x%08X\n", addr);
|
||||
exit(-1); // FIXME!
|
||||
}
|
||||
|
||||
/* data address translation if enabled */
|
||||
if (ppc_state.msr & 0x10) {
|
||||
addr = ppc_mmu_addr_translate(addr, 1);
|
||||
}
|
||||
|
||||
write_phys_mem<uint64_t, true>(&last_write_area, addr, value);
|
||||
}
|
||||
|
||||
|
||||
#define PAGE_SIZE_BITS 12
|
||||
#define TLB_SIZE 4096
|
||||
#define TLB2_WAYS 4
|
||||
#define TLB_INVALID_TAG 0xFFFFFFFF
|
||||
|
||||
typedef struct TLBEntry {
|
||||
uint32_t tag;
|
||||
uint16_t flags;
|
||||
uint16_t lru_bits;
|
||||
union {
|
||||
int64_t host_va_offset;
|
||||
AddressMapEntry* reg_desc;
|
||||
};
|
||||
} TLBEntry;
|
||||
|
||||
// primary TLB for all MMU modes
|
||||
static std::array<TLBEntry, TLB_SIZE> mode1_tlb1;
|
||||
static std::array<TLBEntry, TLB_SIZE> mode2_tlb1;
|
||||
@ -721,13 +706,13 @@ uint32_t tlb_size_mask = TLB_SIZE - 1;
|
||||
uint64_t UnmappedVal = -1ULL;
|
||||
TLBEntry UnmappedMem = {TLB_INVALID_TAG, 0, 0, 0};
|
||||
|
||||
uint8_t MMUMode = {0xFF};
|
||||
uint8_t CurMMUMode = {0xFF}; // current MMU mode
|
||||
|
||||
void mmu_change_mode()
|
||||
{
|
||||
uint8_t mmu_mode = ((ppc_state.msr >> 3) & 0x2) | ((ppc_state.msr >> 14) & 1);
|
||||
|
||||
if (MMUMode != mmu_mode) {
|
||||
if (CurMMUMode != mmu_mode) {
|
||||
switch(mmu_mode) {
|
||||
case 0: // real address mode
|
||||
pCurTLB1 = &mode1_tlb1[0];
|
||||
@ -742,7 +727,7 @@ void mmu_change_mode()
|
||||
pCurTLB2 = &mode3_tlb2[0];
|
||||
break;
|
||||
}
|
||||
MMUMode = mmu_mode;
|
||||
CurMMUMode = mmu_mode;
|
||||
}
|
||||
}
|
||||
|
||||
@ -817,6 +802,7 @@ static TLBEntry* tlb2_target_entry(uint32_t gp_va)
|
||||
static TLBEntry* tlb2_refill(uint32_t guest_va, int is_write)
|
||||
{
|
||||
uint32_t phys_addr;
|
||||
uint16_t flags = 0;
|
||||
TLBEntry *tlb_entry;
|
||||
|
||||
const uint32_t tag = guest_va & ~0xFFFUL;
|
||||
@ -828,18 +814,40 @@ static TLBEntry* tlb2_refill(uint32_t guest_va, int is_write)
|
||||
if (bat_res.hit) {
|
||||
// check block protection
|
||||
if (!bat_res.prot || ((bat_res.prot & 1) && is_write)) {
|
||||
LOG_F(WARNING, "BAT DSI exception in TLB2 refill!");
|
||||
LOG_F(WARNING, "Attempt to write to read-only region, LA=0x%08X, PC=0x%08X!", guest_va, ppc_state.pc);
|
||||
//UnmappedMem.tag = tag;
|
||||
//UnmappedMem.host_va_offset = (int64_t)(&UnmappedVal) - guest_va;
|
||||
//return &UnmappedMem;
|
||||
ppc_state.spr[SPR::DSISR] = 0x08000000 | (is_write << 25);
|
||||
ppc_state.spr[SPR::DAR] = guest_va;
|
||||
mmu_exception_handler(Except_Type::EXC_DSI, 0);
|
||||
}
|
||||
phys_addr = bat_res.phys;
|
||||
flags = TLBFlags::PTE_SET_C; // prevent PTE.C updates for BAT
|
||||
flags |= TLBFlags::TLBE_FROM_BAT; // tell the world we come from
|
||||
if (bat_res.prot == 2) {
|
||||
flags |= TLBFlags::PAGE_WRITABLE;
|
||||
}
|
||||
} else {
|
||||
// page address translation
|
||||
phys_addr = page_address_translate(guest_va, false,
|
||||
!!(ppc_state.msr & 0x4000), is_write);
|
||||
PATResult pat_res = page_address_translate(guest_va, false,
|
||||
!!(ppc_state.msr & 0x4000), is_write);
|
||||
phys_addr = pat_res.phys;
|
||||
flags = TLBFlags::TLBE_FROM_PAT; // tell the world we come from
|
||||
if (pat_res.prot <= 2 || pat_res.prot == 6) {
|
||||
flags |= TLBFlags::PAGE_WRITABLE;
|
||||
}
|
||||
if (is_write || pat_res.pte_c_status) {
|
||||
// C-bit of the PTE is already set so the TLB logic
|
||||
// doesn't need to update it anymore
|
||||
flags |= TLBFlags::PTE_SET_C;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else { // data translation disabled
|
||||
phys_addr = guest_va;
|
||||
flags = TLBFlags::PTE_SET_C; // no PTE.C updates in real addressing mode
|
||||
flags |= TLBFlags::PAGE_WRITABLE; // assume physical pages are writable
|
||||
}
|
||||
|
||||
// look up host virtual address
|
||||
@ -848,11 +856,11 @@ static TLBEntry* tlb2_refill(uint32_t guest_va, int is_write)
|
||||
// refill the secondary TLB
|
||||
tlb_entry = tlb2_target_entry(tag);
|
||||
tlb_entry->tag = tag;
|
||||
if (reg_desc->type & RT_MMIO) {
|
||||
tlb_entry->flags = 2; // MMIO region
|
||||
if (reg_desc->type & RT_MMIO) { // MMIO region
|
||||
tlb_entry->flags = flags | TLBFlags::PAGE_IO;
|
||||
tlb_entry->reg_desc = reg_desc;
|
||||
} else {
|
||||
tlb_entry->flags = 1; // memory region backed by host memory
|
||||
} else { // memory region backed by host memory
|
||||
tlb_entry->flags = flags | TLBFlags::PAGE_MEM;
|
||||
tlb_entry->host_va_offset = (int64_t)reg_desc->mem_ptr - guest_va +
|
||||
(phys_addr - reg_desc->start);
|
||||
}
|
||||
@ -865,7 +873,7 @@ static TLBEntry* tlb2_refill(uint32_t guest_va, int is_write)
|
||||
}
|
||||
}
|
||||
|
||||
void flush_tlb_entry(uint32_t ea)
|
||||
void tlb_flush_entry(uint32_t ea)
|
||||
{
|
||||
TLBEntry *tlb_entry, *tlb1, *tlb2;
|
||||
|
||||
@ -878,12 +886,12 @@ void flush_tlb_entry(uint32_t ea)
|
||||
tlb2 = &mode1_tlb2[0];
|
||||
break;
|
||||
case 1:
|
||||
tlb1 = &mode1_tlb1[0];
|
||||
tlb2 = &mode1_tlb2[0];
|
||||
tlb1 = &mode2_tlb1[0];
|
||||
tlb2 = &mode2_tlb2[0];
|
||||
break;
|
||||
case 2:
|
||||
tlb1 = &mode1_tlb1[0];
|
||||
tlb2 = &mode1_tlb2[0];
|
||||
tlb1 = &mode3_tlb1[0];
|
||||
tlb2 = &mode3_tlb2[0];
|
||||
break;
|
||||
}
|
||||
|
||||
@ -905,6 +913,53 @@ void flush_tlb_entry(uint32_t ea)
|
||||
}
|
||||
}
|
||||
|
||||
void tlb_flush_entries(TLBFlags type)
|
||||
{
|
||||
int i;
|
||||
|
||||
// Flush BAT entries from the primary TLBs
|
||||
for (i = 0; i < TLB_SIZE; i++) {
|
||||
if (mode2_tlb1[i].flags & type) {
|
||||
mode2_tlb1[i].tag = TLB_INVALID_TAG;
|
||||
}
|
||||
|
||||
if (mode3_tlb1[i].flags & type) {
|
||||
mode3_tlb1[i].tag = TLB_INVALID_TAG;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush BAT entries from the secondary TLBs
|
||||
for (i = 0; i < TLB_SIZE * TLB2_WAYS; i++) {
|
||||
if (mode2_tlb2[i].flags & type) {
|
||||
mode2_tlb2[i].tag = TLB_INVALID_TAG;
|
||||
}
|
||||
|
||||
if (mode3_tlb2[i].flags & type) {
|
||||
mode3_tlb2[i].tag = TLB_INVALID_TAG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tlb_flush_bat_entries()
|
||||
{
|
||||
if (!gTLBFlushBatEntries)
|
||||
return;
|
||||
|
||||
tlb_flush_entries(TLBE_FROM_BAT);
|
||||
|
||||
gTLBFlushBatEntries = false;
|
||||
}
|
||||
|
||||
void tlb_flush_pat_entries()
|
||||
{
|
||||
if (!gTLBFlushPatEntries)
|
||||
return;
|
||||
|
||||
tlb_flush_entries(TLBE_FROM_PAT);
|
||||
|
||||
gTLBFlushPatEntries = false;
|
||||
}
|
||||
|
||||
static inline uint64_t tlb_translate_addr(uint32_t guest_va)
|
||||
{
|
||||
TLBEntry *tlb1_entry, *tlb2_entry;
|
||||
@ -951,10 +1006,10 @@ static inline uint64_t tlb_translate_addr(uint32_t guest_va)
|
||||
tlb2_entry = tlb2_refill(guest_va, 0);
|
||||
}
|
||||
|
||||
if (tlb2_entry->flags & 1) { // is it a real memory region?
|
||||
if (tlb2_entry->flags & TLBFlags::PAGE_MEM) { // is it a real memory region?
|
||||
// refill the primary TLB
|
||||
tlb1_entry->tag = tag;
|
||||
tlb1_entry->flags = 1;
|
||||
tlb1_entry->flags = tlb2_entry->flags;
|
||||
tlb1_entry->host_va_offset = tlb2_entry->host_va_offset;
|
||||
MemAccessType = true;
|
||||
MemAddr = tlb1_entry->host_va_offset + guest_va;
|
||||
@ -1056,7 +1111,10 @@ static inline TLBEntry * lookup_secondary_tlb(uint32_t guest_va, uint32_t tag) {
|
||||
return tlb_entry;
|
||||
}
|
||||
|
||||
// Forward declarations.
|
||||
static uint32_t read_unaligned(uint32_t guest_va, uint8_t *host_va, uint32_t size);
|
||||
static void write_unaligned(uint32_t guest_va, uint8_t *host_va, uint32_t value,
|
||||
uint32_t size);
|
||||
|
||||
template <class T>
|
||||
inline T mmu_read_vmem(uint32_t guest_va) {
|
||||
@ -1078,10 +1136,10 @@ inline T mmu_read_vmem(uint32_t guest_va) {
|
||||
tlb2_entry = tlb2_refill(guest_va, 0);
|
||||
}
|
||||
|
||||
if (tlb2_entry->flags & 1) { // is it a real memory region?
|
||||
if (tlb2_entry->flags & TLBFlags::PAGE_MEM) { // is it a real memory region?
|
||||
// refill the primary TLB
|
||||
tlb1_entry->tag = tag;
|
||||
tlb1_entry->flags = 1;
|
||||
tlb1_entry->flags = tlb2_entry->flags;
|
||||
tlb1_entry->host_va_offset = tlb2_entry->host_va_offset;
|
||||
host_va = (uint8_t *)(tlb1_entry->host_va_offset + guest_va);
|
||||
} else { // otherwise, it's an access to a memory-mapped device
|
||||
@ -1117,6 +1175,110 @@ template uint16_t mmu_read_vmem<uint16_t>(uint32_t guest_va);
|
||||
template uint32_t mmu_read_vmem<uint32_t>(uint32_t guest_va);
|
||||
template uint64_t mmu_read_vmem<uint64_t>(uint32_t guest_va);
|
||||
|
||||
template <class T>
|
||||
inline void mmu_write_vmem(uint32_t guest_va, T value) {
|
||||
TLBEntry *tlb1_entry, *tlb2_entry;
|
||||
uint8_t *host_va;
|
||||
|
||||
const uint32_t tag = guest_va & ~0xFFFUL;
|
||||
|
||||
// look up guest virtual address in the primary TLB
|
||||
tlb1_entry = &pCurTLB1[(guest_va >> PAGE_SIZE_BITS) & tlb_size_mask];
|
||||
if (tlb1_entry->tag == tag) { // primary TLB hit -> fast path
|
||||
if (!(tlb1_entry->flags & TLBFlags::PAGE_WRITABLE)) {
|
||||
ppc_state.spr[SPR::DSISR] = 0x08000000 | (1 << 25);
|
||||
ppc_state.spr[SPR::DAR] = guest_va;
|
||||
mmu_exception_handler(Except_Type::EXC_DSI, 0);
|
||||
}
|
||||
if (!(tlb1_entry->flags & TLBFlags::PTE_SET_C)) {
|
||||
// perform full page address translation to update PTE.C bit
|
||||
PATResult pat_res = page_address_translate(guest_va, false,
|
||||
!!(ppc_state.msr & 0x4000), true);
|
||||
tlb1_entry->flags |= TLBFlags::PTE_SET_C;
|
||||
|
||||
// don't forget to update the secondary TLB as well
|
||||
tlb2_entry = lookup_secondary_tlb(guest_va, tag);
|
||||
if (tlb2_entry != nullptr) {
|
||||
tlb2_entry->flags |= TLBFlags::PTE_SET_C;
|
||||
}
|
||||
}
|
||||
host_va = (uint8_t *)(tlb1_entry->host_va_offset + guest_va);
|
||||
MemAccessType = true;
|
||||
MemAddr = (uint64_t)host_va;
|
||||
} else {
|
||||
// primary TLB miss -> look up address in the secondary TLB
|
||||
tlb2_entry = lookup_secondary_tlb(guest_va, tag);
|
||||
if (tlb2_entry == nullptr) {
|
||||
// secondary TLB miss ->
|
||||
// perform full address translation and refill the secondary TLB
|
||||
tlb2_entry = tlb2_refill(guest_va, 1);
|
||||
}
|
||||
|
||||
if (!(tlb2_entry->flags & TLBFlags::PAGE_WRITABLE)) {
|
||||
LOG_F(WARNING, "DSI Exception in mmu_write_vmem! PC=0x%08X", ppc_state.pc);
|
||||
//return;
|
||||
ppc_state.spr[SPR::DSISR] = 0x08000000 | (1 << 25);
|
||||
ppc_state.spr[SPR::DAR] = guest_va;
|
||||
mmu_exception_handler(Except_Type::EXC_DSI, 0);
|
||||
}
|
||||
|
||||
if (!(tlb2_entry->flags & TLBFlags::PTE_SET_C)) {
|
||||
// perform full page address translation to update PTE.C bit
|
||||
PATResult pat_res = page_address_translate(guest_va, false,
|
||||
!!(ppc_state.msr & 0x4000), true);
|
||||
tlb2_entry->flags |= TLBFlags::PTE_SET_C;
|
||||
}
|
||||
|
||||
if (tlb2_entry->flags & TLBFlags::PAGE_MEM) { // is it a real memory region?
|
||||
// refill the primary TLB
|
||||
tlb1_entry->tag = tag;
|
||||
tlb1_entry->flags = tlb2_entry->flags;
|
||||
tlb1_entry->host_va_offset = tlb2_entry->host_va_offset;
|
||||
host_va = (uint8_t *)(tlb1_entry->host_va_offset + guest_va);
|
||||
//MemAccessType = true;
|
||||
//MemAddr = (uint64_t)host_va;
|
||||
} else { // otherwise, it's an access to a memory-mapped device
|
||||
tlb2_entry->reg_desc->devobj->write(tlb2_entry->reg_desc->start,
|
||||
guest_va - tlb2_entry->reg_desc->start, value, sizeof(T));
|
||||
//MemAccessType = false;
|
||||
//Device = tlb2_entry->reg_desc->devobj;
|
||||
//DevOffset = guest_va - tlb2_entry->reg_desc->start;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// handle unaligned memory accesses
|
||||
if (sizeof(T) > 1 && (guest_va & (sizeof(T) - 1))) {
|
||||
write_unaligned(guest_va, host_va, value, sizeof(T));
|
||||
return;
|
||||
}
|
||||
|
||||
#if 1
|
||||
// handle aligned memory accesses
|
||||
switch(sizeof(T)) {
|
||||
case 1:
|
||||
*host_va = value;
|
||||
break;
|
||||
case 2:
|
||||
WRITE_WORD_BE_A(host_va, value);
|
||||
break;
|
||||
case 4:
|
||||
WRITE_DWORD_BE_A(host_va, value);
|
||||
break;
|
||||
case 8:
|
||||
WRITE_QWORD_BE_A(host_va, value);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// explicitely instantiate all required mmu_write_vmem variants
|
||||
// to avoid linking errors
|
||||
template void mmu_write_vmem<uint8_t>(uint32_t guest_va, uint8_t value);
|
||||
template void mmu_write_vmem<uint16_t>(uint32_t guest_va, uint16_t value);
|
||||
template void mmu_write_vmem<uint32_t>(uint32_t guest_va, uint32_t value);
|
||||
template void mmu_write_vmem<uint64_t>(uint32_t guest_va, uint64_t value);
|
||||
|
||||
static uint32_t read_unaligned(uint32_t guest_va, uint8_t *host_va, uint32_t size)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
@ -1142,6 +1304,99 @@ static uint32_t read_unaligned(uint32_t guest_va, uint8_t *host_va, uint32_t siz
|
||||
return result;
|
||||
}
|
||||
|
||||
static void write_unaligned(uint32_t guest_va, uint8_t *host_va, uint32_t value,
|
||||
uint32_t size)
|
||||
{
|
||||
// is it a misaligned cross-page write?
|
||||
if (((guest_va & 0xFFF) + size) > 0x1000) {
|
||||
Unaligned_crosspage = true;
|
||||
|
||||
// Break such a memory access into multiple, bytewise accesses.
|
||||
// Because such accesses suffer a performance penalty, they will be
|
||||
// presumably very rare so don't waste time optimizing the code below.
|
||||
|
||||
uint32_t shift = (size - 1) * 8;
|
||||
|
||||
for (int i = 0; i < size; shift -= 8, guest_va++, i++) {
|
||||
mmu_write_vmem<uint8_t>(guest_va, (value >> shift) & 0xFF);
|
||||
}
|
||||
} else {
|
||||
Unaligned_crosspage = false;
|
||||
#if 1
|
||||
switch(size) {
|
||||
case 2:
|
||||
WRITE_WORD_BE_U(host_va, value);
|
||||
break;
|
||||
case 4:
|
||||
WRITE_DWORD_BE_U(host_va, value);
|
||||
break;
|
||||
case 8: // FIXME: should we raise alignment exception here?
|
||||
WRITE_QWORD_BE_U(host_va, value);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void mem_write_byte(uint32_t addr, uint8_t value) {
|
||||
mmu_write_vmem<uint8_t>(addr, value);
|
||||
|
||||
/* data address translation if enabled */
|
||||
if (ppc_state.msr & 0x10) {
|
||||
addr = ppc_mmu_addr_translate(addr, 1);
|
||||
}
|
||||
|
||||
write_phys_mem<uint8_t, true>(&last_write_area, addr, value);
|
||||
}
|
||||
|
||||
void mem_write_word(uint32_t addr, uint16_t value) {
|
||||
mmu_write_vmem<uint16_t>(addr, value);
|
||||
|
||||
if (addr & 1) {
|
||||
mem_write_unaligned(addr, value, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
/* data address translation if enabled */
|
||||
if (ppc_state.msr & 0x10) {
|
||||
addr = ppc_mmu_addr_translate(addr, 1);
|
||||
}
|
||||
|
||||
write_phys_mem<uint16_t, true>(&last_write_area, addr, value);
|
||||
}
|
||||
|
||||
void mem_write_dword(uint32_t addr, uint32_t value) {
|
||||
mmu_write_vmem<uint32_t>(addr, value);
|
||||
|
||||
if (addr & 3) {
|
||||
mem_write_unaligned(addr, value, 4);
|
||||
return;
|
||||
}
|
||||
|
||||
/* data address translation if enabled */
|
||||
if (ppc_state.msr & 0x10) {
|
||||
addr = ppc_mmu_addr_translate(addr, 1);
|
||||
}
|
||||
|
||||
write_phys_mem<uint32_t, true>(&last_write_area, addr, value);
|
||||
}
|
||||
|
||||
void mem_write_qword(uint32_t addr, uint64_t value) {
|
||||
mmu_write_vmem<uint64_t>(addr, value);
|
||||
|
||||
if (addr & 7) {
|
||||
LOG_F(ERROR, "SOS! Attempt to write unaligned QWORD to 0x%08X\n", addr);
|
||||
exit(-1); // FIXME!
|
||||
}
|
||||
|
||||
/* data address translation if enabled */
|
||||
if (ppc_state.msr & 0x10) {
|
||||
addr = ppc_mmu_addr_translate(addr, 1);
|
||||
}
|
||||
|
||||
write_phys_mem<uint64_t, true>(&last_write_area, addr, value);
|
||||
}
|
||||
|
||||
/** Grab a value from memory into a register */
|
||||
uint8_t mem_grab_byte(uint32_t addr) {
|
||||
tlb_translate_addr(addr);
|
||||
|
@ -27,6 +27,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <vector>
|
||||
#include "devices/memctrlbase.h"
|
||||
|
||||
/* Uncomment this to exhaustive MMU integrity checks. */
|
||||
//#define MMU_INTEGRITY_CHECKS
|
||||
@ -53,6 +54,36 @@ typedef struct BATResult {
|
||||
uint32_t phys;
|
||||
} BATResult;
|
||||
|
||||
/** Result of the page address translation. */
|
||||
typedef struct PATResult {
|
||||
uint32_t phys;
|
||||
uint8_t prot;
|
||||
uint8_t pte_c_status; // status of the C bit of the PTE
|
||||
} PATResult;
|
||||
|
||||
#define PAGE_SIZE_BITS 12
|
||||
#define TLB_SIZE 4096
|
||||
#define TLB2_WAYS 4
|
||||
#define TLB_INVALID_TAG 0xFFFFFFFF
|
||||
|
||||
typedef struct TLBEntry {
|
||||
uint32_t tag;
|
||||
uint16_t flags;
|
||||
uint16_t lru_bits;
|
||||
union {
|
||||
int64_t host_va_offset;
|
||||
AddressMapEntry* reg_desc;
|
||||
};
|
||||
} TLBEntry;
|
||||
|
||||
enum TLBFlags : uint16_t {
|
||||
PAGE_MEM = 1 << 0, // memory page backed by host memory
|
||||
PAGE_IO = 1 << 1, // memory mapped I/O page
|
||||
TLBE_FROM_BAT = 1 << 2, // TLB entry has been translated with BAT
|
||||
TLBE_FROM_PAT = 1 << 3, // TLB entry has been translated with PAT
|
||||
PAGE_WRITABLE = 1 << 4, // page is writable
|
||||
PTE_SET_C = 1 << 5, // tells if C bit of the PTE needs to be updated
|
||||
};
|
||||
|
||||
extern void ibat_update(uint32_t bat_reg);
|
||||
extern void dbat_update(uint32_t bat_reg);
|
||||
@ -60,7 +91,10 @@ extern void dbat_update(uint32_t bat_reg);
|
||||
extern uint8_t* mmu_get_dma_mem(uint32_t addr, uint32_t size);
|
||||
|
||||
extern void mmu_change_mode(void);
|
||||
extern void flush_tlb_entry(uint32_t ea);
|
||||
extern void mmu_pat_ctx_changed();
|
||||
extern void tlb_flush_entry(uint32_t ea);
|
||||
extern void tlb_flush_bat_entries();
|
||||
extern void tlb_flush_pat_entries();
|
||||
|
||||
extern void ppc_set_cur_instruction(const uint8_t* ptr);
|
||||
extern void mem_write_byte(uint32_t addr, uint8_t value);
|
||||
@ -76,5 +110,7 @@ extern uint8_t* quickinstruction_translate(uint32_t address_grab);
|
||||
|
||||
template <class T>
|
||||
extern T mmu_read_vmem(uint32_t guest_va);
|
||||
template <class T>
|
||||
extern void mmu_write_vmem(uint32_t guest_va, T value);
|
||||
|
||||
#endif // PPCMEMORY_H
|
||||
|
@ -26,12 +26,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <thirdparty/loguru/loguru.hpp>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
uint32_t crf_d;
|
||||
uint32_t crf_s;
|
||||
@ -188,6 +190,21 @@ inline void ppc_setsoov(uint32_t a, uint32_t b, uint32_t d) {
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::function<void()> CtxSyncCallback;
|
||||
std::vector<CtxSyncCallback> 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
|
||||
@ -768,6 +785,7 @@ void dppc_interpreter::ppc_mtsr() {
|
||||
reg_s = (ppc_cur_instruction >> 21) & 31;
|
||||
grab_sr = (ppc_cur_instruction >> 16) & 15;
|
||||
ppc_state.sr[grab_sr] = ppc_state.gpr[reg_s];
|
||||
mmu_pat_ctx_changed();
|
||||
}
|
||||
}
|
||||
|
||||
@ -779,6 +797,7 @@ void dppc_interpreter::ppc_mtsrin() {
|
||||
ppc_grab_regssb();
|
||||
grab_sr = ppc_result_b >> 28;
|
||||
ppc_state.sr[grab_sr] = ppc_result_d;
|
||||
mmu_pat_ctx_changed();
|
||||
}
|
||||
}
|
||||
|
||||
@ -853,6 +872,11 @@ void dppc_interpreter::ppc_mtspr() {
|
||||
ppc_state.spr[ref_spr] = ppc_state.gpr[reg_s];
|
||||
}
|
||||
|
||||
if (ref_spr == SPR::SDR1) {
|
||||
LOG_F(INFO, "SDR1 changed to 0x%08X", ppc_state.spr[SPR::SDR1]);
|
||||
mmu_pat_ctx_changed();
|
||||
}
|
||||
|
||||
switch (ref_spr) {
|
||||
// Mirror the TBRs in the SPR range to the user-mode TBRs.
|
||||
case 284:
|
||||
@ -1320,7 +1344,7 @@ void dppc_interpreter::ppc_eieio() {
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_isync() {
|
||||
/* placeholder */
|
||||
do_ctx_sync();
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_sync() {
|
||||
@ -1362,10 +1386,10 @@ void dppc_interpreter::ppc_dcbz() {
|
||||
|
||||
ppc_effective_address &= 0xFFFFFFE0; // align EA on a 32-byte boundary
|
||||
|
||||
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);
|
||||
//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);
|
||||
}
|
||||
|
||||
|
||||
@ -1378,7 +1402,8 @@ void dppc_interpreter::ppc_stb() {
|
||||
ppc_grab_regssa();
|
||||
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
||||
ppc_effective_address += reg_a ? ppc_result_a : 0;
|
||||
mem_write_byte(ppc_effective_address, ppc_result_d);
|
||||
mmu_write_vmem<uint8_t>(ppc_effective_address, ppc_result_d);
|
||||
//mem_write_byte(ppc_effective_address, ppc_result_d);
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_stbx() {
|
||||
@ -1387,7 +1412,8 @@ void dppc_interpreter::ppc_stbx() {
|
||||
#endif
|
||||
ppc_grab_regssab();
|
||||
ppc_effective_address = reg_a ? (ppc_result_a + ppc_result_b) : ppc_result_b;
|
||||
mem_write_byte(ppc_effective_address, ppc_result_d);
|
||||
mmu_write_vmem<uint8_t>(ppc_effective_address, ppc_result_d);
|
||||
//mem_write_byte(ppc_effective_address, ppc_result_d);
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_stbu() {
|
||||
@ -1398,7 +1424,8 @@ void dppc_interpreter::ppc_stbu() {
|
||||
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);
|
||||
mmu_write_vmem<uint8_t>(ppc_effective_address, ppc_result_d);
|
||||
//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, Exc_Cause::ILLEGAL_OP);
|
||||
@ -1412,7 +1439,8 @@ void dppc_interpreter::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);
|
||||
mmu_write_vmem<uint8_t>(ppc_effective_address, ppc_result_d);
|
||||
//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, Exc_Cause::ILLEGAL_OP);
|
||||
@ -1426,7 +1454,8 @@ void dppc_interpreter::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);
|
||||
mmu_write_vmem<uint16_t>(ppc_effective_address, ppc_result_d);
|
||||
//mem_write_word(ppc_effective_address, ppc_result_d);
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_sthu() {
|
||||
@ -1437,7 +1466,8 @@ void dppc_interpreter::ppc_sthu() {
|
||||
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);
|
||||
mmu_write_vmem<uint16_t>(ppc_effective_address, ppc_result_d);
|
||||
//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, Exc_Cause::ILLEGAL_OP);
|
||||
@ -1451,7 +1481,8 @@ void dppc_interpreter::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);
|
||||
mmu_write_vmem<uint16_t>(ppc_effective_address, ppc_result_d);
|
||||
//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, Exc_Cause::ILLEGAL_OP);
|
||||
@ -1464,7 +1495,8 @@ void dppc_interpreter::ppc_sthx() {
|
||||
#endif
|
||||
ppc_grab_regssab();
|
||||
ppc_effective_address = reg_a ? (ppc_result_a + ppc_result_b) : ppc_result_b;
|
||||
mem_write_word(ppc_effective_address, ppc_result_d);
|
||||
mmu_write_vmem<uint16_t>(ppc_effective_address, ppc_result_d);
|
||||
//mem_write_word(ppc_effective_address, ppc_result_d);
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_sthbrx() {
|
||||
@ -1474,8 +1506,10 @@ void dppc_interpreter::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);
|
||||
mmu_write_vmem<uint16_t>(ppc_effective_address, ppc_result_d);
|
||||
//mem_write_word(ppc_effective_address, ppc_result_d);
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_stw() {
|
||||
#ifdef CPU_PROFILING
|
||||
num_int_stores++;
|
||||
@ -1483,7 +1517,8 @@ void dppc_interpreter::ppc_stw() {
|
||||
ppc_grab_regssa();
|
||||
ppc_effective_address = (int32_t)((int16_t)(ppc_cur_instruction & 0xFFFF));
|
||||
ppc_effective_address += reg_a ? ppc_result_a : 0;
|
||||
mem_write_dword(ppc_effective_address, ppc_result_d);
|
||||
mmu_write_vmem<uint32_t>(ppc_effective_address, ppc_result_d);
|
||||
//mem_write_dword(ppc_effective_address, ppc_result_d);
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_stwx() {
|
||||
@ -1492,7 +1527,8 @@ void dppc_interpreter::ppc_stwx() {
|
||||
#endif
|
||||
ppc_grab_regssab();
|
||||
ppc_effective_address = reg_a ? (ppc_result_a + ppc_result_b) : ppc_result_b;
|
||||
mem_write_dword(ppc_effective_address, ppc_result_d);
|
||||
mmu_write_vmem<uint32_t>(ppc_effective_address, ppc_result_d);
|
||||
//mem_write_dword(ppc_effective_address, ppc_result_d);
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_stwcx() {
|
||||
@ -1506,7 +1542,8 @@ void dppc_interpreter::ppc_stwcx() {
|
||||
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);
|
||||
mmu_write_vmem<uint32_t>(ppc_effective_address, ppc_result_d);
|
||||
//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 {
|
||||
@ -1523,7 +1560,8 @@ void dppc_interpreter::ppc_stwu() {
|
||||
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);
|
||||
mmu_write_vmem<uint32_t>(ppc_effective_address, ppc_result_d);
|
||||
//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, Exc_Cause::ILLEGAL_OP);
|
||||
@ -1537,7 +1575,8 @@ void dppc_interpreter::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);
|
||||
mmu_write_vmem<uint32_t>(ppc_effective_address, ppc_result_d);
|
||||
//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, Exc_Cause::ILLEGAL_OP);
|
||||
@ -1551,7 +1590,8 @@ void dppc_interpreter::ppc_stwbrx() {
|
||||
ppc_grab_regssab();
|
||||
ppc_effective_address = reg_a ? (ppc_result_a + ppc_result_b) : ppc_result_b;
|
||||
ppc_result_d = BYTESWAP_32(ppc_result_d);
|
||||
mem_write_dword(ppc_effective_address, ppc_result_d);
|
||||
mmu_write_vmem<uint32_t>(ppc_effective_address, ppc_result_d);
|
||||
//mem_write_dword(ppc_effective_address, ppc_result_d);
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_stmw() {
|
||||
@ -1568,7 +1608,8 @@ void dppc_interpreter::ppc_stmw() {
|
||||
}
|
||||
|
||||
for (; reg_s <= 31; reg_s++) {
|
||||
mem_write_dword(ppc_effective_address, ppc_state.gpr[reg_s]);
|
||||
mmu_write_vmem<uint32_t>(ppc_effective_address, ppc_state.gpr[reg_s]);
|
||||
//mem_write_dword(ppc_effective_address, ppc_state.gpr[reg_s]);
|
||||
ppc_effective_address += 4;
|
||||
}
|
||||
}
|
||||
@ -1886,20 +1927,26 @@ void dppc_interpreter::ppc_lswi() {
|
||||
while (grab_inb > 0) {
|
||||
switch (grab_inb) {
|
||||
case 1:
|
||||
stringed_word = mem_grab_byte(ppc_effective_address) << 24;
|
||||
stringed_word = mmu_read_vmem<uint8_t>(ppc_effective_address) << 24;
|
||||
//stringed_word = mem_grab_byte(ppc_effective_address) << 24;
|
||||
ppc_state.gpr[reg_d] = stringed_word;
|
||||
grab_inb = 0;
|
||||
break;
|
||||
case 2:
|
||||
stringed_word = mem_grab_byte(ppc_effective_address) << 24;
|
||||
stringed_word += mem_grab_byte(ppc_effective_address + 1) << 16;
|
||||
stringed_word = mmu_read_vmem<uint8_t>(ppc_effective_address) << 24;
|
||||
//stringed_word = mem_grab_byte(ppc_effective_address) << 24;
|
||||
stringed_word += mmu_read_vmem<uint8_t>(ppc_effective_address + 1) << 16;
|
||||
//stringed_word += mem_grab_byte(ppc_effective_address + 1) << 16;
|
||||
ppc_state.gpr[reg_d] = stringed_word;
|
||||
grab_inb = 0;
|
||||
break;
|
||||
case 3:
|
||||
stringed_word = mem_grab_byte(ppc_effective_address) << 24;
|
||||
stringed_word += mem_grab_byte(ppc_effective_address + 1) << 16;
|
||||
stringed_word += mem_grab_byte(ppc_effective_address + 2) << 8;
|
||||
stringed_word = mmu_read_vmem<uint8_t>(ppc_effective_address) << 24;
|
||||
//stringed_word = mem_grab_byte(ppc_effective_address) << 24;
|
||||
stringed_word += mmu_read_vmem<uint8_t>(ppc_effective_address + 1) << 16;
|
||||
//stringed_word += mem_grab_byte(ppc_effective_address + 1) << 16;
|
||||
stringed_word += mmu_read_vmem<uint8_t>(ppc_effective_address + 2) << 8;
|
||||
//stringed_word += mem_grab_byte(ppc_effective_address + 2) << 8;
|
||||
ppc_state.gpr[reg_d] = stringed_word;
|
||||
grab_inb = 0;
|
||||
break;
|
||||
@ -1934,20 +1981,26 @@ void dppc_interpreter::ppc_lswx() {
|
||||
while (grab_inb > 0) {
|
||||
switch (grab_inb) {
|
||||
case 1:
|
||||
stringed_word = mem_grab_byte(ppc_effective_address) << 24;
|
||||
stringed_word = mmu_read_vmem<uint8_t>(ppc_effective_address) << 24;
|
||||
//stringed_word = mem_grab_byte(ppc_effective_address) << 24;
|
||||
ppc_state.gpr[reg_d] = stringed_word;
|
||||
grab_inb = 0;
|
||||
break;
|
||||
case 2:
|
||||
stringed_word = mem_grab_byte(ppc_effective_address) << 24;
|
||||
stringed_word += mem_grab_byte(ppc_effective_address + 1) << 16;
|
||||
stringed_word = mmu_read_vmem<uint8_t>(ppc_effective_address) << 24;
|
||||
//stringed_word = mem_grab_byte(ppc_effective_address) << 24;
|
||||
stringed_word += mmu_read_vmem<uint8_t>(ppc_effective_address + 1) << 16;
|
||||
//stringed_word += mem_grab_byte(ppc_effective_address + 1) << 16;
|
||||
ppc_state.gpr[reg_d] = stringed_word;
|
||||
grab_inb = 0;
|
||||
break;
|
||||
case 3:
|
||||
stringed_word = mem_grab_byte(ppc_effective_address) << 24;
|
||||
stringed_word += mem_grab_byte(ppc_effective_address + 1) << 16;
|
||||
stringed_word += mem_grab_byte(ppc_effective_address + 2) << 8;
|
||||
stringed_word = mmu_read_vmem<uint8_t>(ppc_effective_address) << 24;
|
||||
//stringed_word = mem_grab_byte(ppc_effective_address) << 24;
|
||||
stringed_word += mmu_read_vmem<uint8_t>(ppc_effective_address + 1) << 16;
|
||||
//stringed_word += mem_grab_byte(ppc_effective_address + 1) << 16;
|
||||
stringed_word += mmu_read_vmem<uint8_t>(ppc_effective_address + 2) << 8;
|
||||
//stringed_word += mem_grab_byte(ppc_effective_address + 2) << 8;
|
||||
ppc_state.gpr[reg_d] = stringed_word;
|
||||
grab_inb = 0;
|
||||
break;
|
||||
@ -1973,22 +2026,29 @@ void dppc_interpreter::ppc_stswi() {
|
||||
while (grab_inb > 0) {
|
||||
switch (grab_inb) {
|
||||
case 1:
|
||||
mem_write_byte(ppc_effective_address, (ppc_result_d >> 24));
|
||||
mmu_write_vmem<uint8_t>(ppc_effective_address, (ppc_result_d >> 24));
|
||||
//mem_write_byte(ppc_effective_address, (ppc_result_d >> 24));
|
||||
grab_inb = 0;
|
||||
break;
|
||||
case 2:
|
||||
mem_write_byte(ppc_effective_address, ((ppc_result_d >> 24) & 0xFF));
|
||||
mem_write_byte((ppc_effective_address + 1), ((ppc_result_d >> 16) & 0xFF));
|
||||
mmu_write_vmem<uint8_t>(ppc_effective_address, ((ppc_result_d >> 24) & 0xFF));
|
||||
//mem_write_byte(ppc_effective_address, ((ppc_result_d >> 24) & 0xFF));
|
||||
mmu_write_vmem<uint8_t>((ppc_effective_address + 1), ((ppc_result_d >> 16) & 0xFF));
|
||||
//mem_write_byte((ppc_effective_address + 1), ((ppc_result_d >> 16) & 0xFF));
|
||||
grab_inb = 0;
|
||||
break;
|
||||
case 3:
|
||||
mem_write_byte(ppc_effective_address, ((ppc_result_d >> 24) & 0xFF));
|
||||
mem_write_byte((ppc_effective_address + 1), ((ppc_result_d >> 16) & 0xFF));
|
||||
mem_write_byte((ppc_effective_address + 2), ((ppc_result_d >> 8) & 0xFF));
|
||||
mmu_write_vmem<uint8_t>(ppc_effective_address, ((ppc_result_d >> 24) & 0xFF));
|
||||
//mem_write_byte(ppc_effective_address, ((ppc_result_d >> 24) & 0xFF));
|
||||
mmu_write_vmem<uint8_t>((ppc_effective_address + 1), ((ppc_result_d >> 16) & 0xFF));
|
||||
//mem_write_byte((ppc_effective_address + 1), ((ppc_result_d >> 16) & 0xFF));
|
||||
mmu_write_vmem<uint8_t>((ppc_effective_address + 2), ((ppc_result_d >> 8) & 0xFF));
|
||||
//mem_write_byte((ppc_effective_address + 2), ((ppc_result_d >> 8) & 0xFF));
|
||||
grab_inb = 0;
|
||||
break;
|
||||
default:
|
||||
mem_write_dword(ppc_effective_address, ppc_result_d);
|
||||
mmu_write_vmem<uint32_t>(ppc_effective_address, ppc_result_d);
|
||||
//mem_write_dword(ppc_effective_address, ppc_result_d);
|
||||
reg_s++;
|
||||
ppc_effective_address += 4;
|
||||
grab_inb -= 4;
|
||||
@ -2007,22 +2067,29 @@ void dppc_interpreter::ppc_stswx() {
|
||||
while (grab_inb > 0) {
|
||||
switch (grab_inb) {
|
||||
case 1:
|
||||
mem_write_byte(ppc_effective_address, (ppc_result_d >> 24));
|
||||
mmu_write_vmem<uint8_t>(ppc_effective_address, (ppc_result_d >> 24));
|
||||
//mem_write_byte(ppc_effective_address, (ppc_result_d >> 24));
|
||||
grab_inb = 0;
|
||||
break;
|
||||
case 2:
|
||||
mem_write_byte(ppc_effective_address, ((ppc_result_d >> 24) & 0xFF));
|
||||
mem_write_byte((ppc_effective_address + 1), ((ppc_result_d >> 16) & 0xFF));
|
||||
mmu_write_vmem<uint8_t>(ppc_effective_address, ((ppc_result_d >> 24) & 0xFF));
|
||||
//mem_write_byte(ppc_effective_address, ((ppc_result_d >> 24) & 0xFF));
|
||||
mmu_write_vmem<uint8_t>((ppc_effective_address + 1), ((ppc_result_d >> 16) & 0xFF));
|
||||
//mem_write_byte((ppc_effective_address + 1), ((ppc_result_d >> 16) & 0xFF));
|
||||
grab_inb = 0;
|
||||
break;
|
||||
case 3:
|
||||
mem_write_byte(ppc_effective_address, ((ppc_result_d >> 24) & 0xFF));
|
||||
mem_write_byte((ppc_effective_address + 1), ((ppc_result_d >> 16) & 0xFF));
|
||||
mem_write_byte((ppc_effective_address + 2), ((ppc_result_d >> 8) & 0xFF));
|
||||
mmu_write_vmem<uint8_t>(ppc_effective_address, ((ppc_result_d >> 24) & 0xFF));
|
||||
//mem_write_byte(ppc_effective_address, ((ppc_result_d >> 24) & 0xFF));
|
||||