mirror of
https://github.com/kanjitalk755/macemu.git
synced 2024-11-27 02:49:42 +00:00
Rewrite interrupts handling code so that the emulator can work with a
predecode cache. This implies to run in interpreted mode only while processing EmulOps or other native (nested) runs. Note that the FLIGHT_RECORDER with a predecode cache gets slower than without caching at all.
This commit is contained in:
parent
d766049d59
commit
60d34a6816
@ -78,8 +78,9 @@
|
||||
# define ROM_IS_WRITE_PROTECTED 1
|
||||
#endif
|
||||
// Configure PowerPC emulator
|
||||
#define PPC_CHECK_INTERRUPTS 1
|
||||
#define PPC_NO_LAZY_PC_UPDATE 1
|
||||
#define PPC_NO_DECODE_CACHE 1
|
||||
//#define PPC_NO_DECODE_CACHE 1
|
||||
#define PPC_FLIGHT_RECORDER 1
|
||||
#else
|
||||
// Mac ROM is write protected
|
||||
|
@ -44,7 +44,7 @@
|
||||
#include "mon_disass.h"
|
||||
#endif
|
||||
|
||||
#define DEBUG 1
|
||||
#define DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
static void enter_mon(void)
|
||||
@ -89,16 +89,15 @@ class sheepshaver_cpu
|
||||
|
||||
public:
|
||||
|
||||
sheepshaver_cpu()
|
||||
: powerpc_cpu()
|
||||
{ init_decoder(); }
|
||||
// Constructor
|
||||
sheepshaver_cpu();
|
||||
|
||||
// Condition Register accessors
|
||||
uint32 get_cr() const { return cr().get(); }
|
||||
void set_cr(uint32 v) { cr().set(v); }
|
||||
|
||||
// Execution loop
|
||||
void execute(uint32 pc);
|
||||
void execute(uint32 entry, bool enable_cache = false);
|
||||
|
||||
// Execute 68k routine
|
||||
void execute_68k(uint32 entry, M68kRegisters *r);
|
||||
@ -114,6 +113,7 @@ public:
|
||||
|
||||
// Handle MacOS interrupt
|
||||
void interrupt(uint32 entry);
|
||||
void handle_interrupt();
|
||||
|
||||
// spcflags for interrupts handling
|
||||
static uint32 spcflags;
|
||||
@ -131,6 +131,12 @@ public:
|
||||
uint32 sheepshaver_cpu::spcflags = 0;
|
||||
lazy_allocator< sheepshaver_cpu > allocator_helper< sheepshaver_cpu, lazy_allocator >::allocator;
|
||||
|
||||
sheepshaver_cpu::sheepshaver_cpu()
|
||||
: powerpc_cpu()
|
||||
{
|
||||
init_decoder();
|
||||
}
|
||||
|
||||
void sheepshaver_cpu::init_decoder()
|
||||
{
|
||||
#ifndef PPC_NO_STATIC_II_INDEX_TABLE
|
||||
@ -216,38 +222,11 @@ void sheepshaver_cpu::execute_sheep(uint32 opcode)
|
||||
}
|
||||
}
|
||||
|
||||
// Checks for pending interrupts
|
||||
struct execute_nothing {
|
||||
static inline void execute(powerpc_cpu *) { }
|
||||
};
|
||||
|
||||
struct execute_spcflags_check {
|
||||
static inline void execute(powerpc_cpu *cpu) {
|
||||
#if !ASYNC_IRQ
|
||||
if (SPCFLAGS_TEST(SPCFLAG_ALL_BUT_EXEC_RETURN)) {
|
||||
if (SPCFLAGS_TEST( SPCFLAG_ENTER_MON )) {
|
||||
SPCFLAGS_CLEAR( SPCFLAG_ENTER_MON );
|
||||
enter_mon();
|
||||
}
|
||||
if (SPCFLAGS_TEST( SPCFLAG_DOINT )) {
|
||||
SPCFLAGS_CLEAR( SPCFLAG_DOINT );
|
||||
HandleInterrupt();
|
||||
}
|
||||
if (SPCFLAGS_TEST( SPCFLAG_INT )) {
|
||||
SPCFLAGS_CLEAR( SPCFLAG_INT );
|
||||
SPCFLAGS_SET( SPCFLAG_DOINT );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
// Execution loop
|
||||
void sheepshaver_cpu::execute(uint32 entry)
|
||||
void sheepshaver_cpu::execute(uint32 entry, bool enable_cache)
|
||||
{
|
||||
try {
|
||||
pc() = entry;
|
||||
powerpc_cpu::do_execute<execute_nothing, execute_spcflags_check>();
|
||||
powerpc_cpu::execute(entry, enable_cache);
|
||||
}
|
||||
catch (sheepshaver_exec_return const &) {
|
||||
// Nothing, simply return
|
||||
@ -596,8 +575,11 @@ void init_emul_ppc(void)
|
||||
void emul_ppc(uint32 entry)
|
||||
{
|
||||
current_cpu = main_cpu;
|
||||
#if DEBUG
|
||||
current_cpu->start_log();
|
||||
current_cpu->execute(entry);
|
||||
#endif
|
||||
// start emulation loop and enable code translation or caching
|
||||
current_cpu->execute(entry, true);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -610,12 +592,14 @@ void TriggerInterrupt(void)
|
||||
#if 0
|
||||
WriteMacInt32(0x16a, ReadMacInt32(0x16a) + 1);
|
||||
#else
|
||||
SPCFLAGS_SET( SPCFLAG_INT );
|
||||
// Trigger interrupt to main cpu only
|
||||
if (main_cpu)
|
||||
main_cpu->trigger_interrupt();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void HandleInterrupt(void)
|
||||
void sheepshaver_cpu::handle_interrupt(void)
|
||||
{
|
||||
// Do nothing if interrupts are disabled
|
||||
if (int32(ReadMacInt32(XLM_IRQ_NEST)) > 0)
|
||||
@ -634,14 +618,14 @@ void HandleInterrupt(void)
|
||||
// 68k emulator active, trigger 68k interrupt level 1
|
||||
assert(current_cpu == main_cpu);
|
||||
WriteMacInt16(tswap32(kernel_data->v[0x67c >> 2]), 1);
|
||||
main_cpu->set_cr(main_cpu->get_cr() | tswap32(kernel_data->v[0x674 >> 2]));
|
||||
set_cr(get_cr() | tswap32(kernel_data->v[0x674 >> 2]));
|
||||
break;
|
||||
|
||||
#if INTERRUPTS_IN_NATIVE_MODE
|
||||
case MODE_NATIVE:
|
||||
// 68k emulator inactive, in nanokernel?
|
||||
assert(current_cpu == main_cpu);
|
||||
if (main_cpu->gpr(1) != KernelDataAddr) {
|
||||
if (gpr(1) != KernelDataAddr) {
|
||||
// Prepare for 68k interrupt level 1
|
||||
WriteMacInt16(tswap32(kernel_data->v[0x67c >> 2]), 1);
|
||||
WriteMacInt32(tswap32(kernel_data->v[0x658 >> 2]) + 0xdc,
|
||||
|
@ -21,6 +21,22 @@
|
||||
#ifndef PPC_CONFIG_H
|
||||
#define PPC_CONFIG_H
|
||||
|
||||
/**
|
||||
* PPC_CHECK_INTERRUPTS
|
||||
*
|
||||
* Define if interrupts need to be check after each instruction,
|
||||
* in interpreted mode, or at the end of each block, in compiled
|
||||
* mode.
|
||||
*
|
||||
* NOTE: this only checks for user defined interrupts that are
|
||||
* triggered by the program. This is not about OEA interrupts.
|
||||
*/
|
||||
|
||||
#ifndef PPC_CHECK_INTERRUPTS
|
||||
#define PPC_CHECK_INTERRUPTS 0
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* PPC_NO_BASIC_CPU_BASE
|
||||
*
|
||||
|
@ -208,8 +208,14 @@ void powerpc_cpu::initialize()
|
||||
init_registers();
|
||||
init_decode_cache();
|
||||
|
||||
// Init interrupts state
|
||||
#if PPC_CHECK_INTERRUPTS
|
||||
pending_interrupts = INTERRUPT_NONE;
|
||||
#endif
|
||||
|
||||
// Init cache range invalidate recorder
|
||||
cache_range.start = cache_range.end = 0;
|
||||
invalidated_cache = false;
|
||||
|
||||
// Init syscalls handler
|
||||
execute_do_syscall = NULL;
|
||||
@ -275,13 +281,125 @@ void powerpc_cpu::fake_dump_registers(uint32)
|
||||
dump_registers();
|
||||
}
|
||||
|
||||
struct execute_nothing {
|
||||
static inline void execute(powerpc_cpu *) { }
|
||||
};
|
||||
void powerpc_cpu::execute(uint32 entry, bool enable_cache)
|
||||
{
|
||||
pc() = entry;
|
||||
#ifdef PPC_EXECUTE_DUMP_STATE
|
||||
const bool dump_state = true;
|
||||
#endif
|
||||
#ifndef PPC_NO_DECODE_CACHE
|
||||
if (enable_cache) {
|
||||
for (;;) {
|
||||
block_info *bi = block_cache.new_blockinfo();
|
||||
bi->init(pc());
|
||||
|
||||
// Predecode a new block
|
||||
block_info::decode_info *di = bi->di = decode_cache_p;
|
||||
const instr_info_t *ii;
|
||||
uint32 dpc = pc() - 4;
|
||||
do {
|
||||
uint32 opcode = vm_read_memory_4(dpc += 4);
|
||||
ii = decode(opcode);
|
||||
#ifdef PPC_EXECUTE_DUMP_STATE
|
||||
if (dump_state) {
|
||||
di->opcode = opcode;
|
||||
di->execute = &powerpc_cpu::dump_instruction;
|
||||
}
|
||||
#endif
|
||||
#if PPC_FLIGHT_RECORDER
|
||||
if (is_logging()) {
|
||||
di->opcode = opcode;
|
||||
di->execute = &powerpc_cpu::record_step;
|
||||
di++;
|
||||
}
|
||||
#endif
|
||||
di->opcode = opcode;
|
||||
di->execute = ii->execute;
|
||||
di++;
|
||||
#ifdef PPC_EXECUTE_DUMP_STATE
|
||||
if (dump_state) {
|
||||
di->opcode = 0;
|
||||
di->execute = &powerpc_cpu::fake_dump_registers;
|
||||
di++;
|
||||
}
|
||||
#endif
|
||||
if (di >= decode_cache_end_p) {
|
||||
// Invalidate cache and move current code to start
|
||||
invalidate_cache();
|
||||
const int blocklen = di - bi->di;
|
||||
memmove(decode_cache_p, bi->di, blocklen * sizeof(*di));
|
||||
bi->di = decode_cache_p;
|
||||
di = bi->di + blocklen;
|
||||
}
|
||||
} while ((ii->cflow & CFLOW_END_BLOCK) == 0);
|
||||
#ifdef PPC_LAZY_PC_UPDATE
|
||||
bi->end_pc = dpc;
|
||||
#endif
|
||||
bi->size = di - bi->di;
|
||||
block_cache.add_to_cl_list(bi);
|
||||
block_cache.add_to_active_list(bi);
|
||||
decode_cache_p += bi->size;
|
||||
|
||||
// Execute all cached blocks
|
||||
invalidated_cache = false;
|
||||
for (;;) {
|
||||
#ifdef PPC_LAZY_PC_UPDATE
|
||||
pc() = bi->end_pc;
|
||||
#endif
|
||||
di = bi->di;
|
||||
#ifdef PPC_NO_DECODE_CACHE_UNROLL_EXECUTE
|
||||
for (int i = 0; i < bi->size; i++)
|
||||
(this->*(di[i].execute))(di[i].opcode);
|
||||
#else
|
||||
const int r = bi->size % 4;
|
||||
switch (r) {
|
||||
case 3: (this->*(di->execute))(di->opcode); di++;
|
||||
case 2: (this->*(di->execute))(di->opcode); di++;
|
||||
case 1: (this->*(di->execute))(di->opcode); di++;
|
||||
case 0: break;
|
||||
}
|
||||
const int n = bi->size / 4;
|
||||
for (int i = 0; i < n; i++) {
|
||||
(this->*(di[0].execute))(di[0].opcode);
|
||||
(this->*(di[1].execute))(di[1].opcode);
|
||||
(this->*(di[2].execute))(di[2].opcode);
|
||||
(this->*(di[3].execute))(di[3].opcode);
|
||||
di += 4;
|
||||
}
|
||||
#endif
|
||||
check_pending_interrupts();
|
||||
|
||||
if (invalidated_cache || ((bi->pc != pc()) && ((bi = block_cache.find(pc())) == NULL)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
for (;;) {
|
||||
uint32 opcode = vm_read_memory_4(pc());
|
||||
const instr_info_t *ii = decode(opcode);
|
||||
#ifdef PPC_EXECUTE_DUMP_STATE
|
||||
if (dump_state)
|
||||
dump_instruction(opcode);
|
||||
#endif
|
||||
#if PPC_FLIGHT_RECORDER
|
||||
if (is_logging())
|
||||
record_step(opcode);
|
||||
#endif
|
||||
assert(ii->execute != 0);
|
||||
(this->*(ii->execute))(opcode);
|
||||
#ifdef PPC_EXECUTE_DUMP_STATE
|
||||
if (dump_state)
|
||||
dump_registers();
|
||||
#endif
|
||||
check_pending_interrupts();
|
||||
}
|
||||
}
|
||||
|
||||
void powerpc_cpu::execute()
|
||||
{
|
||||
do_execute<execute_nothing, execute_nothing>();
|
||||
execute(pc());
|
||||
}
|
||||
|
||||
void powerpc_cpu::init_decode_cache()
|
||||
@ -322,6 +440,7 @@ void powerpc_cpu::invalidate_cache()
|
||||
block_cache.clear();
|
||||
block_cache.initialize();
|
||||
decode_cache_p = decode_cache;
|
||||
invalidated_cache = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -223,10 +223,26 @@ public:
|
||||
void fake_dump_registers(uint32);
|
||||
|
||||
// Start emulation loop
|
||||
template< class prologue, class epilogue >
|
||||
void do_execute();
|
||||
void execute(uint32 entry, bool enable_cache = true);
|
||||
void execute();
|
||||
|
||||
// Interrupts handling
|
||||
protected:
|
||||
enum {
|
||||
INTERRUPT_NONE = 0,
|
||||
INTERRUPT_TRIGGER = 1,
|
||||
INTERRUPT_HANDLE = 2
|
||||
};
|
||||
#if PPC_CHECK_INTERRUPTS
|
||||
int pending_interrupts;
|
||||
#else
|
||||
static const int pending_interrupts = 0;
|
||||
#endif
|
||||
public:
|
||||
void check_pending_interrupts();
|
||||
void trigger_interrupt();
|
||||
virtual void handle_interrupt() { }
|
||||
|
||||
// Set VALUE to register ID
|
||||
void set_register(int id, any_register const & value);
|
||||
|
||||
@ -241,6 +257,7 @@ public:
|
||||
void invalidate_cache_range(uintptr start, uintptr end);
|
||||
private:
|
||||
struct { uintptr start, end; } cache_range;
|
||||
bool invalidated_cache;
|
||||
|
||||
protected:
|
||||
|
||||
@ -340,119 +357,30 @@ private:
|
||||
void execute_dcbz(uint32 opcode);
|
||||
};
|
||||
|
||||
template< class prologue, class epilogue >
|
||||
inline void powerpc_cpu::do_execute()
|
||||
|
||||
/**
|
||||
* Interrupts handling
|
||||
**/
|
||||
|
||||
inline void powerpc_cpu::trigger_interrupt()
|
||||
{
|
||||
#ifdef PPC_EXECUTE_DUMP_STATE
|
||||
const bool dump_state = true;
|
||||
#if PPC_CHECK_INTERRUPTS
|
||||
pending_interrupts |= INTERRUPT_TRIGGER;
|
||||
#endif
|
||||
#ifdef PPC_NO_DECODE_CACHE
|
||||
for (;;) {
|
||||
prologue::execute(this);
|
||||
uint32 opcode = vm_read_memory_4(pc());
|
||||
const instr_info_t *ii = decode(opcode);
|
||||
#ifdef PPC_EXECUTE_DUMP_STATE
|
||||
if (dump_state)
|
||||
dump_instruction(opcode);
|
||||
#endif
|
||||
#if PPC_FLIGHT_RECORDER
|
||||
if (is_logging())
|
||||
record_step(opcode);
|
||||
#endif
|
||||
assert(ii->execute != 0);
|
||||
(this->*(ii->execute))(opcode);
|
||||
#ifdef PPC_EXECUTE_DUMP_STATE
|
||||
if (dump_state)
|
||||
dump_registers();
|
||||
#endif
|
||||
epilogue::execute(this);
|
||||
}
|
||||
#else
|
||||
for (;;) {
|
||||
block_info *bi = block_cache.new_blockinfo();
|
||||
bi->init(pc());
|
||||
|
||||
// Predecode a new block
|
||||
block_info::decode_info *di = bi->di = decode_cache_p;
|
||||
const instr_info_t *ii;
|
||||
uint32 dpc = pc() - 4;
|
||||
do {
|
||||
uint32 opcode = vm_read_memory_4(dpc += 4);
|
||||
ii = decode(opcode);
|
||||
#ifdef PPC_EXECUTE_DUMP_STATE
|
||||
if (dump_state) {
|
||||
di->opcode = opcode;
|
||||
di->execute = &powerpc_cpu::dump_instruction;
|
||||
inline void powerpc_cpu::check_pending_interrupts()
|
||||
{
|
||||
if (pending_interrupts) {
|
||||
if (pending_interrupts & INTERRUPT_HANDLE) {
|
||||
pending_interrupts &= ~INTERRUPT_HANDLE;
|
||||
handle_interrupt();
|
||||
}
|
||||
#endif
|
||||
#if PPC_FLIGHT_RECORDER
|
||||
if (is_logging()) {
|
||||
di->opcode = opcode;
|
||||
di->execute = &powerpc_cpu::record_step;
|
||||
di++;
|
||||
}
|
||||
#endif
|
||||
di->opcode = opcode;
|
||||
di->execute = ii->execute;
|
||||
di++;
|
||||
#ifdef PPC_EXECUTE_DUMP_STATE
|
||||
if (dump_state) {
|
||||
di->opcode = 0;
|
||||
di->execute = &powerpc_cpu::fake_dump_registers;
|
||||
di++;
|
||||
}
|
||||
#endif
|
||||
if (di >= decode_cache_end_p) {
|
||||
// Invalidate cache and move current code to start
|
||||
invalidate_cache();
|
||||
const int blocklen = di - bi->di;
|
||||
memmove(decode_cache_p, bi->di, blocklen * sizeof(*di));
|
||||
bi->di = decode_cache_p;
|
||||
di = bi->di + blocklen;
|
||||
}
|
||||
} while ((ii->cflow & CFLOW_END_BLOCK) == 0);
|
||||
#ifdef PPC_LAZY_PC_UPDATE
|
||||
bi->end_pc = dpc;
|
||||
#endif
|
||||
bi->size = di - bi->di;
|
||||
block_cache.add_to_cl_list(bi);
|
||||
block_cache.add_to_active_list(bi);
|
||||
decode_cache_p += bi->size;
|
||||
|
||||
// Execute all cached blocks
|
||||
for (;;) {
|
||||
prologue::execute(this);
|
||||
#ifdef PPC_LAZY_PC_UPDATE
|
||||
pc() = bi->end_pc;
|
||||
#endif
|
||||
di = bi->di;
|
||||
#ifdef PPC_NO_DECODE_CACHE_UNROLL_EXECUTE
|
||||
for (int i = 0; i < bi->size; i++)
|
||||
(this->*(di[i].execute))(di[i].opcode);
|
||||
#else
|
||||
const int r = bi->size % 4;
|
||||
switch (r) {
|
||||
case 3: (this->*(di->execute))(di->opcode); di++;
|
||||
case 2: (this->*(di->execute))(di->opcode); di++;
|
||||
case 1: (this->*(di->execute))(di->opcode); di++;
|
||||
case 0: break;
|
||||
}
|
||||
const int n = bi->size / 4;
|
||||
for (int i = 0; i < n; i++) {
|
||||
(this->*(di[0].execute))(di[0].opcode);
|
||||
(this->*(di[1].execute))(di[1].opcode);
|
||||
(this->*(di[2].execute))(di[2].opcode);
|
||||
(this->*(di[3].execute))(di[3].opcode);
|
||||
di += 4;
|
||||
}
|
||||
#endif
|
||||
epilogue::execute(this);
|
||||
|
||||
if ((bi->pc != pc()) && ((bi = block_cache.find(pc())) == NULL))
|
||||
break;
|
||||
if (pending_interrupts & INTERRUPT_TRIGGER) {
|
||||
pending_interrupts &= ~INTERRUPT_TRIGGER;
|
||||
pending_interrupts |= INTERRUPT_HANDLE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* PPC_CPU_H */
|
||||
|
Loading…
Reference in New Issue
Block a user