Merge in old kpx_cpu snapshot for debugging

This commit is contained in:
gbeauche 2003-09-07 14:25:05 +00:00
parent 10aa71cb58
commit 029a7fd85b
19 changed files with 5801 additions and 0 deletions

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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<data>::~lazy_allocator()
{
pool * p = pools;
while (p) {
pool * d = p;
p = p->next;
free(d);
}
}
template< class data >
data * lazy_allocator<data>::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<data>::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<data_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 */

View File

@ -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 */

View File

@ -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 <stdio.h>
#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 <sys/ucontext.h>
#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));
}

View File

@ -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 T> 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<entry> 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 T> class block_allocator >
block_cache< block_info, block_allocator >::block_cache()
{
initialize();
}
template< class block_info, template<class T> class block_allocator >
inline block_cache< block_info, block_allocator >::~block_cache()
{
clear();
}
template< class block_info, template<class T> 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 T> 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 T> 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 T> 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 T> 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 T> 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 T> 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 T> 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 T> 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 T> 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 T> 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 T> 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 T> 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 T> 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 */

View File

@ -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 ";", $_ } (<STDIN>);
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;
}

View File

@ -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<FB, 31> {
enum { value = 0xffffffff >> FB };
};
template< int FB, int FE >
struct bit_field {
static inline uint32 mask() {
return static_mask<FB, FE>::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<bool, false> NAME##_0; \
typedef fake_bit_field<bool, true> 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<int, -1> RA_FIELD_A; // GPR(RA)
typedef rA_field RA_FIELD_G; // RA ? GPR(RA) : 0
typedef fake_bit_field<int, 0> RA_FIELD_0; // R0 -> 0
#endif /* PPC_BITFIELDS_H */

View File

@ -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 */

View File

@ -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 */

View File

@ -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

View File

@ -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 <vector>
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<instr_info_t> 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 */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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<XER_CA_field> {
static inline uint32 get(powerpc_cpu * cpu, uint32) {
return cpu->xer().get_ca();
}
};
template<>
struct xer_operand<XER_COUNT_field> {
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<rA_field, op_compl> operand_RA_compl;
typedef gpr_operand<rA_field> operand_RA;
typedef gpr_operand<rB_field> operand_RB;
typedef gpr_operand<rS_field> operand_RS;
typedef gpr_operand<rD_field> operand_RD;
typedef input_gpr_except<rA_field, 0> 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<frA_field> operand_fp_RA;
typedef fpr_operand<frB_field> operand_fp_RB;
typedef fpr_operand<frC_field> operand_fp_RC;
typedef fpr_operand<frD_field> operand_fp_RD;
typedef fpr_operand<frS_field> operand_fp_RS;
typedef xer_operand<XER_CA_field> operand_XER_CA;
typedef xer_operand<XER_COUNT_field> operand_XER_COUNT;
typedef fpscr_operand<FPSCR_RN_field> 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<FIELD##_field, op_##OP> 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 */

View File

@ -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 <math.h>
/**
* 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 */

View File

@ -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 */

View File

@ -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 */