From 029a7fd85b1db31020bc3a8eab03dcc7ff599900 Mon Sep 17 00:00:00 2001 From: gbeauche <> Date: Sun, 7 Sep 2003 14:25:05 +0000 Subject: [PATCH] Merge in old kpx_cpu snapshot for debugging --- .../src/kpx_cpu/include/basic-blockinfo.hpp | 110 ++ SheepShaver/src/kpx_cpu/include/basic-cpu.hpp | 79 ++ .../src/kpx_cpu/include/basic-plugin.hpp | 29 + .../src/kpx_cpu/include/block-alloc.hpp | 126 ++ .../src/kpx_cpu/include/task-plugin.hpp | 50 + SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp | 836 +++++++++++++ .../src/kpx_cpu/src/cpu/block-cache.hpp | 240 ++++ .../src/kpx_cpu/src/cpu/ppc/genexec.pl | 52 + .../src/kpx_cpu/src/cpu/ppc/ppc-bitfields.hpp | 213 ++++ .../src/kpx_cpu/src/cpu/ppc/ppc-blockinfo.hpp | 43 + .../src/kpx_cpu/src/cpu/ppc/ppc-config.hpp | 193 ++++ .../src/kpx_cpu/src/cpu/ppc/ppc-cpu.cpp | 375 ++++++ .../src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp | 331 ++++++ .../src/kpx_cpu/src/cpu/ppc/ppc-decode.cpp | 1027 ++++++++++++++++ .../src/kpx_cpu/src/cpu/ppc/ppc-execute.cpp | 1029 +++++++++++++++++ .../src/kpx_cpu/src/cpu/ppc/ppc-operands.hpp | 236 ++++ .../kpx_cpu/src/cpu/ppc/ppc-operations.hpp | 198 ++++ .../src/kpx_cpu/src/cpu/ppc/ppc-registers.hpp | 374 ++++++ SheepShaver/src/kpx_cpu/src/cpu/vm.hpp | 260 +++++ 19 files changed, 5801 insertions(+) create mode 100644 SheepShaver/src/kpx_cpu/include/basic-blockinfo.hpp create mode 100644 SheepShaver/src/kpx_cpu/include/basic-cpu.hpp create mode 100644 SheepShaver/src/kpx_cpu/include/basic-plugin.hpp create mode 100644 SheepShaver/src/kpx_cpu/include/block-alloc.hpp create mode 100644 SheepShaver/src/kpx_cpu/include/task-plugin.hpp create mode 100644 SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp create mode 100644 SheepShaver/src/kpx_cpu/src/cpu/block-cache.hpp create mode 100755 SheepShaver/src/kpx_cpu/src/cpu/ppc/genexec.pl create mode 100644 SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-bitfields.hpp create mode 100644 SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-blockinfo.hpp create mode 100644 SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-config.hpp create mode 100644 SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.cpp create mode 100644 SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp create mode 100644 SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-decode.cpp create mode 100644 SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-execute.cpp create mode 100644 SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-operands.hpp create mode 100644 SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-operations.hpp create mode 100644 SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-registers.hpp create mode 100644 SheepShaver/src/kpx_cpu/src/cpu/vm.hpp diff --git a/SheepShaver/src/kpx_cpu/include/basic-blockinfo.hpp b/SheepShaver/src/kpx_cpu/include/basic-blockinfo.hpp new file mode 100644 index 00000000..265431fc --- /dev/null +++ b/SheepShaver/src/kpx_cpu/include/basic-blockinfo.hpp @@ -0,0 +1,110 @@ +/* + * basic-blockinfo.hpp - PowerPC basic block information + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef BASIC_BLOCKINFO_H +#define BASIC_BLOCKINFO_H + +struct basic_block_info +{ + typedef basic_block_info block_info; + static const int MAX_TARGETS = 2; + + struct dependency + { + block_info * source; + block_info * target; + dependency * next; + dependency ** prev_p; + }; + + uintptr pc; + int32 count; + uint32 size; + uint32 c1; + uint32 c2; + + // List of blocks we depend on + dependency dep[MAX_TARGETS]; + + // List of blocks that depends on this block + dependency * deplist; + + void init(uintptr start_pc); + void remove_dep(dependency *d); + void remove_deps(); + void create_jmpdep(block_info *tbi, int i); + void maybe_create_jmpdep(block_info *tbi); +}; + +inline void +basic_block_info::init(uintptr start_pc) +{ + pc = start_pc; + deplist = NULL; + for (int i = 0; i < MAX_TARGETS; i++) { + dep[i].source = NULL; + dep[i].target = NULL; + dep[i].next = NULL; + dep[i].prev_p = NULL; + } +} + +inline void +basic_block_info::remove_dep(dependency *d) +{ + if (d->prev_p) + *(d->prev_p) = d->next; + if (d->next) + d->next->prev_p = d->prev_p; + d->prev_p = NULL; + d->next = NULL; +} + +inline void +basic_block_info::remove_deps() +{ + for (int i = 0; i < MAX_TARGETS; i++) + remove_dep(&dep[i]); +} + +inline void +basic_block_info::create_jmpdep(block_info *tbi, int i) +{ + dep[i].source = this; + dep[i].target = tbi; + dep[i].next = tbi->deplist; + if (dep[i].next) + dep[i].next->prev_p = &(dep[i].next); + dep[i].prev_p = &(tbi->deplist); + tbi->deplist = &(dep[i]); +} + +inline void +basic_block_info::maybe_create_jmpdep(block_info *tbi) +{ + for (int i = 0; i < MAX_TARGETS && dep[i].target != tbi; i++) { + if (dep[i].source == NULL) { + create_jmpdep(tbi, i); + break; + } + } +} + +#endif /* BASIC_BLOCKINFO_H */ diff --git a/SheepShaver/src/kpx_cpu/include/basic-cpu.hpp b/SheepShaver/src/kpx_cpu/include/basic-cpu.hpp new file mode 100644 index 00000000..f909f4c0 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/include/basic-cpu.hpp @@ -0,0 +1,79 @@ +/* + * basic-cpu.hpp - Basic CPU definitions + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef BASIC_CPU_H +#define BASIC_CPU_H + +#include "sysdeps.h" +#include "task-plugin.hpp" + +/** + * Generic register value + **/ + +union any_register +{ + uint32 i; + uint64 j; + float f; + double d; + + // Explicit casts may be required to use those constructors + any_register(uint32 v = 0) : i(v) { } + any_register(uint64 v) : j(v) { } + any_register(float v) : f(v) { } + any_register(double v) : d(v) { } +}; + +/** + * Basic CPU model + **/ + +struct task_struct; + +struct basic_cpu + : public task_plugin +{ + // Basic register set + struct registers + { + enum { + PC = -1, // Program Counter + SP = -2, // Stack Pointer + }; + }; + + // Constructor & destructor + basic_cpu(task_struct * parent_task) : task_plugin(parent_task) { } + + // Start emulation loop + virtual void execute() = 0; + + // Set VALUE to register ID + virtual void set_register(int id, any_register const & value) = 0; + + // Get register ID + virtual any_register get_register(int id) = 0; +}; + +// Alias basic register set +typedef basic_cpu::registers basic_registers; + +#endif /* BASIC_CPU_H */ diff --git a/SheepShaver/src/kpx_cpu/include/basic-plugin.hpp b/SheepShaver/src/kpx_cpu/include/basic-plugin.hpp new file mode 100644 index 00000000..77da79f2 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/include/basic-plugin.hpp @@ -0,0 +1,29 @@ +/* + * basic-plugin.hpp - Basic plugin definition + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef BASIC_PLUGIN_H +#define BASIC_PLUGIN_H + +struct basic_plugin +{ + virtual ~basic_plugin() { } +}; + +#endif /* BASIC_PLUGIN_H */ diff --git a/SheepShaver/src/kpx_cpu/include/block-alloc.hpp b/SheepShaver/src/kpx_cpu/include/block-alloc.hpp new file mode 100644 index 00000000..ce1c7ab1 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/include/block-alloc.hpp @@ -0,0 +1,126 @@ +/* + * block_alloc.hpp - Memory allocation in blocks + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef BLOCK_ALLOC_H +#define BLOCK_ALLOC_H + +/** + * Slow memory allocator + * + * Each time a DATA item is requested (resp. released), the block + * is immediately allocated (resp. free'd). + **/ + +template< class data > +struct slow_allocator +{ + data * acquire() const { return new data; } + void release(data * const x) const { free(x); } +}; + +/** + * Lazy memory allocator + * + * Allocate a big memory block, typically larger than a page, + * that contains up to POOL_COUNT data items of type DATA. + **/ + +template< class data > +class lazy_allocator +{ + static const int pool_size = 4096; + static const int pool_count = 1 + pool_size / sizeof(data); + + struct chunk + { + data value; + chunk * next; + }; + + struct pool + { + chunk chunks[pool_count]; + pool * next; + }; + + pool * pools; + chunk * chunks; + +public: + lazy_allocator() : pools(0), chunks(0) { } + ~lazy_allocator(); + data * acquire(); + void release(data * const); +}; + +template< class data > +lazy_allocator::~lazy_allocator() +{ + pool * p = pools; + while (p) { + pool * d = p; + p = p->next; + free(d); + } +} + +template< class data > +data * lazy_allocator::acquire() +{ + if (!chunks) { + // There is no chunk left, allocate a new pool and link the + // chunks into the free list + pool * p = (pool *)malloc(sizeof(pool)); + for (chunk * c = &p->chunks[0]; c < &p->chunks[pool_count]; c++) { + c->next = chunks; + chunks = c; + } + p->next = pools; + pools = p; + } + chunk * c = chunks; + chunks = c->next; + return &c->value; +} + +template< class data > +void lazy_allocator::release(data * const d) +{ + chunk *c = (chunk *)d; + c->next = chunks; + chunks = c; +} + +/** + * Helper memory allocator + **/ + +template< class data_type, template< class > class allocator_type = lazy_allocator > +class allocator_helper +{ + static allocator_type allocator; +public: + static inline void *allocate() + { return allocator.acquire(); } + static inline void deallocate(void * p) + { allocator.release((data_type *)p); } +}; + +#endif /* BLOCK_ALLOC_H */ diff --git a/SheepShaver/src/kpx_cpu/include/task-plugin.hpp b/SheepShaver/src/kpx_cpu/include/task-plugin.hpp new file mode 100644 index 00000000..a39977a9 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/include/task-plugin.hpp @@ -0,0 +1,50 @@ +/* + * task-plugin.hpp - Task plugin definition + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TASK_PLUGIN_H +#define TASK_PLUGIN_H + +#include "basic-plugin.hpp" + +// Forward declarations +class task_struct; +class basic_kernel; +class basic_cpu; +class program_info; + +// Base class for all task components +class task_plugin + : public basic_plugin +{ + // Parent task + task_struct * task; + +public: + + // Constructor + task_plugin(task_struct * parent_task) : task(parent_task) { } + + // Public accessors to resolve various components of a task + basic_kernel * kernel() const; + basic_cpu * cpu() const; + program_info * program() const; +}; + +#endif /* TASK_PLUGIN_H */ diff --git a/SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp b/SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp new file mode 100644 index 00000000..c9c04bf0 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp @@ -0,0 +1,836 @@ +/* + * sheepshaver_glue.cpp - Glue Kheperix CPU to SheepShaver CPU engine interface + * + * SheepShaver (C) 1997-2002 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 + * the Free Software Foundation; either version 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "cpu_emulation.h" +#include "main.h" +#include "xlowmem.h" +#include "emul_op.h" +#include "rom_patches.h" +#include "macos_util.h" +#include "block-alloc.hpp" +#include "sigsegv.h" +#include "cpu/ppc/ppc-cpu.hpp" +#include "cpu/ppc/ppc-operations.hpp" + +// Used for NativeOp trampolines +#include "video.h" +#include "name_registry.h" +#include "serial.h" + +#include + +#if ENABLE_MON +#include "mon.h" +#include "mon_disass.h" +#endif + +#define DEBUG 1 +#include "debug.h" + +static void enter_mon(void) +{ + // Start up mon in real-mode +#if ENABLE_MON + char *arg[4] = {"mon", "-m", "-r", NULL}; + mon(3, arg); +#endif +} + +// Enable Execute68k() safety checks? +#define SAFE_EXEC_68K 1 + +// Save FP state in Execute68k()? +#define SAVE_FP_EXEC_68K 1 + +// Interrupts in EMUL_OP mode? +#define INTERRUPTS_IN_EMUL_OP_MODE 1 + +// Interrupts in native mode? +#define INTERRUPTS_IN_NATIVE_MODE 1 + +// 68k Emulator Data +struct EmulatorData { + uint32 v[0x400]; +}; + +// Kernel Data +struct KernelData { + uint32 v[0x400]; + EmulatorData ed; +}; + +// Pointer to Kernel Data +static KernelData * const kernel_data = (KernelData *)0x68ffe000; + + +/** + * PowerPC emulator glue with special 'sheep' opcodes + **/ + +struct sheepshaver_exec_return { }; + +class sheepshaver_cpu + : public powerpc_cpu +{ + void init_decoder(); + void execute_sheep(uint32 opcode); + +public: + + sheepshaver_cpu() + : powerpc_cpu() + { init_decoder(); } + + // Stack pointer accessors + uint32 get_sp() const { return gpr(1); } + void set_sp(uint32 v) { gpr(1) = v; } + + // Condition Register accessors + uint32 get_cr() const { return cr().get(); } + void set_cr(uint32 v) { cr().set(v); } + + // Execution loop + void execute(uint32 pc); + + // Execute 68k routine + void execute_68k(uint32 entry, M68kRegisters *r); + + // Execute MacOS/PPC code + uint32 execute_macos_code(uint32 tvect, int nargs, uint32 const *args); + + // Resource manager thunk + void get_resource(uint32 old_get_resource); + + // Handle MacOS interrupt + void interrupt(uint32 entry, uint32 sp); + + // 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); +}; + +lazy_allocator< sheepshaver_cpu > allocator_helper< sheepshaver_cpu, lazy_allocator >::allocator; + +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_fn)&sheepshaver_cpu::execute_sheep, + NULL, + D_form, 6, 0, CFLOW_TRAP + } + }; + + const int ii_count = sizeof(sheep_ii_table)/sizeof(sheep_ii_table[0]); + D(bug("SheepShaver extra decode table has %d entries\n", ii_count)); + + for (int i = 0; i < ii_count; i++) { + const instr_info_t * ii = &sheep_ii_table[i]; + init_decoder_entry(ii); + } +} + +// Forward declaration for native opcode handler +static void NativeOp(int selector); + +// Execute SheepShaver instruction +void sheepshaver_cpu::execute_sheep(uint32 opcode) +{ +// D(bug("Extended opcode %08x at %08x (68k pc %08x)\n", opcode, pc(), gpr(24))); + assert((((opcode >> 26) & 0x3f) == 6) && OP_MAX <= 64 + 3); + + switch (opcode & 0x3f) { + case 0: // EMUL_RETURN + QuitEmulator(); + break; + + case 1: // EXEC_RETURN + throw sheepshaver_exec_return(); + break; + + case 2: // EXEC_NATIVE + NativeOp((opcode >> 6) & 0x1f); + pc() = lr(); + 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); + EmulOp(&r68, gpr(24), (opcode & 0x3f) - 3); + 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); + pc() += 4; + break; + } + } +} + +// Execution loop +void sheepshaver_cpu::execute(uint32 entry) +{ + try { + pc() = entry; + powerpc_cpu::execute(); + } + catch (sheepshaver_exec_return const &) { + // Nothing, simply return + } + catch (...) { + printf("ERROR: execute() received an unknown exception!\n"); + QuitEmulator(); + } +} + +// Handle MacOS interrupt +void sheepshaver_cpu::interrupt(uint32 entry, uint32 sp) +{ + // Create stack frame + gpr(1) = sp - 64; + + // Build trampoline to return from interrupt + uint32 trampoline[] = { POWERPC_EMUL_OP | 1 }; + + // Prepare registers for nanokernel interrupt routine + kernel_data->v[0x004 >> 2] = gpr(1); + kernel_data->v[0x018 >> 2] = gpr(6); + + gpr(6) = kernel_data->v[0x65c >> 2]; + WriteMacInt32(gpr(6) + 0x13c, gpr(7)); + WriteMacInt32(gpr(6) + 0x144, gpr(8)); + WriteMacInt32(gpr(6) + 0x14c, gpr(9)); + WriteMacInt32(gpr(6) + 0x154, gpr(10)); + WriteMacInt32(gpr(6) + 0x15c, gpr(11)); + WriteMacInt32(gpr(6) + 0x164, gpr(12)); + WriteMacInt32(gpr(6) + 0x16c, gpr(13)); + + gpr(1) = KernelDataAddr; + gpr(7) = kernel_data->v[0x660 >> 2]; + gpr(8) = 0; + gpr(10) = (uint32)trampoline; + gpr(12) = (uint32)trampoline; + gpr(13) = cr().get(); + + // rlwimi. r7,r7,8,0,0 + uint32 result = op_ppc_rlwimi::apply(gpr(7), 8, 0x80000000, gpr(7)); + record_cr0(result); + gpr(7) = result; + + gpr(11) = 0xf072; // MSR (SRR1) + cr().set((gpr(11) & 0x0fff0000) | (cr().get() & ~0x0fff0000)); + + // Enter nanokernel + execute(entry); + + // Cleanup stack + gpr(1) += 64; +} + +// Execute 68k routine +void sheepshaver_cpu::execute_68k(uint32 entry, M68kRegisters *r) +{ +#if SAFE_EXEC_68K + if (ReadMacInt32(XLM_RUN_MODE) != MODE_EMUL_OP) + printf("FATAL: Execute68k() not called from EMUL_OP mode\n"); +#endif + + // Save program counters and branch registers + uint32 saved_pc = pc(); + uint32 saved_lr = lr(); + uint32 saved_ctr= ctr(); + + // Create MacOS stack frame + uint32 sp = gpr(1); + gpr(1) -= 56 + 19*4 + 18*8; + WriteMacInt32(gpr(1), sp); + + // Save PowerPC registers + memcpy(Mac2HostAddr(gpr(1)+56), &gpr(13), sizeof(uint32)*(32-13)); +#if SAVE_FP_EXEC_68K + memcpy(Mac2HostAddr(gpr(1)+56+19*4), &fpr(14), sizeof(double)*(32-14)); +#endif + + // Setup registers for 68k emulator + cr().set(0); + cr().set(2, 1); // Supervisor mode + for (int i = 0; i < 8; i++) // d[0]..d[7] + gpr(8 + i) = r->d[i]; + for (int i = 0; i < 7; i++) // a[0]..a[6] + gpr(16 + i) = r->a[i]; + gpr(23) = 0; + gpr(24) = entry; + gpr(25) = ReadMacInt32(XLM_68K_R25); // MSB of SR + gpr(26) = 0; + gpr(28) = 0; // VBR + gpr(29) = kernel_data->ed.v[0x74 >> 2]; // Pointer to opcode table + gpr(30) = kernel_data->ed.v[0x78 >> 2]; // Address of emulator + gpr(31) = KernelDataAddr + 0x1000; + + // Push return address (points to EXEC_RETURN opcode) on stack + gpr(1) -= 4; + WriteMacInt32(gpr(1), XLM_EXEC_RETURN_OPCODE); + + // Rentering 68k emulator + WriteMacInt32(XLM_RUN_MODE, MODE_68K); + + // Set r0 to 0 for 68k emulator + gpr(0) = 0; + + // Execute 68k opcode + uint32 opcode = ReadMacInt16(gpr(24)); + gpr(27) = (int32)(int16)ReadMacInt16(gpr(24) += 2); + gpr(29) += opcode * 8; + execute(gpr(29)); + + // Save r25 (contains current 68k interrupt level) + WriteMacInt32(XLM_68K_R25, gpr(25)); + + // Reentering EMUL_OP mode + WriteMacInt32(XLM_RUN_MODE, MODE_EMUL_OP); + + // Save 68k registers + for (int i = 0; i < 8; i++) // d[0]..d[7] + r->d[i] = gpr(8 + i); + for (int i = 0; i < 7; i++) // a[0]..a[6] + r->a[i] = gpr(16 + i); + + // Restore PowerPC registers + memcpy(&gpr(13), Mac2HostAddr(gpr(1)+56), sizeof(uint32)*(32-13)); +#if SAVE_FP_EXEC_68K + memcpy(&fpr(14), Mac2HostAddr(gpr(1)+56+19*4), sizeof(double)*(32-14)); +#endif + + // Cleanup stack + gpr(1) += 56 + 19*4 + 18*8; + + // Restore program counters and branch registers + pc() = saved_pc; + lr() = saved_lr; + ctr()= saved_ctr; +} + +// Call MacOS PPC code +uint32 sheepshaver_cpu::execute_macos_code(uint32 tvect, int nargs, uint32 const *args) +{ + // Save program counters and branch registers + uint32 saved_pc = pc(); + uint32 saved_lr = lr(); + uint32 saved_ctr= ctr(); + + // Build trampoline with EXEC_RETURN + uint32 trampoline[] = { POWERPC_EMUL_OP | 1 }; + lr() = (uint32)trampoline; + + gpr(1) -= 64; // Create stack frame + uint32 proc = ReadMacInt32(tvect); // Get routine address + uint32 toc = ReadMacInt32(tvect + 4); // Get TOC pointer + + // Save PowerPC registers + uint32 regs[8]; + regs[0] = gpr(2); + for (int i = 0; i < nargs; i++) + regs[i + 1] = gpr(i + 3); + + // Prepare and call MacOS routine + gpr(2) = toc; + for (int i = 0; i < nargs; i++) + gpr(i + 3) = args[i]; + execute(proc); + uint32 retval = gpr(3); + + // Restore PowerPC registers + for (int i = 0; i <= nargs; i++) + gpr(i + 2) = regs[i]; + + // Cleanup stack + gpr(1) += 64; + + // Restore program counters and branch registers + pc() = saved_pc; + lr() = saved_lr; + ctr()= saved_ctr; + + return retval; +} + +// Resource Manager thunk +inline void sheepshaver_cpu::get_resource(uint32 old_get_resource) +{ + printf("ERROR: get_resource() unimplemented\n"); + QuitEmulator(); +} + + +/** + * 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 + +// Dump PPC registers +static void dump_registers(void) +{ + current_cpu->dump_registers(); +} + +// Dump log +static void dump_log(void) +{ + current_cpu->dump_log(); +} + +/* + * Initialize CPU emulation + */ + +static struct sigaction sigsegv_action; + +#if defined(__powerpc__) +#include +#endif + +static void sigsegv_handler(int sig, siginfo_t *sip, void *scp) +{ + const uintptr addr = (uintptr)sip->si_addr; +#if ENABLE_VOSF + // Handle screen fault. + extern bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction); + if (Screen_fault_handler((sigsegv_address_t)addr, SIGSEGV_INVALID_PC)) + return; +#endif +#if defined(__powerpc__) + if (addr >= ROM_BASE && addr < ROM_BASE + ROM_SIZE) { + printf("IGNORE write access to ROM at %08x\n", addr); + (((ucontext_t *)scp)->uc_mcontext.regs)->nip += 4; + return; + } + if (addr >= 0xf3012000 && addr < 0xf3014000 && 0) { + printf("IGNORE write access to ROM at %08x\n", addr); + (((ucontext_t *)scp)->uc_mcontext.regs)->nip += 4; + return; + } +#endif + printf("Caught SIGSEGV at address %p\n", sip->si_addr); + printf("Native PC: %08x\n", (((ucontext_t *)scp)->uc_mcontext.regs)->nip); + printf("Current CPU: %s\n", current_cpu == main_cpu ? "main" : "interrupts"); +#if 1 + dump_registers(); +#else + printf("Main CPU context\n"); + main_cpu->dump_registers(); + printf("Interrupts CPU context\n"); + interrupt_cpu->dump_registers(); +#endif + current_cpu->dump_log(); + WriteMacInt32(XLM_IRQ_NEST, 1); + enter_mon(); + QuitEmulator(); +} + +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)); + WriteMacInt32(XLM_RUN_MODE, MODE_68K); + + // Initialize alternate CPU emulator to handle interrupts + interrupt_cpu = new sheepshaver_cpu(); + + // Install SIGSEGV handler + sigemptyset(&sigsegv_action.sa_mask); + sigsegv_action.sa_sigaction = sigsegv_handler; + sigsegv_action.sa_flags = SA_SIGINFO; + sigsegv_action.sa_restorer = NULL; + sigaction(SIGSEGV, &sigsegv_action, NULL); + +#if ENABLE_MON + // Install "regs" command in cxmon + mon_add_command("regs", dump_registers, "regs Dump PowerPC registers\n"); + mon_add_command("log", dump_log, "log Dump PowerPC emulation log\n"); +#endif +} + +/* + * Emulation loop + */ + +void emul_ppc(uint32 entry) +{ + current_cpu = main_cpu; + current_cpu->start_log(); + current_cpu->execute(entry); +} + +/* + * Handle PowerPC interrupt + */ + +// Atomic operations +extern int atomic_add(int *var, int v); +extern int atomic_and(int *var, int v); +extern int atomic_or(int *var, int v); + +void HandleInterrupt(void) +{ + // Do nothing if interrupts are disabled + if (ReadMacInt32(XLM_IRQ_NEST) > 0 || InterruptFlags == 0) + return; + + // Do nothing if CPU objects are not initialized yet + if (current_cpu == NULL) + return; + + // Disable MacOS stack sniffer + WriteMacInt32(0x110, 0); + + // Interrupt action depends on current run mode + 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); + main_cpu->set_cr(main_cpu->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) { + // Prepare for 68k interrupt level 1 + WriteMacInt16(tswap32(kernel_data->v[0x67c >> 2]), 1); + WriteMacInt32(tswap32(kernel_data->v[0x658 >> 2]) + 0xdc, + ReadMacInt32(tswap32(kernel_data->v[0x658 >> 2]) + 0xdc) + | tswap32(kernel_data->v[0x674 >> 2])); + + // Execute nanokernel interrupt routine (this will activate the 68k emulator) + atomic_add((int32 *)XLM_IRQ_NEST, htonl(1)); + current_cpu = interrupt_cpu; + if (ROMType == ROMTYPE_NEWWORLD) + current_cpu->interrupt(ROM_BASE + 0x312b1c, main_cpu->get_sp()); + else + current_cpu->interrupt(ROM_BASE + 0x312a3c, main_cpu->get_sp()); + current_cpu = main_cpu; + } + break; +#endif + +#if INTERRUPTS_IN_EMUL_OP_MODE + 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) { +#if 1 + // Execute full 68k interrupt routine + M68kRegisters r; + uint32 old_r25 = ReadMacInt32(XLM_68K_R25); // Save interrupt level + WriteMacInt32(XLM_68K_R25, 0x21); // Execute with interrupt level 1 + static const uint16 proc[] = { + 0x3f3c, 0x0000, // move.w #$0000,-(sp) (fake format word) + 0x487a, 0x000a, // pea @1(pc) (return address) + 0x40e7, // move sr,-(sp) (saved SR) + 0x2078, 0x0064, // move.l $64,a0 + 0x4ed0, // jmp (a0) + M68K_RTS // @1 + }; + Execute68k((uint32)proc, &r); + WriteMacInt32(XLM_68K_R25, old_r25); // Restore interrupt level +#else + // Only update cursor + if (HasMacStarted()) { + if (InterruptFlags & INTFLAG_VIA) { + ClearInterruptFlag(INTFLAG_VIA); + ADBInterrupt(); + ExecutePPC(VideoVBL); + } + } +#endif + } + break; +#endif + } +} + +/* + * Execute NATIVE_OP opcode (called by PowerPC emulator) + */ + +#define POWERPC_NATIVE_OP(selector) \ + { tswap32(POWERPC_EMUL_OP | 2 | (((uint32)selector) << 6)) } + +// FIXME: Make sure 32-bit relocations are used +const uint32 NativeOpTable[NATIVE_OP_MAX] = { + POWERPC_NATIVE_OP(NATIVE_PATCH_NAME_REGISTRY), + POWERPC_NATIVE_OP(NATIVE_VIDEO_INSTALL_ACCEL), + POWERPC_NATIVE_OP(NATIVE_VIDEO_VBL), + POWERPC_NATIVE_OP(NATIVE_VIDEO_DO_DRIVER_IO), + POWERPC_NATIVE_OP(NATIVE_ETHER_IRQ), + POWERPC_NATIVE_OP(NATIVE_ETHER_INIT), + POWERPC_NATIVE_OP(NATIVE_ETHER_TERM), + POWERPC_NATIVE_OP(NATIVE_ETHER_OPEN), + POWERPC_NATIVE_OP(NATIVE_ETHER_CLOSE), + POWERPC_NATIVE_OP(NATIVE_ETHER_WPUT), + POWERPC_NATIVE_OP(NATIVE_ETHER_RSRV), + POWERPC_NATIVE_OP(NATIVE_SERIAL_NOTHING), + POWERPC_NATIVE_OP(NATIVE_SERIAL_OPEN), + POWERPC_NATIVE_OP(NATIVE_SERIAL_PRIME_IN), + POWERPC_NATIVE_OP(NATIVE_SERIAL_PRIME_OUT), + POWERPC_NATIVE_OP(NATIVE_SERIAL_CONTROL), + POWERPC_NATIVE_OP(NATIVE_SERIAL_STATUS), + POWERPC_NATIVE_OP(NATIVE_SERIAL_CLOSE), + POWERPC_NATIVE_OP(NATIVE_GET_RESOURCE), + POWERPC_NATIVE_OP(NATIVE_GET_1_RESOURCE), + POWERPC_NATIVE_OP(NATIVE_GET_IND_RESOURCE), + POWERPC_NATIVE_OP(NATIVE_GET_1_IND_RESOURCE), + POWERPC_NATIVE_OP(NATIVE_R_GET_RESOURCE), +}; + +static void get_resource(void); +static void get_1_resource(void); +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) +{ + switch (selector) { + case NATIVE_PATCH_NAME_REGISTRY: + DoPatchNameRegistry(); + break; + case NATIVE_VIDEO_INSTALL_ACCEL: + VideoInstallAccel(); + break; + case NATIVE_VIDEO_VBL: + 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)); + break; + case NATIVE_GET_RESOURCE: + get_resource(); + break; + case NATIVE_GET_1_RESOURCE: + get_1_resource(); + break; + case NATIVE_GET_IND_RESOURCE: + get_ind_resource(); + break; + case NATIVE_GET_1_IND_RESOURCE: + get_1_ind_resource(); + break; + case NATIVE_R_GET_RESOURCE: + r_get_resource(); + break; + case NATIVE_SERIAL_NOTHING: + case NATIVE_SERIAL_OPEN: + case NATIVE_SERIAL_PRIME_IN: + case NATIVE_SERIAL_PRIME_OUT: + case NATIVE_SERIAL_CONTROL: + case NATIVE_SERIAL_STATUS: + case NATIVE_SERIAL_CLOSE: { + typedef int16 (*SerialCallback)(uint32, uint32); + static const SerialCallback serial_callbacks[] = { + SerialNothing, + SerialOpen, + SerialPrimeIn, + SerialPrimeOut, + SerialControl, + SerialStatus, + SerialClose + }; + GPR(3) = serial_callbacks[selector - NATIVE_SERIAL_NOTHING](GPR(3), GPR(4)); + break; + } + default: + printf("FATAL: NATIVE_OP called with bogus selector %d\n", selector); + QuitEmulator(); + break; + } +} + +/* + * Execute native subroutine (LR must contain return address) + */ + +void ExecuteNative(int selector) +{ + uint32 tvect[2]; + tvect[0] = tswap32(POWERPC_NATIVE_OP_FUNC(selector)); + tvect[1] = 0; // Fake TVECT + RoutineDescriptor desc = BUILD_PPC_ROUTINE_DESCRIPTOR(0, tvect); + M68kRegisters r; + Execute68k((uint32)&desc, &r); +} + +/* + * Execute 68k subroutine (must be ended with EXEC_RETURN) + * This must only be called by the emul_thread when in EMUL_OP mode + * r->a[7] is unused, the routine runs on the caller's stack + */ + +void Execute68k(uint32 pc, M68kRegisters *r) +{ + current_cpu->execute_68k(pc, r); +} + +/* + * Execute 68k A-Trap from EMUL_OP routine + * r->a[7] is unused, the routine runs on the caller's stack + */ + +void Execute68kTrap(uint16 trap, M68kRegisters *r) +{ + uint16 proc[2] = {trap, M68K_RTS}; + Execute68k((uint32)proc, r); +} + +/* + * Call MacOS PPC code + */ + +uint32 call_macos(uint32 tvect) +{ + return current_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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +/* + * Atomic operations + */ + +int atomic_add(int *var, int v) +{ + int ret = *var; + *var += v; + return ret; +} + +int atomic_and(int *var, int v) +{ + int ret = *var; + *var &= v; + return ret; +} + +int atomic_or(int *var, int v) +{ + int ret = *var; + *var |= v; + return ret; +} + +/* + * Resource Manager thunks + */ + +extern "C" void check_load_invoc(uint32 type, int16 id, uint16 **h); + +void get_resource(void) +{ + current_cpu->get_resource(ReadMacInt32(XLM_GET_RESOURCE)); +} + +void get_1_resource(void) +{ + current_cpu->get_resource(ReadMacInt32(XLM_GET_1_RESOURCE)); +} + +void get_ind_resource(void) +{ + current_cpu->get_resource(ReadMacInt32(XLM_GET_IND_RESOURCE)); +} + +void get_1_ind_resource(void) +{ + current_cpu->get_resource(ReadMacInt32(XLM_GET_1_IND_RESOURCE)); +} + +void r_get_resource(void) +{ + current_cpu->get_resource(ReadMacInt32(XLM_R_GET_RESOURCE)); +} diff --git a/SheepShaver/src/kpx_cpu/src/cpu/block-cache.hpp b/SheepShaver/src/kpx_cpu/src/cpu/block-cache.hpp new file mode 100644 index 00000000..7fb4ad66 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/src/cpu/block-cache.hpp @@ -0,0 +1,240 @@ +/* + * block-cache.hpp - Basic block cache management + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef BLOCK_CACHE_H +#define BLOCK_CACHE_H + +#include "block-alloc.hpp" + +template< class block_info, template class block_allocator = slow_allocator > +class block_cache +{ +private: + static const uint32 HASH_BITS = 15; + static const uint32 HASH_SIZE = 1 << HASH_BITS; + static const uint32 HASH_MASK = HASH_SIZE - 1; + + struct entry + : public block_info + { + entry * next_same_cl; + entry ** prev_same_cl_p; + entry * next; + entry ** prev_p; + }; + + block_allocator allocator; + entry * cache_tags[HASH_SIZE]; + entry * active; + entry * dormant; + + uint32 cacheline(uint32 addr) const { + return (addr >> 2) & HASH_MASK; + } + +public: + + block_cache(); + ~block_cache(); + + block_info *new_blockinfo(); + void delete_blockinfo(block_info *bi); + + void initialize(); + void clear(); + block_info *find(uintptr pc); + entry *first_active() const; + entry *first_dormant() const; + + void remove_from_cl_list(block_info *bi); + void remove_from_list(block_info *bi); + void remove_from_lists(block_info *bi); + + void add_to_cl_list(block_info *bi); + void raise_in_cl_list(block_info *bi); + + void add_to_active_list(block_info *bi); + void add_to_dormant_list(block_info *bi); +}; + +template< class block_info, template class block_allocator > +block_cache< block_info, block_allocator >::block_cache() +{ + initialize(); +} + +template< class block_info, template class block_allocator > +inline block_cache< block_info, block_allocator >::~block_cache() +{ + clear(); +} + +template< class block_info, template class block_allocator > +void block_cache< block_info, block_allocator >::initialize() +{ + for (int i = 0; i < HASH_SIZE; i++) + cache_tags[i] = NULL; +} + +template< class block_info, template class block_allocator > +void block_cache< block_info, block_allocator >::clear() +{ + entry *p; + + p = active; + while (p) { + entry *d = p; + p = p->next; + delete_blockinfo(d); + } + active = NULL; + + p = dormant; + while (p) { + entry *d = p; + p = p->next; + delete_blockinfo(d); + } + dormant = NULL; +} + +template< class block_info, template class block_allocator > +inline block_info *block_cache< block_info, block_allocator >::new_blockinfo() +{ + entry * bce = allocator.acquire(); + return bce; +} + +template< class block_info, template class block_allocator > +inline void block_cache< block_info, block_allocator >::delete_blockinfo(block_info *bi) +{ + entry * bce = (entry *)bi; + allocator.release(bce); +} + +template< class block_info, template class block_allocator > +block_info *block_cache< block_info, block_allocator >::find(uintptr pc) +{ + // Hit: return immediately + entry * bce = cache_tags[cacheline(pc)]; + if (bce && bce->pc == pc) + return bce; + + // Miss: perform full list search and move block to front if found + while (bce) { + bce = bce->next_same_cl; + if (bce && bce->pc == pc) { + raise_in_cl_list(bce); + return bce; + } + } + + // Found none, will have to create a new block + return NULL; +} + +template< class block_info, template class block_allocator > +inline typename block_cache< block_info, block_allocator >::entry * +block_cache< block_info, block_allocator >::first_active() const +{ + return active; +} + +template< class block_info, template class block_allocator > +inline typename block_cache< block_info, block_allocator >::entry * +block_cache< block_info, block_allocator >::first_dormant() const +{ + return dormant; +} + +template< class block_info, template class block_allocator > +void block_cache< block_info, block_allocator >::remove_from_cl_list(block_info *bi) +{ + entry * bce = (entry *)bi; + if (bce->prev_same_cl_p) + *bce->prev_same_cl_p = bce->next_same_cl; + if (bce->next_same_cl) + bce->next_same_cl->prev_same_cl_p = bce->prev_same_cl_p; +} + +template< class block_info, template class block_allocator > +void block_cache< block_info, block_allocator >::add_to_cl_list(block_info *bi) +{ + entry * bce = (entry *)bi; + const uint32 cl = cacheline(bi->pc); + if (cache_tags[cl]) + cache_tags[cl]->prev_same_cl_p = &bce->next_same_cl; + bce->next_same_cl = cache_tags[cl]; + + cache_tags[cl] = bce; + bce->prev_same_cl_p = &cache_tags[cl]; +} + +template< class block_info, template class block_allocator > +inline void block_cache< block_info, block_allocator >::raise_in_cl_list(block_info *bi) +{ + remove_from_cl_list(bi); + add_to_cl_list(bi); +} + +template< class block_info, template class block_allocator > +void block_cache< block_info, block_allocator >::remove_from_list(block_info *bi) +{ + entry * bce = (entry *)bi; + if (bce->prev_p) + *bce->prev_p = bce->next; + if (bce->next) + bce->next->prev_p = bce->prev_p; +} + +template< class block_info, template class block_allocator > +void block_cache< block_info, block_allocator >::add_to_active_list(block_info *bi) +{ + entry * bce = (entry *)bi; + + if (active) + active->prev_p = &bce->next; + bce->next = active; + + active = bce; + bce->prev_p = &active; +} + +template< class block_info, template class block_allocator > +void block_cache< block_info, block_allocator >::add_to_dormant_list(block_info *bi) +{ + entry * bce = (entry *)bi; + + if (dormant) + dormant->prev_p = &bce->next; + bce->next = dormant; + + dormant = bce; + bce->prev_p = &dormant; +} + +template< class block_info, template class block_allocator > +inline void block_cache< block_info, block_allocator >::remove_from_lists(block_info *bi) +{ + remove_from_cl_list(bi); + remove_from_list(bi); +} + +#endif /* BLOCK_CACHE_H */ diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/genexec.pl b/SheepShaver/src/kpx_cpu/src/cpu/ppc/genexec.pl new file mode 100755 index 00000000..5a6841f2 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/genexec.pl @@ -0,0 +1,52 @@ +#!/usr/bin/perl +use strict; + +my (@handlers, @extra_handlers, %templates); + +sub split_arglist($) { + (map { $_ =~ s/\s//g; $_ } split ",", $_[0]); +} + +my @lines = map { split ";", $_ } (); + +my $is_template = 0; +my $e; +foreach (@lines) { + $_ =~ s/;/&\n/g; + if (/^DEFINE_TEMPLATE\((\w+),.+,.+\((.+)\)\)/) { + $is_template = 1; + $e = { name => $1 }; + push @{$e->{args}}, split_arglist $2; + } + elsif ($is_template && /^\}/) { + $is_template = 0; + $templates{$e->{name}} = $e; + } + elsif (/(powerpc_cpu::execute_\w+)<(.+)>/) { + my $h = { name => $1, args => $2 }; + if ($is_template) { + push @{$e->{handlers}}, $h; + } + else { + push @handlers, $h; + } + } + elsif (/template.+decode_(\w+)<(.+)>/) { + my $template = $templates{$1}; + my @template_args = @{$template->{args}}; + my @args = split_arglist $2; + my %vars; + $vars{$template_args[$_]} = $args[$_] foreach (0 .. $#template_args); + foreach my $h (@{$template->{handlers}}) { + my @new_args = map { $vars{$_} || $_ } split_arglist $h->{args}; + push @extra_handlers, { name => $h->{name}, args => join(", ", @new_args) }; + } + } +} + +my %output_handlers; +foreach (@handlers, @extra_handlers) { + my $line = "template void $_->{name}<".join(", ", $_->{args}).">(uint32);"; + print "$line\n" if (!$output_handlers{$line}); + $output_handlers{$line} = 1; +} diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-bitfields.hpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-bitfields.hpp new file mode 100644 index 00000000..45aa41b4 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-bitfields.hpp @@ -0,0 +1,213 @@ +/* + * ppc-bitfields.hpp - Instruction fields + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PPC_BITFIELDS_H +#define PPC_BITFIELDS_H + +#include "ppc-operations.hpp" + +/// +/// Bitfield management +/// + +template< int FB, int FE > +struct static_mask { + enum { value = (0xffffffff >> FB) ^ (0xffffffff >> (FE + 1)) }; +}; + +template< int FB > +struct static_mask { + enum { value = 0xffffffff >> FB }; +}; + +template< int FB, int FE > +struct bit_field { + static inline uint32 mask() { + return static_mask::value; + } + static inline bool test(uint32 value) { + return value & mask(); + } + static inline uint32 extract(uint32 value) { + const uint32 m = mask() >> (31 - FE); + return (value >> (31 - FE)) & m; + } + static inline void insert(uint32 & data, uint32 value) { + const uint32 m = mask(); + data = (data & ~m) | ((value << (31 - FE)) & m); + } +}; + +template< class type, type value > +struct fake_bit_field { + static inline bool test(uint32) { + return value; + } + static inline type extract(uint32) { + return value; + } +}; + +/// +/// Instruction Fields +/// + +// Primary and extended opcode fields +typedef bit_field< 0, 5 > OPCD_field; +typedef bit_field< 21, 30 > XO_10_field; +typedef bit_field< 22, 30 > XO_9_field; +typedef bit_field< 26, 30 > XO_5_field; + +// General purpose registers +typedef bit_field< 11, 15 > rA_field; +typedef bit_field< 16, 20 > rB_field; +typedef bit_field< 6, 10 > rD_field; +typedef bit_field< 6, 10 > rS_field; + +// Floating-point registers +typedef bit_field< 11, 15 > frA_field; +typedef bit_field< 16, 20 > frB_field; +typedef bit_field< 21, 25 > frC_field; +typedef bit_field< 6, 10 > frD_field; +typedef bit_field< 6, 10 > frS_field; + +// Condition registers +typedef bit_field< 11, 15 > crbA_field; +typedef bit_field< 16, 20 > crbB_field; +typedef bit_field< 6, 10 > crbD_field; +typedef bit_field< 6, 8 > crfD_field; +typedef bit_field< 11, 13 > crfS_field; +typedef bit_field< 12, 19 > CRM_field; +typedef bit_field< 7, 14 > FM_field; + +// CR register fields +template< int CRn > struct CR_field : bit_field< 4*CRn+0, 4*CRn+3 > { }; +template< int CRn > struct CR_LT_field : bit_field< 4*CRn+0, 4*CRn+0 > { }; +template< int CRn > struct CR_GT_field : bit_field< 4*CRn+1, 4*CRn+1 > { }; +template< int CRn > struct CR_EQ_field : bit_field< 4*CRn+2, 4*CRn+2 > { }; +template< int CRn > struct CR_SO_field : bit_field< 4*CRn+3, 4*CRn+3 > { }; +template< int CRn > struct CR_UN_field : bit_field< 4*CRn+3, 4*CRn+3 > { }; + +// Aliases used for CR storage optimization +typedef CR_LT_field<7> standalone_CR_LT_field; +typedef CR_GT_field<7> standalone_CR_GT_field; +typedef CR_EQ_field<7> standalone_CR_EQ_field; +typedef CR_SO_field<7> standalone_CR_SO_field; + +// XER register fields +typedef bit_field< 0, 0 > XER_SO_field; +typedef bit_field< 1, 1 > XER_OV_field; +typedef bit_field< 2, 2 > XER_CA_field; +typedef bit_field< 25, 31 > XER_COUNT_field; + +// FPSCR register fields +typedef bit_field< 0, 0 > FPSCR_FX_field; +typedef bit_field< 1, 1 > FPSCR_FEX_field; +typedef bit_field< 2, 2 > FPSCR_VX_field; +typedef bit_field< 3, 3 > FPSCR_OX_field; +typedef bit_field< 4, 4 > FPSCR_UX_field; +typedef bit_field< 5, 5 > FPSCR_ZX_field; +typedef bit_field< 6, 6 > FPSCR_XX_field; +typedef bit_field< 7, 7 > FPSCR_VXSNAN_field; +typedef bit_field< 8, 8 > FPSCR_VXISI_field; +typedef bit_field< 9, 9 > FPSCR_VXIDI_field; +typedef bit_field< 10, 10 > FPSCR_VXZDZ_field; +typedef bit_field< 11, 11 > FPSCR_VXIMZ_field; +typedef bit_field< 12, 12 > FPSCR_VXVC_field; +typedef bit_field< 13, 13 > FPSCR_FR_field; +typedef bit_field< 14, 14 > FPSCR_FI_field; +typedef bit_field< 15, 19 > FPSCR_FPRF_field; +typedef bit_field< 21, 21 > FPSCR_VXSOFT_field; +typedef bit_field< 22, 22 > FPSCR_VXSQRT_field; +typedef bit_field< 23, 23 > FPSCR_VXCVI_field; +typedef bit_field< 24, 24 > FPSCR_VE_field; +typedef bit_field< 25, 25 > FPSCR_OE_field; +typedef bit_field< 26, 26 > FPSCR_UE_field; +typedef bit_field< 27, 27 > FPSCR_ZE_field; +typedef bit_field< 28, 28 > FPSCR_XE_field; +typedef bit_field< 29, 29 > FPSCR_NI_field; +typedef bit_field< 30, 31 > FPSCR_RN_field; +typedef bit_field< 16, 19 > FPSCR_FPCC_field; +typedef bit_field< 15, 15 > FPSCR_FPRF_C_field; // C +typedef bit_field< 16, 16 > FPSCR_FPRF_FL_field; // < +typedef bit_field< 17, 17 > FPSCR_FPRF_FG_field; // > +typedef bit_field< 18, 18 > FPSCR_FPRF_FE_field; // = +typedef bit_field< 19, 19 > FPSCR_FPRF_FU_field; // ? + +// Define variations for branch instructions +typedef bit_field< 30, 30 > AA_field; +typedef bit_field< 31, 31 > LK_field; +typedef bit_field< 16, 29 > BD_field; +typedef bit_field< 11, 15 > BI_field; +typedef bit_field< 6, 10 > BO_field; + +// Helper macros to deal with BO field +#define BO_MAKE(COND, TRUE, DCTR, CTR0) (((COND) ? 0 : 16) | ((TRUE) ? 8 : 0) | ((DCTR) ? 0 : 4) | ((CTR0) ? 2 : 0)) +#define BO_DECREMENT_CTR(BO) (((BO) & 0x04) == 0) +#define BO_BRANCH_IF_CTR_ZERO(BO) (((BO) & 0x02) != 0) +#define BO_CONDITIONAL_BRANCH(BO) (((BO) & 0x10) == 0) +#define BO_BRANCH_IF_TRUE(BO) (((BO) & 0x08) != 0) + +// Define variations for ALU instructions +typedef bit_field< 31, 31 > Rc_field; +typedef bit_field< 21, 21 > OE_field; +typedef bit_field< 21, 25 > MB_field; +typedef bit_field< 26, 30 > ME_field; +typedef bit_field< 16, 20 > NB_field; +typedef bit_field< 16, 20 > SH_field; + +// Immediates +typedef bit_field< 16, 19 > IMM_field; +typedef bit_field< 16, 31 > d_field; +typedef bit_field< 6, 29 > LI_field; +typedef bit_field< 16, 31 > SIMM_field; +typedef bit_field< 16, 31 > UIMM_field; + +// Misc +typedef bit_field< 12, 15 > SR_field; +typedef bit_field< 6, 10 > TO_field; +typedef bit_field< 11, 20 > SPR_field; +typedef bit_field< 11, 20 > TBR_field; + +// Aliases to ease filling in decode table +#define DEFINE_FAKE_FIELD_ALIAS(NAME) \ +typedef fake_bit_field NAME##_0; \ +typedef fake_bit_field NAME##_1 + +#define DEFINE_FIELD_ALIAS(NAME, FIELD) \ +typedef FIELD##_field NAME##_G; \ +DEFINE_FAKE_FIELD_ALIAS(NAME) + +DEFINE_FAKE_FIELD_ALIAS(CA_BIT); +DEFINE_FIELD_ALIAS(RC_BIT, Rc); +DEFINE_FIELD_ALIAS(OE_BIT, OE); +DEFINE_FIELD_ALIAS(AA_BIT, AA); +DEFINE_FIELD_ALIAS(LK_BIT, LK); +DEFINE_FIELD_ALIAS(BO_BIT, BO); +DEFINE_FIELD_ALIAS(BI_BIT, BI); + +#undef DEFINE_FIELD_ALIAS +#undef DEFINE_FAKE_FIELD_ALIAS + +typedef fake_bit_field RA_FIELD_A; // GPR(RA) +typedef rA_field RA_FIELD_G; // RA ? GPR(RA) : 0 +typedef fake_bit_field RA_FIELD_0; // R0 -> 0 + +#endif /* PPC_BITFIELDS_H */ diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-blockinfo.hpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-blockinfo.hpp new file mode 100644 index 00000000..80e28d54 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-blockinfo.hpp @@ -0,0 +1,43 @@ +/* + * ppc-blockinfo.hpp - PowerPC basic block information + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PPC_BLOCKINFO_H +#define PPC_BLOCKINFO_H + +#include "basic-blockinfo.hpp" + +class powerpc_cpu; + +struct powerpc_block_info + : public basic_block_info +{ + typedef void (powerpc_cpu::*execute_fn)(uint32 opcode); + + struct decode_info + { + execute_fn execute; + uint32 opcode; + }; + + uint32 end_pc; + decode_info * di; +}; + +#endif /* PPC_BLOCKINFO_H */ diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-config.hpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-config.hpp new file mode 100644 index 00000000..4c7bb26c --- /dev/null +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-config.hpp @@ -0,0 +1,193 @@ +/* + * ppc-config.hpp - PowerPC core emulator config + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PPC_CONFIG_H +#define PPC_CONFIG_H + +/** + * PPC_NO_BASIC_CPU_BASE + * + * Define to not inherit from basic_cpu, thus removing two + * vtables. Otherwise, access to registers require an extra + * offset from "this" because vtables are stored before other + * regular members. + **/ + +#ifndef PPC_NO_BASIC_CPU_BASE +#undef PPC_NO_BASIC_CPU_BASE +#endif + + +/** + * PPC_NO_STATIC_II_INDEX_TABLE + * + * Define to make sure the ii_index_table[] is a non static + * member so that powerpc_cpu object size is reduced by 64 + * KB. This is only supported for mono CPU configurations. + **/ + +#ifndef PPC_NO_STATIC_II_INDEX_TABLE +#define PPC_NO_STATIC_II_INDEX_TABLE +#endif + + +/** + * PPC_OPCODE_HASH_XO_PRIMARY + * + * Define to hash opcode hash (xo, primary opcode) instead of + * (primary opcode, xo). This simply reduces the computation + * index into instr_info[] table by one operation. + **/ + +#ifndef PPC_OPCODE_HASH_XO_PRIMARY +#define PPC_OPCODE_HASH_XO_PRIMARY +#endif + + +/** + * PPC_NO_FPSCR_UPDATE + * + * Define to not touch to FPSCR register. This is only useful for + * debugging purposes and side-by-side comparision with other + * PowerPC emulators that don't handle the FPSCR register. + **/ + +#ifndef PPC_NO_FPSCR_UPDATE +#define PPC_NO_FPSCR_UPDATE +#endif + + +/** + * PPC_LAZY_PC_UPDATE + * + * Define to update program counter lazily, i.e. update it only + * on branch instructions. On entry of a block, program counter + * is speculatively set to the last instruction of that block. + **/ + +#ifndef PPC_LAZY_PC_UPDATE +#define PPC_LAZY_PC_UPDATE +#endif + + +/** + * PPC_LAZY_CC_UPDATE + * + * Define to update condition code register lazily, i.e. (LT, GT, + * EQ) fields will be computed on-demand from the last recorded + * operation result. (SO) is always copied from the XER register. + * + * This implies PPC_HAVE_SPLIT_CR to be set. See below. + **/ + +#ifndef PPC_LAZY_CC_UPDATE +#undef PPC_LAZY_CC_UPDATE +#endif + + +/** + * PPC_HAVE_SPLIT_CR + * + * Define to split condition register fields into 8 smaller + * aggregates. This is only useful for JIT backends where we + * don't want to bother shift-masking CR values. + **/ + +#ifndef PPC_HAVE_SPLIT_CR +#undef PPC_HAVE_SPLIT_CR +#endif + + +/** + * PPC_NO_DECODE_CACHE + * + * Define to disable the decode cache. This is only useful for + * debugging purposes and side-by-side comparison with other + * PowerPC emulators. + **/ + +#ifndef PPC_NO_DECODE_CACHE +#undef PPC_NO_DECODE_CACHE +#endif + + +/** + * PPC_NO_DECODE_CACHE_UNROLL_EXECUTE + * + * Define to disable decode_cache[] execute loop unrolling. This + * is a manual unrolling as a Duff's device makes things worse. + **/ + +#ifndef PPC_NO_DECODE_CACHE_UNROLL_EXECUTE +#undef PPC_NO_DECODE_CACHE_UNROLL_EXECUTE +#endif + + +/** + * PPC_EXECUTE_DUMP_STATE + * + * Define to dump state after each instruction. This also + * disables the decode cache. + **/ + +#ifndef PPC_EXECUTE_DUMP_STATE +#undef PPC_EXECUTE_DUMP_STATE +#endif + + +/** + * PPC_FLIGHT_RECORDER + * + * Define to enable the flight recorder. If set to 2, the + * complete register state will be recorder after each + * instruction execution. + **/ + +#ifndef PPC_FLIGHT_RECORDER +#undef PPC_FLIGHT_RECORDER +#endif + + +/** + * Sanity checks and features enforcements + **/ + +#ifdef SHEEPSHAVER +#define PPC_NO_BASIC_CPU_BASE +#undef PPC_NO_STATIC_II_INDEX_TABLE +#endif + +#if defined(PPC_FLIGHT_RECORDER) && !defined(PPC_NO_DECODE_CACHE) +#define PPC_NO_DECODE_CACHE +#endif + +#if defined(PPC_EXECUTE_DUMP_STATE) && !defined(PPC_NO_DECODE_CACHE) +#define PPC_NO_DECODE_CACHE +#endif + +#ifdef PPC_NO_DECODE_CACHE +#undef PPC_LAZY_PC_UPDATE +#endif + +#if defined(PPC_LAZY_CC_UPDATE) && !defined(PPC_HAVE_SPLIT_CR) +#define PPC_HAVE_SPLIT_CR +#endif + +#endif /* PPC_CONFIG_H */ diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.cpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.cpp new file mode 100644 index 00000000..47bad2a8 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.cpp @@ -0,0 +1,375 @@ +/* + * ppc-cpu.cpp - PowerPC CPU definition + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sysdeps.h" +#include "vm_alloc.h" +#include "ppc-cpu.hpp" +#include "vm.hpp" + +#if ENABLE_MON +#include "mon.h" +#include "mon_disass.h" +#endif + +#define DEBUG 0 +#include "debug.h" + +void powerpc_cpu::set_register(int id, any_register const & value) +{ + if (id >= powerpc_registers::GPR(0) && id <= powerpc_registers::GPR(31)) { + regs.gpr[id - powerpc_registers::GPR_BASE] = value.i; + return; + } + if (id >= powerpc_registers::FPR(0) && id <= powerpc_registers::FPR(31)) { + regs.fpr[id - powerpc_registers::FPR_BASE] = value.d; + return; + } + switch (id) { + case powerpc_registers::CR: cr().set(value.i); break; + case powerpc_registers::FPSCR: fpscr() = value.i; break; + case powerpc_registers::XER: xer().set(value.i); break; + case powerpc_registers::LR: lr() = value.i; break; + case powerpc_registers::CTR: ctr() = value.i; break; + case powerpc_registers::TBL: tbl() = value.i; break; + case powerpc_registers::TBU: tbu() = value.i; break; + case basic_registers::PC: + case powerpc_registers::PC: pc() = value.i; break; + case basic_registers::SP: + case powerpc_registers::SP: gpr(1)= value.i; break; + default: abort(); break; + } +} + +any_register powerpc_cpu::get_register(int id) +{ + any_register value; + if (id >= powerpc_registers::GPR(0) && id <= powerpc_registers::GPR(31)) { + value.i = regs.gpr[id - powerpc_registers::GPR_BASE]; + return value; + } + if (id >= powerpc_registers::FPR(0) && id <= powerpc_registers::FPR(31)) { + value.d = regs.fpr[id - powerpc_registers::FPR_BASE]; + return value; + } + switch (id) { + case powerpc_registers::CR: value.i = cr().get(); break; + case powerpc_registers::FPSCR: value.i = fpscr(); break; + case powerpc_registers::XER: value.i = xer().get(); break; + case powerpc_registers::LR: value.i = lr(); break; + case powerpc_registers::CTR: value.i = ctr(); break; + case powerpc_registers::TBL: value.i = tbl(); break; + case powerpc_registers::TBU: value.i = tbu(); break; + case basic_registers::PC: + case powerpc_registers::PC: value.i = pc(); break; + case basic_registers::SP: + case powerpc_registers::SP: value.i = gpr(1); break; + default: abort(); break; + } + return value; +} + +uint32 powerpc_registers::reserve_valid = 0; +uint32 powerpc_registers::reserve_addr = 0; +uint32 powerpc_registers::reserve_data = 0; + +void powerpc_cpu::init_registers() +{ + for (int i = 0; i < 32; i++) { + gpr(i) = 0; + fpr(i) = 0; + } + cr().set(0); + fpscr() = 0; + xer().set(0); + lr() = 0; + ctr() = 0; + pc() = 0; + tbl() = 0; + tbu() = 0; +} + +void powerpc_cpu::init_flight_recorder() +{ +#if PPC_FLIGHT_RECORDER + log_ptr = 0; + log_ptr_wrapped = false; +#endif +} + +#if PPC_FLIGHT_RECORDER +void powerpc_cpu::record_step(uint32 opcode) +{ +#if PPC_FLIGHT_RECORDER + log[log_ptr].pc = pc(); + log[log_ptr].opcode = opcode; +#if PPC_FLIGHT_RECORDER >= 2 + for (int i = 0; i < 32; i++) { + log[log_ptr].r[i] = gpr(i); + log[log_ptr].fr[i] = fpr(i); + } + log[log_ptr].lr = lr(); + log[log_ptr].ctr = ctr(); + log[log_ptr].cr = cr().get(); + log[log_ptr].xer = xer().get(); + log[log_ptr].fpscr = fpscr(); +#endif + log_ptr++; + if (log_ptr == LOG_SIZE) { + log_ptr = 0; + log_ptr_wrapped = true; + } +#endif +} + +void powerpc_cpu::dump_log(const char *filename) +{ + if (filename == NULL) + filename = "ppc.log"; + + FILE *f = fopen(filename, "w"); + if (f == NULL) + return; + + int start_ptr = 0; + int log_size = log_ptr; + if (log_ptr_wrapped) { + start_ptr = log_ptr; + log_size = LOG_SIZE; + } + + for (int i = 0; i < log_size; i++) { + int j = (i + start_ptr) % LOG_SIZE; +#if PPC_FLIGHT_RECORDER >= 2 + fprintf(f, " pc %08x lr %08x ctr %08x cr %08x xer %08x ", log[j].pc, log[j].lr, log[j].ctr, log[j].cr, log[j].xer); + fprintf(f, " r0 %08x r1 %08x r2 %08x r3 %08x ", log[j].r[0], log[j].r[1], log[j].r[2], log[j].r[3]); + fprintf(f, " r4 %08x r5 %08x r6 %08x r7 %08x ", log[j].r[4], log[j].r[5], log[j].r[6], log[j].r[7]); + fprintf(f, " r8 %08x r9 %08x r10 %08x r11 %08x ", log[j].r[8], log[j].r[9], log[j].r[10], log[j].r[11]); + fprintf(f, "r12 %08x r13 %08x r14 %08x r15 %08x ", log[j].r[12], log[j].r[13], log[j].r[14], log[j].r[15]); + fprintf(f, "r16 %08x r17 %08x r18 %08x r19 %08x ", log[j].r[16], log[j].r[17], log[j].r[18], log[j].r[19]); + fprintf(f, "r20 %08x r21 %08x r22 %08x r23 %08x ", log[j].r[20], log[j].r[21], log[j].r[22], log[j].r[23]); + fprintf(f, "r24 %08x r25 %08x r26 %08x r27 %08x ", log[j].r[24], log[j].r[25], log[j].r[26], log[j].r[27]); + fprintf(f, "r28 %08x r29 %08x r30 %08x r31 %08x\n", log[j].r[28], log[j].r[29], log[j].r[30], log[j].r[31]); + fprintf(f, "opcode %08x\n", log[j].opcode); +#else + fprintf(f, " pc %08x opc %08x | ", log[j].pc, log[j].opcode); +#if !ENABLE_MON + fprintf(f, "\n"); +#endif +#endif +#if ENABLE_MON + disass_ppc(f, log[j].pc, log[j].opcode); +#endif + } + fclose(f); +} +#endif + +void powerpc_cpu::initialize() +{ + init_flight_recorder(); + init_decoder(); + init_registers(); + init_decode_cache(); + + // Init syscalls handler + execute_do_syscall = NULL; + + // Init field2mask + for (int i = 0; i < 256; i++) { + uint32 mask = 0; + if (i & 0x01) mask |= 0x0000000f; + if (i & 0x02) mask |= 0x000000f0; + if (i & 0x04) mask |= 0x00000f00; + if (i & 0x08) mask |= 0x0000f000; + if (i & 0x10) mask |= 0x000f0000; + if (i & 0x20) mask |= 0x00f00000; + if (i & 0x40) mask |= 0x0f000000; + if (i & 0x80) mask |= 0xf0000000; + field2mask[i] = mask; + } + +#if ENABLE_MON + mon_init(); +#endif +} + +powerpc_cpu::~powerpc_cpu() +{ + kill_decode_cache(); + +#if ENABLE_MON + mon_exit(); +#endif +} + +void powerpc_cpu::dump_registers() +{ + fprintf(stderr, " r0 %08x r1 %08x r2 %08x r3 %08x\n", gpr(0), gpr(1), gpr(2), gpr(3)); + fprintf(stderr, " r4 %08x r5 %08x r6 %08x r7 %08x\n", gpr(4), gpr(5), gpr(6), gpr(7)); + fprintf(stderr, " r8 %08x r9 %08x r10 %08x r11 %08x\n", gpr(8), gpr(9), gpr(10), gpr(11)); + fprintf(stderr, "r12 %08x r13 %08x r14 %08x r15 %08x\n", gpr(12), gpr(13), gpr(14), gpr(15)); + fprintf(stderr, "r16 %08x r17 %08x r18 %08x r19 %08x\n", gpr(16), gpr(17), gpr(18), gpr(19)); + fprintf(stderr, "r20 %08x r21 %08x r22 %08x r23 %08x\n", gpr(20), gpr(21), gpr(22), gpr(23)); + fprintf(stderr, "r24 %08x r25 %08x r26 %08x r27 %08x\n", gpr(24), gpr(25), gpr(26), gpr(27)); + fprintf(stderr, "r28 %08x r29 %08x r30 %08x r31 %08x\n", gpr(28), gpr(29), gpr(30), gpr(31)); + fprintf(stderr, " f0 %02.5f f1 %02.5f f2 %02.5f f3 %02.5f\n", fpr(0), fpr(1), fpr(2), fpr(3)); + fprintf(stderr, " f4 %02.5f f5 %02.5f f6 %02.5f f7 %02.5f\n", fpr(4), fpr(5), fpr(6), fpr(7)); + fprintf(stderr, " f8 %02.5f f9 %02.5f f10 %02.5f f11 %02.5f\n", fpr(8), fpr(9), fpr(10), fpr(11)); + fprintf(stderr, "f12 %02.5f f13 %02.5f f14 %02.5f f15 %02.5f\n", fpr(12), fpr(13), fpr(14), fpr(15)); + fprintf(stderr, "f16 %02.5f f17 %02.5f f18 %02.5f f19 %02.5f\n", fpr(16), fpr(17), fpr(18), fpr(19)); + fprintf(stderr, "f20 %02.5f f21 %02.5f f22 %02.5f f23 %02.5f\n", fpr(20), fpr(21), fpr(22), fpr(23)); + fprintf(stderr, "f24 %02.5f f25 %02.5f f26 %02.5f f27 %02.5f\n", fpr(24), fpr(25), fpr(26), fpr(27)); + fprintf(stderr, "f28 %02.5f f29 %02.5f f30 %02.5f f31 %02.5f\n", fpr(28), fpr(29), fpr(30), fpr(31)); + fprintf(stderr, " lr %08x ctr %08x cr %08x xer %08x\n", lr(), ctr(), cr().get(), xer().get()); + fprintf(stderr, " pc %08x fpscr %08x\n", pc(), fpscr()); + fflush(stderr); +} + +void powerpc_cpu::execute() +{ +#ifdef PPC_NO_DECODE_CACHE + for (;;) { + uint32 opcode = vm_read_memory_4(pc()); + const instr_info_t *ii = decode(opcode); +// D(bug("[%08x]-> %08x: %s\n", pc(), opcode, ii->name)); +#ifdef PPC_EXECUTE_DUMP_STATE + fprintf(stderr, "[%08x]-> %08x\n", pc(), opcode); +#endif + if (logging) + record_step(opcode); + assert(ii->execute != NULL); + (this->*(ii->execute))(opcode); +#ifdef PPC_EXECUTE_DUMP_STATE + dump_registers(); +#endif + } +#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); + di->opcode = opcode; + di->execute = ii->execute; + if (++di >= decode_cache_end_p) { + // Invalidate cache and move current code to start + invalidate_cache_all(); + 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; +// bi->count = 10; + block_cache.add_to_cl_list(bi); + block_cache.add_to_active_list(bi); + decode_cache_p += bi->size; + + // Execute all cached blocks + 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 + + if (0 && --bi->count <= 0) { +#if ENABLE_MON + printf("Recompile block: %08x (%d insns)\n", bi->pc, bi->size); + uint32 dpc = bi->pc; + for (int i = 0; i < bi->size; i++, dpc += 4) + disass_ppc(stdout, dpc, bi->di[i].opcode); +#endif + bi->count = 0x7fffffff; + } + if ((bi->pc != pc()) && ((bi = block_cache.find(pc())) == NULL)) + break; + } + } +#endif +} + +void powerpc_cpu::init_decode_cache() +{ +#ifndef PPC_NO_DECODE_CACHE + decode_cache = (block_info::decode_info *)vm_acquire(DECODE_CACHE_SIZE); + if (decode_cache == VM_MAP_FAILED) { + fprintf(stderr, "powerpc_cpu: Could not allocate decode cache\n"); + abort(); + } + + D(bug("powerpc_cpu: Allocated decode cache: %d KB at %p\n", DECODE_CACHE_SIZE / 1024, decode_cache)); + decode_cache_p = decode_cache; + decode_cache_end_p = decode_cache + DECODE_CACHE_MAX_ENTRIES; + + block_cache.initialize(); +#endif +} + +void powerpc_cpu::kill_decode_cache() +{ +#ifndef PPC_NO_DECODE_CACHE + vm_release(decode_cache, DECODE_CACHE_SIZE); +#endif +} + +#ifndef PPC_NO_DECODE_CACHE +void powerpc_cpu::invalidate_cache_all() +{ + block_cache.clear(); + block_cache.initialize(); + decode_cache_p = decode_cache; +} + +void powerpc_cpu::invalidate_cache_lazy() +{ + // TODO: implement this! + invalidate_cache_all(); +} +#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 new file mode 100644 index 00000000..580dfb6d --- /dev/null +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp @@ -0,0 +1,331 @@ +/* + * ppc-cpu.hpp - PowerPC CPU definition + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PPC_CPU_H +#define PPC_CPU_H + +#include "basic-cpu.hpp" +#include "cpu/block-cache.hpp" +#include "cpu/ppc/ppc-config.hpp" +#include "cpu/ppc/ppc-bitfields.hpp" +#include "cpu/ppc/ppc-blockinfo.hpp" +#include "cpu/ppc/ppc-registers.hpp" +#include + +class powerpc_cpu +#ifndef PPC_NO_BASIC_CPU_BASE + : public basic_cpu +#endif +{ + powerpc_registers regs; + +protected: + + powerpc_cr_register & cr() { return regs.cr; } + powerpc_cr_register const & cr() const { return regs.cr; } + powerpc_xer_register & xer() { return regs.xer; } + powerpc_xer_register const & xer() const { return regs.xer; } + + uint32 & fpscr() { return regs.fpscr; } + uint32 fpscr() const { return regs.fpscr; } + uint32 & lr() { return regs.lr; } + uint32 lr() const { return regs.lr; } + uint32 & ctr() { return regs.ctr; } + uint32 ctr() const { return regs.ctr; } + uint32 & pc() { return regs.pc; } + uint32 pc() const { return regs.pc; } +#ifdef PPC_LAZY_PC_UPDATE + void increment_pc(int o) { } +#else + void increment_pc(int o) { pc() += o; } +#endif + uint32 & tbl() { return regs.tbl; } + uint32 tbl() const { return regs.tbl; } + uint32 & tbu() { return regs.tbu; } + uint32 tbu() const { return regs.tbu; } + + friend class pc_operand; + friend class lr_operand; + friend class ctr_operand; + friend class cr_operand; + template< class field > friend class xer_operand; + template< class field > friend class fpscr_operand; + +public: + + uint32 & gpr(int i) { return regs.gpr[i]; } + uint32 gpr(int i) const { return regs.gpr[i]; } + double & fpr(int i) { return regs.fpr[i]; } + double fpr(int i) const { return regs.fpr[i]; } + +protected: + + // Condition codes management + void record_cr(int crfd, int32 value) + { cr().compute(crfd, value); cr().set_so(crfd, xer().get_so()); } + void record_cr0(int32 value) + { record_cr(0, value); } + void record_cr1() + { cr().set((cr().get() & ~CR_field<1>::mask()) | ((fpscr() >> 4) & 0x0f000000)); } + + void fp_classify(double x); + +protected: + + // Flight recorder + struct rec_step { +#if PPC_FLIGHT_RECORDER >= 2 + uint32 r[32]; + double fr[32]; + uint32 lr, ctr; + uint32 cr, xer; + uint32 fpscr; +#endif + uint32 pc; + uint32 opcode; + }; + + // Instruction formats + enum instr_format_t { + INVALID_form = 0, + A_form, + B_form, + D_form, DS_form, + I_form, + M_form, + MD_form, MDS_form, + SC_form, + X_form, + XFL_form, XFX_form, XL_form, XO_form, XS_form + }; + + // Control flow types + enum control_flow_t { + CFLOW_NORMAL = 0, + CFLOW_BRANCH = 1, + CFLOW_JUMP = 2, + CFLOW_TRAP = 4, + CFLOW_CONST_JUMP = 8, +#ifdef PPC_LAZY_PC_UPDATE + CFLOW_END_BLOCK = 7 +#else + // Instructions that can trap don't mark the end of a block + CFLOW_END_BLOCK = 3 +#endif + }; + + // Callbacks associated with each instruction + typedef void (powerpc_cpu::*execute_fn)(uint32 opcode); + + // Instruction information structure + struct instr_info_t { + char name[8]; // Mnemonic + execute_fn execute; // Semantic routine for this instruction + execute_fn execute_rc; // variant to record computed value + uint16 format; // Instruction format (XO-form, D-form, etc.) + uint16 opcode; // Primary opcode + uint16 xo; // Extended opcode + uint16 cflow; // Mask of control flow information + }; + +private: + + // Flight recorder data + static const int LOG_SIZE = 32768; +#if PPC_FLIGHT_RECORDER + rec_step log[LOG_SIZE]; + bool logging; + int log_ptr; + bool log_ptr_wrapped; +#else + static const bool logging = false; +#endif + void record_step(uint32 opcode); + + // Syscall callback must return TRUE if no error occurred + typedef bool (*syscall_fn)(powerpc_cpu *cpu); + syscall_fn execute_do_syscall; + +#ifdef PPC_NO_STATIC_II_INDEX_TABLE +#define PPC_STATIC_II_TABLE +#else +#define PPC_STATIC_II_TABLE static +#endif + + static const instr_info_t powerpc_ii_table[]; + PPC_STATIC_II_TABLE std::vector ii_table; + typedef uint8 ii_index_t; + static const int II_INDEX_TABLE_SIZE = 0x10000; + PPC_STATIC_II_TABLE ii_index_t ii_index_table[II_INDEX_TABLE_SIZE]; + +#ifdef PPC_OPCODE_HASH_XO_PRIMARY + uint32 make_ii_index(uint32 opcode, uint32 xo) { return opcode | (xo << 6); } + uint32 get_ii_index(uint32 opcode) { return (opcode >> 26) | ((opcode & 0x7fe) << 5); } +#else + uint32 make_ii_index(uint32 opcode, uint32 xo) { return opcode << 10 | xo; } + uint32 get_ii_index(uint32 opcode) { return ((opcode >> 16) & 0xfc00) | ((opcode >> 1) & 0x3ff); } +#endif + + // Convert 8-bit field mask (e.g. mtcrf) to bit mask + uint32 field2mask[256]; + +public: + + // Initialization & finalization +#ifdef PPC_NO_BASIC_CPU_BASE + powerpc_cpu() +#else + powerpc_cpu(task_struct *parent_task) + : basic_cpu(parent_task) +#endif + { initialize(); } + void initialize(); + ~powerpc_cpu(); + + // Handle flight recorder +#if PPC_FLIGHT_RECORDER + void start_log() { logging = true; } + void stop_log() { logging = false; } + void dump_log(const char *filename = NULL); +#else + void start_log() { } + void stop_log() { } + void dump_log(const char *filename = NULL) { } +#endif + + // Dump registers + void dump_registers(); + + // Start emulation loop + void execute(); + + // Set VALUE to register ID + void set_register(int id, any_register const & value); + + // Get register ID + any_register get_register(int id); + + // Set syscall callback + void set_syscall_callback(syscall_fn fn) { execute_do_syscall = fn; } + +protected: + + // Init decoder with one instruction info + void init_decoder_entry(const instr_info_t * ii); + +private: + + // Initializers & destructors + void init_flight_recorder(); + void init_registers(); + void init_decoder(); + void init_decode_cache(); + void kill_decode_cache(); + + // Get instruction info for opcode + const instr_info_t *decode(uint32 opcode) { + return &ii_table[ii_index_table[get_ii_index(opcode)]]; + } + + // Decode Cache + typedef powerpc_block_info block_info; + block_cache< block_info, lazy_allocator > block_cache; + + static const uint32 DECODE_CACHE_MAX_ENTRIES = 20000; + static const uint32 DECODE_CACHE_SIZE = DECODE_CACHE_MAX_ENTRIES * sizeof(block_info::decode_info); + block_info::decode_info * decode_cache; + block_info::decode_info * decode_cache_p; + block_info::decode_info * decode_cache_end_p; + + void invalidate_cache_all(); + void invalidate_cache_lazy(); + + // Instruction handlers + void execute_nop(uint32 opcode); + void execute_illegal(uint32 opcode); + template< class RA, class RB, class RC, class CA, class OE, class Rc > + void execute_addition(uint32 opcode); + template< class OP, class RD, class RA, class RB, class RC, class OE, class Rc > + void execute_generic_arith(uint32 opcode); + template< class PC, class BO, class DP, class AA, class LK > + void execute_branch(uint32 opcode); + template< class RB, typename CT > + void execute_compare(uint32 opcode); + template< class OP > + void execute_cr_op(uint32 opcode); + template< bool SB, class OE, class Rc > + void execute_divide(uint32 opcode); + template< class OP, class RD, class RA, class RB, class RC, class Rc, bool FPSCR > + void execute_fp_arith(uint32 opcode); + template< class OP, class RA, class RB, bool LD, int SZ, bool UP, bool RX > + void execute_loadstore(uint32 opcode); + template< class RA, class DP, bool LD > + void execute_loadstore_multiple(uint32 opcode); + template< class RA, bool IM, class NB > + void execute_load_string(uint32 opcode); + template< class RA, bool IM, class NB > + void execute_store_string(uint32 opcode); + template< class RA > + void execute_lwarx(uint32 opcode); + template< class RA > + void execute_stwcx(uint32 opcode); + void execute_mcrf(uint32 opcode); + void execute_mtcrf(uint32 opcode); + template< class FM, class RB, class Rc > + void execute_mtfsf(uint32 opcode); + template< class RB, class Rc > + void execute_mtfsfi(uint32 opcode); + template< class RB, class Rc > + void execute_mtfsb(uint32 opcode); + template< bool HI, bool SB, class OE, class Rc > + void execute_multiply(uint32 opcode); + template< class Rc > + void execute_mffs(uint32 opcode); + void execute_mfmsr(uint32 opcode); + template< class SPR > + void execute_mfspr(uint32 opcode); + template< class TBR > + void execute_mftbr(uint32 opcode); + template< class SPR > + void execute_mtspr(uint32 opcode); + template< class SH, class MA, class Rc > + void execute_rlwimi(uint32 opcode); + template< class OP, class RD, class RA, class SH, class SO, class CA, class Rc > + void execute_shift(uint32 opcode); + void execute_syscall(uint32 opcode); + template< bool OC > + void execute_fp_compare(uint32 opcode); + template< class RA, class RB, bool LD, bool DB, bool UP > + void execute_fp_loadstore(uint32 opcode); + template< class RN, class Rc > + void execute_fp_int_convert(uint32 opcode); + template< class Rc > + void execute_fp_round(uint32 opcode); + + // Instruction decoders + template< class RA, class RB, class RC, class CA > + execute_fn decode_addition(uint32 opcode); + template< class PC, class DP, class AA > + execute_fn decode_branch(uint32 opcode); + template< class RA, class RS > + execute_fn decode_rlwinm(uint32 opcode); +}; + +#endif /* PPC_CPU_H */ diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-decode.cpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-decode.cpp new file mode 100644 index 00000000..f9dc79b2 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-decode.cpp @@ -0,0 +1,1027 @@ +/* + * ppc-decode.cpp - PowerPC instructions decoder + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ppc-cpu.hpp" +#include "ppc-bitfields.hpp" +#include "ppc-operands.hpp" +#include "ppc-operations.hpp" + +#define DEBUG 0 +#include "debug.h" + +#define EXECUTE_0(HANDLER) \ +&powerpc_cpu::execute_##HANDLER + +#define EXECUTE_1(HANDLER, ARG1) \ +&powerpc_cpu::execute_##HANDLER + +#define EXECUTE_2(HANDLER, ARG1, ARG2) \ +&powerpc_cpu::execute_##HANDLER + +#define EXECUTE_3(HANDLER, ARG1, ARG2, ARG3) \ +&powerpc_cpu::execute_##HANDLER + +#define EXECUTE_4(HANDLER, ARG1, ARG2, ARG3, ARG4) \ +&powerpc_cpu::execute_##HANDLER + +#define EXECUTE_7(HANDLER, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7) \ +&powerpc_cpu::execute_##HANDLER + +#define EXECUTE_ADDITION(RA, RB, RC, CA, OE, Rc) \ +&powerpc_cpu::execute_addition + +#define EXECUTE_GENERIC_ARITH(OP, RD, RA, RB, RC, OE, Rc) \ +&powerpc_cpu::execute_generic_arith + +#define EXECUTE_BRANCH(PC, BO, DP, AA, LK) \ +&powerpc_cpu::execute_branch + +#define EXECUTE_COMPARE(RB, CT) \ +&powerpc_cpu::execute_compare + +#define EXECUTE_CR_OP(OP) \ +&powerpc_cpu::execute_cr_op + +#define EXECUTE_FP_ARITH(OP, RD, RA, RB, RC, Rc, FPSCR) \ +&powerpc_cpu::execute_fp_arith + +#define EXECUTE_LOADSTORE(OP, RA, RB, LD, SZ, UP, RX) \ +&powerpc_cpu::execute_loadstore + +#define EXECUTE_LOADSTORE_MULTIPLE(RA, DP, LD) \ +&powerpc_cpu::execute_loadstore_multiple + +#define EXECUTE_LOAD_STRING(RA, IM, NB) \ +&powerpc_cpu::execute_load_string + +#define EXECUTE_STORE_STRING(RA, IM, NB) \ +&powerpc_cpu::execute_store_string + +#define EXECUTE_SHIFT(OP, RD, RA, SH, SO, CA, Rc) \ +&powerpc_cpu::execute_shift + +#define EXECUTE_FP_LOADSTORE(RA, RB, LD, DB, UP) \ +&powerpc_cpu::execute_fp_loadstore + +const powerpc_cpu::instr_info_t powerpc_cpu::powerpc_ii_table[] = { + { "invalid", + EXECUTE_0(illegal), + NULL, + INVALID_form, 0, 0, CFLOW_TRAP + }, + { "add", + EXECUTE_ADDITION(RA, RB, NONE, CA_BIT_0, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 266, CFLOW_NORMAL + }, + { "addc", + EXECUTE_ADDITION(RA, RB, NONE, CA_BIT_1, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 10, CFLOW_NORMAL + }, + { "adde", + EXECUTE_ADDITION(RA, RB, XER_CA, CA_BIT_1, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 138, CFLOW_NORMAL + }, + { "addi", + EXECUTE_ADDITION(RA_or_0, SIMM, NONE, CA_BIT_0, OE_BIT_0, RC_BIT_0), + NULL, + D_form, 14, 0, CFLOW_NORMAL + }, + { "addic", + EXECUTE_ADDITION(RA, SIMM, NONE, CA_BIT_1, OE_BIT_0, RC_BIT_0), + NULL, + D_form, 12, 0, CFLOW_NORMAL + }, + { "addic.", + EXECUTE_ADDITION(RA, SIMM, NONE, CA_BIT_1, OE_BIT_0, RC_BIT_1), + NULL, + D_form, 13, 0, CFLOW_NORMAL + }, + { "addis", + EXECUTE_ADDITION(RA_or_0, SIMM_shifted, NONE, CA_BIT_0, OE_BIT_0, RC_BIT_0), + NULL, + D_form, 15, 0, CFLOW_NORMAL + }, + { "addme", + EXECUTE_ADDITION(RA_or_0, MINUS_ONE, XER_CA, CA_BIT_1, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 234, CFLOW_NORMAL + }, + { "addze", + EXECUTE_ADDITION(RA_or_0, ZERO, XER_CA, CA_BIT_1, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 202, CFLOW_NORMAL + }, + { "and", + EXECUTE_GENERIC_ARITH(and, RA, RS, RB, NONE, OE_BIT_0, RC_BIT_G), + NULL, + X_form, 31, 28, CFLOW_NORMAL + }, + { "andc", + EXECUTE_GENERIC_ARITH(andc, RA, RS, RB, NONE, OE_BIT_0, RC_BIT_G), + NULL, + X_form, 31, 60, CFLOW_NORMAL + }, + { "andi.", + EXECUTE_GENERIC_ARITH(and, RA, RS, UIMM, NONE, OE_BIT_0, RC_BIT_1), + NULL, + D_form, 28, 0, CFLOW_NORMAL + }, + { "andis.", + EXECUTE_GENERIC_ARITH(and, RA, RS, UIMM_shifted, NONE, OE_BIT_0, RC_BIT_1), + NULL, + D_form, 29, 0, CFLOW_NORMAL + }, + { "b", + EXECUTE_BRANCH(PC, immediate_value, LI, AA_BIT_G, LK_BIT_G), + NULL, + I_form, 18, 0, CFLOW_BRANCH + }, + { "bc", + EXECUTE_BRANCH(PC, operand_BO, BD, AA_BIT_G, LK_BIT_G), + NULL, + B_form, 16, 0, CFLOW_BRANCH + }, + { "bcctr", + EXECUTE_BRANCH(CTR, operand_BO, ZERO, AA_BIT_0, LK_BIT_G), + NULL, + XL_form, 19, 528, CFLOW_BRANCH + }, + { "bclr", + EXECUTE_BRANCH(LR, operand_BO, ZERO, AA_BIT_0, LK_BIT_G), + NULL, + XL_form, 19, 16, CFLOW_BRANCH + }, + { "cmp", + EXECUTE_COMPARE(RB, int32), + NULL, + X_form, 31, 0, CFLOW_NORMAL + }, + { "cmpi", + EXECUTE_COMPARE(SIMM, int32), + NULL, + D_form, 11, 0, CFLOW_NORMAL + }, + { "cmpl", + EXECUTE_COMPARE(RB, uint32), + NULL, + X_form, 31, 32, CFLOW_NORMAL + }, + { "cmpli", + EXECUTE_COMPARE(UIMM, uint32), + NULL, + D_form, 10, 0, CFLOW_NORMAL + }, + { "cntlzw", + EXECUTE_GENERIC_ARITH(cntlzw, RA, RS, NONE, NONE, OE_BIT_0, RC_BIT_G), + NULL, + X_form, 31, 26, CFLOW_NORMAL + }, + { "crand", + EXECUTE_CR_OP(and), + NULL, + XL_form, 19, 257, CFLOW_NORMAL + }, + { "crandc", + EXECUTE_CR_OP(andc), + NULL, + XL_form, 19, 129, CFLOW_NORMAL + }, + { "creqv", + EXECUTE_CR_OP(eqv), + NULL, + XL_form, 19, 289, CFLOW_NORMAL + }, + { "crnand", + EXECUTE_CR_OP(nand), + NULL, + XL_form, 19, 225, CFLOW_NORMAL + }, + { "crnor", + EXECUTE_CR_OP(nor), + NULL, + XL_form, 19, 33, CFLOW_NORMAL + }, + { "cror", + EXECUTE_CR_OP(or), + NULL, + XL_form, 19, 449, CFLOW_NORMAL + }, + { "crorc", + EXECUTE_CR_OP(orc), + NULL, + XL_form, 19, 417, CFLOW_NORMAL + }, + { "crxor", + EXECUTE_CR_OP(xor), + NULL, + XL_form, 19, 193, CFLOW_NORMAL + }, +#if 1 + // FIXME: handle translation cache + { "dcba", + EXECUTE_0(nop), + NULL, + X_form, 31, 758, CFLOW_NORMAL + }, + { "dcbf", + EXECUTE_0(nop), + NULL, + X_form, 31, 86, CFLOW_NORMAL + }, + { "dcbi", + EXECUTE_0(nop), + NULL, + X_form, 31, 470, CFLOW_NORMAL + }, + { "dcbst", + EXECUTE_0(nop), + NULL, + X_form, 31, 54, CFLOW_NORMAL + }, + { "dcbt", + EXECUTE_0(nop), + NULL, + X_form, 31, 278, CFLOW_NORMAL + }, + { "dcbtst", + EXECUTE_0(nop), + NULL, + X_form, 31, 246, CFLOW_NORMAL + }, + { "dcbz", + EXECUTE_0(nop), + NULL, + X_form, 31, 1014, CFLOW_NORMAL + }, +#endif + { "divw", + EXECUTE_3(divide, true, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 491, CFLOW_NORMAL + }, + { "divwu", + EXECUTE_3(divide, false, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 459, CFLOW_NORMAL + }, +#if 1 + { "eciwx", + EXECUTE_0(nop), + NULL, + X_form, 31, 310, CFLOW_NORMAL + }, + { "ecowx", + EXECUTE_0(nop), + NULL, + X_form, 31, 438, CFLOW_NORMAL + }, + { "eieio", + EXECUTE_0(nop), + NULL, + X_form, 31, 854, CFLOW_NORMAL + }, +#endif + { "eqv", + EXECUTE_GENERIC_ARITH(eqv, RA, RS, RB, NONE, OE_BIT_0, RC_BIT_G), + NULL, + X_form, 31, 284, CFLOW_NORMAL + }, + { "extsb", + EXECUTE_GENERIC_ARITH(sign_extend_8_32, RA, RS, NONE, NONE, OE_BIT_0, RC_BIT_G), + NULL, + X_form, 31, 954, CFLOW_NORMAL + }, + { "extsh", + EXECUTE_GENERIC_ARITH(sign_extend_16_32, RA, RS, NONE, NONE, OE_BIT_0, RC_BIT_G), + NULL, + X_form, 31, 922, CFLOW_NORMAL + }, + { "fabs", + EXECUTE_FP_ARITH(fabs, RD, RB, NONE, NONE, RC_BIT_G, false), + NULL, + X_form, 63, 264, CFLOW_NORMAL + }, + { "fadd", + EXECUTE_FP_ARITH(fadd, RD, RA, RB, NONE, RC_BIT_G, true), + NULL, + A_form, 63, 21, CFLOW_NORMAL + }, + { "fadds", + EXECUTE_FP_ARITH(fadds, RD, RA, RB, NONE, RC_BIT_G, true), + NULL, + A_form, 59, 21, CFLOW_NORMAL + }, + { "fcmpo", + EXECUTE_1(fp_compare, true), + NULL, + X_form, 63, 32, CFLOW_NORMAL + }, + { "fcmpu", + EXECUTE_1(fp_compare, false), + NULL, + X_form, 63, 0, CFLOW_NORMAL + }, + { "fctiw", + EXECUTE_2(fp_int_convert, operand_FPSCR_RN, RC_BIT_G), + NULL, + X_form, 63, 14, CFLOW_NORMAL + }, + { "fctiwz", + EXECUTE_2(fp_int_convert, operand_ONE, RC_BIT_G), + NULL, + X_form, 63, 15, CFLOW_NORMAL + }, + { "fdiv", + EXECUTE_FP_ARITH(fdiv, RD, RA, RB, NONE, RC_BIT_G, true), + NULL, + A_form, 63, 18, CFLOW_NORMAL + }, + { "fdivs", + EXECUTE_FP_ARITH(fdivs, RD, RA, RB, NONE, RC_BIT_G, true), + NULL, + A_form, 59, 18, CFLOW_NORMAL + }, + { "fmadd", + EXECUTE_FP_ARITH(fmadd, RD, RA, RC, RB, RC_BIT_G, true), + NULL, + A_form, 63, 29, CFLOW_NORMAL + }, + { "fmadds", + EXECUTE_FP_ARITH(fmadds, RD, RA, RC, RB, RC_BIT_G, true), + NULL, + A_form, 59, 29, CFLOW_NORMAL + }, + { "fmr", + EXECUTE_FP_ARITH(fnop, RD, RB, NONE, NONE, RC_BIT_G, false), + NULL, + X_form, 63, 72, CFLOW_NORMAL + }, + { "fmsub", + EXECUTE_FP_ARITH(fmsub, RD, RA, RC, RB, RC_BIT_G, true), + NULL, + A_form, 63, 28, CFLOW_NORMAL + }, + { "fmsubs", + EXECUTE_FP_ARITH(fmsubs, RD, RA, RC, RB, RC_BIT_G, true), + NULL, + A_form, 59, 28, CFLOW_NORMAL + }, + { "fmul", + EXECUTE_FP_ARITH(fmul, RD, RA, RC, NONE, RC_BIT_G, true), + NULL, + A_form, 63, 25, CFLOW_NORMAL + }, + { "fmuls", + EXECUTE_FP_ARITH(fmuls, RD, RA, RC, NONE, RC_BIT_G, true), + NULL, + A_form, 59, 25, CFLOW_NORMAL + }, + { "fnabs", + EXECUTE_FP_ARITH(fnabs, RD, RB, NONE, NONE, RC_BIT_G, false), + NULL, + X_form, 63, 136, CFLOW_NORMAL + }, + { "fneg", + EXECUTE_FP_ARITH(fneg, RD, RB, NONE, NONE, RC_BIT_G, false), + NULL, + X_form, 63, 40, CFLOW_NORMAL + }, + { "fnmadd", + EXECUTE_FP_ARITH(fnmadd, RD, RA, RC, RB, RC_BIT_G, true), + NULL, + A_form, 63, 31, CFLOW_NORMAL + }, + { "fnmadds", + EXECUTE_FP_ARITH(fnmadds, RD, RA, RC, RB, RC_BIT_G, true), + NULL, + A_form, 59, 31, CFLOW_NORMAL + }, + { "fnmsub", + EXECUTE_FP_ARITH(fnmsub, RD, RA, RC, RB, RC_BIT_G, true), + NULL, + A_form, 63, 30, CFLOW_NORMAL + }, + { "fnmsubs", + EXECUTE_FP_ARITH(fnmsubs, RD, RA, RC, RB, RC_BIT_G, true), + NULL, + A_form, 59, 30, CFLOW_NORMAL + }, + { "frsp", + EXECUTE_1(fp_round, RC_BIT_G), + NULL, + X_form, 63, 12, CFLOW_NORMAL + }, + { "fsub", + EXECUTE_FP_ARITH(fsub, RD, RA, RB, NONE, RC_BIT_G, true), + NULL, + A_form, 63, 20, CFLOW_NORMAL + }, + { "fsubs", + EXECUTE_FP_ARITH(fsubs, RD, RA, RB, NONE, RC_BIT_G, true), + NULL, + A_form, 59, 20, CFLOW_NORMAL + }, + { "nand", + EXECUTE_GENERIC_ARITH(nand, RA, RS, RB, NONE, OE_BIT_0, RC_BIT_G), + NULL, + X_form, 31, 476, CFLOW_NORMAL + }, +#if 1 + { "icbi", + EXECUTE_0(nop), + NULL, + X_form, 31, 982, CFLOW_NORMAL + }, + { "isync", + EXECUTE_0(nop), + NULL, + X_form, 19, 150, CFLOW_NORMAL + }, +#endif + { "lbz", + EXECUTE_LOADSTORE(nop, RA_or_0, D, true, 1, false, false), + NULL, + D_form, 34, 0, CFLOW_NORMAL + }, + { "lbzu", + EXECUTE_LOADSTORE(nop, RA, D, true, 1, true, false), + NULL, + D_form, 35, 0, CFLOW_NORMAL + }, + { "lbzux", + EXECUTE_LOADSTORE(nop, RA, RB, true, 1, true, false), + NULL, + X_form, 31, 119, CFLOW_NORMAL + }, + { "lbzx", + EXECUTE_LOADSTORE(nop, RA_or_0, RB, true, 1, false, false), + NULL, + X_form, 31, 87, CFLOW_NORMAL + }, + { "lfd", + EXECUTE_FP_LOADSTORE(RA_or_0, D, true, true, false), + NULL, + D_form, 50, 0, CFLOW_NORMAL + }, + { "lfdu", + EXECUTE_FP_LOADSTORE(RA, D, true, true, true), + NULL, + D_form, 51, 0, CFLOW_NORMAL + }, + { "lfdux", + EXECUTE_FP_LOADSTORE(RA, RB, true, true, true), + NULL, + X_form, 31, 631, CFLOW_NORMAL + }, + { "lfdx", + EXECUTE_FP_LOADSTORE(RA_or_0, RB, true, true, false), + NULL, + X_form, 31, 599, CFLOW_NORMAL + }, + { "lfs", + EXECUTE_FP_LOADSTORE(RA_or_0, D, true, false, false), + NULL, + D_form, 48, 0, CFLOW_NORMAL + }, + { "lfsu", + EXECUTE_FP_LOADSTORE(RA, D, true, false, true), + NULL, + D_form, 49, 0, CFLOW_NORMAL + }, + { "lfsux", + EXECUTE_FP_LOADSTORE(RA, RB, true, false, true), + NULL, + X_form, 31, 567, CFLOW_NORMAL + }, + { "lfsx", + EXECUTE_FP_LOADSTORE(RA_or_0, RB, true, false, false), + NULL, + X_form, 31, 535, CFLOW_NORMAL + }, + { "lha", + EXECUTE_LOADSTORE(sign_extend_16_32, RA_or_0, D, true, 2, false, false), + NULL, + D_form, 42, 0, CFLOW_NORMAL + }, + { "lhau", + EXECUTE_LOADSTORE(sign_extend_16_32, RA, D, true, 2, true, false), + NULL, + D_form, 43, 0, CFLOW_NORMAL + }, + { "lhaux", + EXECUTE_LOADSTORE(sign_extend_16_32, RA, RB, true, 2, true, false), + NULL, + X_form, 31, 375, CFLOW_NORMAL + }, + { "lhax", + EXECUTE_LOADSTORE(sign_extend_16_32, RA_or_0, RB, true, 2, false, false), + NULL, + X_form, 31, 343, CFLOW_NORMAL + }, + { "lhbrx", + EXECUTE_LOADSTORE(nop, RA_or_0, RB, true, 2, false, true), + NULL, + X_form, 31, 790, CFLOW_NORMAL + }, + { "lhz", + EXECUTE_LOADSTORE(nop, RA_or_0, D, true, 2, false, false), + NULL, + D_form, 40, 0, CFLOW_NORMAL + }, + { "lhzu", + EXECUTE_LOADSTORE(nop, RA, D, true, 2, true, false), + NULL, + D_form, 41, 0, CFLOW_NORMAL + }, + { "lhzux", + EXECUTE_LOADSTORE(nop, RA, RB, true, 2, true, false), + NULL, + X_form, 31, 311, CFLOW_NORMAL + }, + { "lhzx", + EXECUTE_LOADSTORE(nop, RA_or_0, RB, true, 2, false, false), + NULL, + X_form, 31, 279, CFLOW_NORMAL + }, + { "lmw", + EXECUTE_LOADSTORE_MULTIPLE(RA_or_0, D, true), + NULL, + D_form, 46, 0, CFLOW_NORMAL + }, + { "lswi", + EXECUTE_LOAD_STRING(RA_or_0, true, NB), + NULL, + X_form, 31, 597, CFLOW_NORMAL + }, + { "lswx", + EXECUTE_LOAD_STRING(RA_or_0, false, XER_COUNT), + NULL, + X_form, 31, 533, CFLOW_NORMAL + }, + { "lwarx", + EXECUTE_1(lwarx, operand_RA_or_0), + NULL, + X_form, 31, 20, CFLOW_NORMAL + }, + { "lwbrx", + EXECUTE_LOADSTORE(nop, RA_or_0, RB, true, 4, false, true), + NULL, + X_form, 31, 534, CFLOW_NORMAL + }, + { "lwz", + EXECUTE_LOADSTORE(nop, RA_or_0, D, true, 4, false, false), + NULL, + D_form, 32, 0, CFLOW_NORMAL + }, + { "lwzu", + EXECUTE_LOADSTORE(nop, RA, D, true, 4, true, false), + NULL, + D_form, 33, 0, CFLOW_NORMAL + }, + { "lwzux", + EXECUTE_LOADSTORE(nop, RA, RB, true, 4, true, false), + NULL, + X_form, 31, 55, CFLOW_NORMAL + }, + { "lwzx", + EXECUTE_LOADSTORE(nop, RA_or_0, RB, true, 4, false, false), + NULL, + X_form, 31, 23, CFLOW_NORMAL + }, + { "mcrf", + EXECUTE_0(mcrf), + NULL, + XL_form, 19, 0, CFLOW_NORMAL + }, + { "mfcr", + EXECUTE_GENERIC_ARITH(nop, RD, CR, NONE, NONE, OE_BIT_0, RC_BIT_0), + NULL, + X_form, 31, 19, CFLOW_NORMAL + }, + { "mffs", + EXECUTE_1(mffs, RC_BIT_G), + NULL, + X_form, 63, 583, CFLOW_NORMAL + }, + { "mfmsr", + EXECUTE_0(mfmsr), + NULL, + X_form, 31, 83, CFLOW_NORMAL + }, + { "mfspr", + EXECUTE_1(mfspr, operand_SPR), + NULL, + XFX_form, 31, 339, CFLOW_NORMAL + }, + { "mftb", + EXECUTE_1(mftbr, operand_TBR), + NULL, + XFX_form, 31, 371, CFLOW_NORMAL + }, + { "mtcrf", + EXECUTE_0(mtcrf), + NULL, + XFX_form, 31, 144, CFLOW_NORMAL + }, + { "mtfsb0", + EXECUTE_2(mtfsb, immediate_value<0>, RC_BIT_G), + NULL, + X_form, 63, 70, CFLOW_NORMAL + }, + { "mtfsb1", + EXECUTE_2(mtfsb, immediate_value<1>, RC_BIT_G), + NULL, + X_form, 63, 38, CFLOW_NORMAL + }, + { "mtfsf", + EXECUTE_3(mtfsf, operand_FM, operand_RB, RC_BIT_G), + NULL, + XFL_form, 63, 711, CFLOW_NORMAL + }, + { "mtfsfi", + EXECUTE_2(mtfsfi, operand_IMM, RC_BIT_G), + NULL, + X_form, 63, 134, CFLOW_NORMAL + }, + { "mtspr", + EXECUTE_1(mtspr, operand_SPR), + NULL, + XFX_form, 31, 467, CFLOW_NORMAL + }, + { "mulhw", + EXECUTE_4(multiply, true, true, OE_BIT_0, RC_BIT_G), + NULL, + XO_form, 31, 75, CFLOW_NORMAL + }, + { "mulhuw", + EXECUTE_4(multiply, true, false, OE_BIT_0, RC_BIT_G), + NULL, + XO_form, 31, 11, CFLOW_NORMAL + }, + { "mulli", + EXECUTE_GENERIC_ARITH(smul, RD, RA, SIMM, NONE, OE_BIT_0, RC_BIT_0), + NULL, + D_form, 7, 0, CFLOW_NORMAL + }, + { "mullw", + EXECUTE_4(multiply, false, true, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 235, CFLOW_NORMAL + }, + { "nand", + EXECUTE_GENERIC_ARITH(nand, RD, RS, RB, NONE, OE_BIT_0, RC_BIT_G), + NULL, + X_form, 31, 476, CFLOW_NORMAL + }, + { "neg", + EXECUTE_GENERIC_ARITH(neg, RD, RA, NONE, NONE, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 104, CFLOW_NORMAL + }, + { "nor", + EXECUTE_GENERIC_ARITH(nor, RA, RS, RB, NONE, OE_BIT_0, RC_BIT_G), + NULL, + XO_form, 31, 124, CFLOW_NORMAL + }, + { "or", + EXECUTE_GENERIC_ARITH(or, RA, RS, RB, NONE, OE_BIT_0, RC_BIT_G), + NULL, + XO_form, 31, 444, CFLOW_NORMAL + }, + { "orc", + EXECUTE_GENERIC_ARITH(orc, RA, RS, RB, NONE, OE_BIT_0, RC_BIT_G), + NULL, + XO_form, 31, 412, CFLOW_NORMAL + }, + { "ori", + EXECUTE_GENERIC_ARITH(or, RA, RS, UIMM, NONE, OE_BIT_0, RC_BIT_0), + NULL, + D_form, 24, 0, CFLOW_NORMAL + }, + { "oris", + EXECUTE_GENERIC_ARITH(or, RA, RS, UIMM_shifted, NONE, OE_BIT_0, RC_BIT_G), + NULL, + D_form, 25, 0, CFLOW_NORMAL + }, + { "rlwimi", + EXECUTE_3(rlwimi, operand_SH, operand_MASK, RC_BIT_G), + NULL, + M_form, 20, 0, CFLOW_NORMAL + }, + { "rlwinm", + EXECUTE_GENERIC_ARITH(ppc_rlwinm, RA, RS, SH, MASK, OE_BIT_0, RC_BIT_G), + NULL, + M_form, 21, 0, CFLOW_NORMAL + }, + { "rlwnm", + EXECUTE_GENERIC_ARITH(ppc_rlwnm, RA, RS, RB, MASK, OE_BIT_0, RC_BIT_G), + NULL, + M_form, 23, 0, CFLOW_NORMAL + }, + { "sc", + EXECUTE_0(syscall), + NULL, + SC_form, 17, 0, CFLOW_NORMAL + }, + { "slw", + EXECUTE_SHIFT(shll, RA, RS, RB, andi<0x1f>, CA_BIT_0, RC_BIT_G), + NULL, + X_form, 31, 24, CFLOW_NORMAL + }, + { "sraw", + EXECUTE_SHIFT(shra, RA, RS, RB, andi<0x3f>, CA_BIT_1, RC_BIT_G), + NULL, + X_form, 31, 792, CFLOW_NORMAL + }, + { "srawi", + EXECUTE_SHIFT(shra, RA, RS, SH, nop, CA_BIT_1, RC_BIT_G), + NULL, + X_form, 31, 824, CFLOW_NORMAL + }, + { "srw", + EXECUTE_SHIFT(shrl, RA, RS, RB, andi<0x1f>, CA_BIT_0, RC_BIT_G), + NULL, + X_form, 31, 536, CFLOW_NORMAL + }, + { "stb", + EXECUTE_LOADSTORE(nop, RA_or_0, D, false, 1, false, false), + NULL, + D_form, 38, 0, CFLOW_NORMAL + }, + { "stbu", + EXECUTE_LOADSTORE(nop, RA, D, false, 1, true, false), + NULL, + D_form, 39, 0, CFLOW_NORMAL + }, + { "stbux", + EXECUTE_LOADSTORE(nop, RA, RB, false, 1, true, false), + NULL, + X_form, 31, 247, CFLOW_NORMAL + }, + { "stbx", + EXECUTE_LOADSTORE(nop, RA_or_0, RB, false, 1, false, false), + NULL, + X_form, 31, 215, CFLOW_NORMAL + }, + { "stfd", + EXECUTE_FP_LOADSTORE(RA_or_0, D, false, true, false), + NULL, + D_form, 54, 0, CFLOW_NORMAL + }, + { "stfdu", + EXECUTE_FP_LOADSTORE(RA, D, false, true, true), + NULL, + D_form, 55, 0, CFLOW_NORMAL + }, + { "stfdux", + EXECUTE_FP_LOADSTORE(RA, RB, false, true, true), + NULL, + X_form, 31, 759, CFLOW_NORMAL + }, + { "stfdx", + EXECUTE_FP_LOADSTORE(RA_or_0, RB, false, true, false), + NULL, + X_form, 31, 727, CFLOW_NORMAL + }, + { "stfs", + EXECUTE_FP_LOADSTORE(RA_or_0, D, false, false, false), + NULL, + D_form, 52, 0, CFLOW_NORMAL + }, + { "stfsu", + EXECUTE_FP_LOADSTORE(RA, D, false, false, true), + NULL, + D_form, 53, 0, CFLOW_NORMAL + }, + { "stfsux", + EXECUTE_FP_LOADSTORE(RA, RB, false, false, true), + NULL, + X_form, 31, 695, CFLOW_NORMAL + }, + { "stfsx", + EXECUTE_FP_LOADSTORE(RA_or_0, RB, false, false, false), + NULL, + X_form, 31, 663, CFLOW_NORMAL + }, + { "sth", + EXECUTE_LOADSTORE(nop, RA_or_0, D, false, 2, false, false), + NULL, + D_form, 44, 0, CFLOW_NORMAL + }, + { "sthbrx", + EXECUTE_LOADSTORE(nop, RA_or_0, RB, false, 2, false, true), + NULL, + X_form, 31, 918, CFLOW_NORMAL + }, + { "sthu", + EXECUTE_LOADSTORE(nop, RA, D, false, 2, true, false), + NULL, + D_form, 45, 0, CFLOW_NORMAL + }, + { "sthux", + EXECUTE_LOADSTORE(nop, RA, RB, false, 2, true, false), + NULL, + X_form, 31, 439, CFLOW_NORMAL + }, + { "sthx", + EXECUTE_LOADSTORE(nop, RA_or_0, RB, false, 2, false, false), + NULL, + X_form, 31, 407, CFLOW_NORMAL + }, + { "stmw", + EXECUTE_LOADSTORE_MULTIPLE(RA_or_0, D, false), + NULL, + D_form, 47, 0, CFLOW_NORMAL + }, + { "stswi", + EXECUTE_STORE_STRING(RA_or_0, true, NB), + NULL, + X_form, 31, 725, CFLOW_NORMAL + }, + { "stswx", + EXECUTE_STORE_STRING(RA_or_0, false, XER_COUNT), + NULL, + X_form, 31, 661, CFLOW_NORMAL + }, + { "stw", + EXECUTE_LOADSTORE(nop, RA_or_0, D, false, 4, false, false), + NULL, + D_form, 36, 0, CFLOW_NORMAL + }, + { "stwbrx", + EXECUTE_LOADSTORE(nop, RA_or_0, RB, false, 4, false, true), + NULL, + X_form, 31, 662, CFLOW_NORMAL + }, + { "stwcx.", + EXECUTE_1(stwcx, operand_RA_or_0), + NULL, + X_form, 31, 150, CFLOW_NORMAL + }, + { "stwu", + EXECUTE_LOADSTORE(nop, RA, D, false, 4, true, false), + NULL, + D_form, 37, 0, CFLOW_NORMAL + }, + { "stwux", + EXECUTE_LOADSTORE(nop, RA, RB, false, 4, true, false), + NULL, + X_form, 31, 183, CFLOW_NORMAL + }, + { "stwx", + EXECUTE_LOADSTORE(nop, RA_or_0, RB, false, 4, false, false), + NULL, + X_form, 31, 151, CFLOW_NORMAL + }, + { "subf", + EXECUTE_ADDITION(RA_compl, RB, ONE, CA_BIT_0, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 40, CFLOW_NORMAL + }, + { "subfc", + EXECUTE_ADDITION(RA_compl, RB, ONE, CA_BIT_1, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 8, CFLOW_NORMAL + }, + { "subfe", + EXECUTE_ADDITION(RA_compl, RB, XER_CA, CA_BIT_1, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 136, CFLOW_NORMAL + }, + { "subfic", + EXECUTE_ADDITION(RA_compl, SIMM, ONE, CA_BIT_1, OE_BIT_0, RC_BIT_0), + NULL, + D_form, 8, 0, CFLOW_NORMAL + }, + { "subfme", + EXECUTE_ADDITION(RA_compl, XER_CA, MINUS_ONE, CA_BIT_1, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 232, CFLOW_NORMAL + }, + { "subfze", + EXECUTE_ADDITION(RA_compl, XER_CA, ZERO, CA_BIT_1, OE_BIT_G, RC_BIT_G), + NULL, + XO_form, 31, 200, CFLOW_NORMAL + }, + { "sync", + EXECUTE_0(nop), + NULL, + X_form, 31, 598, CFLOW_NORMAL + }, + { "xor", + EXECUTE_GENERIC_ARITH(xor, RA, RS, RB, NONE, OE_BIT_0, RC_BIT_G), + NULL, + X_form, 31, 316, CFLOW_NORMAL + }, + { "xori", + EXECUTE_GENERIC_ARITH(xor, RA, RS, UIMM, NONE, OE_BIT_0, RC_BIT_0), + NULL, + D_form, 26, 0, CFLOW_NORMAL + }, + { "xoris", + EXECUTE_GENERIC_ARITH(xor, RA, RS, UIMM_shifted, NONE, OE_BIT_0, RC_BIT_0), + NULL, + D_form, 27, 0, CFLOW_NORMAL + } +}; + +#ifndef PPC_NO_STATIC_II_INDEX_TABLE +powerpc_cpu::ii_index_t powerpc_cpu::ii_index_table[II_INDEX_TABLE_SIZE]; +std::vector powerpc_cpu::ii_table; +#endif + +void powerpc_cpu::init_decoder() +{ +#ifndef PPC_NO_STATIC_II_INDEX_TABLE + static bool initialized = false; + if (initialized) + return; + initialized = true; +#endif + + const int ii_count = sizeof(powerpc_ii_table)/sizeof(powerpc_ii_table[0]); + D(bug("PowerPC decode table has %d entries\n", ii_count)); + assert(ii_count < (1 << (8 * sizeof(ii_index_t)))); + ii_table.reserve(ii_count); + + for (int i = 0; i < ii_count; i++) { + const instr_info_t * ii = &powerpc_ii_table[i]; + init_decoder_entry(ii); + } +} + +void powerpc_cpu::init_decoder_entry(const instr_info_t * ii) +{ + ii_table.push_back(*ii); + const ii_index_t ii_index = ii_table.size() - 1; + + assert((ii->format == INVALID_form && ii_index == 0) || + (ii->format != INVALID_form && ii_index != 0) ); + + switch (ii->format) { + case INVALID_form: + // Initialize all index table + for (int i = 0; i < II_INDEX_TABLE_SIZE; i++) + ii_index_table[i] = ii_index; + break; + + case B_form: + case D_form: + case I_form: + case M_form: + // Primary opcode only + for (int j = 0; j < 1024; j++) + ii_index_table[make_ii_index(ii->opcode, j)] = ii_index; + break; + + case SC_form: + // Primary opcode only, with reserved bits + ii_index_table[make_ii_index(ii->opcode, 1)] = ii_index; + break; + + case X_form: + case XL_form: + case XFX_form: + case XFL_form: + // Extended opcode in bits 21..30 + ii_index_table[make_ii_index(ii->opcode, ii->xo)] = ii_index; + break; + + case XO_form: + // Extended opcode in bits 22..30, with OE bit 21 + ii_index_table[make_ii_index(ii->opcode, ii->xo)] = ii_index; + ii_index_table[make_ii_index(ii->opcode, 1 << 9 | ii->xo)] = ii_index; + break; + + case A_form: + // Extended opcode in bits 26..30 + for (int j = 0; j < 32; j++) + ii_index_table[make_ii_index(ii->opcode, (j << 5) | ii->xo)] = ii_index; + break; + + default: + fprintf(stderr, "Unhandled form %d\n", ii->format); + abort(); + break; + } +} diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-execute.cpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-execute.cpp new file mode 100644 index 00000000..01a33dc1 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-execute.cpp @@ -0,0 +1,1029 @@ +/* + * ppc-execute.cpp - PowerPC semantics + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "sysdeps.h" +#include "vm.hpp" +#include "ppc-cpu.hpp" +#include "ppc-bitfields.hpp" +#include "ppc-operands.hpp" +#include "ppc-operations.hpp" + +#if ENABLE_MON +#include "mon.h" +#include "mon_disass.h" +#endif + +#define DEBUG 1 +#include "debug.h" + +/** + * Helper class to apply an unary/binary/trinary operation + * + * OP Operation to perform + * RA Input operand register + * RB Input operand register or immediate (optional: operand_NONE) + * RC Input operand register or immediate (optional: operand_NONE) + **/ + +template< class OP, class RA, class RB, class RC > +struct op_apply { + template< class T > + static inline T apply(T a, T b, T c) { + return OP::apply(a, b, c); + } +}; + +template< class OP, class RA, class RB > +struct op_apply { + template< class T > + static inline T apply(T a, T b, T c) { + return OP::apply(a, b); + } +}; + +template< class OP, class RA > +struct op_apply { + template< class T > + static inline T apply(T a, T b, T c) { + return OP::apply(a); + } +}; + +/** + * Illegal & NOP instructions + **/ + +void powerpc_cpu::execute_illegal(uint32 opcode) +{ + fprintf(stderr, "Illegal instruction at %08x, opcode = %08x\n", pc(), opcode); +#if ENABLE_MON + disass_ppc(stdout, pc(), opcode); + + // Start up mon in real-mode + char *arg[4] = {"mon", "-m", "-r", NULL}; + mon(3, arg); +#endif + abort(); +} + +void powerpc_cpu::execute_nop(uint32 opcode) +{ + increment_pc(4); +} + +/** + * Helper class to compute the overflow/carry condition + * + * OP Operation to perform + */ + +template< class OP > +struct op_carry { + static inline bool apply(uint32, uint32, uint32) { + return false; + } +}; + +template<> +struct op_carry { + static inline bool apply(uint32 a, uint32 b, uint32 c) { + return (((uint64)a + (uint64)b + c) >> 32) != 0; + } +}; + +template< class OP > +struct op_overflow { + static inline bool apply(uint32, uint32, uint32) { + return false; + } +}; + +template<> +struct op_overflow { + static inline bool apply(uint32 a, uint32, uint32) { + return a == 0x80000000; + }; +}; + +template<> +struct op_overflow { + static inline bool apply(uint32 a, uint32 b, uint32 c) { + return op_carry::apply(a, b, c) ^ (((a & 0x7fffffff) + (b & 0x7fffffff) + c) >> 31); + } +}; + +/** + * Perform an addition/substraction + * + * RA Input operand register, possibly 0 + * RB Input operand either register or immediate + * RC Input carry + * CA Predicate to compute the carry out of the operation + * OE Predicate to compute the overflow flag + * Rc Predicate to record CR0 + **/ + +template< class RA, class RB, class RC, class CA, class OE, class Rc > +void powerpc_cpu::execute_addition(uint32 opcode) +{ + const uint32 a = RA::get(this, opcode); + const uint32 b = RB::get(this, opcode); + const uint32 c = RC::get(this, opcode); + uint32 d = a + b + c; + + // Set XER (CA) if instruction affects carry bit + if (CA::test(opcode)) + xer().set_ca(op_carry::apply(a, b, c)); + + // Set XER (OV, SO) if instruction has OE set + if (OE::test(opcode)) + xer().set_ov(op_overflow::apply(a, b, c)); + + // Set CR0 (LT, GT, EQ, SO) if instruction has Rc set + if (Rc::test(opcode)) + record_cr0((int32)d); + + // Commit result to output operand + operand_RD::set(this, opcode, d); + + increment_pc(4); +} + +/** + * Generic arithmetic instruction + * + * OP Operation to perform + * RD Output register + * RA Input operand register + * RB Input operand register or immediate (optional: operand_NONE) + * RC Input operand register or immediate (optional: operand_NONE) + * OE Predicate to compute overflow flag + * Rc Predicate to record CR0 + **/ + +template< class OP, class RD, class RA, class RB, class RC, class OE, class Rc > +void powerpc_cpu::execute_generic_arith(uint32 opcode) +{ + const uint32 a = RA::get(this, opcode); + const uint32 b = RB::get(this, opcode); + const uint32 c = RC::get(this, opcode); + + uint32 d = op_apply::apply(a, b, c); + + // Set XER (OV, SO) if instruction has OE set + if (OE::test(opcode)) + xer().set_ov(op_overflow::apply(a, b, c)); + + // Set CR0 (LT, GT, EQ, SO) if instruction has Rc set + if (Rc::test(opcode)) + record_cr0((int32)d); + + // commit result to output operand + RD::set(this, opcode, d); + + increment_pc(4); +} + +/** + * Rotate Left Word Immediate then Mask Insert + * + * SH Shift count + * MA Mask value + * Rc Predicate to record CR0 + **/ + +template< class SH, class MA, class Rc > +void powerpc_cpu::execute_rlwimi(uint32 opcode) +{ + const uint32 n = SH::get(this, opcode); + const uint32 m = MA::get(this, opcode); + const uint32 rs = operand_RS::get(this, opcode); + const uint32 ra = operand_RA::get(this, opcode); + uint32 d = op_ppc_rlwimi::apply(rs, n, m, ra); + + // Set CR0 (LT, GT, EQ, SO) if instruction has Rc set + if (Rc::test(opcode)) + record_cr0((int32)d); + + // Commit result to output operand + operand_RA::set(this, opcode, d); + + increment_pc(4); +} + +/** + * Shift instructions + * + * OP Operation to perform + * RD Output operand + * RA Source operand + * SH Shift count + * SO Shift operation + * CA Predicate to compute carry bit + * Rc Predicate to record CR0 + **/ + +template< class OP, class RD, class RA, class SH, class SO, class CA, class Rc > +void powerpc_cpu::execute_shift(uint32 opcode) +{ + const uint32 n = SO::apply(SH::get(this, opcode)); + const uint32 r = RA::get(this, opcode); + uint32 d = OP::apply(r, n); + + // Set XER (CA) if instruction is algebraic variant + if (CA::test(opcode)) { + const uint32 carry = (r & 0x80000000) && (r & ~(0xffffffff << n)); + xer().set_ca(carry); + } + + // Set CR0 (LT, GT, EQ, SO) if instruction has Rc set + if (Rc::test(opcode)) + record_cr0((int32)d); + + // Commit result to output operand + RD::set(this, opcode, d); + + increment_pc(4); +} + +/** + * Branch conditional instructions + * + * PC Input program counter (PC, LR, CTR) + * BO BO operand + * DP Displacement operand + * AA Predicate for absolute address + * LK Predicate to record NPC into link register + **/ + +template< class PC, class BO, class DP, class AA, class LK > +void powerpc_cpu::execute_branch(uint32 opcode) +{ + const int bo = BO::get(this, opcode); + bool ctr_ok = true; + bool cond_ok = true; + + if (BO_CONDITIONAL_BRANCH(bo)) { + cond_ok = cr().test(BI_field::extract(opcode)); + if (!BO_BRANCH_IF_TRUE(bo)) + cond_ok = !cond_ok; + } + + if (BO_DECREMENT_CTR(bo)) { + ctr_ok = (ctr() -= 1) == 0; + if (!BO_BRANCH_IF_CTR_ZERO(bo)) + ctr_ok = !ctr_ok; + } + + const uint32 npc = pc() + 4; + if (ctr_ok && cond_ok) + pc() = ((AA::test(opcode) ? 0 : PC::get(this, opcode)) + DP::get(this, opcode)) & -4; + else + pc() = npc; + + if (LK::test(opcode)) + lr() = npc; +} + +/** + * Compare instructions + * + * RB Second operand (GPR, SIMM, UIMM) + * CT Type of variables to be compared (uint32, int32) + **/ + +template< class RB, typename CT > +void powerpc_cpu::execute_compare(uint32 opcode) +{ + const uint32 a = operand_RA::get(this, opcode); + const uint32 b = RB::get(this, opcode); + const uint32 crfd = crfD_field::extract(opcode); + record_cr(crfd, (CT)a < (CT)b ? -1 : ((CT)a > (CT)b ? +1 : 0)); + increment_pc(4); +} + +/** + * Operations on condition register + * + * OP Operation to perform + **/ + +template< class OP > +void powerpc_cpu::execute_cr_op(uint32 opcode) +{ +#if PPC_HAVE_SPLIT_CR + const uint32 crbA = crbA_field::extract(opcode); + uint32 a = (cr().get(crbA / 4) << (crbA % 4)); + const uint32 crbB = crbB_field::extract(opcode); + uint32 b = (cr().get(crbB / 4) << (crbB % 4)); + const uint32 crbD = crbD_field::extract(opcode); + uint32 d = ((OP::apply(a, b) & 8) >> (crbD % 4)); + cr().set(crbD / 4, d | (cr().get(crbD / 4) & ~(1 << (3 - (crbD % 4))))); +#else + const uint32 crbA = crbA_field::extract(opcode); + uint32 a = (cr().get() >> (31 - crbA)) & 1; + const uint32 crbB = crbB_field::extract(opcode); + uint32 b = (cr().get() >> (31 - crbB)) & 1; + const uint32 crbD = crbD_field::extract(opcode); + uint32 d = OP::apply(a, b) & 1; + cr().set((cr().get() & ~(1 << (31 - crbD))) | (d << (31 - crbD))); +#endif + increment_pc(4); +} + +/** + * Divide instructions + * + * SB Signed division + * OE Predicate to compute overflow + * Rc Predicate to record CR0 + **/ + +template< bool SB, class OE, class Rc > +void powerpc_cpu::execute_divide(uint32 opcode) +{ + const uint32 a = operand_RA::get(this, opcode); + const uint32 b = operand_RB::get(this, opcode); + + if (b == 0 || (SB && a == 0x80000000 && b == 0xffffffff)) { + if (OE::test(opcode)) + xer().set_ov(1); + // TODO: contents of rD is undefined, set it to zero anyway? + } + else { + uint32 d = SB ? (int32)a/(int32)b : a/b; + operand_RD::set(this, opcode, d); + if (OE::test(opcode)) + xer().set_ov(0); + if (Rc::test(opcode)) + record_cr0((int32)d); + } + + increment_pc(4); +} + +/** + * Multiply instructions + * + * HI Predicate for multiply high word + * SB Predicate for signed operation + * OE Predicate to compute overflow + * Rc Predicate to record CR0 + **/ + +template< bool HI, bool SB, class OE, class Rc > +void powerpc_cpu::execute_multiply(uint32 opcode) +{ + const uint32 a = operand_RA::get(this, opcode); + const uint32 b = operand_RB::get(this, opcode); + uint64 d = SB ? (int64)(int32)a * (int64)(int32)b : (uint64)a * (uint64)b; + + // Set XER (OV, SO) if instruction has OE set + if (OE::test(opcode)) + xer().set_ov((d >> 32) != 0); + + // Only keep high word if multiply high instruction + if (HI) + d >>= 32; + + // Set CR0 (LT, GT, EQ, SO) if instruction has Rc set + if (Rc::test(opcode)) + record_cr0((uint32)d); + + // Commit result to output operand + operand_RD::set(this, opcode, (uint32)d); + + increment_pc(4); +} + +/** + * Floating-point arithmetics + * + * OP Operation to perform + * RD Output register + * RA Input operand + * RB Input operand (optional) + * RC Input operand (optional) + * Rc Predicate to record CR1 + * FPSCR Predicate to compute FPSCR bits + **/ + +template< class OP, class RD, class RA, class RB, class RC, class Rc, bool FPSCR > +void powerpc_cpu::execute_fp_arith(uint32 opcode) +{ + const double a = RA::get(this, opcode); + const double b = RB::get(this, opcode); + const double c = RC::get(this, opcode); + double d = op_apply::apply(a, b, c); + +#if 0 + // FIXME: Compute FPSCR bits if instruction requests it + if (FPSCR) { + + // Always update VX + if (fpscr() & (FPSCR_VXSNAN_field::mask() | FPSCR_VXISI_field::mask() | \ + FPSCR_VXISI_field::mask() | FPSCR_VXIDI_field::mask() | \ + FPSCR_VXZDZ_field::mask() | FPSCR_VXIMZ_field::mask() | \ + FPSCR_VXVC_field::mask() | FPSCR_VXSOFT_field::mask() | \ + FPSCR_VXSQRT_field::mask() | FPSCR_VXCVI_field::mask())) + fpscr() |= FPSCR_VX_field::mask(); + else + fpscr() &= ~FPSCR_VX_field::mask(); + + // Always update FEX + if (((fpscr() & FPSCR_VX_field::mask()) && (fpscr() & FPSCR_VE_field::mask())) \ + || ((fpscr() & FPSCR_OX_field::mask()) && (fpscr() & FPSCR_OE_field::mask())) \ + || ((fpscr() & FPSCR_UX_field::mask()) && (fpscr() & FPSCR_UE_field::mask())) \ + || ((fpscr() & FPSCR_ZX_field::mask()) && (fpscr() & FPSCR_ZE_field::mask())) \ + || ((fpscr() & FPSCR_XX_field::mask()) && (fpscr() & FPSCR_XE_field::mask()))) + fpscr() |= FPSCR_FEX_field::mask(); + else + fpscr() &= ~FPSCR_FEX_field::mask(); + } +#endif + + // Set CR1 (FX, FEX, VX, VOX) if instruction has Rc set + if (Rc::test(opcode)) + record_cr1(); + + // Commit result to output operand + RD::set(this, opcode, d); + increment_pc(4); +} + +/** + * Load/store instructions + * + * OP Operation to perform on loaded value + * RA Base operand + * RB Displacement (GPR(RB), EXTS(d)) + * LD Load operation? + * SZ Size of load/store operation + * UP Update RA with EA + * RX Reverse operand + **/ + +template< int SZ, bool RX > +struct memory_helper; + +#define DEFINE_MEMORY_HELPER(SIZE) \ +template< bool RX > \ +struct memory_helper \ +{ \ + static inline uint32 load(uint32 ea) { \ + return RX ? vm_read_memory_##SIZE##_reversed(ea) : vm_read_memory_##SIZE(ea); \ + } \ + static inline void store(uint32 ea, uint32 value) { \ + RX ? vm_write_memory_##SIZE##_reversed(ea, value) : vm_write_memory_##SIZE(ea, value); \ + } \ +} + +DEFINE_MEMORY_HELPER(1); +DEFINE_MEMORY_HELPER(2); +DEFINE_MEMORY_HELPER(4); + +template< class OP, class RA, class RB, bool LD, int SZ, bool UP, bool RX > +void powerpc_cpu::execute_loadstore(uint32 opcode) +{ + const uint32 a = RA::get(this, opcode); + const uint32 b = RB::get(this, opcode); + const uint32 ea = a + b; + + if (LD) + operand_RD::set(this, opcode, OP::apply(memory_helper::load(ea))); + else + memory_helper::store(ea, operand_RS::get(this, opcode)); + + if (UP) + RA::set(this, opcode, ea); + + increment_pc(4); +} + +template< class RA, class DP, bool LD > +void powerpc_cpu::execute_loadstore_multiple(uint32 opcode) +{ + const uint32 a = RA::get(this, opcode); + const uint32 d = DP::get(this, opcode); + uint32 ea = a + d; + + // FIXME: generate exception if ea is not word-aligned + if ((ea & 3) != 0) + abort(); + + int r = LD ? rD_field::extract(opcode) : rS_field::extract(opcode); + while (r <= 31) { + if (LD) + gpr(r) = vm_read_memory_4(ea); + else + vm_write_memory_4(ea, gpr(r)); + r++; + ea += 4; + } + + increment_pc(4); +} + +/** + * Floating-point load/store instructions + * + * RA Base operand + * RB Displacement (GPR(RB), EXTS(d)) + * LD Load operation? + * DB Predicate for double value + * UP Predicate to update RA with EA + **/ + +template< class RA, class RB, bool LD, bool DB, bool UP > +void powerpc_cpu::execute_fp_loadstore(uint32 opcode) +{ + const uint32 a = RA::get(this, opcode); + const uint32 b = RB::get(this, opcode); + const uint32 ea = a + b; + any_register d; + + if (LD) { + if (DB) { + d.j = vm_read_memory_8(ea); + operand_fp_RD::set(this, opcode, d.d); + } + else { + d.i = vm_read_memory_4(ea); + operand_fp_RD::set(this, opcode, (double)d.f); + } + } + else { + if (DB) { + d.d = operand_fp_RS::get(this, opcode); + vm_write_memory_8(ea, d.j); + } + else { + d.f = (float)operand_fp_RS::get(this, opcode); + vm_write_memory_4(ea, d.i); + } + } + + if (UP) + RA::set(this, opcode, ea); + + increment_pc(4); +} + +/** + * Load/Store String Word instruction + * + * RA Input operand as base EA + * IM lswi mode? + * NB Number of bytes to transfer + **/ + +template< class RA, bool IM, class NB > +void powerpc_cpu::execute_load_string(uint32 opcode) +{ + uint32 ea = RA::get(this, opcode); + if (!IM) + ea += operand_RB::get(this, opcode); + + int nb = NB::get(this, opcode); + if (IM && nb == 0) + nb = 32; + + int rd = rD_field::extract(opcode); +#if 1 + int i; + for (i = 0; nb - i >= 4; i += 4, rd = (rd + 1) & 0x1f) + gpr(rd) = vm_read_memory_4(ea + i); + switch (nb - i) { + case 1: + gpr(rd) = vm_read_memory_1(ea + i) << 24; + break; + case 2: + gpr(rd) = vm_read_memory_2(ea + i) << 16; + break; + case 3: + gpr(rd) = (vm_read_memory_2(ea + i) << 16) + (vm_read_memory_1(ea + i + 2) << 8); + break; + } +#else + for (int i = 0; i < nb; i++) { + switch (i & 3) { + case 0: + gpr(rd) = vm_read_memory_1(ea + i) << 24; + break; + case 1: + gpr(rd) = (gpr(rd) & 0xff00ffff) | (vm_read_memory_1(ea + i) << 16); + break; + case 2: + gpr(rd) = (gpr(rd) & 0xffff00ff) | (vm_read_memory_1(ea + i) << 8); + break; + case 3: + gpr(rd) = (gpr(rd) & 0xffffff00) | vm_read_memory_1(ea + i); + rd = (rd + 1) & 0x1f; + break; + } + } +#endif + + increment_pc(4); +} + +template< class RA, bool IM, class NB > +void powerpc_cpu::execute_store_string(uint32 opcode) +{ + uint32 ea = RA::get(this, opcode); + if (!IM) + ea += operand_RB::get(this, opcode); + + int nb = NB::get(this, opcode); + if (IM && nb == 0) + nb = 32; + + int rs = rS_field::extract(opcode); + int sh = 24; + for (int i = 0; i < nb; i++) { + vm_write_memory_1(ea + i, gpr(rs) >> sh); + sh -= 8; + if (sh < 0) { + sh = 24; + rs = (rs + 1) & 0x1f; + } + } + + increment_pc(4); +} + +/** + * Load Word and Reserve Indexed / Store Word Conditional Indexed + * + * RA Input operand as base EA + **/ + +template< class RA > +void powerpc_cpu::execute_lwarx(uint32 opcode) +{ + const uint32 ea = RA::get(this, opcode) + operand_RB::get(this, opcode); + regs.reserve_valid = 1; + regs.reserve_addr = ea; + regs.reserve_data = vm_read_memory_4(ea); + operand_RD::set(this, opcode, regs.reserve_data); + increment_pc(4); +} + +template< class RA > +void powerpc_cpu::execute_stwcx(uint32 opcode) +{ + const uint32 ea = RA::get(this, opcode) + operand_RB::get(this, opcode); + cr().clear(0); + if (regs.reserve_valid) { + if (regs.reserve_addr == ea /* physical_addr(EA) */ + && /* HACK */ regs.reserve_data == vm_read_memory_4(ea)) { + vm_write_memory_4(ea, operand_RS::get(this, opcode)); + cr().set(0, standalone_CR_EQ_field::mask()); + } + regs.reserve_valid = 0; + } + cr().set_so(0, xer().get_so()); + increment_pc(4); +} + +/** + * Floating-point compare instruction + * + * OC Predicate for ordered compare + **/ + +static inline bool is_NaN(double v) { + any_register x; x.d = v; + return (((x.j & UVAL64(0x7ff0000000000000)) == UVAL64(0x7ff0000000000000)) && + ((x.j & UVAL64(0x000fffffffffffff)) != 0)); +} + +static inline bool is_SNaN(double v) { + any_register x; x.d = v; + return is_NaN(v) && !(x.j & UVAL64(0x0008000000000000)) ? signbit(v) : false; +} + +static inline bool is_QNaN(double v) { + return is_NaN(v) && !is_SNaN(v); +} + +static inline bool is_NaN(float v) { + any_register x; x.f = v; + return (((x.i & 0x7f800000) == 0x7f800000) && + ((x.i & 0x007fffff) != 0)); +} + +static inline bool is_SNaN(float v) { + any_register x; x.f = v; + return is_NaN(v) && !(x.i & 0x00400000) ? signbit(v) : false; +} + +static inline bool is_QNaN(float v) { + return is_NaN(v) && !is_SNaN(v); +} + +template< bool OC > +void powerpc_cpu::execute_fp_compare(uint32 opcode) +{ + const double a = operand_fp_RA::get(this, opcode); + const double b = operand_fp_RB::get(this, opcode); + const int crfd = crfD_field::extract(opcode); + + if (is_NaN(a) || is_NaN(b)) + cr().set(crfd, 1); + else if (isless(a, b)) + cr().set(crfd, 8); + else if (isgreater(a, b)) + cr().set(crfd, 4); + else + cr().set(crfd, 2); + +#ifndef PPC_NO_FPSCR_UPDATE + fpscr() = (fpscr() & ~FPSCR_FPCC_field::mask()) | (cr().get(crfd) << 12); + if (is_SNaN(a) || is_SNaN(b)) { + fpscr() |= FPSCR_VXSNAN_field::mask(); + if (OC && !FPSCR_VE_field::test(fpscr())) + fpscr() |= FPSCR_VXVC_field::mask(); + } + else if (OC && (is_QNaN(a) || is_QNaN(b))) + fpscr() |= FPSCR_VXVC_field::mask(); +#endif + + increment_pc(4); +} + +/** + * Floating Convert to Integer Word instructions + * + * RN Rounding mode + * Rc Predicate to record CR1 + **/ + +template< class RN, class Rc > +void powerpc_cpu::execute_fp_int_convert(uint32 opcode) +{ + const double b = operand_fp_RB::get(this, opcode); + const uint32 r = RN::get(this, opcode); + any_register d; + + switch (r) { + case 0: // Round to nearest + d.j = (int32)(b + 0.5); + break; + case 1: // Round toward zero + d.j = (int32)b; + break; + case 2: // Round toward +infinity + d.j = (int32)ceil(b); + break; + case 3: // Round toward -infinity + d.j = (int32)floor(b); + break; + } + + // Set CR1 (FX, FEX, VX, VOX) if instruction has Rc set + if (Rc::test(opcode)) + record_cr1(); + + // Commit result to output operand + operand_fp_RD::set(this, opcode, d.d); + increment_pc(4); +} + +/** + * Floating-point Round to Single + * + * Rc Predicate to record CR1 + **/ + +void powerpc_cpu::fp_classify(double x) +{ +#ifndef PPC_NO_FPSCR_UPDATE + uint32 c = fpscr() & ~FPSCR_FPRF_field::mask(); + uint8 fc = fpclassify(x); + switch (fc) { + case FP_NAN: + c |= FPSCR_FPRF_FU_field::mask() | FPSCR_FPRF_C_field::mask(); + break; + case FP_ZERO: + c |= FPSCR_FPRF_FE_field::mask(); + if (signbit(x)) + c |= FPSCR_FPRF_C_field::mask(); + break; + case FP_INFINITE: + c |= FPSCR_FPRF_FU_field::mask(); + goto FL_FG_field; + case FP_SUBNORMAL: + c |= FPSCR_FPRF_C_field::mask(); + // fall-through + case FP_NORMAL: + FL_FG_field: + if (x < 0) + c |= FPSCR_FPRF_FL_field::mask(); + else + c |= FPSCR_FPRF_FG_field::mask(); + break; + } + fpscr() = c; +#endif +} + +template< class Rc > +void powerpc_cpu::execute_fp_round(uint32 opcode) +{ + const double b = operand_fp_RB::get(this, opcode); + float d = (float)b; + + // FPSCR[FPRF] is set to the class and sign of the result + fp_classify(d); + + // Set CR1 (FX, FEX, VX, VOX) if instruction has Rc set + if (Rc::test(opcode)) + record_cr1(); + + // Commit result to output operand + operand_fp_RD::set(this, opcode, (double)d); + increment_pc(4); +} + +/** + * System Call instruction + **/ + +void powerpc_cpu::execute_syscall(uint32 opcode) +{ + cr().set_so(0, execute_do_syscall && !execute_do_syscall(this)); + increment_pc(4); +} + +/** + * Instructions dealing with system registers + **/ + +void powerpc_cpu::execute_mcrf(uint32 opcode) +{ + const int crfS = crfS_field::extract(opcode); + const int crfD = crfD_field::extract(opcode); + cr().set(crfD, cr().get(crfS)); + increment_pc(4); +} + +void powerpc_cpu::execute_mtcrf(uint32 opcode) +{ + uint32 mask = field2mask[CRM_field::extract(opcode)]; + cr().set((operand_RS::get(this, opcode) & mask) | (cr().get() & ~mask)); + increment_pc(4); +} + +template< class FM, class RB, class Rc > +void powerpc_cpu::execute_mtfsf(uint32 opcode) +{ + any_register x; + x.d = RB::get(this, opcode); + + const uint32 f = FM::get(this, opcode); + uint32 m = field2mask[f]; + + // FPSCR[FX] is altered only if FM[0] = 1 + if ((f & 0x80) == 0) + m &= ~FPSCR_FX_field::mask(); + +#ifndef PPC_NO_FPSCR_UPDATE + // Move frB bits to FPSCR according to field mask + fpscr() = (x.i & m) | (fpscr() & ~m); +#endif + + // Set CR1 (FX, FEX, VX, VOX) if instruction has Rc set + if (Rc::test(opcode)) + record_cr1(); + + increment_pc(4); +} + +template< class RB, class Rc > +void powerpc_cpu::execute_mtfsfi(uint32 opcode) +{ + const uint32 crfD = crfD_field::extract(opcode); + uint32 m = 0xf << (4 * (7 - crfD)); + + // FPSCR[FX] is altered only if crfD = 0 + if (crfD == 0) + m &= ~FPSCR_FX_field::mask(); + +#ifndef PPC_NO_FPSCR_UPDATE + // Move immediate to FPSCR according to field crfD + fpscr() = (RB::get(this, opcode) & m) | (fpscr() & ~m); +#endif + + // Set CR1 (FX, FEX, VX, VOX) if instruction has Rc set + if (Rc::test(opcode)) + record_cr1(); + + increment_pc(4); +} + +template< class RB, class Rc > +void powerpc_cpu::execute_mtfsb(uint32 opcode) +{ + // Bit crbD of the FPSCR is set or cleared + const uint32 crbD = crbD_field::extract(opcode); + fpscr() = (fpscr() & ~(1 << (31 - crbD))) | (RB::get(this, opcode) << (31 - crbD)); + + // Set CR1 (FX, FEX, VX, VOX) if instruction has Rc set + if (Rc::test(opcode)) + record_cr1(); + + increment_pc(4); +} + +template< class Rc > +void powerpc_cpu::execute_mffs(uint32 opcode) +{ + // Move FPSCR to FPR(FRD) + any_register x; + x.j = fpscr(); + operand_fp_RD::set(this, opcode, x.d); + + // Set CR1 (FX, FEX, VX, VOX) if instruction has Rc set + if (Rc::test(opcode)) + record_cr1(); + + increment_pc(4); +} + +void powerpc_cpu::execute_mfmsr(uint32 opcode) +{ + operand_RD::set(this, opcode, 0xf072); + increment_pc(4); +} + +template< class SPR > +void powerpc_cpu::execute_mfspr(uint32 opcode) +{ + uint32 spr = SPR::get(this, opcode); + uint32 d; + switch (spr) { + case 1: d = xer().get(); break; + case 8: d = lr(); break; + case 9: d = ctr(); break; + default: execute_illegal(opcode); + } + operand_RD::set(this, opcode, d); + increment_pc(4); +} + +template< class SPR > +void powerpc_cpu::execute_mtspr(uint32 opcode) +{ + uint32 spr = SPR::get(this, opcode); + const uint32 s = operand_RS::get(this, opcode); + + switch (spr) { + case 1: xer().set(s); break; + case 8: lr() = s; break; + case 9: ctr() = s; break; + default: execute_illegal(opcode); + } + + increment_pc(4); +} + +template< class TBR > +void powerpc_cpu::execute_mftbr(uint32 opcode) +{ + uint32 tbr = TBR::get(this, opcode); + uint32 d; + switch (tbr) { + case 268: d = tbl(); break; + case 269: d = tbu(); break; + default: execute_illegal(opcode); + } + operand_RD::set(this, opcode, d); + increment_pc(4); +} + +/** + * Explicit template instantiations + **/ + +#include "ppc-execute-impl.cpp" diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-operands.hpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-operands.hpp new file mode 100644 index 00000000..1040071c --- /dev/null +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-operands.hpp @@ -0,0 +1,236 @@ +/* + * ppc-operands.hpp - PowerPC operands definition + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PPC_OPERANDS_H +#define PPC_OPERANDS_H + +/** + * General purpose registers + **/ + +template< class field, class op > +struct input_gpr_op { + static inline uint32 get(powerpc_cpu * cpu, uint32 opcode) { + return op::apply(cpu->gpr(field::extract(opcode))); + } +}; + +template< class field > +struct input_gpr : input_gpr_op< field, op_nop > { }; + +template< class field > +struct output_gpr { + static inline void set(powerpc_cpu * cpu, uint32 opcode, uint32 value) { + cpu->gpr(field::extract(opcode)) = value; + } +}; + +template< class field > +struct gpr_operand : input_gpr< field >, output_gpr< field > { }; + +template< class field, int except_regno > +struct input_gpr_except { + static inline uint32 get(powerpc_cpu * cpu, uint32 opcode) { + const int regno = field::extract(opcode); + return regno != except_regno ? cpu->gpr(regno) : 0; + }; + static inline void set(powerpc_cpu * cpu, uint32 opcode, uint32 value) { + const int regno = field::extract(opcode); + if (regno == except_regno) abort(); + cpu->gpr(regno) = value; + } +}; + +/** + * Floating-point registers + **/ + +template< class field > +struct input_fpr { + static inline double get(powerpc_cpu * cpu, uint32 opcode) { + return cpu->fpr(field::extract(opcode)); + } +}; + +template< class field > +struct output_fpr { + static inline void set(powerpc_cpu * cpu, uint32 opcode, double value) { + cpu->fpr(field::extract(opcode)) = value; + } +}; + +template< class field > +struct fpr_operand : input_fpr< field >, output_fpr< field > { }; + +/** + * Immediate operands + **/ + +struct null_operand { + static inline uint32 get(powerpc_cpu *, uint32) { + return 0; + } +}; +template< class field, class operation > +struct immediate_operand { + static inline uint32 get(powerpc_cpu *, uint32 opcode) { + return operation::apply(field::extract(opcode)); + } +}; + +template< int32 N > +struct immediate_value { + static inline uint32 get(powerpc_cpu *, uint32) { + return (uint32)N; + } +}; + +struct mask_operand { + static inline uint32 get(powerpc_cpu *, uint32 opcode) { + const uint32 mb = MB_field::extract(opcode); + const uint32 me = ME_field::extract(opcode); + return ((mb > me) ? + ~(((uint32)-1 >> mb) ^ ((me >= 31) ? 0 : (uint32)-1 >> (me + 1))) : + (((uint32)-1 >> mb) ^ ((me >= 31) ? 0 : (uint32)-1 >> (me + 1)))); + } +}; + +/** + * Special purpose registers + **/ + +struct pc_operand { + static inline uint32 get(powerpc_cpu * cpu, uint32) { + return cpu->pc(); + }; +}; + +struct lr_operand { + static inline uint32 get(powerpc_cpu * cpu, uint32) { + return cpu->lr(); + }; +}; + +struct ctr_operand { + static inline uint32 get(powerpc_cpu * cpu, uint32) { + return cpu->ctr(); + }; +}; + +struct cr_operand { + static inline uint32 get(powerpc_cpu * cpu, uint32) { + return cpu->cr().get(); + } +}; + +template< class field > +struct xer_operand { + static inline uint32 get(powerpc_cpu * cpu, uint32) { + return field::extract(cpu->xer().get()); + } +}; + +template<> +struct xer_operand { + static inline uint32 get(powerpc_cpu * cpu, uint32) { + return cpu->xer().get_ca(); + } +}; + +template<> +struct xer_operand { + static inline uint32 get(powerpc_cpu * cpu, uint32) { + return cpu->xer().get_count(); + } +}; + +template< class field > +struct fpscr_operand { + static inline uint32 get(powerpc_cpu * cpu, uint32) { + return field::extract(cpu->fpscr()); + } +}; + +struct spr_operand { + static inline uint32 get(powerpc_cpu *, uint32 opcode) { + uint32 spr = SPR_field::extract(opcode); + return ((spr & 0x1f) << 5) | ((spr >> 5) & 0x1f); + } +}; + +struct tbr_operand { + static inline uint32 get(powerpc_cpu *, uint32 opcode) { + uint32 tbr = TBR_field::extract(opcode); + return ((tbr & 0x1f) << 5) | ((tbr >> 5) & 0x1f); + } +}; + + +/** + * Operand aliases for decode table + **/ + +typedef null_operand operand_NONE; +typedef cr_operand operand_CR; +typedef pc_operand operand_PC; +typedef lr_operand operand_LR; +typedef ctr_operand operand_CTR; +typedef input_gpr_op operand_RA_compl; +typedef gpr_operand operand_RA; +typedef gpr_operand operand_RB; +typedef gpr_operand operand_RS; +typedef gpr_operand operand_RD; +typedef input_gpr_except operand_RA_or_0; // RA ? GPR(RA) : 0 +typedef immediate_value<0> operand_RA_is_0; // RA -> 0 +typedef immediate_value<1> operand_ONE; +typedef immediate_value<0> operand_ZERO; +typedef immediate_value<-1> operand_MINUS_ONE; +typedef null_operand operand_fp_NONE; +typedef fpr_operand operand_fp_RA; +typedef fpr_operand operand_fp_RB; +typedef fpr_operand operand_fp_RC; +typedef fpr_operand operand_fp_RD; +typedef fpr_operand operand_fp_RS; +typedef xer_operand operand_XER_CA; +typedef xer_operand operand_XER_COUNT; +typedef fpscr_operand operand_FPSCR_RN; +typedef spr_operand operand_SPR; +typedef tbr_operand operand_TBR; +typedef mask_operand operand_MASK; + +#define DEFINE_IMMEDIATE_OPERAND(NAME, FIELD, OP) \ +typedef immediate_operand operand_##NAME + +DEFINE_IMMEDIATE_OPERAND(LI, LI, sign_extend_LI_32); +DEFINE_IMMEDIATE_OPERAND(BO, BO, nop); +DEFINE_IMMEDIATE_OPERAND(BD, BD, sign_extend_BD_32); +DEFINE_IMMEDIATE_OPERAND(IMM, IMM, nop); +DEFINE_IMMEDIATE_OPERAND(UIMM, UIMM, zero_extend_16_32); +DEFINE_IMMEDIATE_OPERAND(UIMM_shifted, UIMM, zero_extend_16_32_shifted); +DEFINE_IMMEDIATE_OPERAND(SIMM, SIMM, sign_extend_16_32); +DEFINE_IMMEDIATE_OPERAND(SIMM_shifted, SIMM, sign_extend_16_32_shifted); +DEFINE_IMMEDIATE_OPERAND(D, d, sign_extend_16_32); +DEFINE_IMMEDIATE_OPERAND(NB, NB, nop); +DEFINE_IMMEDIATE_OPERAND(SH, SH, nop); +DEFINE_IMMEDIATE_OPERAND(FM, FM, nop); + +#undef DEFINE_IMMEDIATE_OPERAND + +#endif /* PPC_OPERANDS_H */ diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-operations.hpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-operations.hpp new file mode 100644 index 00000000..89d21238 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-operations.hpp @@ -0,0 +1,198 @@ +/* + * ppc-operations.cpp - PowerPC specific operations + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PPC_OPERATIONS_H +#define PPC_OPERATIONS_H + +#include + +/** + * Define an unary/binary/trinary operation + * + * NAME Name of the operation + * TYPE Type of operands and result + * EXPR C++ expression defining the operation, parameters are x/y/z/t + **/ + +#define DEFINE_OP1(NAME, TYPE, EXPR) \ +struct op_##NAME { \ + static inline TYPE apply(TYPE x) { \ + return EXPR; \ + } \ +} + +#define DEFINE_OP2(NAME, TYPE, EXPR) \ +struct op_##NAME { \ + static inline TYPE apply(TYPE x, TYPE y) { \ + return EXPR; \ + } \ +} + +#define DEFINE_OP3(NAME, TYPE, EXPR) \ +struct op_##NAME { \ + static inline TYPE apply(TYPE x, TYPE y, TYPE z) { \ + return EXPR; \ + } \ +} + +#define DEFINE_OP4(NAME, TYPE, EXPR) \ +struct op_##NAME { \ + static inline TYPE apply(TYPE x, TYPE y, TYPE z, TYPE t) { \ + return EXPR; \ + } \ +} + +// Integer basic operations + +DEFINE_OP1(nop, uint32, x); +DEFINE_OP1(neg, uint32, -x); +DEFINE_OP1(compl, uint32, ~x); +DEFINE_OP2(add, uint32, x + y); +DEFINE_OP2(sub, uint32, x - y); +DEFINE_OP2(mul, uint32, x * y); +DEFINE_OP2(smul, int32, x * y); +DEFINE_OP2(div, uint32, x / y); +DEFINE_OP2(sdiv, int32, x / y); +DEFINE_OP2(mod, uint32, x % y); +DEFINE_OP2(and, uint32, x & y); +DEFINE_OP2(or, uint32, x | y); +DEFINE_OP2(xor, uint32, x ^ y); +DEFINE_OP2(orc, uint32, x | ~y); +DEFINE_OP2(andc,uint32, x & ~y); +DEFINE_OP2(nand,uint32, ~(x & y)); +DEFINE_OP2(nor, uint32, ~(x | y)); +DEFINE_OP2(eqv, uint32, ~(x ^ y)); +DEFINE_OP2(shll, uint32, x << y); +DEFINE_OP2(shrl, uint32, x >> y); +DEFINE_OP2(shra, uint32, (int32)x >> y); +DEFINE_OP2(rotl, uint32, ((x << y) | (x >> (32 - y)))); +DEFINE_OP2(rotr, uint32, ((x >> y) | (x << (32 - y)))); + +DEFINE_OP4(ppc_rlwimi, uint32, (op_rotl::apply(x, y) & z) | (t & ~z)); +DEFINE_OP3(ppc_rlwinm, uint32, (op_rotl::apply(x, y) & z)); +DEFINE_OP3(ppc_rlwnm, uint32, (op_rotl::apply(x, (y & 0x1f)) & z)); + + +// Floating-point basic operations + +DEFINE_OP1(fnop, double, x); +DEFINE_OP1(fabs, double, fabs(x)); +DEFINE_OP2(fadd, double, x + y); +DEFINE_OP2(fadds, float, x + y); +DEFINE_OP2(fdiv, double, x / y); +DEFINE_OP2(fdivs, float, x / y); +DEFINE_OP3(fmadd, double, (x * y) + z); +DEFINE_OP3(fmadds, float, (x * y) + z); +DEFINE_OP3(fmsub, double, (x * y) - z); +DEFINE_OP3(fmsubs, float, (x * y) - z); +DEFINE_OP2(fmul, double, x * y); +DEFINE_OP2(fmuls, float, x * y); +DEFINE_OP1(fnabs, double, -fabs(x)); +DEFINE_OP1(fneg, double, -x); +DEFINE_OP3(fnmadd, double, -((x * y) + z)); +DEFINE_OP3(fnmadds, float, -((x * y) + z)); +DEFINE_OP3(fnmsub, double, -((x * y) - z)); +DEFINE_OP3(fnmsubs, float, -((x * y) - z)); +DEFINE_OP2(fsub, double, x - y); +DEFINE_OP2(fsubs, float, x - y); + +#undef DEFINE_OP1 +#undef DEFINE_OP2 +#undef DEFINE_OP3 +#undef DEFINE_OP4 + + +// Sign/Zero-extend operation + +struct op_sign_extend_16_32 { + static inline uint32 apply(uint32 value) { + return (uint32)(int32)(int16)value; + } +}; + +struct op_sign_extend_8_32 { + static inline uint32 apply(uint32 value) { + return (uint32)(int32)(int8)value; + } +}; + +struct op_zero_extend_16_32 { + static inline uint32 apply(uint32 value) { + return (uint32)(uint16)value; + } +}; + +struct op_zero_extend_8_32 { + static inline uint32 apply(uint32 value) { + return (uint32)(uint8)value; + } +}; + +struct op_sign_extend_16_32_shifted { + static inline uint32 apply(uint32 value) { + return op_sign_extend_16_32::apply(value) << 16; + } +}; + +struct op_zero_extend_16_32_shifted { + static inline uint32 apply(uint32 value) { + return op_zero_extend_16_32::apply(value) << 16; + } +}; + +struct op_sign_extend_BD_32 { + static inline uint32 apply(uint32 value) { + return op_sign_extend_16_32::apply(value << 2); + } +}; + +struct op_sign_extend_LI_32 { + static inline uint32 apply(uint32 value) { + if (value & 0x800000) + value |= 0xff000000; + return value << 2; + } +}; + + +// And Word with Immediate value + +template< uint32 value > +struct op_andi { + static inline uint32 apply(uint32 x) { + return x & value; + } +}; + + +// Count Leading Zero Word + +struct op_cntlzw { + static inline uint32 apply(uint32 x) { + uint32 n; + uint32 m = 0x80000000; + for (n = 0; n < 32; n++, m >>= 1) + if (x & m) + break; + return n; + } +}; + +#endif /* PPC_OPERATIONS_H */ diff --git a/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-registers.hpp b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-registers.hpp new file mode 100644 index 00000000..55c8c2f3 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-registers.hpp @@ -0,0 +1,374 @@ +/* + * ppc-registers.hpp - PowerPC registers definition + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PPC_REGISTERS_H +#define PPC_REGISTERS_H + +/** + * Condition Register + **/ + +#ifdef PPC_HAVE_SPLIT_CR +class powerpc_crf_register +{ + union { + struct { + uint8 lt; + uint8 gt; + uint8 eq; + uint8 so; + } parts; + uint32 value; + }; +#ifdef PPC_LAZY_CC_UPDATE + bool lazy_mode; + int32 cc_dest; +#endif +public: + powerpc_crf_register() { clear(); } + void set_so(bool v) { parts.so = v; } + bool is_overflow() const { return parts.so; } + bool is_less() const; + bool is_greater() const; + bool is_zero() const; + void clear(); + bool test(int condition) const; + void set(uint32 v); + uint32 get() const; + void compute(int32 v); +}; + +inline void +powerpc_crf_register::clear() +{ + value = 0; +#ifdef PPC_LAZY_CC_UPDATE + lazy_mode = false; + cc_dest = 0; +#endif +} + +inline bool +powerpc_crf_register::is_less() const +{ +#ifdef PPC_LAZY_CC_UPDATE + if (lazy_mode) + return cc_dest < 0; +#endif + return parts.lt; +} + +inline bool +powerpc_crf_register::is_greater() const +{ +#ifdef PPC_LAZY_CC_UPDATE + if (lazy_mode) + return cc_dest > 0; +#endif + return parts.gt; +} + +inline bool +powerpc_crf_register::is_zero() const +{ +#ifdef PPC_LAZY_CC_UPDATE + if (lazy_mode) + return cc_dest == 0; +#endif + return parts.eq; +} + +inline bool +powerpc_crf_register::test(int condition) const +{ + switch (condition) { + case 0: return is_less(); + case 1: return is_greater(); + case 2: return is_zero(); + case 3: return is_overflow(); + } + abort(); + return false; +} + +inline void +powerpc_crf_register::set(uint32 v) +{ + parts.so = standalone_CR_SO_field::extract(v); +#ifdef PPC_LAZY_CC_UPDATE + const uint32 rd = v & (standalone_CR_LT_field::mask() | + standalone_CR_GT_field::mask() | + standalone_CR_EQ_field::mask()); + lazy_mode = false; + if (rd == standalone_CR_LT_field::mask()) { + lazy_mode = true; + cc_dest = -1; + return; + } + if (rd == standalone_CR_GT_field::mask()) { + lazy_mode = true; + cc_dest = 1; + return; + } + if (rd == standalone_CR_EQ_field::mask()) { + lazy_mode = true; + cc_dest = 0; + return; + } +#endif + parts.lt = standalone_CR_LT_field::extract(v); + parts.gt = standalone_CR_GT_field::extract(v); + parts.eq = standalone_CR_EQ_field::extract(v); +} + +inline uint32 +powerpc_crf_register::get() const +{ + uint32 value = parts.so; +#ifdef PPC_LAZY_CC_UPDATE + if (lazy_mode) { + if ((int32)cc_dest < 0) + value |= standalone_CR_LT_field::mask(); + else if ((int32)cc_dest > 0) + value |= standalone_CR_GT_field::mask(); + else + value |= standalone_CR_EQ_field::mask(); + return value; + } +#endif + return (parts.lt << 3) | (parts.gt << 2) | (parts.eq << 1) | value; +} + +inline void +powerpc_crf_register::compute(int32 v) +{ +#ifdef PPC_LAZY_CC_UPDATE + lazy_mode = true; + cc_dest = v; +#else + if (v < 0) + parts.lt = 1, parts.gt = 0, parts.eq = 0; + else if (v > 0) + parts.lt = 0, parts.gt = 1, parts.eq = 0; + else + parts.lt = 0, parts.gt = 0, parts.eq = 1; +#endif +} +#endif + +class powerpc_cr_register +{ +#ifdef PPC_HAVE_SPLIT_CR + powerpc_crf_register crf[8]; +#else + uint32 cr; +#endif +public: + bool test(int condition) const; + void set(uint32 v); + uint32 get() const; + void clear(int crfd); + void set(int crfd, uint32 v); + uint32 get(int crfd) const; + void set_so(int crfd, bool v); + void compute(int crfd, int32 v); +}; + +inline void +powerpc_cr_register::clear(int crfd) +{ +#ifdef PPC_HAVE_SPLIT_CR + crf[crfd].clear(); +#else + cr &= ~(0xf << (28 - 4 * crfd)); +#endif +} + +inline void +powerpc_cr_register::set(int crfd, uint32 v) +{ +#ifdef PPC_HAVE_SPLIT_CR + crf[crfd].set(v); +#else + clear(crfd); + cr |= v << (28 - 4 * crfd); +#endif +} + +inline uint32 +powerpc_cr_register::get(int crfd) const +{ +#ifdef PPC_HAVE_SPLIT_CR + return crf[crfd].get(); +#else + return (cr >> (28 - 4 * crfd)) & 0xf; +#endif +} + +inline void +powerpc_cr_register::set_so(int crfd, bool v) +{ +#ifdef PPC_HAVE_SPLIT_CR + crf[crfd].set_so(v); +#else + const uint32 m = standalone_CR_SO_field::mask() << (28 - 4 * crfd); + cr = (cr & ~m) | (v ? m : 0); +#endif +} + +inline void +powerpc_cr_register::compute(int crfd, int32 v) +{ +#ifdef PPC_HAVE_SPLIT_CR + crf[crfd].compute(v); +#else + const uint32 m = (standalone_CR_LT_field::mask() | + standalone_CR_GT_field::mask() | + standalone_CR_EQ_field::mask() ) << (28 - 4 * crfd); + cr = (cr & ~m); + if (v < 0) + cr |= standalone_CR_LT_field::mask() << (28 - 4 * crfd); + else if (v > 0) + cr |= standalone_CR_GT_field::mask() << (28 - 4 * crfd); + else + cr |= standalone_CR_EQ_field::mask() << (28 - 4 * crfd); +#endif +} + +inline void +powerpc_cr_register::set(uint32 v) +{ +#ifdef PPC_HAVE_SPLIT_CR + crf[0].set(CR_field<0>::extract(v)); + crf[1].set(CR_field<1>::extract(v)); + crf[2].set(CR_field<2>::extract(v)); + crf[3].set(CR_field<3>::extract(v)); + crf[4].set(CR_field<4>::extract(v)); + crf[5].set(CR_field<5>::extract(v)); + crf[6].set(CR_field<6>::extract(v)); + crf[7].set(CR_field<7>::extract(v)); +#else + cr = v; +#endif +} + +inline uint32 +powerpc_cr_register::get() const +{ +#ifdef PPC_HAVE_SPLIT_CR + uint32 cr = crf[0].get(); + for (int i = 1; i < 8; i++) + cr = (cr << 4) | crf[i].get(); +#endif + return cr; +} + +inline bool +powerpc_cr_register::test(int condition) const +{ +#ifdef PPC_HAVE_SPLIT_CR + return crf[condition / 4].test(condition % 4); +#else + return (cr << condition) & 0x80000000; +#endif +} + + +/** + * XER register (SPR1) + **/ + +class powerpc_xer_register +{ + bool so; + bool ov; + bool ca; + uint32 byte_count; +public: + powerpc_xer_register(); + void set(uint32 xer); + uint32 get() const; + void set_so(bool v) { so = v; } + bool get_so() const { return so; } + void set_ov(bool v) { ov = v; if (v) so = true; } + bool get_ov() const { return ov; } + void set_ca(bool v) { ca = v; } + bool get_ca() const { return ca; } + void set_count(uint32 v) { byte_count = v; } + uint32 get_count() const { return byte_count; } +}; + +inline +powerpc_xer_register::powerpc_xer_register() + : so(false), ov(false), ca(false), byte_count(0) +{ } + +inline uint32 +powerpc_xer_register::get() const +{ + return (so << 31) | (ov << 30) | (ca << 29) | byte_count; +} + +inline void +powerpc_xer_register::set(uint32 xer) +{ + so = XER_SO_field::extract(xer); + ov = XER_OV_field::extract(xer); + ca = XER_CA_field::extract(xer); + byte_count = XER_COUNT_field::extract(xer); +} + + +/** + * VEA Register Set + **/ + +struct powerpc_registers +{ + enum { + GPR_BASE = 0, + FPR_BASE = 32, + CR = 64, + FPSCR, + XER, + LR, CTR, + TBL, TBU, + PC, + SP = GPR_BASE + 1 + }; + + static inline int GPR(int r) { return GPR_BASE + r; } + static inline int FPR(int r) { return FPR_BASE + r; } + + uint32 gpr[32]; // General-Purpose Registers + double fpr[32]; // Floating-Point Registers + powerpc_cr_register cr; // Condition Register + uint32 fpscr; // Floating-Point Status and Control Register + powerpc_xer_register xer; // XER Register (SPR 1) + uint32 lr; // Link Register (SPR 8) + uint32 ctr; // Count Register (SPR 9) + uint32 pc; // Program Counter + uint32 tbl, tbu; // Time Base + static uint32 reserve_valid; + static uint32 reserve_addr; + static uint32 reserve_data; +}; + +#endif /* PPC_REGISTERS_H */ diff --git a/SheepShaver/src/kpx_cpu/src/cpu/vm.hpp b/SheepShaver/src/kpx_cpu/src/cpu/vm.hpp new file mode 100644 index 00000000..154518a3 --- /dev/null +++ b/SheepShaver/src/kpx_cpu/src/cpu/vm.hpp @@ -0,0 +1,260 @@ +/* + * vm.hpp - Virtual memory management + * + * Kheperix (C) 2003 Gwenole Beauchesne + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef VM_H +#define VM_H + +/// +/// Optimized memory accessors +/// + +#if defined(__i386__) || defined(__powerpc__) || defined(__m68k__) || defined(__x86_64__) +# define VM_CAN_ACCESS_UNALIGNED +#endif + +#ifdef WORDS_BIGENDIAN + +#ifdef VM_CAN_ACCESS_UNALIGNED + +#ifndef VM_OPTIMIZED_MEMORY_ACCESS_2 +#define VM_OPTIMIZED_MEMORY_ACCESS_2 +static inline uint32 vm_do_read_memory_2(uint16 *a) { return *a; } +static inline void vm_do_write_memory_2(uint16 *a, uint32 v) { *a = v; } +#endif + +#ifndef VM_OPTIMIZED_MEMORY_ACCESS_4 +#define VM_OPTIMIZED_MEMORY_ACCESS_4 +static inline uint32 vm_do_read_memory_4(uint32 *a) { return *a; } +static inline void vm_do_write_memory_4(uint32 *a, uint32 v) { *a = v; } +#endif + +#ifndef VM_OPTIMIZED_MEMORY_ACCESS_8 +#define VM_OPTIMIZED_MEMORY_ACCESS_8 +static inline uint64 vm_do_read_memory_8(uint64 *a) { return *a; } +static inline void vm_do_write_memory_8(uint64 *a, uint64 v) { *a = v; } +#endif + +#endif /* VM_CAN_ACCESS_UNALIGNED */ + +#else + +#endif /* WORDS_BIGENDIAN */ + +/// +/// Generic core memory accessors +/// + +static inline uint32 vm_do_read_memory_1(uint8 *a) +{ + return *a; +} +static inline void vm_do_write_memory_1(uint8 *a, uint32 v) +{ + *a = v; +} + +#ifndef VM_OPTIMIZED_MEMORY_ACCESS_2 +static inline uint32 vm_do_read_memory_2(uint16 *a) +{ + uint8 * b = (uint8 *)a; + return (b[0] << 8) | b[1]; +} +static inline void vm_do_write_memory_2(uint16 *a, uint32 v) +{ + uint8 * b = (uint8 *)a; + b[0] = v >> 8; + b[1] = v; +} +#endif + +#ifndef VM_OPTIMIZED_MEMORY_ACCESS_4 +static inline uint32 vm_do_read_memory_4(uint32 *a) +{ + uint8 * b = (uint8 *)a; + return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; +} +static inline void vm_do_write_memory_4(uint32 *a, uint32 v) +{ + uint8 * b = (uint8 *)a; + b[0] = v >> 24; + b[1] = v >> 16; + b[2] = v >> 8; + b[3] = v; +} +#endif + +#ifndef VM_OPTIMIZED_MEMORY_ACCESS_8 +static inline uint64 vm_do_read_memory_8(uint64 *a) +{ + uint8 * b = (uint8 *)a; + return + ((uint64)b[0] << 56) | + ((uint64)b[1] << 48) | + ((uint64)b[2] << 40) | + ((uint64)b[3] << 32) | + (b[4] << 24) | (b[5] << 16) | (b[6] << 8) | b[7]; +} + +static inline void vm_do_write_memory_8(uint64 *a, uint64 v) +{ + uint8 * b = (uint8 *)a; + b[0] = v >> 56; + b[1] = v >> 48; + b[2] = v >> 40; + b[3] = v >> 32; + b[4] = v >> 24; + b[5] = v >> 16; + b[6] = v >> 8; + b[7] = v; +} +#endif + +#ifndef VM_OPTIMIZED_MEMORY_ACCESS_2_REVERSED +static inline uint32 vm_do_read_memory_2_reversed(uint16 *a) +{ + uint8 * b = (uint8 *)a; + return b[0] | (b[1] << 8); +} +static inline void vm_do_write_memory_2_reversed(uint16 *a, uint32 v) +{ + uint8 * b = (uint8 *)a; + b[0] = v; + b[1] = v >> 8; +} +#endif + +#ifndef VM_OPTIMIZED_MEMORY_ACCESS_4_REVERSED +static inline uint32 vm_do_read_memory_4_reversed(uint32 *a) +{ + uint8 * b = (uint8 *)a; + return b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24); +} +static inline void vm_do_write_memory_4_reversed(uint32 *a, uint32 v) +{ + uint8 * b = (uint8 *)a; + b[0] = v; + b[1] = v >> 8; + b[2] = v >> 16; + b[3] = v >> 24; +} +#endif + +/// +/// Actual memory accessors visible to CPU through virtual addresses +/// + +typedef uintptr vm_addr_t; + +#if REAL_ADDRESSING +const uintptr VMBaseDiff = 0; +#endif + +#if REAL_ADDRESSING +static inline uint8 * vm_do_get_real_address(vm_addr_t addr) +{ + return (uint8 *)VMBaseDiff + addr; +} +static inline vm_addr_t vm_do_get_virtual_address(uint8 *addr) +{ + return (uintptr)addr - VMBaseDiff; +} +static inline uint32 vm_read_memory_1(vm_addr_t addr) +{ + uint8 * const m = vm_do_get_real_address(addr); + return vm_do_read_memory_1(m); +} +static inline uint32 vm_read_memory_2(vm_addr_t addr) +{ + uint16 * const m = (uint16 *)vm_do_get_real_address(addr); + return vm_do_read_memory_2(m); +} +static inline uint32 vm_read_memory_4(vm_addr_t addr) +{ + uint32 * const m = (uint32 *)vm_do_get_real_address(addr); + return vm_do_read_memory_4(m); +} +static inline uint64 vm_read_memory_8(vm_addr_t addr) +{ + uint64 * const m = (uint64 *)vm_do_get_real_address(addr); + return vm_do_read_memory_8(m); +} +#define vm_read_memory_1_reversed vm_read_memory_1 +static inline uint32 vm_read_memory_2_reversed(vm_addr_t addr) +{ + uint16 * const m = (uint16 *)vm_do_get_real_address(addr); + return vm_do_read_memory_2_reversed(m); +} +static inline uint32 vm_read_memory_4_reversed(vm_addr_t addr) +{ + uint32 * const m = (uint32 *)vm_do_get_real_address(addr); + return vm_do_read_memory_4_reversed(m); +} +static inline void vm_write_memory_1(vm_addr_t addr, uint32 value) +{ + uint8 * const m = vm_do_get_real_address(addr); + return vm_do_write_memory_1(m, value); +} +static inline void vm_write_memory_2(vm_addr_t addr, uint32 value) +{ + uint16 * const m = (uint16 *)vm_do_get_real_address(addr); + return vm_do_write_memory_2(m, value); +} +static inline void vm_write_memory_4(vm_addr_t addr, uint32 value) +{ + uint32 * const m = (uint32 *)vm_do_get_real_address(addr); + return vm_do_write_memory_4(m, value); +} +static inline void vm_write_memory_8(vm_addr_t addr, uint64 value) +{ + uint64 * const m = (uint64 *)vm_do_get_real_address(addr); + return vm_do_write_memory_8(m, value); +} +#define vm_write_memory_1_reversed vm_write_memory_1 +static inline void vm_write_memory_2_reversed(vm_addr_t addr, uint32 value) +{ + uint16 * const m = (uint16 *)vm_do_get_real_address(addr); + return vm_do_write_memory_2_reversed(m, value); +} +static inline void vm_write_memory_4_reversed(vm_addr_t addr, uint32 value) +{ + uint32 * const m = (uint32 *)vm_do_get_real_address(addr); + return vm_do_write_memory_4_reversed(m, value); +} +static inline void *vm_memset(vm_addr_t addr, int c, size_t n) +{ + uint8 * const m = (uint8 *)vm_do_get_real_address(addr); + return memset(m, c, n); +} +static inline void *vm_memcpy(void *dest, vm_addr_t src, size_t n) +{ + return memcpy(dest, vm_do_get_real_address(src), n); +} +static inline void *vm_memcpy(vm_addr_t dest, const void *src, size_t n) +{ + return memcpy(vm_do_get_real_address(dest), src, n); +} +static inline void *vm_memcpy(vm_addr_t dest, vm_addr_t src, size_t n) +{ + return memcpy(vm_do_get_real_address(dest), vm_do_get_real_address(src), n); +} +#endif + +#endif /* VM_H */ +