mirror of
https://github.com/dingusdev/dingusppc.git
synced 2024-12-24 12:30:05 +00:00
acdb14a10a
While booting Mac OS X 10.2 installer CD, a return from RFI didn't change the instruction address virtual memory page but did change the physical memory page so we must always recalculate the physical address after RFI. Perhaps there are other cases where this may be required?
938 lines
30 KiB
C++
938 lines
30 KiB
C++
/*
|
|
DingusPPC - The Experimental PowerPC Macintosh emulator
|
|
Copyright (C) 2018-21 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <core/timermanager.h>
|
|
#include <loguru.hpp>
|
|
#include "ppcemu.h"
|
|
#include "ppcmmu.h"
|
|
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <setjmp.h>
|
|
#include <stdexcept>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
|
|
using namespace std;
|
|
using namespace dppc_interpreter;
|
|
|
|
MemCtrlBase* mem_ctrl_instance = 0;
|
|
|
|
bool is_601 = false;
|
|
|
|
bool power_on = 1;
|
|
|
|
SetPRS ppc_state;
|
|
|
|
bool rc_flag = 0; // Record flag
|
|
bool oe_flag = 0; // Overflow flag
|
|
|
|
bool grab_return;
|
|
bool grab_breakpoint;
|
|
|
|
uint32_t ppc_cur_instruction; // Current instruction for the PPC
|
|
uint32_t ppc_effective_address;
|
|
uint32_t ppc_next_instruction_address; // Used for branching, setting up the NIA
|
|
|
|
unsigned exec_flags; // execution control flags
|
|
bool int_pin = false; // interrupt request pin state: true - asserted
|
|
bool dec_exception_pending = false;
|
|
|
|
/* copy of local variable bb_start_la. Need for correct
|
|
calculation of CPU cycles after setjmp that clobbers
|
|
non-volatile local variables. */
|
|
uint32_t glob_bb_start_la;
|
|
|
|
/* variables related to virtual time */
|
|
uint64_t g_icycles;
|
|
int icnt_factor;
|
|
|
|
/* global variables related to the timebase facility */
|
|
uint64_t tbr_wr_timestamp; // stores vCPU virtual time of the last TBR write
|
|
uint64_t rtc_timestamp; // stores vCPU virtual time of the last RTC write
|
|
uint64_t tbr_wr_value; // last value written to the TBR
|
|
uint32_t tbr_freq_ghz; // TBR/RTC driving frequency in GHz expressed as a 32 bit fraction less than 1.0 (999.999999 MHz maximum).
|
|
uint64_t tbr_period_ns; // TBR/RTC period in ns expressed as a 64 bit value with 32 fractional bits (<1 Hz minimum).
|
|
uint64_t timebase_counter; // internal timebase counter
|
|
uint64_t dec_wr_timestamp; // stores vCPU virtual time of the last DEC write
|
|
uint32_t dec_wr_value; // last value written to the DEC register
|
|
uint32_t rtc_lo; // MPC601 RTC lower, counts nanoseconds
|
|
uint32_t rtc_hi; // MPC601 RTC upper, counts seconds
|
|
|
|
#ifdef CPU_PROFILING
|
|
|
|
/* global variables for lightweight CPU profiling */
|
|
uint64_t num_executed_instrs;
|
|
uint64_t num_supervisor_instrs;
|
|
uint64_t num_int_loads;
|
|
uint64_t num_int_stores;
|
|
uint64_t exceptions_processed;
|
|
|
|
#include "utils/profiler.h"
|
|
#include <memory>
|
|
|
|
class CPUProfile : public BaseProfile {
|
|
public:
|
|
CPUProfile() : BaseProfile("PPC_CPU") {};
|
|
|
|
void populate_variables(std::vector<ProfileVar>& vars) {
|
|
vars.clear();
|
|
|
|
vars.push_back({.name = "Executed Instructions Total",
|
|
.format = ProfileVarFmt::DEC,
|
|
.value = num_executed_instrs});
|
|
|
|
vars.push_back({.name = "Executed Supervisor Instructions",
|
|
.format = ProfileVarFmt::DEC,
|
|
.value = num_supervisor_instrs});
|
|
|
|
vars.push_back({.name = "Integer Load Instructions",
|
|
.format = ProfileVarFmt::DEC,
|
|
.value = num_int_loads});
|
|
|
|
vars.push_back({.name = "Integer Store Instructions",
|
|
.format = ProfileVarFmt::DEC,
|
|
.value = num_int_stores});
|
|
|
|
vars.push_back({.name = "Exceptions processed",
|
|
.format = ProfileVarFmt::DEC,
|
|
.value = exceptions_processed});
|
|
};
|
|
|
|
void reset() {
|
|
num_executed_instrs = 0;
|
|
num_supervisor_instrs = 0;
|
|
num_int_loads = 0;
|
|
num_int_stores = 0;
|
|
exceptions_processed = 0;
|
|
};
|
|
};
|
|
|
|
#endif
|
|
|
|
/** Opcode lookup tables. */
|
|
|
|
/** Primary opcode (bits 0...5) lookup table. */
|
|
static PPCOpcode OpcodeGrabber[] = {
|
|
ppc_illegalop, ppc_illegalop, ppc_illegalop, ppc_twi, ppc_illegalop, ppc_illegalop,
|
|
ppc_illegalop, ppc_mulli, ppc_subfic, power_dozi, ppc_cmpli, ppc_cmpi,
|
|
ppc_addic, ppc_addicdot, ppc_addi, ppc_addis, ppc_opcode16, ppc_sc,
|
|
ppc_opcode18, ppc_opcode19, ppc_rlwimi, ppc_rlwinm, power_rlmi, ppc_rlwnm,
|
|
ppc_ori, ppc_oris, ppc_xori, ppc_xoris, ppc_andidot, ppc_andisdot,
|
|
ppc_illegalop, ppc_opcode31, ppc_lwz, ppc_lwzu, ppc_lbz, ppc_lbzu,
|
|
ppc_stw, ppc_stwu, ppc_stb, ppc_stbu, ppc_lhz, ppc_lhzu,
|
|
ppc_lha, ppc_lhau, ppc_sth, ppc_sthu, ppc_lmw, ppc_stmw,
|
|
ppc_lfs, ppc_lfsu, ppc_lfd, ppc_lfdu, ppc_stfs, ppc_stfsu,
|
|
ppc_stfd, ppc_stfdu, ppc_illegalop, ppc_illegalop, ppc_illegalop, ppc_opcode59,
|
|
ppc_illegalop, ppc_illegalop, ppc_illegalop, ppc_opcode63};
|
|
|
|
/** Lookup tables for branch instructions. */
|
|
static PPCOpcode SubOpcode16Grabber[] = {
|
|
dppc_interpreter::ppc_bc,
|
|
dppc_interpreter::ppc_bcl,
|
|
dppc_interpreter::ppc_bca,
|
|
dppc_interpreter::ppc_bcla};
|
|
|
|
static PPCOpcode SubOpcode18Grabber[] = {
|
|
dppc_interpreter::ppc_b,
|
|
dppc_interpreter::ppc_bl,
|
|
dppc_interpreter::ppc_ba,
|
|
dppc_interpreter::ppc_bla};
|
|
|
|
/** Instructions decoding tables for integer,
|
|
single floating-point, and double-floating point ops respectively */
|
|
|
|
PPCOpcode SubOpcode31Grabber[1024] = { ppc_illegalop };
|
|
PPCOpcode SubOpcode59Grabber[32] = { ppc_illegalop };
|
|
PPCOpcode SubOpcode63Grabber[1024] = { ppc_illegalop };
|
|
|
|
/** Exception helpers. */
|
|
|
|
void ppc_illegalop() {
|
|
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);
|
|
}
|
|
|
|
void ppc_fpu_off() {
|
|
ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::FPU_OFF);
|
|
}
|
|
|
|
void ppc_assert_int() {
|
|
int_pin = true;
|
|
if (ppc_state.msr & 0x8000) {
|
|
LOG_F(5, "CPU ExtIntHandler called");
|
|
ppc_exception_handler(Except_Type::EXC_EXT_INT, 0);
|
|
} else {
|
|
LOG_F(5, "CPU IRQ ignored!");
|
|
}
|
|
}
|
|
|
|
void ppc_release_int() {
|
|
int_pin = false;
|
|
}
|
|
|
|
/** Opcode decoding functions. */
|
|
|
|
void ppc_opcode16() {
|
|
SubOpcode16Grabber[ppc_cur_instruction & 3]();
|
|
}
|
|
|
|
void ppc_opcode18() {
|
|
SubOpcode18Grabber[ppc_cur_instruction & 3]();
|
|
}
|
|
|
|
void ppc_opcode19() {
|
|
uint16_t subop_grab = ppc_cur_instruction & 0x7FF;
|
|
|
|
#ifdef EXHAUSTIVE_DEBUG
|
|
uint32_t regrab = (uint32_t)subop_grab;
|
|
LOG_F(INFO, "Executing Opcode 19 table subopcode entry", regrab);
|
|
#endif // EXHAUSTIVE_DEBUG
|
|
|
|
if (subop_grab == 32) {
|
|
ppc_bclr();
|
|
} else if (subop_grab == 33) {
|
|
ppc_bclrl();
|
|
} else if (subop_grab == 1056) {
|
|
ppc_bcctr();
|
|
} else if (subop_grab == 1057) {
|
|
ppc_bcctrl();
|
|
|
|
} else {
|
|
switch (subop_grab) {
|
|
case 0:
|
|
ppc_mcrf();
|
|
break;
|
|
case 66:
|
|
ppc_crnor();
|
|
break;
|
|
case 100:
|
|
ppc_rfi();
|
|
break;
|
|
case 258:
|
|
ppc_crandc();
|
|
break;
|
|
case 300:
|
|
ppc_isync();
|
|
break;
|
|
case 386:
|
|
ppc_crxor();
|
|
break;
|
|
case 450:
|
|
ppc_crnand();
|
|
break;
|
|
case 514:
|
|
ppc_crand();
|
|
break;
|
|
case 578:
|
|
ppc_creqv();
|
|
break;
|
|
case 834:
|
|
ppc_crorc();
|
|
break;
|
|
case 898:
|
|
ppc_cror();
|
|
break;
|
|
default:
|
|
ppc_illegalop();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ppc_opcode31() {
|
|
uint16_t subop_grab = (ppc_cur_instruction & 0x7FF) >> 1;
|
|
|
|
rc_flag = ppc_cur_instruction & 0x1;
|
|
oe_flag = ppc_cur_instruction & 0x400;
|
|
|
|
#ifdef EXHAUSTIVE_DEBUG
|
|
LOG_F(INFO, "Executing Opcode 31 table subopcode entry %n", (uint32_t)subop_grab);
|
|
#endif // EXHAUSTIVE_DEBUG
|
|
|
|
SubOpcode31Grabber[subop_grab]();
|
|
}
|
|
|
|
void ppc_opcode59() {
|
|
uint16_t subop_grab = (ppc_cur_instruction >> 1) & 0x1F;
|
|
rc_flag = ppc_cur_instruction & 1;
|
|
#ifdef EXHAUSTIVE_DEBUG
|
|
LOG_F(INFO, "Executing Opcode 59 table subopcode entry %n", (uint32_t)subop_grab);
|
|
#endif // EXHAUSTIVE_DEBUG
|
|
SubOpcode59Grabber[subop_grab]();
|
|
}
|
|
|
|
void ppc_opcode63() {
|
|
uint16_t subop_grab = (ppc_cur_instruction >> 1) & 0x3FF;
|
|
rc_flag = ppc_cur_instruction & 1;
|
|
#ifdef EXHAUSTIVE_DEBUG
|
|
LOG_F(INFO, "Executing Opcode 63 table subopcode entry %n", (uint32_t)subop_grab);
|
|
#endif // EXHAUSTIVE_DEBUG
|
|
SubOpcode63Grabber[subop_grab]();
|
|
}
|
|
|
|
/* Dispatch using main opcode */
|
|
void ppc_main_opcode()
|
|
{
|
|
#ifdef CPU_PROFILING
|
|
num_executed_instrs++;
|
|
#endif
|
|
OpcodeGrabber[(ppc_cur_instruction >> 26) & 0x3F]();
|
|
}
|
|
|
|
uint64_t get_virt_time_ns()
|
|
{
|
|
return g_icycles << icnt_factor;
|
|
}
|
|
|
|
uint64_t process_events()
|
|
{
|
|
uint64_t slice_ns = TimerManager::get_instance()->process_timers(get_virt_time_ns());
|
|
if (slice_ns == 0) {
|
|
// execute 10.000 cycles
|
|
// if there are no pending timers
|
|
return g_icycles + 10000;
|
|
}
|
|
return g_icycles + ((slice_ns + (1 << icnt_factor)) >> icnt_factor);
|
|
}
|
|
|
|
void force_cycle_counter_reload()
|
|
{
|
|
// tell the interpreter loop to reload cycle counter
|
|
exec_flags |= EXEF_TIMER;
|
|
}
|
|
|
|
/** Execute PPC code as long as power is on. */
|
|
// inner interpreter loop
|
|
static void ppc_exec_inner()
|
|
{
|
|
uint64_t max_cycles;
|
|
uint32_t page_start, eb_start, eb_end;
|
|
uint8_t* pc_real;
|
|
|
|
max_cycles = 0;
|
|
|
|
while (power_on) {
|
|
// define boundaries of the next execution block
|
|
// max execution block length = one memory page
|
|
eb_start = ppc_state.pc;
|
|
page_start = eb_start & PAGE_MASK;
|
|
eb_end = page_start + PAGE_SIZE - 1;
|
|
exec_flags = 0;
|
|
|
|
pc_real = mmu_translate_imem(eb_start);
|
|
|
|
// interpret execution block
|
|
while (ppc_state.pc < eb_end) {
|
|
ppc_main_opcode();
|
|
if (g_icycles++ >= max_cycles) {
|
|
max_cycles = process_events();
|
|
}
|
|
|
|
if (exec_flags) {
|
|
if (!power_on)
|
|
break;
|
|
// reload cycle counter if requested
|
|
if (exec_flags & EXEF_TIMER) {
|
|
max_cycles = process_events();
|
|
if (!(exec_flags & ~EXEF_TIMER)) {
|
|
ppc_state.pc += 4;
|
|
pc_real += 4;
|
|
ppc_set_cur_instruction(pc_real);
|
|
exec_flags = 0;
|
|
continue;
|
|
}
|
|
}
|
|
// define next execution block
|
|
eb_start = ppc_next_instruction_address;
|
|
if (!(exec_flags & EXEF_RFI) && (eb_start & PAGE_MASK) == page_start) {
|
|
pc_real += (int)eb_start - (int)ppc_state.pc;
|
|
ppc_set_cur_instruction(pc_real);
|
|
} else {
|
|
page_start = eb_start & PAGE_MASK;
|
|
eb_end = page_start + PAGE_SIZE - 1;
|
|
pc_real = mmu_translate_imem(eb_start);
|
|
}
|
|
ppc_state.pc = eb_start;
|
|
exec_flags = 0;
|
|
} else {
|
|
ppc_state.pc += 4;
|
|
pc_real += 4;
|
|
ppc_set_cur_instruction(pc_real);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// outer interpreter loop
|
|
void ppc_exec()
|
|
{
|
|
if (setjmp(exc_env)) {
|
|
// process low-level exceptions
|
|
//LOG_F(9, "PPC-EXEC: low_level exception raised!");
|
|
ppc_state.pc = ppc_next_instruction_address;
|
|
}
|
|
|
|
while (power_on) {
|
|
ppc_exec_inner();
|
|
}
|
|
}
|
|
|
|
/** Execute one PPC instruction. */
|
|
void ppc_exec_single()
|
|
{
|
|
if (setjmp(exc_env)) {
|
|
// process low-level exceptions
|
|
//LOG_F(9, "PPC-EXEC: low_level exception raised!");
|
|
ppc_state.pc = ppc_next_instruction_address;
|
|
exec_flags = 0;
|
|
return;
|
|
}
|
|
|
|
mmu_translate_imem(ppc_state.pc);
|
|
ppc_main_opcode();
|
|
g_icycles++;
|
|
process_events();
|
|
|
|
if (exec_flags) {
|
|
if (exec_flags & EXEF_TIMER) {
|
|
ppc_state.pc += 4;
|
|
} else {
|
|
ppc_state.pc = ppc_next_instruction_address;
|
|
}
|
|
exec_flags = 0;
|
|
} else {
|
|
ppc_state.pc += 4;
|
|
}
|
|
}
|
|
|
|
/** Execute PPC code until goal_addr is reached. */
|
|
|
|
// inner interpreter loop
|
|
static void ppc_exec_until_inner(const uint32_t goal_addr)
|
|
{
|
|
uint64_t max_cycles;
|
|
uint32_t page_start, eb_start, eb_end;
|
|
uint8_t* pc_real;
|
|
|
|
max_cycles = 0;
|
|
|
|
while (ppc_state.pc != goal_addr) {
|
|
// define boundaries of the next execution block
|
|
// max execution block length = one memory page
|
|
eb_start = ppc_state.pc;
|
|
page_start = eb_start & PAGE_MASK;
|
|
eb_end = page_start + PAGE_SIZE - 1;
|
|
exec_flags = 0;
|
|
|
|
pc_real = mmu_translate_imem(eb_start);
|
|
|
|
// interpret execution block
|
|
while ((ppc_state.pc != goal_addr) && (ppc_state.pc < eb_end)) {
|
|
ppc_main_opcode();
|
|
if (g_icycles++ >= max_cycles) {
|
|
max_cycles = process_events();
|
|
}
|
|
|
|
if (exec_flags) {
|
|
// reload cycle counter if requested
|
|
if (exec_flags & EXEF_TIMER) {
|
|
max_cycles = process_events();
|
|
if (!(exec_flags & ~EXEF_TIMER)) {
|
|
ppc_state.pc += 4;
|
|
pc_real += 4;
|
|
ppc_set_cur_instruction(pc_real);
|
|
exec_flags = 0;
|
|
continue;
|
|
}
|
|
}
|
|
// define next execution block
|
|
eb_start = ppc_next_instruction_address;
|
|
if (!(exec_flags & EXEF_RFI) && (eb_start & PAGE_MASK) == page_start) {
|
|
pc_real += (int)eb_start - (int)ppc_state.pc;
|
|
ppc_set_cur_instruction(pc_real);
|
|
} else {
|
|
page_start = eb_start & PAGE_MASK;
|
|
eb_end = page_start + PAGE_SIZE - 1;
|
|
pc_real = mmu_translate_imem(eb_start);
|
|
}
|
|
ppc_state.pc = eb_start;
|
|
exec_flags = 0;
|
|
} else {
|
|
ppc_state.pc += 4;
|
|
pc_real += 4;
|
|
ppc_set_cur_instruction(pc_real);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// outer interpreter loop
|
|
void ppc_exec_until(volatile uint32_t goal_addr)
|
|
{
|
|
if (setjmp(exc_env)) {
|
|
// process low-level exceptions
|
|
//LOG_F(9, "PPC-EXEC: low_level exception raised!");
|
|
ppc_state.pc = ppc_next_instruction_address;
|
|
}
|
|
|
|
while (ppc_state.pc != goal_addr) {
|
|
ppc_exec_until_inner(goal_addr);
|
|
}
|
|
}
|
|
|
|
/** Execute PPC code until control is reached the specified region. */
|
|
|
|
// inner interpreter loop
|
|
static void ppc_exec_dbg_inner(const uint32_t start_addr, const uint32_t size)
|
|
{
|
|
uint64_t max_cycles;
|
|
uint32_t page_start, eb_start, eb_end;
|
|
uint8_t* pc_real;
|
|
|
|
max_cycles = 0;
|
|
|
|
while (ppc_state.pc < start_addr || ppc_state.pc >= start_addr + size) {
|
|
// define boundaries of the next execution block
|
|
// max execution block length = one memory page
|
|
eb_start = ppc_state.pc;
|
|
page_start = eb_start & PAGE_MASK;
|
|
eb_end = page_start + PAGE_SIZE - 1;
|
|
exec_flags = 0;
|
|
|
|
pc_real = mmu_translate_imem(eb_start);
|
|
|
|
// interpret execution block
|
|
while ((ppc_state.pc < start_addr || ppc_state.pc >= start_addr + size)
|
|
&& (ppc_state.pc < eb_end)) {
|
|
ppc_main_opcode();
|
|
if (g_icycles++ >= max_cycles) {
|
|
max_cycles = process_events();
|
|
}
|
|
|
|
if (exec_flags) {
|
|
// reload cycle counter if requested
|
|
if (exec_flags & EXEF_TIMER) {
|
|
max_cycles = process_events();
|
|
if (!(exec_flags & ~EXEF_TIMER)) {
|
|
ppc_state.pc += 4;
|
|
pc_real += 4;
|
|
ppc_set_cur_instruction(pc_real);
|
|
exec_flags = 0;
|
|
continue;
|
|
}
|
|
}
|
|
// define next execution block
|
|
eb_start = ppc_next_instruction_address;
|
|
if (!(exec_flags & EXEF_RFI) && (eb_start & PAGE_MASK) == page_start) {
|
|
pc_real += (int)eb_start - (int)ppc_state.pc;
|
|
ppc_set_cur_instruction(pc_real);
|
|
} else {
|
|
page_start = eb_start & PAGE_MASK;
|
|
eb_end = page_start + PAGE_SIZE - 1;
|
|
pc_real = mmu_translate_imem(eb_start);
|
|
}
|
|
ppc_state.pc = eb_start;
|
|
exec_flags = 0;
|
|
} else {
|
|
ppc_state.pc += 4;
|
|
pc_real += 4;
|
|
ppc_set_cur_instruction(pc_real);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// outer interpreter loop
|
|
void ppc_exec_dbg(volatile uint32_t start_addr, volatile uint32_t size)
|
|
{
|
|
if (setjmp(exc_env)) {
|
|
// process low-level exceptions
|
|
//LOG_F(9, "PPC-EXEC: low_level exception raised!");
|
|
ppc_state.pc = ppc_next_instruction_address;
|
|
}
|
|
|
|
while (ppc_state.pc < start_addr || ppc_state.pc >= start_addr + size) {
|
|
ppc_exec_dbg_inner(start_addr, size);
|
|
}
|
|
}
|
|
|
|
void initialize_ppc_opcode_tables() {
|
|
SubOpcode31Grabber[0] = ppc_cmp;
|
|
SubOpcode31Grabber[4] = ppc_tw;
|
|
SubOpcode31Grabber[32] = ppc_cmpl;
|
|
|
|
SubOpcode31Grabber[8] = SubOpcode31Grabber[520] = ppc_subfc;
|
|
SubOpcode31Grabber[40] = SubOpcode31Grabber[552] = ppc_subf;
|
|
SubOpcode31Grabber[104] = SubOpcode31Grabber[616] = ppc_neg;
|
|
SubOpcode31Grabber[136] = SubOpcode31Grabber[648] = ppc_subfe;
|
|
SubOpcode31Grabber[200] = SubOpcode31Grabber[712] = ppc_subfze;
|
|
SubOpcode31Grabber[232] = SubOpcode31Grabber[744] = ppc_subfme;
|
|
|
|
SubOpcode31Grabber[10] = SubOpcode31Grabber[522] = ppc_addc;
|
|
SubOpcode31Grabber[138] = SubOpcode31Grabber[650] = ppc_adde;
|
|
SubOpcode31Grabber[202] = SubOpcode31Grabber[714] = ppc_addze;
|
|
SubOpcode31Grabber[234] = SubOpcode31Grabber[746] = ppc_addme;
|
|
SubOpcode31Grabber[266] = SubOpcode31Grabber[778] = ppc_add;
|
|
|
|
SubOpcode31Grabber[11] = ppc_mulhwu;
|
|
SubOpcode31Grabber[75] = ppc_mulhw;
|
|
SubOpcode31Grabber[235] = SubOpcode31Grabber[747] = ppc_mullw;
|
|
SubOpcode31Grabber[459] = SubOpcode31Grabber[971] = ppc_divwu;
|
|
SubOpcode31Grabber[491] = SubOpcode31Grabber[1003] = ppc_divw;
|
|
|
|
SubOpcode31Grabber[20] = ppc_lwarx;
|
|
SubOpcode31Grabber[23] = ppc_lwzx;
|
|
SubOpcode31Grabber[55] = ppc_lwzux;
|
|
SubOpcode31Grabber[87] = ppc_lbzx;
|
|
SubOpcode31Grabber[119] = ppc_lbzux;
|
|
SubOpcode31Grabber[279] = ppc_lhzx;
|
|
SubOpcode31Grabber[311] = ppc_lhzux;
|
|
SubOpcode31Grabber[343] = ppc_lhax;
|
|
SubOpcode31Grabber[375] = ppc_lhaux;
|
|
SubOpcode31Grabber[533] = ppc_lswx;
|
|
SubOpcode31Grabber[534] = ppc_lwbrx;
|
|
SubOpcode31Grabber[535] = ppc_lfsx;
|
|
SubOpcode31Grabber[567] = ppc_lfsux;
|
|
SubOpcode31Grabber[597] = ppc_lswi;
|
|
SubOpcode31Grabber[599] = ppc_lfdx;
|
|
SubOpcode31Grabber[631] = ppc_lfdux;
|
|
SubOpcode31Grabber[790] = ppc_lhbrx;
|
|
|
|
SubOpcode31Grabber[150] = ppc_stwcx;
|
|
SubOpcode31Grabber[151] = ppc_stwx;
|
|
SubOpcode31Grabber[183] = ppc_stwux;
|
|
SubOpcode31Grabber[215] = ppc_stbx;
|
|
SubOpcode31Grabber[247] = ppc_stbux;
|
|
SubOpcode31Grabber[407] = ppc_sthx;
|
|
SubOpcode31Grabber[439] = ppc_sthux;
|
|
SubOpcode31Grabber[661] = ppc_stswx;
|
|
SubOpcode31Grabber[662] = ppc_stwbrx;
|
|
SubOpcode31Grabber[663] = ppc_stfsx;
|
|
SubOpcode31Grabber[695] = ppc_stfsux;
|
|
SubOpcode31Grabber[725] = ppc_stswi;
|
|
SubOpcode31Grabber[727] = ppc_stfdx;
|
|
SubOpcode31Grabber[759] = ppc_stfdux;
|
|
SubOpcode31Grabber[918] = ppc_sthbrx;
|
|
SubOpcode31Grabber[983] = ppc_stfiwx;
|
|
|
|
SubOpcode31Grabber[310] = ppc_eciwx;
|
|
SubOpcode31Grabber[438] = ppc_ecowx;
|
|
|
|
SubOpcode31Grabber[24] = ppc_slw;
|
|
SubOpcode31Grabber[28] = ppc_and;
|
|
SubOpcode31Grabber[60] = ppc_andc;
|
|
SubOpcode31Grabber[124] = ppc_nor;
|
|
SubOpcode31Grabber[284] = ppc_eqv;
|
|
SubOpcode31Grabber[316] = ppc_xor;
|
|
SubOpcode31Grabber[412] = ppc_orc;
|
|
SubOpcode31Grabber[444] = ppc_or;
|
|
SubOpcode31Grabber[476] = ppc_nand;
|
|
SubOpcode31Grabber[536] = ppc_srw;
|
|
SubOpcode31Grabber[792] = ppc_sraw;
|
|
SubOpcode31Grabber[824] = ppc_srawi;
|
|
SubOpcode31Grabber[922] = ppc_extsh;
|
|
SubOpcode31Grabber[954] = ppc_extsb;
|
|
|
|
SubOpcode31Grabber[26] = ppc_cntlzw;
|
|
|
|
SubOpcode31Grabber[19] = ppc_mfcr;
|
|
SubOpcode31Grabber[83] = ppc_mfmsr;
|
|
SubOpcode31Grabber[144] = ppc_mtcrf;
|
|
SubOpcode31Grabber[146] = ppc_mtmsr;
|
|
SubOpcode31Grabber[210] = ppc_mtsr;
|
|
SubOpcode31Grabber[242] = ppc_mtsrin;
|
|
SubOpcode31Grabber[339] = ppc_mfspr;
|
|
SubOpcode31Grabber[371] = ppc_mftb;
|
|
SubOpcode31Grabber[467] = ppc_mtspr;
|
|
SubOpcode31Grabber[512] = ppc_mcrxr;
|
|
SubOpcode31Grabber[595] = ppc_mfsr;
|
|
SubOpcode31Grabber[659] = ppc_mfsrin;
|
|
|
|
SubOpcode31Grabber[54] = ppc_dcbst;
|
|
SubOpcode31Grabber[86] = ppc_dcbf;
|
|
SubOpcode31Grabber[246] = ppc_dcbtst;
|
|
SubOpcode31Grabber[278] = ppc_dcbt;
|
|
SubOpcode31Grabber[598] = ppc_sync;
|
|
SubOpcode31Grabber[470] = ppc_dcbi;
|
|
SubOpcode31Grabber[1014] = ppc_dcbz;
|
|
|
|
SubOpcode31Grabber[29] = power_maskg;
|
|
SubOpcode31Grabber[107] = SubOpcode31Grabber[619] = power_mul;
|
|
SubOpcode31Grabber[152] = power_slq;
|
|
SubOpcode31Grabber[153] = power_sle;
|
|
SubOpcode31Grabber[184] = power_sliq;
|
|
SubOpcode31Grabber[216] = power_sllq;
|
|
SubOpcode31Grabber[217] = power_sleq;
|
|
SubOpcode31Grabber[248] = power_slliq;
|
|
SubOpcode31Grabber[264] = SubOpcode31Grabber[776] = power_doz;
|
|
SubOpcode31Grabber[277] = power_lscbx;
|
|
SubOpcode31Grabber[331] = SubOpcode31Grabber[843] = power_div;
|
|
SubOpcode31Grabber[360] = SubOpcode31Grabber[872] = power_abs;
|
|
SubOpcode31Grabber[363] = SubOpcode31Grabber[875] = power_divs;
|
|
SubOpcode31Grabber[488] = SubOpcode31Grabber[1000] = power_nabs;
|
|
SubOpcode31Grabber[531] = power_clcs;
|
|
SubOpcode31Grabber[537] = power_rrib;
|
|
SubOpcode31Grabber[541] = power_maskir;
|
|
SubOpcode31Grabber[664] = power_srq;
|
|
SubOpcode31Grabber[665] = power_sre;
|
|
SubOpcode31Grabber[696] = power_sriq;
|
|
SubOpcode31Grabber[728] = power_srlq;
|
|
SubOpcode31Grabber[729] = power_sreq;
|
|
SubOpcode31Grabber[760] = power_srliq;
|
|
SubOpcode31Grabber[920] = power_sraq;
|
|
SubOpcode31Grabber[921] = power_srea;
|
|
SubOpcode31Grabber[952] = power_sraiq;
|
|
|
|
SubOpcode31Grabber[306] = ppc_tlbie;
|
|
SubOpcode31Grabber[370] = ppc_tlbia;
|
|
SubOpcode31Grabber[566] = ppc_tlbsync;
|
|
SubOpcode31Grabber[854] = ppc_eieio;
|
|
SubOpcode31Grabber[982] = ppc_icbi;
|
|
SubOpcode31Grabber[978] = ppc_tlbld;
|
|
SubOpcode31Grabber[1010] = ppc_tlbli;
|
|
|
|
SubOpcode59Grabber[18] = ppc_fdivs;
|
|
SubOpcode59Grabber[20] = ppc_fsubs;
|
|
SubOpcode59Grabber[21] = ppc_fadds;
|
|
SubOpcode59Grabber[22] = ppc_fsqrts;
|
|
SubOpcode59Grabber[24] = ppc_fres;
|
|
SubOpcode59Grabber[25] = ppc_fmuls;
|
|
SubOpcode59Grabber[28] = ppc_fmsubs;
|
|
SubOpcode59Grabber[29] = ppc_fmadds;
|
|
SubOpcode59Grabber[30] = ppc_fnmsubs;
|
|
SubOpcode59Grabber[31] = ppc_fnmadds;
|
|
|
|
SubOpcode63Grabber[0] = ppc_fcmpu;
|
|
SubOpcode63Grabber[12] = ppc_frsp;
|
|
SubOpcode63Grabber[14] = ppc_fctiw;
|
|
SubOpcode63Grabber[15] = ppc_fctiwz;
|
|
SubOpcode63Grabber[18] = ppc_fdiv;
|
|
SubOpcode63Grabber[20] = ppc_fsub;
|
|
SubOpcode63Grabber[21] = ppc_fadd;
|
|
SubOpcode63Grabber[22] = ppc_fsqrt;
|
|
SubOpcode63Grabber[26] = ppc_frsqrte;
|
|
SubOpcode63Grabber[32] = ppc_fcmpo;
|
|
SubOpcode63Grabber[38] = ppc_mtfsb1;
|
|
SubOpcode63Grabber[40] = ppc_fneg;
|
|
SubOpcode63Grabber[64] = ppc_mcrfs;
|
|
SubOpcode63Grabber[70] = ppc_mtfsb0;
|
|
SubOpcode63Grabber[72] = ppc_fmr;
|
|
SubOpcode63Grabber[134] = ppc_mtfsfi;
|
|
SubOpcode63Grabber[136] = ppc_fnabs;
|
|
SubOpcode63Grabber[264] = ppc_fabs;
|
|
SubOpcode63Grabber[583] = ppc_mffs;
|
|
SubOpcode63Grabber[711] = ppc_mtfsf;
|
|
|
|
for (int i = 0; i < 1024; i += 32) {
|
|
SubOpcode63Grabber[i + 23] = ppc_fsel;
|
|
SubOpcode63Grabber[i + 25] = ppc_fmul;
|
|
SubOpcode63Grabber[i + 28] = ppc_fmsub;
|
|
SubOpcode63Grabber[i + 29] = ppc_fmadd;
|
|
SubOpcode63Grabber[i + 30] = ppc_fnmsub;
|
|
SubOpcode63Grabber[i + 31] = ppc_fnmadd;
|
|
}
|
|
}
|
|
|
|
void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq)
|
|
{
|
|
int i;
|
|
|
|
mem_ctrl_instance = mem_ctrl;
|
|
|
|
initialize_ppc_opcode_tables();
|
|
|
|
// initialize emulator timers
|
|
TimerManager::get_instance()->set_time_now_cb(&get_virt_time_ns);
|
|
TimerManager::get_instance()->set_notify_changes_cb(&force_cycle_counter_reload);
|
|
|
|
// initialize time base facility
|
|
g_icycles = 0;
|
|
icnt_factor = 4;
|
|
tbr_wr_timestamp = 0;
|
|
rtc_timestamp = 0;
|
|
tbr_wr_value = 0;
|
|
tbr_freq_ghz = (tb_freq << 32) / NS_PER_SEC;
|
|
tbr_period_ns = ((uint64_t)NS_PER_SEC << 32) / tb_freq;
|
|
|
|
exec_flags = 0;
|
|
|
|
timebase_counter = 0;
|
|
dec_wr_value = 0;
|
|
|
|
/* zero all GPRs as prescribed for MPC601 */
|
|
/* For later PPC CPUs, GPR content is undefined */
|
|
for (i = 0; i < 32; i++) {
|
|
ppc_state.gpr[i] = 0;
|
|
}
|
|
|
|
/* zero all FPRs as prescribed for MPC601 */
|
|
/* For later PPC CPUs, GPR content is undefined */
|
|
for (i = 0; i < 32; i++) {
|
|
ppc_state.fpr[i].int64_r = 0;
|
|
}
|
|
|
|
/* zero all segment registers as prescribed for MPC601 */
|
|
/* For later PPC CPUs, SR content is undefined */
|
|
for (i = 0; i < 16; i++) {
|
|
ppc_state.sr[i] = 0;
|
|
}
|
|
|
|
ppc_state.cr = 0;
|
|
ppc_state.fpscr = 0;
|
|
|
|
ppc_state.pc = 0;
|
|
|
|
ppc_state.tbr[0] = 0;
|
|
ppc_state.tbr[1] = 0;
|
|
|
|
/* zero all SPRs */
|
|
for (i = 0; i < 1024; i++) {
|
|
ppc_state.spr[i] = 0;
|
|
}
|
|
|
|
ppc_state.spr[SPR::PVR] = cpu_version;
|
|
is_601 = (cpu_version >> 16) == 1;
|
|
|
|
if (is_601) {
|
|
/* MPC601 sets MSR[ME] bit during hard reset / Power-On */
|
|
ppc_state.msr = 0x1040;
|
|
} else {
|
|
ppc_state.msr = 0x40;
|
|
ppc_state.spr[SPR::DEC] = 0xFFFFFFFFUL;
|
|
}
|
|
|
|
ppc_mmu_init();
|
|
|
|
/* redirect code execution to reset vector */
|
|
ppc_state.pc = 0xFFF00100;
|
|
|
|
#ifdef CPU_PROFILING
|
|
gProfilerObj->register_profile("PPC_CPU",
|
|
std::unique_ptr<BaseProfile>(new CPUProfile()));
|
|
#endif
|
|
}
|
|
|
|
void print_fprs() {
|
|
for (int i = 0; i < 32; i++)
|
|
cout << "FPR " << dec << i << " : " << ppc_state.fpr[i].dbl64_r << endl;
|
|
}
|
|
|
|
static map<string, int> SPRName2Num = {
|
|
{"XER", SPR::XER}, {"LR", SPR::LR}, {"CTR", SPR::CTR}, {"DEC", SPR::DEC}, {"PVR", SPR::PVR}};
|
|
|
|
uint64_t reg_op(string& reg_name, uint64_t val, bool is_write) {
|
|
string reg_name_u, reg_num_str;
|
|
unsigned reg_num;
|
|
map<string, int>::iterator spr;
|
|
|
|
if (reg_name.length() < 2)
|
|
goto bail_out;
|
|
|
|
reg_name_u = reg_name;
|
|
|
|
/* convert reg_name string to uppercase */
|
|
std::for_each(reg_name_u.begin(), reg_name_u.end(), [](char& c) { c = ::toupper(c); });
|
|
|
|
try {
|
|
if (reg_name_u == "PC") {
|
|
if (is_write)
|
|
ppc_state.pc = (uint32_t)val;
|
|
return ppc_state.pc;
|
|
}
|
|
if (reg_name_u == "MSR") {
|
|
if (is_write)
|
|
ppc_state.msr = (uint32_t)val;
|
|
return ppc_state.msr;
|
|
}
|
|
if (reg_name_u == "CR") {
|
|
if (is_write)
|
|
ppc_state.cr = (uint32_t)val;
|
|
return ppc_state.cr;
|
|
}
|
|
if (reg_name_u == "FPSCR") {
|
|
if (is_write)
|
|
ppc_state.fpscr = (uint32_t)val;
|
|
return ppc_state.fpscr;
|
|
}
|
|
|
|
if (reg_name_u.substr(0, 1) == "R") {
|
|
reg_num_str = reg_name_u.substr(1);
|
|
reg_num = (unsigned)stoul(reg_num_str, NULL, 0);
|
|
if (reg_num < 32) {
|
|
if (is_write)
|
|
ppc_state.gpr[reg_num] = (uint32_t)val;
|
|
return ppc_state.gpr[reg_num];
|
|
}
|
|
}
|
|
|
|
if (reg_name_u.substr(0, 1) == "FR") {
|
|
reg_num_str = reg_name_u.substr(2);
|
|
reg_num = (unsigned)stoul(reg_num_str, NULL, 0);
|
|
if (reg_num < 32) {
|
|
if (is_write)
|
|
ppc_state.fpr[reg_num].int64_r = val;
|
|
return ppc_state.fpr[reg_num].int64_r;
|
|
}
|
|
}
|
|
|
|
if (reg_name_u.substr(0, 3) == "SPR") {
|
|
reg_num_str = reg_name_u.substr(3);
|
|
reg_num = (unsigned)stoul(reg_num_str, NULL, 0);
|
|
if (reg_num < 1024) {
|
|
if (is_write)
|
|
ppc_state.spr[reg_num] = (uint32_t)val;
|
|
return ppc_state.spr[reg_num];
|
|
}
|
|
}
|
|
|
|
if (reg_name_u.substr(0, 2) == "SR") {
|
|
reg_num_str = reg_name_u.substr(2);
|
|
reg_num = (unsigned)stoul(reg_num_str, NULL, 0);
|
|
if (reg_num < 16) {
|
|
if (is_write)
|
|
ppc_state.sr[reg_num] = (uint32_t)val;
|
|
return ppc_state.sr[reg_num];
|
|
}
|
|
}
|
|
|
|
spr = SPRName2Num.find(reg_name_u);
|
|
if (spr != SPRName2Num.end()) {
|
|
if (is_write)
|
|
ppc_state.spr[spr->second] = (uint32_t)val;
|
|
return ppc_state.spr[spr->second];
|
|
}
|
|
} catch (...) {
|
|
}
|
|
|
|
bail_out:
|
|
throw std::invalid_argument(string("Unknown register ") + reg_name);
|
|
}
|
|
|
|
uint64_t get_reg(string reg_name) {
|
|
return reg_op(reg_name, 0, false);
|
|
}
|
|
|
|
void set_reg(string reg_name, uint64_t val) {
|
|
reg_op(reg_name, val, true);
|
|
}
|