--- SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp 2004/01/12 15:37:24 1.25 +++ SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp 2004/05/15 17:26:28 1.37 @@ -38,8 +38,10 @@ #include "name_registry.h" #include "serial.h" #include "ether.h" +#include "timer.h" #include +#include #if ENABLE_MON #include "mon.h" @@ -76,12 +78,18 @@ 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,6 +102,9 @@ 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; @@ -122,6 +133,14 @@ 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(); + public: // Constructor @@ -133,6 +152,9 @@ public: uint32 get_xer() const { return xer().get(); } void set_xer(uint32 v) { xer().set(v); } + // Execute EMUL_OP routine + void execute_emul_op(uint32 emul_op); + // Execute 68k routine void execute_68k(uint32 entry, M68kRegisters *r); @@ -142,6 +164,9 @@ public: // Execute MacOS/PPC code uint32 execute_macos_code(uint32 tvect, int nargs, uint32 const *args); + // Compile one instruction + virtual bool compile1(codegen_context_t & cg_context); + // Resource manager thunk void get_resource(uint32 old_get_resource); @@ -149,20 +174,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 +218,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, @@ -201,16 +240,88 @@ void sheepshaver_cpu::init_decoder() 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) { @@ -234,30 +345,148 @@ void sheepshaver_cpu::execute_sheep(uint 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 +bool 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 false; + + bool compiled = false; + powerpc_dyngen & dg = cg_context.codegen; + uint32 opcode = cg_context.opcode; + + switch (opcode & 0x3f) { + case 0: // EMUL_RETURN + dg.gen_invoke(QuitEmulator); + compiled = true; + break; + + case 1: // EXEC_RETURN + dg.gen_spcflags_set(SPCFLAG_CPU_EXEC_RETURN); + compiled = true; + break; + + case 2: { // EXEC_NATIVE + uint32 selector = NATIVE_OP_field::extract(opcode); + switch (selector) { + case NATIVE_PATCH_NAME_REGISTRY: + dg.gen_invoke(DoPatchNameRegistry); + compiled = true; + break; + case NATIVE_VIDEO_INSTALL_ACCEL: + dg.gen_invoke(VideoInstallAccel); + compiled = true; + break; + case NATIVE_VIDEO_VBL: + dg.gen_invoke(VideoVBL); + compiled = true; + 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); + compiled = true; + break; + } + case NATIVE_DISABLE_INTERRUPT: + dg.gen_invoke(DisableInterrupt); + compiled = true; + break; + case NATIVE_ENABLE_INTERRUPT: + dg.gen_invoke(EnableInterrupt); + compiled = true; + 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); + compiled = true; + break; + case NATIVE_BITBLT: + dg.gen_load_T0_GPR(3); + dg.gen_invoke_T0((void (*)(uint32))NQD_bitblt); + compiled = true; + break; + case NATIVE_INVRECT: + dg.gen_load_T0_GPR(3); + dg.gen_invoke_T0((void (*)(uint32))NQD_invrect); + compiled = true; + break; + case NATIVE_FILLRECT: + dg.gen_load_T0_GPR(3); + dg.gen_invoke_T0((void (*)(uint32))NQD_fillrect); + compiled = true; + break; + } + if (FN_field::test(opcode)) { + if (compiled) { + dg.gen_load_A0_LR(); + dg.gen_set_PC_A0(); + } + cg_context.done_compile = true; + } + else + cg_context.done_compile = false; + 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; + compiled = true; + break; + } +#endif + 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; + compiled = true; + break; + } } + return compiled; +#endif + return false; } // Handle MacOS interrupt @@ -268,6 +497,17 @@ void sheepshaver_cpu::interrupt(uint32 e const clock_t interrupt_start = clock(); #endif +#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 +#if SAFE_INTERRUPT_PPC >= 2 + uint32 saved_regs[32]; + memcpy(&saved_regs[0], &gpr(0), sizeof(saved_regs)); +#endif + #if !MULTICORE_CPU // Save program counters and branch registers uint32 saved_pc = pc(); @@ -325,6 +565,14 @@ void sheepshaver_cpu::interrupt(uint32 e #if EMUL_TIME_STATS interrupt_time += (clock() - interrupt_start); #endif + +#if SAFE_INTERRUPT_PPC >= 2 + if (memcmp(&saved_regs[0], &gpr(0), sizeof(saved_regs)) != 0) + printf("FATAL: dirty PowerPC registers\n"); +#endif +#if SAFE_INTERRUPT_PPC + depth--; +#endif } // Execute 68k routine @@ -493,8 +741,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); @@ -604,6 +850,10 @@ static sigsegv_return_t sigsegv_handler( else if (pc == ROM_BASE + 0x4a10a0 && (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")) return SIGSEGV_RETURN_SKIP_INSTRUCTION; @@ -860,6 +1110,24 @@ static void NativeOp(int selector) 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: @@ -905,6 +1173,9 @@ static void NativeOp(int selector) case NATIVE_MAKE_EXECUTABLE: 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); QuitEmulator();