From 60d34a6816eb94f2b49bf15d5821853aa5717f28 Mon Sep 17 00:00:00 2001 From: gbeauche <> Date: Sun, 26 Oct 2003 13:59:04 +0000 Subject: [PATCH] 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. --- SheepShaver/src/Unix/sysdeps.h | 3 +- SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp | 62 +++---- .../src/kpx_cpu/src/cpu/ppc/ppc-config.hpp | 16 ++ .../src/kpx_cpu/src/cpu/ppc/ppc-cpu.cpp | 127 ++++++++++++++- .../src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp | 154 +++++------------- 5 files changed, 205 insertions(+), 157 deletions(-) diff --git a/SheepShaver/src/Unix/sysdeps.h b/SheepShaver/src/Unix/sysdeps.h index 40577f71..ed8dba31 100644 --- a/SheepShaver/src/Unix/sysdeps.h +++ b/SheepShaver/src/Unix/sysdeps.h @@ -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 diff --git a/SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp b/SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp index 61c0057d..848bdbf6 100644 --- a/SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp +++ b/SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp @@ -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(); + 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, diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-config.hpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-config.hpp index a68c94e6..e26c9fe6 100644 --- a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-config.hpp +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-config.hpp @@ -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 * diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.cpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.cpp index 06a64460..6dcb6383 100644 --- a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.cpp +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.cpp @@ -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(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 } diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp index 3de7fdda..4eee56fd 100644 --- a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp @@ -223,9 +223,25 @@ 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; -#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; - } -#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 PPC_CHECK_INTERRUPTS + pending_interrupts |= INTERRUPT_TRIGGER; #endif } +inline void powerpc_cpu::check_pending_interrupts() +{ + if (pending_interrupts) { + if (pending_interrupts & INTERRUPT_HANDLE) { + pending_interrupts &= ~INTERRUPT_HANDLE; + handle_interrupt(); + } + if (pending_interrupts & INTERRUPT_TRIGGER) { + pending_interrupts &= ~INTERRUPT_TRIGGER; + pending_interrupts |= INTERRUPT_HANDLE; + } + } +} + #endif /* PPC_CPU_H */