mirror of
https://github.com/kanjitalk755/macemu.git
synced 2024-12-20 15:29:48 +00:00
Merge in old kpx_cpu snapshot for debugging
This commit is contained in:
parent
10aa71cb58
commit
029a7fd85b
110
SheepShaver/src/kpx_cpu/include/basic-blockinfo.hpp
Normal file
110
SheepShaver/src/kpx_cpu/include/basic-blockinfo.hpp
Normal 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 */
|
79
SheepShaver/src/kpx_cpu/include/basic-cpu.hpp
Normal file
79
SheepShaver/src/kpx_cpu/include/basic-cpu.hpp
Normal 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 */
|
29
SheepShaver/src/kpx_cpu/include/basic-plugin.hpp
Normal file
29
SheepShaver/src/kpx_cpu/include/basic-plugin.hpp
Normal 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 */
|
126
SheepShaver/src/kpx_cpu/include/block-alloc.hpp
Normal file
126
SheepShaver/src/kpx_cpu/include/block-alloc.hpp
Normal 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 */
|
50
SheepShaver/src/kpx_cpu/include/task-plugin.hpp
Normal file
50
SheepShaver/src/kpx_cpu/include/task-plugin.hpp
Normal 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 */
|
836
SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp
Normal file
836
SheepShaver/src/kpx_cpu/sheepshaver_glue.cpp
Normal 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));
|
||||||
|
}
|
240
SheepShaver/src/kpx_cpu/src/cpu/block-cache.hpp
Normal file
240
SheepShaver/src/kpx_cpu/src/cpu/block-cache.hpp
Normal 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 */
|
52
SheepShaver/src/kpx_cpu/src/cpu/ppc/genexec.pl
Executable file
52
SheepShaver/src/kpx_cpu/src/cpu/ppc/genexec.pl
Executable 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;
|
||||||
|
}
|
213
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-bitfields.hpp
Normal file
213
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-bitfields.hpp
Normal 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 */
|
43
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-blockinfo.hpp
Normal file
43
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-blockinfo.hpp
Normal 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 */
|
193
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-config.hpp
Normal file
193
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-config.hpp
Normal 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 */
|
375
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.cpp
Normal file
375
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.cpp
Normal 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
|
331
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp
Normal file
331
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-cpu.hpp
Normal 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 */
|
1027
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-decode.cpp
Normal file
1027
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-decode.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1029
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-execute.cpp
Normal file
1029
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-execute.cpp
Normal file
File diff suppressed because it is too large
Load Diff
236
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-operands.hpp
Normal file
236
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-operands.hpp
Normal 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 */
|
198
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-operations.hpp
Normal file
198
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-operations.hpp
Normal 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 */
|
374
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-registers.hpp
Normal file
374
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-registers.hpp
Normal 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 */
|
260
SheepShaver/src/kpx_cpu/src/cpu/vm.hpp
Normal file
260
SheepShaver/src/kpx_cpu/src/cpu/vm.hpp
Normal 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 */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user