--- SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp 2003/12/25 23:54:36 1.24 +++ SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp 2004/06/24 15:37:26 1.47 @@ -1,7 +1,7 @@ /* * sheepshaver_glue.cpp - Glue Kheperix CPU to SheepShaver CPU engine interface * - * SheepShaver (C) 1997-2002 Christian Bauer and Marc Hellwig + * SheepShaver (C) 1997-2004 Christian Bauer and Marc Hellwig * * 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 @@ -38,8 +38,14 @@ #include "name_registry.h" #include "serial.h" #include "ether.h" +#include "timer.h" #include +#include + +#ifdef USE_SDL_VIDEO +#include +#endif #if ENABLE_MON #include "mon.h" @@ -50,11 +56,13 @@ #include "debug.h" // Emulation time statistics -#define EMUL_TIME_STATS 1 +#ifndef EMUL_TIME_STATS +#define EMUL_TIME_STATS 0 +#endif #if EMUL_TIME_STATS static clock_t emul_start_time; -static uint32 interrupt_count = 0; +static uint32 interrupt_count = 0, ppc_interrupt_count = 0; static clock_t interrupt_time = 0; static uint32 exec68k_count = 0; static clock_t exec68k_time = 0; @@ -76,11 +84,14 @@ static void enter_mon(void) // From main_*.cpp extern uintptr SignalStackBase(); +// From rsrc_patches.cpp +extern "C" void check_load_invoc(uint32 type, int16 id, uint32 h); + // PowerPC EmulOp to exit from emulation looop const uint32 POWERPC_EXEC_RETURN = POWERPC_EMUL_OP | 1; -// Enable multicore (main/interrupts) cpu emulation? -#define MULTICORE_CPU (ASYNC_IRQ ? 1 : 0) +// Enable interrupt routine safety checks? +#define SAFE_INTERRUPT_PPC 1 // Enable Execute68k() safety checks? #define SAFE_EXEC_68K 1 @@ -94,12 +105,21 @@ const uint32 POWERPC_EXEC_RETURN = POWER // Interrupts in native mode? #define INTERRUPTS_IN_NATIVE_MODE 1 +// Enable native EMUL_OPs to be run without a mode switch +#define ENABLE_NATIVE_EMUL_OP 1 + // Pointer to Kernel Data static KernelData * const kernel_data = (KernelData *)KERNEL_DATA_BASE; // SIGSEGV handler static sigsegv_return_t sigsegv_handler(sigsegv_address_t, sigsegv_address_t); +#if PPC_ENABLE_JIT && PPC_REENTRANT_JIT +// Special trampolines for EmulOp and NativeOp +static uint8 *emul_op_trampoline; +static uint8 *native_op_trampoline; +#endif + // JIT Compiler enabled? static inline bool enable_jit_p() { @@ -122,6 +142,29 @@ class sheepshaver_cpu void init_decoder(); void execute_sheep(uint32 opcode); + // Filter out EMUL_OP routines that only call native code + bool filter_execute_emul_op(uint32 emul_op); + + // "Native" EMUL_OP routines + void execute_emul_op_microseconds(); + void execute_emul_op_idle_time_1(); + void execute_emul_op_idle_time_2(); + + // CPU context to preserve on interrupt + class interrupt_context { + uint32 gpr[32]; + uint32 pc; + uint32 lr; + uint32 ctr; + uint32 cr; + uint32 xer; + sheepshaver_cpu *cpu; + const char *where; + public: + interrupt_context(sheepshaver_cpu *_cpu, const char *_where); + ~interrupt_context(); + }; + public: // Constructor @@ -133,6 +176,12 @@ public: uint32 get_xer() const { return xer().get(); } void set_xer(uint32 v) { xer().set(v); } + // Execute NATIVE_OP routine + void execute_native_op(uint32 native_op); + + // Execute EMUL_OP routine + void execute_emul_op(uint32 emul_op); + // Execute 68k routine void execute_68k(uint32 entry, M68kRegisters *r); @@ -142,6 +191,9 @@ public: // Execute MacOS/PPC code uint32 execute_macos_code(uint32 tvect, int nargs, uint32 const *args); + // Compile one instruction + virtual int compile1(codegen_context_t & cg_context); + // Resource manager thunk void get_resource(uint32 old_get_resource); @@ -149,20 +201,41 @@ public: void interrupt(uint32 entry); void handle_interrupt(); - // Lazy memory allocator (one item at a time) - void *operator new(size_t size) - { return allocator_helper< sheepshaver_cpu, lazy_allocator >::allocate(); } - void operator delete(void *p) - { allocator_helper< sheepshaver_cpu, lazy_allocator >::deallocate(p); } - // FIXME: really make surre array allocation fail at link time? - void *operator new[](size_t); - void operator delete[](void *p); - // Make sure the SIGSEGV handler can access CPU registers friend sigsegv_return_t sigsegv_handler(sigsegv_address_t, sigsegv_address_t); }; -lazy_allocator< sheepshaver_cpu > allocator_helper< sheepshaver_cpu, lazy_allocator >::allocator; +// Memory allocator returning areas aligned on 16-byte boundaries +void *operator new(size_t size) +{ + void *p; + +#if defined(HAVE_POSIX_MEMALIGN) + if (posix_memalign(&p, 16, size) != 0) + throw std::bad_alloc(); +#elif defined(HAVE_MEMALIGN) + p = memalign(16, size); +#elif defined(HAVE_VALLOC) + p = valloc(size); // page-aligned! +#else + /* XXX: handle padding ourselves */ + p = malloc(size); +#endif + + return p; +} + +void operator delete(void *p) +{ +#if defined(HAVE_MEMALIGN) || defined(HAVE_VALLOC) +#if defined(__GLIBC__) + // this is known to work only with GNU libc + free(p); +#endif +#else + free(p); +#endif +} sheepshaver_cpu::sheepshaver_cpu() : powerpc_cpu(enable_jit_p()) @@ -172,13 +245,6 @@ sheepshaver_cpu::sheepshaver_cpu() void sheepshaver_cpu::init_decoder() { -#ifndef PPC_NO_STATIC_II_INDEX_TABLE - static bool initialized = false; - if (initialized) - return; - initialized = true; -#endif - static const instr_info_t sheep_ii_table[] = { { "sheep", (execute_pmf)&sheepshaver_cpu::execute_sheep, @@ -197,20 +263,89 @@ void sheepshaver_cpu::init_decoder() } } -// Forward declaration for native opcode handler -static void NativeOp(int selector); - /* NativeOp instruction format: - +------------+--------------------------+--+----------+------------+ - | 6 | |FN| OP | 2 | - +------------+--------------------------+--+----------+------------+ - 0 5 |6 19 20 21 25 26 31 + +------------+-------------------------+--+-----------+------------+ + | 6 | |FN| OP | 2 | + +------------+-------------------------+--+-----------+------------+ + 0 5 |6 18 19 20 25 26 31 */ -typedef bit_field< 20, 20 > FN_field; -typedef bit_field< 21, 25 > NATIVE_OP_field; +typedef bit_field< 19, 19 > FN_field; +typedef bit_field< 20, 25 > NATIVE_OP_field; typedef bit_field< 26, 31 > EMUL_OP_field; +// "Native" EMUL_OP routines +#define GPR_A(REG) gpr(16 + (REG)) +#define GPR_D(REG) gpr( 8 + (REG)) + +void sheepshaver_cpu::execute_emul_op_microseconds() +{ + Microseconds(GPR_A(0), GPR_D(0)); +} + +void sheepshaver_cpu::execute_emul_op_idle_time_1() +{ + // Sleep if no events pending + if (ReadMacInt32(0x14c) == 0) + Delay_usec(16667); + GPR_A(0) = ReadMacInt32(0x2b6); +} + +void sheepshaver_cpu::execute_emul_op_idle_time_2() +{ + // Sleep if no events pending + if (ReadMacInt32(0x14c) == 0) + Delay_usec(16667); + GPR_D(0) = (uint32)-2; +} + +// Filter out EMUL_OP routines that only call native code +bool sheepshaver_cpu::filter_execute_emul_op(uint32 emul_op) +{ + switch (emul_op) { + case OP_MICROSECONDS: + execute_emul_op_microseconds(); + return true; + case OP_IDLE_TIME: + execute_emul_op_idle_time_1(); + return true; + case OP_IDLE_TIME_2: + execute_emul_op_idle_time_2(); + return true; + } + return false; +} + +// Execute EMUL_OP routine +void sheepshaver_cpu::execute_emul_op(uint32 emul_op) +{ +#if ENABLE_NATIVE_EMUL_OP + // First, filter out EMUL_OPs that can be executed without a mode switch + if (filter_execute_emul_op(emul_op)) + return; +#endif + + M68kRegisters r68; + WriteMacInt32(XLM_68K_R25, gpr(25)); + WriteMacInt32(XLM_RUN_MODE, MODE_EMUL_OP); + for (int i = 0; i < 8; i++) + r68.d[i] = gpr(8 + i); + for (int i = 0; i < 7; i++) + r68.a[i] = gpr(16 + i); + r68.a[7] = gpr(1); + uint32 saved_cr = get_cr() & CR_field<2>::mask(); + uint32 saved_xer = get_xer(); + EmulOp(&r68, gpr(24), emul_op); + set_cr(saved_cr); + set_xer(saved_xer); + for (int i = 0; i < 8; i++) + gpr(8 + i) = r68.d[i]; + for (int i = 0; i < 7; i++) + gpr(16 + i) = r68.a[i]; + gpr(1) = r68.a[7]; + WriteMacInt32(XLM_RUN_MODE, MODE_68K); +} + // Execute SheepShaver instruction void sheepshaver_cpu::execute_sheep(uint32 opcode) { @@ -227,54 +362,254 @@ void sheepshaver_cpu::execute_sheep(uint break; case 2: // EXEC_NATIVE - NativeOp(NATIVE_OP_field::extract(opcode)); + execute_native_op(NATIVE_OP_field::extract(opcode)); if (FN_field::test(opcode)) pc() = lr(); else pc() += 4; break; - default: { // EMUL_OP - M68kRegisters r68; - WriteMacInt32(XLM_68K_R25, gpr(25)); - WriteMacInt32(XLM_RUN_MODE, MODE_EMUL_OP); - for (int i = 0; i < 8; i++) - r68.d[i] = gpr(8 + i); - for (int i = 0; i < 7; i++) - r68.a[i] = gpr(16 + i); - r68.a[7] = gpr(1); - uint32 saved_cr = get_cr() & CR_field<2>::mask(); - uint32 saved_xer = get_xer(); - EmulOp(&r68, gpr(24), EMUL_OP_field::extract(opcode) - 3); - set_cr(saved_cr); - set_xer(saved_xer); - for (int i = 0; i < 8; i++) - gpr(8 + i) = r68.d[i]; - for (int i = 0; i < 7; i++) - gpr(16 + i) = r68.a[i]; - gpr(1) = r68.a[7]; - WriteMacInt32(XLM_RUN_MODE, MODE_68K); + default: // EMUL_OP + execute_emul_op(EMUL_OP_field::extract(opcode) - 3); pc() += 4; break; } +} + +// Compile one instruction +int sheepshaver_cpu::compile1(codegen_context_t & cg_context) +{ +#if PPC_ENABLE_JIT + const instr_info_t *ii = cg_context.instr_info; + if (ii->mnemo != PPC_I(SHEEP)) + return COMPILE_FAILURE; + + int status = COMPILE_FAILURE; + powerpc_dyngen & dg = cg_context.codegen; + uint32 opcode = cg_context.opcode; + + switch (opcode & 0x3f) { + case 0: // EMUL_RETURN + dg.gen_invoke(QuitEmulator); + status = COMPILE_CODE_OK; + break; + + case 1: // EXEC_RETURN + dg.gen_spcflags_set(SPCFLAG_CPU_EXEC_RETURN); + // Don't check for pending interrupts, we do know we have to + // get out of this block ASAP + dg.gen_exec_return(); + status = COMPILE_EPILOGUE_OK; + break; + + case 2: { // EXEC_NATIVE + uint32 selector = NATIVE_OP_field::extract(opcode); + switch (selector) { +#if !PPC_REENTRANT_JIT + // Filter out functions that may invoke Execute68k() or + // CallMacOS(), this would break reentrancy as they could + // invalidate the translation cache and even overwrite + // continuation code when we are done with them. + case NATIVE_PATCH_NAME_REGISTRY: + dg.gen_invoke(DoPatchNameRegistry); + status = COMPILE_CODE_OK; + break; + case NATIVE_VIDEO_INSTALL_ACCEL: + dg.gen_invoke(VideoInstallAccel); + status = COMPILE_CODE_OK; + break; + case NATIVE_VIDEO_VBL: + dg.gen_invoke(VideoVBL); + status = COMPILE_CODE_OK; + break; + case NATIVE_GET_RESOURCE: + case NATIVE_GET_1_RESOURCE: + case NATIVE_GET_IND_RESOURCE: + case NATIVE_GET_1_IND_RESOURCE: + case NATIVE_R_GET_RESOURCE: { + static const uint32 get_resource_ptr[] = { + XLM_GET_RESOURCE, + XLM_GET_1_RESOURCE, + XLM_GET_IND_RESOURCE, + XLM_GET_1_IND_RESOURCE, + XLM_R_GET_RESOURCE + }; + uint32 old_get_resource = ReadMacInt32(get_resource_ptr[selector - NATIVE_GET_RESOURCE]); + typedef void (*func_t)(dyngen_cpu_base, uint32); + func_t func = (func_t)nv_mem_fun(&sheepshaver_cpu::get_resource).ptr(); + dg.gen_invoke_CPU_im(func, old_get_resource); + status = COMPILE_CODE_OK; + break; + } + case NATIVE_CHECK_LOAD_INVOC: + dg.gen_load_T0_GPR(3); + dg.gen_load_T1_GPR(4); + dg.gen_se_16_32_T1(); + dg.gen_load_T2_GPR(5); + dg.gen_invoke_T0_T1_T2((void (*)(uint32, uint32, uint32))check_load_invoc); + status = COMPILE_CODE_OK; + break; +#endif + case NATIVE_BITBLT: + dg.gen_load_T0_GPR(3); + dg.gen_invoke_T0((void (*)(uint32))NQD_bitblt); + status = COMPILE_CODE_OK; + break; + case NATIVE_INVRECT: + dg.gen_load_T0_GPR(3); + dg.gen_invoke_T0((void (*)(uint32))NQD_invrect); + status = COMPILE_CODE_OK; + break; + case NATIVE_FILLRECT: + dg.gen_load_T0_GPR(3); + dg.gen_invoke_T0((void (*)(uint32))NQD_fillrect); + status = COMPILE_CODE_OK; + break; + } + // Could we fully translate this NativeOp? + if (status == COMPILE_CODE_OK) { + if (!FN_field::test(opcode)) + cg_context.done_compile = false; + else { + dg.gen_load_A0_LR(); + dg.gen_set_PC_A0(); + cg_context.done_compile = true; + } + break; + } +#if PPC_REENTRANT_JIT + // Try to execute NativeOp trampoline + if (!FN_field::test(opcode)) + dg.gen_set_PC_im(cg_context.pc + 4); + else { + dg.gen_load_A0_LR(); + dg.gen_set_PC_A0(); + } + dg.gen_mov_32_T0_im(selector); + dg.gen_jmp(native_op_trampoline); + cg_context.done_compile = true; + status = COMPILE_EPILOGUE_OK; + break; +#endif + // Invoke NativeOp handler + if (!FN_field::test(opcode)) { + typedef void (*func_t)(dyngen_cpu_base, uint32); + func_t func = (func_t)nv_mem_fun(&sheepshaver_cpu::execute_native_op).ptr(); + dg.gen_invoke_CPU_im(func, selector); + cg_context.done_compile = false; + status = COMPILE_CODE_OK; + } + // Otherwise, let it generate a call to execute_sheep() which + // will cause necessary updates to the program counter + break; + } + + default: { // EMUL_OP + uint32 emul_op = EMUL_OP_field::extract(opcode) - 3; +#if ENABLE_NATIVE_EMUL_OP + typedef void (*emul_op_func_t)(dyngen_cpu_base); + emul_op_func_t emul_op_func = 0; + switch (emul_op) { + case OP_MICROSECONDS: + emul_op_func = (emul_op_func_t)nv_mem_fun(&sheepshaver_cpu::execute_emul_op_microseconds).ptr(); + break; + case OP_IDLE_TIME: + emul_op_func = (emul_op_func_t)nv_mem_fun(&sheepshaver_cpu::execute_emul_op_idle_time_1).ptr(); + break; + case OP_IDLE_TIME_2: + emul_op_func = (emul_op_func_t)nv_mem_fun(&sheepshaver_cpu::execute_emul_op_idle_time_2).ptr(); + break; + } + if (emul_op_func) { + dg.gen_invoke_CPU(emul_op_func); + cg_context.done_compile = false; + status = COMPILE_CODE_OK; + break; + } +#endif +#if PPC_REENTRANT_JIT + // Try to execute EmulOp trampoline + dg.gen_set_PC_im(cg_context.pc + 4); + dg.gen_mov_32_T0_im(emul_op); + dg.gen_jmp(emul_op_trampoline); + cg_context.done_compile = true; + status = COMPILE_EPILOGUE_OK; + break; +#endif + // Invoke EmulOp handler + typedef void (*func_t)(dyngen_cpu_base, uint32); + func_t func = (func_t)nv_mem_fun(&sheepshaver_cpu::execute_emul_op).ptr(); + dg.gen_invoke_CPU_im(func, emul_op); + cg_context.done_compile = false; + status = COMPILE_CODE_OK; + break; + } + } + return status; +#endif + return COMPILE_FAILURE; +} + +// CPU context to preserve on interrupt +sheepshaver_cpu::interrupt_context::interrupt_context(sheepshaver_cpu *_cpu, const char *_where) +{ +#if SAFE_INTERRUPT_PPC >= 2 + cpu = _cpu; + where = _where; + + // Save interrupt context + memcpy(&gpr[0], &cpu->gpr(0), sizeof(gpr)); + pc = cpu->pc(); + lr = cpu->lr(); + ctr = cpu->ctr(); + cr = cpu->get_cr(); + xer = cpu->get_xer(); +#endif +} + +sheepshaver_cpu::interrupt_context::~interrupt_context() +{ +#if SAFE_INTERRUPT_PPC >= 2 + // Check whether CPU context was preserved by interrupt + if (memcmp(&gpr[0], &cpu->gpr(0), sizeof(gpr)) != 0) { + printf("FATAL: %s: interrupt clobbers registers\n", where); + for (int i = 0; i < 32; i++) + if (gpr[i] != cpu->gpr(i)) + printf(" r%d: %08x -> %08x\n", i, gpr[i], cpu->gpr(i)); } + if (pc != cpu->pc()) + printf("FATAL: %s: interrupt clobbers PC\n", where); + if (lr != cpu->lr()) + printf("FATAL: %s: interrupt clobbers LR\n", where); + if (ctr != cpu->ctr()) + printf("FATAL: %s: interrupt clobbers CTR\n", where); + if (cr != cpu->get_cr()) + printf("FATAL: %s: interrupt clobbers CR\n", where); + if (xer != cpu->get_xer()) + printf("FATAL: %s: interrupt clobbers XER\n", where); +#endif } // Handle MacOS interrupt void sheepshaver_cpu::interrupt(uint32 entry) { #if EMUL_TIME_STATS - interrupt_count++; + ppc_interrupt_count++; const clock_t interrupt_start = clock(); #endif -#if !MULTICORE_CPU +#if SAFE_INTERRUPT_PPC + static int depth = 0; + if (depth != 0) + printf("FATAL: sheepshaver_cpu::interrupt() called more than once: %d\n", depth); + depth++; +#endif + // Save program counters and branch registers uint32 saved_pc = pc(); uint32 saved_lr = lr(); uint32 saved_ctr= ctr(); uint32 saved_sp = gpr(1); -#endif // Initialize stack pointer to SheepShaver alternate stack base gpr(1) = SignalStackBase() - 64; @@ -314,17 +649,19 @@ void sheepshaver_cpu::interrupt(uint32 e // Enter nanokernel execute(entry); -#if !MULTICORE_CPU // Restore program counters and branch registers pc() = saved_pc; lr() = saved_lr; ctr()= saved_ctr; gpr(1) = saved_sp; -#endif #if EMUL_TIME_STATS interrupt_time += (clock() - interrupt_start); #endif + +#if SAFE_INTERRUPT_PPC + depth--; +#endif } // Execute 68k routine @@ -493,8 +830,6 @@ inline void sheepshaver_cpu::execute_ppc } // Resource Manager thunk -extern "C" void check_load_invoc(uint32 type, int16 id, uint32 h); - inline void sheepshaver_cpu::get_resource(uint32 old_get_resource) { uint32 type = gpr(3); @@ -520,43 +855,25 @@ inline void sheepshaver_cpu::get_resourc * SheepShaver CPU engine interface **/ -static sheepshaver_cpu *main_cpu = NULL; // CPU emulator to handle usual control flow -static sheepshaver_cpu *interrupt_cpu = NULL; // CPU emulator to handle interrupts -static sheepshaver_cpu *current_cpu = NULL; // Current CPU emulator context +// PowerPC CPU emulator +static sheepshaver_cpu *ppc_cpu = NULL; void FlushCodeCache(uintptr start, uintptr end) { D(bug("FlushCodeCache(%08x, %08x)\n", start, end)); - main_cpu->invalidate_cache_range(start, end); -#if MULTICORE_CPU - interrupt_cpu->invalidate_cache_range(start, end); -#endif -} - -static inline void cpu_push(sheepshaver_cpu *new_cpu) -{ -#if MULTICORE_CPU - current_cpu = new_cpu; -#endif -} - -static inline void cpu_pop() -{ -#if MULTICORE_CPU - current_cpu = main_cpu; -#endif + ppc_cpu->invalidate_cache_range(start, end); } // Dump PPC registers static void dump_registers(void) { - current_cpu->dump_registers(); + ppc_cpu->dump_registers(); } // Dump log static void dump_log(void) { - current_cpu->dump_log(); + ppc_cpu->dump_log(); } /* @@ -579,11 +896,11 @@ static sigsegv_return_t sigsegv_handler( return SIGSEGV_RETURN_SKIP_INSTRUCTION; // Get program counter of target CPU - sheepshaver_cpu * const cpu = current_cpu; + sheepshaver_cpu * const cpu = ppc_cpu; const uint32 pc = cpu->pc(); // Fault in Mac ROM or RAM? - bool mac_fault = (pc >= ROM_BASE) && (pc < (ROM_BASE + ROM_AREA_SIZE)) || (pc >= RAMBase) && (pc < (RAMBase + RAMSize)); + bool mac_fault = (pc >= ROM_BASE) && (pc < (ROM_BASE + ROM_AREA_SIZE)) || (pc >= RAMBase) && (pc < (RAMBase + RAMSize)) || (pc >= DR_CACHE_BASE && pc < (DR_CACHE_BASE + DR_CACHE_SIZE)); if (mac_fault) { // "VM settings" during MacOS 8 installation @@ -603,6 +920,16 @@ static sigsegv_return_t sigsegv_handler( return SIGSEGV_RETURN_SKIP_INSTRUCTION; else if (pc == ROM_BASE + 0x4a10a0 && (cpu->gpr(20) == 0xf3012002 || cpu->gpr(20) == 0xf3012000)) return SIGSEGV_RETURN_SKIP_INSTRUCTION; + + // MacOS 8.6 serial drivers on startup (with DR Cache and OldWorld ROM) + else if ((pc - DR_CACHE_BASE) < DR_CACHE_SIZE && (cpu->gpr(16) == 0xf3012002 || cpu->gpr(16) == 0xf3012000)) + return SIGSEGV_RETURN_SKIP_INSTRUCTION; + else if ((pc - DR_CACHE_BASE) < DR_CACHE_SIZE && (cpu->gpr(20) == 0xf3012002 || cpu->gpr(20) == 0xf3012000)) + return SIGSEGV_RETURN_SKIP_INSTRUCTION; + + // Ignore writes to the zero page + else if ((uint32)(addr - SheepMem::ZeroPage()) < (uint32)SheepMem::PageSize()) + return SIGSEGV_RETURN_SKIP_INSTRUCTION; // Ignore all other faults, if requested if (PrefsFindBool("ignoresegv")) @@ -615,9 +942,8 @@ static sigsegv_return_t sigsegv_handler( printf("SIGSEGV\n"); printf(" pc %p\n", fault_instruction); printf(" ea %p\n", fault_address); - printf(" cpu %s\n", current_cpu == main_cpu ? "main" : "interrupts"); dump_registers(); - current_cpu->dump_log(); + ppc_cpu->dump_log(); enter_mon(); QuitEmulator(); @@ -627,16 +953,11 @@ static sigsegv_return_t sigsegv_handler( void init_emul_ppc(void) { // Initialize main CPU emulator - main_cpu = new sheepshaver_cpu(); - main_cpu->set_register(powerpc_registers::GPR(3), any_register((uint32)ROM_BASE + 0x30d000)); - main_cpu->set_register(powerpc_registers::GPR(4), any_register(KernelDataAddr + 0x1000)); + ppc_cpu = new sheepshaver_cpu(); + ppc_cpu->set_register(powerpc_registers::GPR(3), any_register((uint32)ROM_BASE + 0x30d000)); + ppc_cpu->set_register(powerpc_registers::GPR(4), any_register(KernelDataAddr + 0x1000)); WriteMacInt32(XLM_RUN_MODE, MODE_68K); -#if MULTICORE_CPU - // Initialize alternate CPU emulator to handle interrupts - interrupt_cpu = new sheepshaver_cpu(); -#endif - // Install the handler for SIGSEGV sigsegv_install_handler(sigsegv_handler); @@ -665,6 +986,8 @@ void exit_emul_ppc(void) printf("Total emulation time : %.1f sec\n", double(emul_time) / double(CLOCKS_PER_SEC)); printf("Total interrupt count: %d (%2.1f Hz)\n", interrupt_count, (double(interrupt_count) * CLOCKS_PER_SEC) / double(emul_time)); + printf("Total ppc interrupt count: %d (%2.1f %%)\n", ppc_interrupt_count, + (double(ppc_interrupt_count) * 100.0) / double(interrupt_count)); #define PRINT_STATS(LABEL, VAR_PREFIX) do { \ printf("Total " LABEL " count : %d\n", VAR_PREFIX##_count); \ @@ -681,57 +1004,80 @@ void exit_emul_ppc(void) printf("\n"); #endif - delete main_cpu; -#if MULTICORE_CPU - delete interrupt_cpu; -#endif + delete ppc_cpu; } +#if PPC_ENABLE_JIT && PPC_REENTRANT_JIT +// Initialize EmulOp trampolines +void init_emul_op_trampolines(basic_dyngen & dg) +{ + typedef void (*func_t)(dyngen_cpu_base, uint32); + func_t func; + + // EmulOp + emul_op_trampoline = dg.gen_start(); + func = (func_t)nv_mem_fun(&sheepshaver_cpu::execute_emul_op).ptr(); + dg.gen_invoke_CPU_T0(func); + dg.gen_exec_return(); + dg.gen_end(); + + // NativeOp + native_op_trampoline = dg.gen_start(); + func = (func_t)nv_mem_fun(&sheepshaver_cpu::execute_native_op).ptr(); + dg.gen_invoke_CPU_T0(func); + dg.gen_exec_return(); + dg.gen_end(); + + D(bug("EmulOp trampoline: %p\n", emul_op_trampoline)); + D(bug("NativeOp trampoline: %p\n", native_op_trampoline)); +} +#endif + /* * Emulation loop */ void emul_ppc(uint32 entry) { - current_cpu = main_cpu; #if 0 - current_cpu->start_log(); + ppc_cpu->start_log(); #endif // start emulation loop and enable code translation or caching - current_cpu->execute(entry); + ppc_cpu->execute(entry); } /* * Handle PowerPC interrupt */ -#if ASYNC_IRQ -void HandleInterrupt(void) -{ - main_cpu->handle_interrupt(); -} -#else void TriggerInterrupt(void) { #if 0 WriteMacInt32(0x16a, ReadMacInt32(0x16a) + 1); #else // Trigger interrupt to main cpu only - if (main_cpu) - main_cpu->trigger_interrupt(); + if (ppc_cpu) + ppc_cpu->trigger_interrupt(); #endif } -#endif void sheepshaver_cpu::handle_interrupt(void) { +#ifdef USE_SDL_VIDEO + // We must fill in the events queue in the same thread that did call SDL_SetVideoMode() + SDL_PumpEvents(); +#endif + // Do nothing if interrupts are disabled - if (*(int32 *)XLM_IRQ_NEST > 0) + if (int32(ReadMacInt32(XLM_IRQ_NEST)) > 0) return; - // Do nothing if there is no interrupt pending - if (InterruptFlags == 0) - return; + // Current interrupt nest level + static int interrupt_depth = 0; + ++interrupt_depth; +#if EMUL_TIME_STATS + interrupt_count++; +#endif // Disable MacOS stack sniffer WriteMacInt32(0x110, 0); @@ -740,7 +1086,6 @@ void sheepshaver_cpu::handle_interrupt(v switch (ReadMacInt32(XLM_RUN_MODE)) { case MODE_68K: // 68k emulator active, trigger 68k interrupt level 1 - assert(current_cpu == main_cpu); WriteMacInt16(tswap32(kernel_data->v[0x67c >> 2]), 1); set_cr(get_cr() | tswap32(kernel_data->v[0x674 >> 2])); break; @@ -748,8 +1093,9 @@ void sheepshaver_cpu::handle_interrupt(v #if INTERRUPTS_IN_NATIVE_MODE case MODE_NATIVE: // 68k emulator inactive, in nanokernel? - assert(current_cpu == main_cpu); - if (gpr(1) != KernelDataAddr) { + if (gpr(1) != KernelDataAddr && interrupt_depth == 1) { + interrupt_context ctx(this, "PowerPC mode"); + // Prepare for 68k interrupt level 1 WriteMacInt16(tswap32(kernel_data->v[0x67c >> 2]), 1); WriteMacInt32(tswap32(kernel_data->v[0x658 >> 2]) + 0xdc, @@ -758,12 +1104,10 @@ void sheepshaver_cpu::handle_interrupt(v // Execute nanokernel interrupt routine (this will activate the 68k emulator) DisableInterrupt(); - cpu_push(interrupt_cpu); if (ROMType == ROMTYPE_NEWWORLD) - current_cpu->interrupt(ROM_BASE + 0x312b1c); + ppc_cpu->interrupt(ROM_BASE + 0x312b1c); else - current_cpu->interrupt(ROM_BASE + 0x312a3c); - cpu_pop(); + ppc_cpu->interrupt(ROM_BASE + 0x312a3c); } break; #endif @@ -772,6 +1116,10 @@ void sheepshaver_cpu::handle_interrupt(v case MODE_EMUL_OP: // 68k emulator active, within EMUL_OP routine, execute 68k interrupt routine directly when interrupt level is 0 if ((ReadMacInt32(XLM_68K_R25) & 7) == 0) { + interrupt_context ctx(this, "68k mode"); +#if EMUL_TIME_STATS + const clock_t interrupt_start = clock(); +#endif #if 1 // Execute full 68k interrupt routine M68kRegisters r; @@ -797,10 +1145,16 @@ void sheepshaver_cpu::handle_interrupt(v } } #endif +#if EMUL_TIME_STATS + interrupt_time += (clock() - interrupt_start); +#endif } break; #endif } + + // We are done with this interrupt + --interrupt_depth; } static void get_resource(void); @@ -809,9 +1163,8 @@ static void get_ind_resource(void); static void get_1_ind_resource(void); static void r_get_resource(void); -#define GPR(REG) current_cpu->gpr(REG) - -static void NativeOp(int selector) +// Execute NATIVE_OP routine +void sheepshaver_cpu::execute_native_op(uint32 selector) { #if EMUL_TIME_STATS native_exec_count++; @@ -829,37 +1182,55 @@ static void NativeOp(int selector) VideoVBL(); break; case NATIVE_VIDEO_DO_DRIVER_IO: - GPR(3) = (int32)(int16)VideoDoDriverIO((void *)GPR(3), (void *)GPR(4), - (void *)GPR(5), GPR(6), GPR(7)); + gpr(3) = (int32)(int16)VideoDoDriverIO((void *)gpr(3), (void *)gpr(4), + (void *)gpr(5), gpr(6), gpr(7)); break; #ifdef WORDS_BIGENDIAN case NATIVE_ETHER_IRQ: EtherIRQ(); break; case NATIVE_ETHER_INIT: - GPR(3) = InitStreamModule((void *)GPR(3)); + gpr(3) = InitStreamModule((void *)gpr(3)); break; case NATIVE_ETHER_TERM: TerminateStreamModule(); break; case NATIVE_ETHER_OPEN: - GPR(3) = ether_open((queue_t *)GPR(3), (void *)GPR(4), GPR(5), GPR(6), (void*)GPR(7)); + gpr(3) = ether_open((queue_t *)gpr(3), (void *)gpr(4), gpr(5), gpr(6), (void*)gpr(7)); break; case NATIVE_ETHER_CLOSE: - GPR(3) = ether_close((queue_t *)GPR(3), GPR(4), (void *)GPR(5)); + gpr(3) = ether_close((queue_t *)gpr(3), gpr(4), (void *)gpr(5)); break; case NATIVE_ETHER_WPUT: - GPR(3) = ether_wput((queue_t *)GPR(3), (mblk_t *)GPR(4)); + gpr(3) = ether_wput((queue_t *)gpr(3), (mblk_t *)gpr(4)); break; case NATIVE_ETHER_RSRV: - GPR(3) = ether_rsrv((queue_t *)GPR(3)); + gpr(3) = ether_rsrv((queue_t *)gpr(3)); break; #else case NATIVE_ETHER_INIT: // FIXME: needs more complicated thunks - GPR(3) = false; + gpr(3) = false; break; #endif + case NATIVE_SYNC_HOOK: + gpr(3) = NQD_sync_hook(gpr(3)); + break; + case NATIVE_BITBLT_HOOK: + gpr(3) = NQD_bitblt_hook(gpr(3)); + break; + case NATIVE_BITBLT: + NQD_bitblt(gpr(3)); + break; + case NATIVE_FILLRECT_HOOK: + gpr(3) = NQD_fillrect_hook(gpr(3)); + break; + case NATIVE_INVRECT: + NQD_invrect(gpr(3)); + break; + case NATIVE_FILLRECT: + NQD_fillrect(gpr(3)); + break; case NATIVE_SERIAL_NOTHING: case NATIVE_SERIAL_OPEN: case NATIVE_SERIAL_PRIME_IN: @@ -877,7 +1248,7 @@ static void NativeOp(int selector) SerialStatus, SerialClose }; - GPR(3) = serial_callbacks[selector - NATIVE_SERIAL_NOTHING](GPR(3), GPR(4)); + gpr(3) = serial_callbacks[selector - NATIVE_SERIAL_NOTHING](gpr(3), gpr(4)); break; } case NATIVE_GET_RESOURCE: @@ -887,23 +1258,20 @@ static void NativeOp(int selector) case NATIVE_R_GET_RESOURCE: { typedef void (*GetResourceCallback)(void); static const GetResourceCallback get_resource_callbacks[] = { - get_resource, - get_1_resource, - get_ind_resource, - get_1_ind_resource, - r_get_resource + ::get_resource, + ::get_1_resource, + ::get_ind_resource, + ::get_1_ind_resource, + ::r_get_resource }; get_resource_callbacks[selector - NATIVE_GET_RESOURCE](); break; } - case NATIVE_DISABLE_INTERRUPT: - DisableInterrupt(); - break; - case NATIVE_ENABLE_INTERRUPT: - EnableInterrupt(); - break; case NATIVE_MAKE_EXECUTABLE: - MakeExecutable(0, (void *)GPR(4), GPR(5)); + MakeExecutable(0, (void *)gpr(4), gpr(5)); + break; + case NATIVE_CHECK_LOAD_INVOC: + check_load_invoc(gpr(3), gpr(4), gpr(5)); break; default: printf("FATAL: NATIVE_OP called with bogus selector %d\n", selector); @@ -924,7 +1292,7 @@ static void NativeOp(int selector) void Execute68k(uint32 pc, M68kRegisters *r) { - current_cpu->execute_68k(pc, r); + ppc_cpu->execute_68k(pc, r); } /* @@ -947,49 +1315,49 @@ void Execute68kTrap(uint16 trap, M68kReg uint32 call_macos(uint32 tvect) { - return current_cpu->execute_macos_code(tvect, 0, NULL); + return ppc_cpu->execute_macos_code(tvect, 0, NULL); } uint32 call_macos1(uint32 tvect, uint32 arg1) { const uint32 args[] = { arg1 }; - return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); + return ppc_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); } uint32 call_macos2(uint32 tvect, uint32 arg1, uint32 arg2) { const uint32 args[] = { arg1, arg2 }; - return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); + return ppc_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); } uint32 call_macos3(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3) { const uint32 args[] = { arg1, arg2, arg3 }; - return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); + return ppc_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); } uint32 call_macos4(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4) { const uint32 args[] = { arg1, arg2, arg3, arg4 }; - return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); + return ppc_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); } uint32 call_macos5(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4, uint32 arg5) { const uint32 args[] = { arg1, arg2, arg3, arg4, arg5 }; - return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); + return ppc_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); } uint32 call_macos6(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4, uint32 arg5, uint32 arg6) { const uint32 args[] = { arg1, arg2, arg3, arg4, arg5, arg6 }; - return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); + return ppc_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); } uint32 call_macos7(uint32 tvect, uint32 arg1, uint32 arg2, uint32 arg3, uint32 arg4, uint32 arg5, uint32 arg6, uint32 arg7) { const uint32 args[] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7 }; - return current_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); + return ppc_cpu->execute_macos_code(tvect, sizeof(args)/sizeof(args[0]), args); } /* @@ -998,25 +1366,25 @@ uint32 call_macos7(uint32 tvect, uint32 void get_resource(void) { - current_cpu->get_resource(ReadMacInt32(XLM_GET_RESOURCE)); + ppc_cpu->get_resource(ReadMacInt32(XLM_GET_RESOURCE)); } void get_1_resource(void) { - current_cpu->get_resource(ReadMacInt32(XLM_GET_1_RESOURCE)); + ppc_cpu->get_resource(ReadMacInt32(XLM_GET_1_RESOURCE)); } void get_ind_resource(void) { - current_cpu->get_resource(ReadMacInt32(XLM_GET_IND_RESOURCE)); + ppc_cpu->get_resource(ReadMacInt32(XLM_GET_IND_RESOURCE)); } void get_1_ind_resource(void) { - current_cpu->get_resource(ReadMacInt32(XLM_GET_1_IND_RESOURCE)); + ppc_cpu->get_resource(ReadMacInt32(XLM_GET_1_IND_RESOURCE)); } void r_get_resource(void) { - current_cpu->get_resource(ReadMacInt32(XLM_R_GET_RESOURCE)); + ppc_cpu->get_resource(ReadMacInt32(XLM_R_GET_RESOURCE)); }