mirror of
https://github.com/kanjitalk755/macemu.git
synced 2024-12-25 17:29:19 +00:00
Merge in-progress PowerPC "JIT1" engine for AMD64, IA-32, PPC.
The merge probably got wrong as there are some problems probably due to the experiment begining with CR deferred evaluation. With nbench/ppc, performance improvement was around 2x. With nbench on x86, performance improvement was around 4x on average. Incompatible change: instr_info_t has a new field in the middle. But since insertion of PPC_I(XXX) identifiers is auto-generated, there is no problem.
This commit is contained in:
parent
7968a20100
commit
73d51962f6
2360
SheepShaver/src/kpx_cpu/include/elf-defs.h
Normal file
2360
SheepShaver/src/kpx_cpu/include/elf-defs.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -45,7 +45,7 @@ class nv_mem_fun_t : public std::unary_function<T, R> {
|
||||
public:
|
||||
nv_mem_fun_t(pmf_t pmf) : pf(nv_mem_fun_of<pmf_t, pf_t>(pmf)) {}
|
||||
R operator()(T *p) const { return (*pf)(p); }
|
||||
operator bool () const { return pf; }
|
||||
pf_t ptr() const { return pf; }
|
||||
};
|
||||
|
||||
template< class R, class T >
|
||||
@ -56,7 +56,7 @@ class const_nv_mem_fun_t : public std::unary_function<T, R> {
|
||||
public:
|
||||
const_nv_mem_fun_t(pmf_t const pmf) : pf(nv_mem_fun_of<pmf_t, pf_t>(pmf)) {}
|
||||
R operator()(const T *p) const { return (*pf)(p); }
|
||||
operator bool () const { return pf; }
|
||||
pf_t ptr() const { return pf; }
|
||||
};
|
||||
|
||||
template< class R, class T, class A >
|
||||
@ -67,7 +67,7 @@ class nv_mem_fun1_t : public std::binary_function<T, A, R> {
|
||||
public:
|
||||
nv_mem_fun1_t(pmf_t pmf) : pf(nv_mem_fun_of<pmf_t, pf_t>(pmf)) {}
|
||||
R operator()(T *p, A x) const { return (*pf)(p, x); }
|
||||
operator bool () const { return pf; }
|
||||
pf_t ptr() const { return pf; }
|
||||
};
|
||||
|
||||
template< class R, class T, class A >
|
||||
@ -78,45 +78,49 @@ class const_nv_mem_fun1_t : public std::binary_function<T, A, R> {
|
||||
public:
|
||||
const_nv_mem_fun1_t(pmf_t const pmf) : pf(nv_mem_fun_of<pmf_t, pf_t>(pmf)) {}
|
||||
R operator()(const T *p, A x) const { return (*pf)(p, x); }
|
||||
operator bool () const { return pf; }
|
||||
pf_t ptr() const { return pf; }
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
template< class R, class T >
|
||||
class nv_mem_fun_t : public std::unary_function<T, R> {
|
||||
R (T::*pf)();
|
||||
typedef R (T::*pmf_t)();
|
||||
pmf_t pf;
|
||||
public:
|
||||
nv_mem_fun_t(R (T::*pmf)()) : pf(pmf) {}
|
||||
R operator()(T *p) const { return (p->*pf)(); }
|
||||
operator bool () const { return pf; }
|
||||
pmf_t ptr() const { return pf; }
|
||||
};
|
||||
|
||||
template< class R, class T >
|
||||
class const_nv_mem_fun_t : public std::unary_function<T, R> {
|
||||
R (T::*pf)() const;
|
||||
typedef R (T::*pmf_t)() const;
|
||||
pmf_t pf;
|
||||
public:
|
||||
const_nv_mem_fun_t(R (T::*pmf)() const) : pf(pmf) {}
|
||||
R operator()(const T *p) const { return (p->*pf)(); }
|
||||
operator bool () const { return pf; }
|
||||
pmf_t ptr() const { return pf; }
|
||||
};
|
||||
|
||||
template< class R, class T, class A >
|
||||
class nv_mem_fun1_t : public std::binary_function<T, A, R> {
|
||||
R (T::*pf)(A);
|
||||
typedef R (T::*pmf_t)(A);
|
||||
pmf_t pf;
|
||||
public:
|
||||
nv_mem_fun1_t(R (T::*pmf)(A)) : pf(pmf) {}
|
||||
R operator()(T *p, A x) const { return (p->*pf)(x); }
|
||||
operator bool () const { return pf; }
|
||||
pmf_t ptr() const { return pf; }
|
||||
};
|
||||
|
||||
template< class R, class T, class A >
|
||||
class const_nv_mem_fun1_t : public std::binary_function<T, A, R> {
|
||||
R (T::*pf)(A) const;
|
||||
typedef R (T::*pmf_t)(A) const;
|
||||
pmf_t pf;
|
||||
public:
|
||||
const_nv_mem_fun1_t(R (T::*pmf)(A) const) : pf(pmf) {}
|
||||
R operator()(const T *p, A x) const { return (p->*pf)(x); }
|
||||
operator bool () const { return pf; }
|
||||
pmf_t ptr() const { return pf; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "sigsegv.h"
|
||||
#include "cpu/ppc/ppc-cpu.hpp"
|
||||
#include "cpu/ppc/ppc-operations.hpp"
|
||||
#include "cpu/ppc/ppc-instructions.hpp"
|
||||
|
||||
// Used for NativeOp trampolines
|
||||
#include "video.h"
|
||||
@ -97,6 +98,11 @@ static sigsegv_return_t sigsegv_handler(sigsegv_address_t, sigsegv_address_t);
|
||||
* PowerPC emulator glue with special 'sheep' opcodes
|
||||
**/
|
||||
|
||||
enum {
|
||||
PPC_I(SHEEP) = PPC_I(MAX),
|
||||
PPC_I(SHEEP_MAX)
|
||||
};
|
||||
|
||||
class sheepshaver_cpu
|
||||
: public powerpc_cpu
|
||||
{
|
||||
@ -165,6 +171,7 @@ void sheepshaver_cpu::init_decoder()
|
||||
{ "sheep",
|
||||
(execute_pmf)&sheepshaver_cpu::execute_sheep,
|
||||
NULL,
|
||||
PPC_I(SHEEP),
|
||||
D_form, 6, 0, CFLOW_JUMP | CFLOW_TRAP
|
||||
}
|
||||
};
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* dyngen defines for micro operation code
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef DYNGEN_TARGET_EXEC_H
|
||||
#define DYNGEN_TARGET_EXEC_H
|
||||
|
||||
enum {
|
||||
/* callee save registers */
|
||||
#define AREG0 "rbp"
|
||||
AREG0_ID = 5,
|
||||
|
||||
#define AREG1 "rbx"
|
||||
AREG1_ID = 3,
|
||||
|
||||
#define AREG2 "r12"
|
||||
AREG2_ID = 12,
|
||||
|
||||
#define AREG3 "r13"
|
||||
AREG3_ID = 13,
|
||||
|
||||
#define AREG4 "r14"
|
||||
AREG4_ID = 14,
|
||||
|
||||
#define AREG5 "r15"
|
||||
AREG5_ID = 15,
|
||||
};
|
||||
|
||||
#endif /* DYNGEN_TARGET_EXEC_H */
|
@ -0,0 +1 @@
|
||||
#include "cpu/jit/t-dummy/jit-target-cache.hpp"
|
548
SheepShaver/src/kpx_cpu/src/cpu/jit/basic-dyngen-ops.cpp
Normal file
548
SheepShaver/src/kpx_cpu/src/cpu/jit/basic-dyngen-ops.cpp
Normal file
@ -0,0 +1,548 @@
|
||||
/*
|
||||
* dyngen-ops.hpp - Synthetic opcodes
|
||||
*
|
||||
* 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 "cpu/vm.hpp"
|
||||
#include "cpu/jit/dyngen-exec.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
// We need at least 5 general purpose registers
|
||||
struct basic_cpu;
|
||||
#ifdef REG_CPU
|
||||
register basic_cpu *CPU asm(REG_CPU);
|
||||
#else
|
||||
#define CPU ((basic_cpu *)CPUPARAM)
|
||||
#endif
|
||||
#if SIZEOF_VOID_P == 8
|
||||
#define REG32(X) ((uint32)X)
|
||||
#else
|
||||
#define REG32(X) X
|
||||
#endif
|
||||
#define A0 REG32(reg_A0)
|
||||
register uintptr reg_A0 asm(REG_A0);
|
||||
#define T0 REG32(reg_T0)
|
||||
register uintptr reg_T0 asm(REG_T0);
|
||||
#define T1 REG32(reg_T1)
|
||||
register uintptr reg_T1 asm(REG_T1);
|
||||
#ifdef REG_T2
|
||||
#define T2 REG32(reg_T2)
|
||||
register uintptr reg_T2 asm(REG_T2);
|
||||
#endif
|
||||
#ifdef REG_T3
|
||||
#define T3 REG32(reg_T3)
|
||||
register uintptr reg_T3 asm(REG_T3);
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Native ALU operations optimization
|
||||
**/
|
||||
|
||||
#ifndef do_udiv_32
|
||||
#define do_udiv_32(x, y) ((uint32)x / (uint32)y)
|
||||
#endif
|
||||
#ifndef do_sdiv_32
|
||||
#define do_sdiv_32(x, y) ((int32)x / (int32)y)
|
||||
#endif
|
||||
#ifndef do_rol_32
|
||||
#define do_rol_32(x, y) ((x << y) | (x >> (32 - y)))
|
||||
#endif
|
||||
#ifndef do_ror_32
|
||||
#define do_ror_32(x, y) ((x >> y) | (x << (32 - y)))
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* ALU operations
|
||||
**/
|
||||
|
||||
#define DEFINE_OP(NAME, CODE) \
|
||||
void OPPROTO op_##NAME(void) \
|
||||
{ \
|
||||
CODE; \
|
||||
}
|
||||
|
||||
// Register moves
|
||||
DEFINE_OP(mov_32_T0_im, T0 = PARAM1);
|
||||
DEFINE_OP(mov_32_T0_T1, T0 = T1);
|
||||
DEFINE_OP(mov_32_T0_A0, T0 = A0);
|
||||
DEFINE_OP(mov_32_T1_im, T1 = PARAM1);
|
||||
DEFINE_OP(mov_32_T1_T0, T1 = T0);
|
||||
DEFINE_OP(mov_32_T1_A0, T1 = A0);
|
||||
DEFINE_OP(mov_32_A0_im, A0 = PARAM1);
|
||||
DEFINE_OP(mov_32_A0_T0, A0 = T0);
|
||||
DEFINE_OP(mov_32_A0_T1, A0 = T1);
|
||||
|
||||
void OPPROTO op_mov_ad_A0_im(void)
|
||||
{
|
||||
#if SIZEOF_VOID_P == 8
|
||||
#if defined(__x86_64__)
|
||||
asm volatile ("movabsq $__op_param1,%" REG_A0);
|
||||
#else
|
||||
#error "unsupported 64-bit value move in"
|
||||
#endif
|
||||
#else
|
||||
A0 = PARAM1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Arithmetic operations
|
||||
DEFINE_OP(add_32_T0_T1, T0 += T1);
|
||||
DEFINE_OP(add_32_T0_im, T0 += PARAM1);
|
||||
DEFINE_OP(add_32_T0_1, T0 += 1);
|
||||
DEFINE_OP(add_32_T0_2, T0 += 2);
|
||||
DEFINE_OP(add_32_T0_4, T0 += 4);
|
||||
DEFINE_OP(add_32_T0_8, T0 += 8);
|
||||
DEFINE_OP(sub_32_T0_T1, T0 -= T1);
|
||||
DEFINE_OP(sub_32_T0_im, T0 -= PARAM1);
|
||||
DEFINE_OP(sub_32_T0_1, T0 -= 1);
|
||||
DEFINE_OP(sub_32_T0_2, T0 -= 2);
|
||||
DEFINE_OP(sub_32_T0_4, T0 -= 4);
|
||||
DEFINE_OP(sub_32_T0_8, T0 -= 8);
|
||||
DEFINE_OP(add_32_T1_T0, T1 += T0);
|
||||
DEFINE_OP(add_32_T1_im, T1 += PARAM1);
|
||||
DEFINE_OP(add_32_T1_1, T1 += 1);
|
||||
DEFINE_OP(add_32_T1_2, T1 += 2);
|
||||
DEFINE_OP(add_32_T1_4, T1 += 4);
|
||||
DEFINE_OP(add_32_T1_8, T1 += 8);
|
||||
DEFINE_OP(sub_32_T1_T0, T1 -= T0);
|
||||
DEFINE_OP(sub_32_T1_im, T1 -= PARAM1);
|
||||
DEFINE_OP(sub_32_T1_1, T1 -= 1);
|
||||
DEFINE_OP(sub_32_T1_2, T1 -= 2);
|
||||
DEFINE_OP(sub_32_T1_4, T1 -= 4);
|
||||
DEFINE_OP(sub_32_T1_8, T1 -= 8);
|
||||
DEFINE_OP(add_32_A0_T1, A0 += T1);
|
||||
DEFINE_OP(add_32_A0_im, A0 += PARAM1);
|
||||
DEFINE_OP(add_32_A0_1, A0 += 1);
|
||||
DEFINE_OP(add_32_A0_2, A0 += 2);
|
||||
DEFINE_OP(add_32_A0_4, A0 += 4);
|
||||
DEFINE_OP(add_32_A0_8, A0 += 8);
|
||||
DEFINE_OP(sub_32_A0_T1, A0 -= T1);
|
||||
DEFINE_OP(sub_32_A0_im, A0 -= PARAM1);
|
||||
DEFINE_OP(sub_32_A0_1, A0 -= 1);
|
||||
DEFINE_OP(sub_32_A0_2, A0 -= 2);
|
||||
DEFINE_OP(sub_32_A0_4, A0 -= 4);
|
||||
DEFINE_OP(sub_32_A0_8, A0 -= 8);
|
||||
DEFINE_OP(umul_32_T0_T1, T0 = (uint32)T0 * (uint32)T1);
|
||||
DEFINE_OP(smul_32_T0_T1, T0 = (int32)T0 * (int32)T1);
|
||||
DEFINE_OP(udiv_32_T0_T1, T0 = do_udiv_32(T0, T1));
|
||||
DEFINE_OP(sdiv_32_T0_T1, T0 = do_sdiv_32(T0, T1));
|
||||
DEFINE_OP(xchg_32_T0_T1, { uint32 tmp = T0; T0 = T1; T1 = tmp; });
|
||||
DEFINE_OP(bswap_16_T0, T0 = bswap_16(T0));
|
||||
DEFINE_OP(bswap_32_T0, T0 = bswap_32(T0));
|
||||
|
||||
// Logical operations
|
||||
DEFINE_OP(neg_32_T0, T0 = -T0);
|
||||
DEFINE_OP(not_32_T0, T0 = !T0);
|
||||
DEFINE_OP(not_32_T1, T1 = !T1);
|
||||
DEFINE_OP(and_32_T0_T1, T0 &= T1);
|
||||
DEFINE_OP(and_32_T0_im, T0 &= PARAM1);
|
||||
DEFINE_OP(or_32_T0_T1, T0 |= T1);
|
||||
DEFINE_OP(or_32_T0_im, T0 |= PARAM1);
|
||||
DEFINE_OP(xor_32_T0_T1, T0 ^= T1);
|
||||
DEFINE_OP(xor_32_T0_im, T0 ^= PARAM1);
|
||||
DEFINE_OP(orc_32_T0_T1, T0 |= ~T1);
|
||||
DEFINE_OP(andc_32_T0_T1, T0 &= ~T1);
|
||||
DEFINE_OP(nand_32_T0_T1, T0 = ~(T0 & T1));
|
||||
DEFINE_OP(nor_32_T0_T1, T0 = ~(T0 | T1));
|
||||
DEFINE_OP(eqv_32_T0_T1, T0 = ~(T0 ^ T1));
|
||||
DEFINE_OP(and_logical_T0_T1, T0 = T0 && T1);
|
||||
DEFINE_OP(or_logical_T0_T1, T0 = T0 || T1);
|
||||
|
||||
// Shift/Rotate operations
|
||||
DEFINE_OP(lsl_32_T0_T1, T0 = T0 << T1);
|
||||
DEFINE_OP(lsl_32_T0_im, T0 = T0 << PARAM1);
|
||||
DEFINE_OP(lsr_32_T0_T1, T0 = T0 >> T1);
|
||||
DEFINE_OP(lsr_32_T0_im, T0 = T0 >> PARAM1);
|
||||
DEFINE_OP(asr_32_T0_T1, T0 = ((int32)T0) >> T1);
|
||||
DEFINE_OP(asr_32_T0_im, T0 = ((int32)T0) >> PARAM1);
|
||||
DEFINE_OP(rol_32_T0_T1, T0 = do_rol_32(T0, T1));
|
||||
DEFINE_OP(rol_32_T0_im, T0 = do_rol_32(T0, PARAM1));
|
||||
DEFINE_OP(ror_32_T0_T1, T0 = do_ror_32(T0, T1));
|
||||
DEFINE_OP(ror_32_T0_im, T0 = do_ror_32(T0, PARAM1));
|
||||
|
||||
// Sign-/Zero-extension
|
||||
DEFINE_OP(se_16_32_T0, T0 = (int32)(int16)T0);
|
||||
DEFINE_OP(ze_16_32_T0, T0 = (uint32)(uint16)T0);
|
||||
DEFINE_OP(se_8_32_T0, T0 = (int32)(int8)T0);
|
||||
DEFINE_OP(ze_8_32_T0, T0 = (uint32)(uint8)T0);
|
||||
|
||||
#undef DEFINE_OP
|
||||
|
||||
|
||||
/**
|
||||
* Native FP operations optimization
|
||||
**/
|
||||
|
||||
#ifndef do_fabs
|
||||
#define do_fabs(x) fabs(x)
|
||||
#endif
|
||||
#ifndef do_fadd
|
||||
#define do_fadd(x, y) x + y
|
||||
#endif
|
||||
#ifndef do_fdiv
|
||||
#define do_fdiv(x, y) x / y
|
||||
#endif
|
||||
#ifndef do_fmadd
|
||||
#define do_fmadd(x, y, z) ((x * y) + z)
|
||||
#endif
|
||||
#ifndef do_fmsub
|
||||
#define do_fmsub(x, y, z) ((x * y) - z)
|
||||
#endif
|
||||
#ifndef do_fmul
|
||||
#define do_fmul(x, y) (x * y)
|
||||
#endif
|
||||
#ifndef do_fnabs
|
||||
#define do_fnabs(x) -fabs(x)
|
||||
#endif
|
||||
#ifndef do_fneg
|
||||
#define do_fneg(x) -x
|
||||
#endif
|
||||
#ifndef do_fnmadd
|
||||
#define do_fnmadd(x, y, z) -((x * y) + z)
|
||||
#endif
|
||||
#ifndef do_fnmsub
|
||||
#define do_fnmsub(x, y, z) -((x * y) - z)
|
||||
#endif
|
||||
#ifndef do_fsub
|
||||
#define do_fsub(x, y) x - y
|
||||
#endif
|
||||
#ifndef do_fmov
|
||||
#define do_fmov(x) x
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* FP double operations
|
||||
**/
|
||||
|
||||
#if 0
|
||||
|
||||
double OPPROTO op_lfd(void)
|
||||
{
|
||||
union { double d; uint64 j; } r;
|
||||
r.j = vm_do_read_memory_8((uint64 *)T1);
|
||||
return r.d;
|
||||
}
|
||||
|
||||
float OPPROTO op_lfs(void)
|
||||
{
|
||||
union { float f; uint32 i; } r;
|
||||
r.i = vm_do_read_memory_4((uint32 *)T1);
|
||||
return r.f;
|
||||
}
|
||||
|
||||
#define DEFINE_OP(NAME, OP, ARGS) \
|
||||
double OPPROTO op_##NAME(double F0, double F1, double F2) \
|
||||
{ \
|
||||
return do_##OP ARGS; \
|
||||
}
|
||||
|
||||
DEFINE_OP(fmov_F1, fmov, (F1));
|
||||
DEFINE_OP(fmov_F2, fmov, (F2));
|
||||
DEFINE_OP(fabs, fabs, (F0));
|
||||
DEFINE_OP(fadd, fadd, (F0, F1));
|
||||
DEFINE_OP(fdiv, fdiv, (F0, F1));
|
||||
DEFINE_OP(fmadd, fmadd, (F0, F1, F2));
|
||||
DEFINE_OP(fmsub, fmsub, (F0, F1, F2));
|
||||
DEFINE_OP(fmul, fmul, (F0, F1));
|
||||
DEFINE_OP(fnabs, fnabs, (F0));
|
||||
DEFINE_OP(fneg, fneg, (F0));
|
||||
DEFINE_OP(fnmadd, fnmadd, (F0, F1, F2));
|
||||
DEFINE_OP(fnmsub, fnmsub, (F0, F1, F2));
|
||||
DEFINE_OP(fsub, fsub, (F0, F1));
|
||||
|
||||
#undef DEFINE_OP
|
||||
|
||||
|
||||
/**
|
||||
* FP single operations
|
||||
**/
|
||||
|
||||
#define DEFINE_OP(NAME, OP, ARGS) \
|
||||
float OPPROTO op_##NAME(float F0, float F1, float F2) \
|
||||
{ \
|
||||
return do_##OP ARGS; \
|
||||
}
|
||||
|
||||
DEFINE_OP(fmovs_F1, fmov, (F1));
|
||||
DEFINE_OP(fmovs_F2, fmov, (F2));
|
||||
DEFINE_OP(fabss_F0, fabs, (F0));
|
||||
DEFINE_OP(fadds_F0_F1, fadd, (F0, F1));
|
||||
DEFINE_OP(fdivs_F0_F1, fdiv, (F0, F1));
|
||||
DEFINE_OP(fmadds_F0_F1_F2, fmadd, (F0, F1, F2));
|
||||
DEFINE_OP(fmsubs_F0_F1_F2, fmsub, (F0, F1, F2));
|
||||
DEFINE_OP(fmuls_F0_F1, fmul, (F0, F1));
|
||||
DEFINE_OP(fnabss_F0, fnabs, (F0));
|
||||
DEFINE_OP(fnegs_F0, fneg, (F0));
|
||||
DEFINE_OP(fnmadds_F0_F1_F2, fnmadd, (F0, F1, F2));
|
||||
DEFINE_OP(fnmsubs_F0_F1_F2, fnmsub, (F0, F1, F2));
|
||||
DEFINE_OP(fsubs_F0_F1, fsub, (F0, F1));
|
||||
|
||||
#undef DEFINE_OP
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Load/Store instructions
|
||||
**/
|
||||
|
||||
#define im PARAM1
|
||||
#define DEFINE_OP(BITS,REG,SIZE,OFFSET) \
|
||||
void OPPROTO op_load_u##BITS##_##REG##_A0_##OFFSET(void) \
|
||||
{ \
|
||||
REG = (uint32)(uint##BITS)vm_read_memory_##SIZE(A0 + OFFSET); \
|
||||
} \
|
||||
void OPPROTO op_load_s##BITS##_##REG##_A0_##OFFSET(void) \
|
||||
{ \
|
||||
REG = (int32)(int##BITS)vm_read_memory_##SIZE(A0 + OFFSET); \
|
||||
} \
|
||||
void OPPROTO op_store_##BITS##_##REG##_A0_##OFFSET(void) \
|
||||
{ \
|
||||
vm_write_memory_##SIZE(A0 + OFFSET, REG); \
|
||||
}
|
||||
|
||||
DEFINE_OP(32,T0,4,0);
|
||||
DEFINE_OP(32,T0,4,im);
|
||||
DEFINE_OP(32,T0,4,T1);
|
||||
DEFINE_OP(16,T0,2,0);
|
||||
DEFINE_OP(16,T0,2,im);
|
||||
DEFINE_OP(16,T0,2,T1);
|
||||
DEFINE_OP(8,T0,1,0);
|
||||
DEFINE_OP(8,T0,1,im);
|
||||
DEFINE_OP(8,T0,1,T1);
|
||||
|
||||
#undef im
|
||||
#undef DEFINE_OP
|
||||
|
||||
|
||||
/**
|
||||
* Control flow
|
||||
**/
|
||||
|
||||
#ifdef __i386__
|
||||
#define FORCE_RET() asm volatile ("ret")
|
||||
#endif
|
||||
#ifdef __x86_64__
|
||||
#define FORCE_RET() asm volatile ("ret")
|
||||
#endif
|
||||
#ifdef __powerpc__
|
||||
#define FORCE_RET() asm volatile ("blr")
|
||||
#endif
|
||||
#ifdef __s390__
|
||||
#define FORCE_RET() asm volatile ("br %r14")
|
||||
#endif
|
||||
#ifdef __alpha__
|
||||
#define FORCE_RET() asm volatile ("ret")
|
||||
#endif
|
||||
#ifdef __ia64__
|
||||
#define FORCE_RET() asm volatile ("br.ret.sptk.many b0;;")
|
||||
#endif
|
||||
#ifdef __sparc__
|
||||
#define FORCE_RET() asm volatile ("jmpl %i0 + 8, %g0\n" \
|
||||
"nop")
|
||||
#endif
|
||||
#ifdef __arm__
|
||||
#define FORCE_RET() asm volatile ("b exec_loop")
|
||||
#endif
|
||||
#ifdef __mc68000
|
||||
#define FORCE_RET() asm volatile ("rts")
|
||||
#endif
|
||||
|
||||
#define SLOW_DISPATCH(TARGET) do { \
|
||||
static const void __attribute__((unused)) *label1 = &&dummy_label1; \
|
||||
static const void __attribute__((unused)) *label2 = &&dummy_label2; \
|
||||
goto *((void *)TARGET); \
|
||||
dummy_label1: \
|
||||
dummy_label2: \
|
||||
dyngen_barrier(); \
|
||||
} while (0)
|
||||
|
||||
#if defined(__powerpc__)
|
||||
#define FAST_DISPATCH(TARGET) asm volatile ("b " #TARGET)
|
||||
#endif
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#define FAST_DISPATCH(TARGET) asm volatile ("jmp " #TARGET)
|
||||
#endif
|
||||
|
||||
extern "C" void OPPROTO op_execute(uint8 *entry_point, basic_cpu *this_cpu);
|
||||
void OPPROTO op_execute(uint8 *entry_point, basic_cpu *this_cpu)
|
||||
{
|
||||
typedef void (*func_t)(void);
|
||||
func_t func = (func_t)entry_point;
|
||||
#ifdef REG_CPU
|
||||
volatile uintptr saved_CPU = (uintptr)CPU;
|
||||
CPU = this_cpu;
|
||||
#endif
|
||||
#ifdef REG_A0
|
||||
volatile uintptr saved_A0 = reg_A0;
|
||||
#endif
|
||||
#ifdef REG_T0
|
||||
volatile uintptr saved_T0 = reg_T0;
|
||||
#endif
|
||||
#ifdef REG_T1
|
||||
volatile uintptr saved_T1 = reg_T1;
|
||||
#endif
|
||||
#ifdef REG_T2
|
||||
volatile uintptr saved_T2 = reg_T2;
|
||||
#endif
|
||||
#ifdef REG_T3
|
||||
volatile uintptr saved_T3 = reg_T3;
|
||||
#endif
|
||||
SLOW_DISPATCH(entry_point);
|
||||
func(); // NOTE: never called, fake to make compiler save return point
|
||||
asm volatile (".section \".data\"");
|
||||
asm volatile (".global op_exec_return_offset");
|
||||
asm volatile ("op_exec_return_offset:");
|
||||
asm volatile (".long 1f-op_execute");
|
||||
asm volatile (".size op_exec_return_offset,.-op_exec_return_offset");
|
||||
asm volatile (".previous");
|
||||
asm volatile ("1:");
|
||||
#ifdef REG_T3
|
||||
reg_T3 = saved_T3;
|
||||
#endif
|
||||
#ifdef REG_T2
|
||||
reg_T2 = saved_T2;
|
||||
#endif
|
||||
#ifdef REG_T1
|
||||
reg_T1 = saved_T1;
|
||||
#endif
|
||||
#ifdef REG_T0
|
||||
reg_T0 = saved_T0;
|
||||
#endif
|
||||
#ifdef REG_A0
|
||||
reg_A0 = saved_A0;
|
||||
#endif
|
||||
#ifdef REG_CPU
|
||||
CPU = (basic_cpu *)saved_CPU;
|
||||
#endif
|
||||
}
|
||||
|
||||
void OPPROTO op_jmp_slow(void)
|
||||
{
|
||||
SLOW_DISPATCH(PARAM1);
|
||||
}
|
||||
|
||||
void OPPROTO op_jmp_fast(void)
|
||||
{
|
||||
#ifdef FAST_DISPATCH
|
||||
FAST_DISPATCH(__op_param1);
|
||||
#else
|
||||
SLOW_DISPATCH(PARAM1);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Register calling conventions based arches don't need a stack frame
|
||||
#if defined(__powerpc__) || defined(__x86_64__)
|
||||
#define DEFINE_OP(NAME, CODE) \
|
||||
static void OPPROTO impl_##NAME(void) \
|
||||
{ \
|
||||
asm volatile (#NAME ":"); \
|
||||
CODE; \
|
||||
FORCE_RET(); \
|
||||
asm volatile ("." #NAME ":"); \
|
||||
asm volatile (".size " #NAME ",." #NAME "-" #NAME); \
|
||||
} \
|
||||
void OPPROTO helper_##NAME(void) __attribute__((weak, alias(#NAME)));
|
||||
#else
|
||||
#define DEFINE_OP(NAME, CODE) \
|
||||
void OPPROTO NAME(void) \
|
||||
{ \
|
||||
CODE; \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define CALL(CALL_CODE) CALL_CODE
|
||||
|
||||
DEFINE_OP(op_invoke, {
|
||||
typedef void (*func_t)(void);
|
||||
func_t func = (func_t)reg_A0;
|
||||
CALL(func());
|
||||
});
|
||||
|
||||
DEFINE_OP(op_invoke_T0, {
|
||||
typedef void (*func_t)(uint32);
|
||||
func_t func = (func_t)reg_A0;
|
||||
CALL(func(T0));
|
||||
});
|
||||
|
||||
DEFINE_OP(op_invoke_im, {
|
||||
typedef void (*func_t)(uint32);
|
||||
func_t func = (func_t)reg_A0;
|
||||
CALL(func(PARAM1));
|
||||
});
|
||||
|
||||
DEFINE_OP(op_invoke_CPU, {
|
||||
typedef void (*func_t)(void *);
|
||||
func_t func = (func_t)reg_A0;
|
||||
CALL(func(CPU));
|
||||
});
|
||||
|
||||
DEFINE_OP(op_invoke_CPU_T0, {
|
||||
typedef void (*func_t)(void *, uint32);
|
||||
func_t func = (func_t)reg_A0;
|
||||
CALL(func(CPU, T0));
|
||||
});
|
||||
|
||||
DEFINE_OP(op_invoke_CPU_im, {
|
||||
typedef void (*func_t)(void *, uint32);
|
||||
func_t func = (func_t)reg_A0;
|
||||
CALL(func(CPU, PARAM1));
|
||||
});
|
||||
|
||||
DEFINE_OP(op_invoke_direct, {
|
||||
typedef void (*func_t)(void);
|
||||
func_t func = (func_t)PARAM1;
|
||||
CALL(func());
|
||||
});
|
||||
|
||||
DEFINE_OP(op_invoke_direct_T0, {
|
||||
typedef void (*func_t)(uint32);
|
||||
func_t func = (func_t)PARAM1;
|
||||
CALL(func(T0));
|
||||
});
|
||||
|
||||
DEFINE_OP(op_invoke_direct_im, {
|
||||
typedef void (*func_t)(uint32);
|
||||
func_t func = (func_t)PARAM1;
|
||||
CALL(func(PARAM2));
|
||||
});
|
||||
|
||||
DEFINE_OP(op_invoke_direct_CPU, {
|
||||
typedef void (*func_t)(void *);
|
||||
func_t func = (func_t)PARAM1;
|
||||
CALL(func(CPU));
|
||||
});
|
||||
|
||||
DEFINE_OP(op_invoke_direct_CPU_T0, {
|
||||
typedef void (*func_t)(void *, uint32);
|
||||
func_t func = (func_t)PARAM1;
|
||||
CALL(func(CPU, T0));
|
||||
});
|
||||
|
||||
DEFINE_OP(op_invoke_direct_CPU_im, {
|
||||
typedef void (*func_t)(void *, uint32);
|
||||
func_t func = (func_t)PARAM1;
|
||||
CALL(func(CPU, PARAM2));
|
||||
});
|
||||
|
||||
#undef DEFINE_OP
|
104
SheepShaver/src/kpx_cpu/src/cpu/jit/basic-dyngen.cpp
Normal file
104
SheepShaver/src/kpx_cpu/src/cpu/jit/basic-dyngen.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* dyngen-glue.hpp - Glue to QEMU dyngen infrastructure
|
||||
*
|
||||
* 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 "basic-dyngen.hpp"
|
||||
|
||||
int __op_param1, __op_param2, __op_param3;
|
||||
int __op_jmp0, __op_jmp1;
|
||||
|
||||
#define DYNGEN_IMPL 1
|
||||
#define DEFINE_GEN(NAME,ARGS) void basic_dyngen::NAME ARGS
|
||||
#include "basic-dyngen-ops.hpp"
|
||||
|
||||
basic_dyngen::basic_dyngen(dyngen_cpu_base cpu)
|
||||
: parent_cpu(cpu)
|
||||
{
|
||||
execute_func = gen_start();
|
||||
gen_op_execute();
|
||||
gen_end();
|
||||
set_code_start(code_ptr());
|
||||
}
|
||||
|
||||
void
|
||||
basic_dyngen::gen_invoke(void (*func)())
|
||||
{
|
||||
if (direct_call_possible((uintptr)func))
|
||||
gen_op_invoke_direct((uintptr)func);
|
||||
else {
|
||||
gen_op_mov_ad_A0_im((uintptr)func);
|
||||
gen_op_invoke();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
basic_dyngen::gen_invoke_T0(void (*func)(uint32))
|
||||
{
|
||||
if (direct_call_possible((uintptr)func))
|
||||
gen_op_invoke_direct_T0((uintptr)func);
|
||||
else {
|
||||
gen_op_mov_ad_A0_im((uintptr)func);
|
||||
gen_op_invoke_T0();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
basic_dyngen::gen_invoke_im(void (*func)(uint32), uint32 value)
|
||||
{
|
||||
if (direct_call_possible((uintptr)func))
|
||||
gen_op_invoke_direct_im((uintptr)func, value);
|
||||
else {
|
||||
gen_op_mov_ad_A0_im((uintptr)func);
|
||||
gen_op_invoke_im(value);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
basic_dyngen::gen_invoke_CPU(void (*func)(dyngen_cpu_base))
|
||||
{
|
||||
if (direct_call_possible((uintptr)func))
|
||||
gen_op_invoke_direct_CPU((uintptr)func);
|
||||
else {
|
||||
gen_op_mov_ad_A0_im((uintptr)func);
|
||||
gen_op_invoke_CPU();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
basic_dyngen::gen_invoke_CPU_T0(void (*func)(dyngen_cpu_base, uint32))
|
||||
{
|
||||
if (direct_call_possible((uintptr)func))
|
||||
gen_op_invoke_direct_CPU_T0((uintptr)func);
|
||||
else {
|
||||
gen_op_mov_ad_A0_im((uintptr)func);
|
||||
gen_op_invoke_CPU_T0();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
basic_dyngen::gen_invoke_CPU_im(void (*func)(dyngen_cpu_base, uint32), uint32 value)
|
||||
{
|
||||
if (direct_call_possible((uintptr)func))
|
||||
gen_op_invoke_direct_CPU_im((uintptr)func, value);
|
||||
else {
|
||||
gen_op_mov_ad_A0_im((uintptr)func);
|
||||
gen_op_invoke_CPU_im(value);
|
||||
}
|
||||
}
|
315
SheepShaver/src/kpx_cpu/src/cpu/jit/basic-dyngen.hpp
Normal file
315
SheepShaver/src/kpx_cpu/src/cpu/jit/basic-dyngen.hpp
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* basic-dyngen.hpp - Basic code generator
|
||||
*
|
||||
* 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_DYNGEN_H
|
||||
#define BASIC_DYNGEN_H
|
||||
|
||||
#include "cpu/jit/jit-config.hpp"
|
||||
#include "cpu/jit/jit-cache.hpp"
|
||||
#include JIT_TARGET_INCLUDE(jit-target-cache.hpp)
|
||||
|
||||
#ifdef SHEEPSHAVER
|
||||
class powerpc_cpu;
|
||||
typedef powerpc_cpu *dyngen_cpu_base;
|
||||
#else
|
||||
class basic_cpu;
|
||||
typedef basic_cpu *dyngen_cpu_base;
|
||||
#endif
|
||||
|
||||
class basic_dyngen
|
||||
: public basic_jit_cache
|
||||
{
|
||||
uint8 *execute_func;
|
||||
uint8 *gen_code_start;
|
||||
dyngen_cpu_base parent_cpu;
|
||||
|
||||
// Can we generate a direct call to target function?
|
||||
bool direct_jump_possible(uintptr target) const;
|
||||
bool direct_call_possible(uintptr target) const;
|
||||
|
||||
// Generic code generators
|
||||
# define DEFINE_CST(NAME,VALUE) static const unsigned long NAME = VALUE;
|
||||
# define DEFINE_GEN(NAME,ARGS) void NAME ARGS;
|
||||
# include "basic-dyngen-ops.hpp"
|
||||
|
||||
public:
|
||||
|
||||
// Constructor, parent CPU required
|
||||
basic_dyngen(dyngen_cpu_base cpu);
|
||||
|
||||
// Return CPU context associated to this code generator
|
||||
dyngen_cpu_base cpu() const
|
||||
{ return parent_cpu; }
|
||||
|
||||
// Start code generation of a new block
|
||||
// Align on 16-byte boundaries
|
||||
// Returns pointer to entry point
|
||||
uint8 *gen_start();
|
||||
|
||||
// Stop code generation of the block
|
||||
// Returns FALSE if translation cache is full
|
||||
bool gen_end() const;
|
||||
|
||||
// Execute compiled function at ENTRY_POINT
|
||||
void execute(uint8 *entry_point);
|
||||
|
||||
// Return from compiled code
|
||||
void gen_exec_return();
|
||||
|
||||
// Function calls
|
||||
void gen_jmp(const uint8 *target);
|
||||
void gen_invoke(void (*func)(void));
|
||||
void gen_invoke_T0(void (*func)(uint32));
|
||||
void gen_invoke_im(void (*func)(uint32), uint32 value);
|
||||
void gen_invoke_CPU(void (*func)(dyngen_cpu_base));
|
||||
void gen_invoke_CPU_T0(void (*func)(dyngen_cpu_base, uint32));
|
||||
void gen_invoke_CPU_im(void (*func)(dyngen_cpu_base, uint32), uint32 value);
|
||||
|
||||
// Raw aliases
|
||||
#define DEFINE_ALIAS_RAW(NAME, ARGLIST, ARGS) \
|
||||
void gen_##NAME ARGLIST { gen_op_##NAME ARGS; }
|
||||
|
||||
#define DEFINE_ALIAS_0(NAME) DEFINE_ALIAS_RAW(NAME,(),())
|
||||
#define DEFINE_ALIAS_1(NAME) DEFINE_ALIAS_RAW(NAME,(long p1),(p1))
|
||||
#define DEFINE_ALIAS_2(NAME) DEFINE_ALIAS_RAW(NAME,(long p1,long p2),(p1,p2))
|
||||
#define DEFINE_ALIAS_3(NAME) DEFINE_ALIAS_RAW(NAME,(long p1,long p2,long p3),(p1,p2,p3))
|
||||
#define DEFINE_ALIAS(NAME, N) DEFINE_ALIAS_##N(NAME)
|
||||
|
||||
// Register moves
|
||||
DEFINE_ALIAS(mov_32_T0_im,1);
|
||||
DEFINE_ALIAS(mov_32_T0_T1,0);
|
||||
DEFINE_ALIAS(mov_32_T0_A0,0);
|
||||
DEFINE_ALIAS(mov_32_T1_im,1);
|
||||
DEFINE_ALIAS(mov_32_T1_T0,0);
|
||||
DEFINE_ALIAS(mov_32_T1_A0,0);
|
||||
DEFINE_ALIAS(mov_32_A0_im,1);
|
||||
DEFINE_ALIAS(mov_32_A0_T0,0);
|
||||
DEFINE_ALIAS(mov_32_A0_T1,0);
|
||||
DEFINE_ALIAS(mov_ad_A0_im,1);
|
||||
|
||||
// Arithmetic operations
|
||||
DEFINE_ALIAS(add_32_T0_T1,0);
|
||||
void gen_add_32_T0_im(int32 value);
|
||||
DEFINE_ALIAS(sub_32_T0_T1,0);
|
||||
void gen_sub_32_T0_im(int32 value);
|
||||
DEFINE_ALIAS(add_32_T1_T0,0);
|
||||
void gen_add_32_T1_im(int32 value);
|
||||
DEFINE_ALIAS(sub_32_T1_T0,0);
|
||||
void gen_sub_32_T1_im(int32 value);
|
||||
DEFINE_ALIAS(add_32_A0_T1,0);
|
||||
void gen_add_32_A0_im(int32 value);
|
||||
DEFINE_ALIAS(sub_32_A0_T1,0);
|
||||
void gen_sub_32_A0_im(int32 value);
|
||||
DEFINE_ALIAS(umul_32_T0_T1,0);
|
||||
DEFINE_ALIAS(smul_32_T0_T1,0);
|
||||
DEFINE_ALIAS(udiv_32_T0_T1,0);
|
||||
DEFINE_ALIAS(sdiv_32_T0_T1,0);
|
||||
DEFINE_ALIAS(xchg_32_T0_T1,0);
|
||||
DEFINE_ALIAS(bswap_16_T0,0);
|
||||
DEFINE_ALIAS(bswap_32_T0,0);
|
||||
|
||||
// Logical operations
|
||||
DEFINE_ALIAS(neg_32_T0,0);
|
||||
DEFINE_ALIAS(not_32_T0,0);
|
||||
DEFINE_ALIAS(not_32_T1,0);
|
||||
DEFINE_ALIAS(and_32_T0_T1,0);
|
||||
DEFINE_ALIAS(and_32_T0_im,1);
|
||||
DEFINE_ALIAS(or_32_T0_T1,0);
|
||||
DEFINE_ALIAS(or_32_T0_im,1);
|
||||
DEFINE_ALIAS(xor_32_T0_T1,0);
|
||||
DEFINE_ALIAS(xor_32_T0_im,1);
|
||||
DEFINE_ALIAS(orc_32_T0_T1,0);
|
||||
DEFINE_ALIAS(andc_32_T0_T1,0);
|
||||
DEFINE_ALIAS(nand_32_T0_T1,0);
|
||||
DEFINE_ALIAS(nor_32_T0_T1,0);
|
||||
DEFINE_ALIAS(eqv_32_T0_T1,0);
|
||||
DEFINE_ALIAS(and_logical_T0_T1,0);
|
||||
DEFINE_ALIAS(or_logical_T0_T1,0);
|
||||
|
||||
// Shift/Rotate operations
|
||||
DEFINE_ALIAS(lsl_32_T0_T1,0);
|
||||
DEFINE_ALIAS(lsl_32_T0_im,1);
|
||||
DEFINE_ALIAS(lsr_32_T0_T1,0);
|
||||
DEFINE_ALIAS(lsr_32_T0_im,1);
|
||||
DEFINE_ALIAS(asr_32_T0_T1,0);
|
||||
DEFINE_ALIAS(asr_32_T0_im,1);
|
||||
DEFINE_ALIAS(rol_32_T0_T1,0);
|
||||
DEFINE_ALIAS(rol_32_T0_im,1);
|
||||
DEFINE_ALIAS(ror_32_T0_T1,0);
|
||||
DEFINE_ALIAS(ror_32_T0_im,1);
|
||||
|
||||
// Sign-/Zero-extension
|
||||
DEFINE_ALIAS(se_16_32_T0,0);
|
||||
DEFINE_ALIAS(ze_16_32_T0,0);
|
||||
DEFINE_ALIAS(se_8_32_T0,0);
|
||||
DEFINE_ALIAS(ze_8_32_T0,0);
|
||||
|
||||
// Jump instructions
|
||||
DEFINE_ALIAS(jmp_slow,1);
|
||||
DEFINE_ALIAS(jmp_fast,1);
|
||||
|
||||
// Load/Store instructions
|
||||
DEFINE_ALIAS(load_u32_T0_A0_T1,0);
|
||||
void gen_load_u32_T0_A0_im(int32 offset);
|
||||
DEFINE_ALIAS(load_s32_T0_A0_T1,0);
|
||||
void gen_load_s32_T0_A0_im(int32 offset);
|
||||
DEFINE_ALIAS(load_u16_T0_A0_T1,0);
|
||||
void gen_load_u16_T0_A0_im(int32 offset);
|
||||
DEFINE_ALIAS(load_s16_T0_A0_T1,0);
|
||||
void gen_load_s16_T0_A0_im(int32 offset);
|
||||
DEFINE_ALIAS(load_u8_T0_A0_T1,0);
|
||||
void gen_load_u8_T0_A0_im(int32 offset);
|
||||
DEFINE_ALIAS(load_s8_T0_A0_T1,0);
|
||||
void gen_load_s8_T0_A0_im(int32 offset);
|
||||
DEFINE_ALIAS(store_32_T0_A0_T1,0);
|
||||
void gen_store_32_T0_A0_im(int32 offset);
|
||||
DEFINE_ALIAS(store_16_T0_A0_T1,0);
|
||||
void gen_store_16_T0_A0_im(int32 offset);
|
||||
DEFINE_ALIAS(store_8_T0_A0_T1,0);
|
||||
void gen_store_8_T0_A0_im(int32 offset);
|
||||
|
||||
#undef DEFINE_ALIAS
|
||||
#undef DEFINE_ALIAS_0
|
||||
#undef DEFINE_ALIAS_1
|
||||
#undef DEFINE_ALIAS_2
|
||||
#undef DEFINE_ALIAS_3
|
||||
#undef DEFINE_ALIAS_RAW
|
||||
};
|
||||
|
||||
inline bool
|
||||
basic_dyngen::direct_jump_possible(uintptr target) const
|
||||
{
|
||||
#if defined(__powerpc__)
|
||||
const uintptr LI_OFFSET_MAX = 1 << 26;
|
||||
return (((target - (uintptr)code_ptr()) < LI_OFFSET_MAX) ||
|
||||
(((uintptr)code_ptr() - target) < LI_OFFSET_MAX));
|
||||
#endif
|
||||
#if defined(__i386__)
|
||||
return true;
|
||||
#endif
|
||||
#if defined(__x86_64__)
|
||||
const intptr offset = (intptr)target - (intptr)code_ptr() - sizeof(void *);
|
||||
return offset <= 0xffffffff;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void
|
||||
basic_dyngen::gen_jmp(const uint8 *target_p)
|
||||
{
|
||||
const uintptr target = (uintptr)target_p;
|
||||
if (direct_jump_possible(target))
|
||||
gen_op_jmp_fast(target);
|
||||
else
|
||||
gen_op_jmp_slow(target);
|
||||
}
|
||||
|
||||
inline void
|
||||
basic_dyngen::execute(uint8 *entry_point)
|
||||
{
|
||||
typedef void (*func_t)(uint8 *, dyngen_cpu_base);
|
||||
func_t func = (func_t)execute_func;
|
||||
func(entry_point, parent_cpu);
|
||||
}
|
||||
|
||||
inline void
|
||||
basic_dyngen::gen_exec_return()
|
||||
{
|
||||
gen_jmp(execute_func + op_exec_return_offset);
|
||||
}
|
||||
|
||||
inline bool
|
||||
basic_dyngen::direct_call_possible(uintptr target) const
|
||||
{
|
||||
#if defined(__powerpc__)
|
||||
const uintptr LI_OFFSET_MAX = 1 << 26;
|
||||
return (((target - (uintptr)code_ptr()) < LI_OFFSET_MAX) ||
|
||||
(((uintptr)code_ptr() - target) < LI_OFFSET_MAX));
|
||||
#endif
|
||||
#if defined(__i386__)
|
||||
return true;
|
||||
#endif
|
||||
#if defined(__x86_64__)
|
||||
const intptr offset = (intptr)target - (intptr)code_ptr() - sizeof(void *);
|
||||
return offset <= 0xffffffff;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
inline uint8 *
|
||||
basic_dyngen::gen_start()
|
||||
{
|
||||
while ((uintptr)code_ptr() & 15)
|
||||
inc_code_ptr(1);
|
||||
gen_code_start = code_ptr();
|
||||
return gen_code_start;
|
||||
}
|
||||
|
||||
inline bool
|
||||
basic_dyngen::gen_end() const
|
||||
{
|
||||
flush_icache_range((unsigned long)gen_code_start, (unsigned long)code_ptr());
|
||||
return !full_translation_cache();
|
||||
}
|
||||
|
||||
#define DEFINE_OP(OP,REG) \
|
||||
inline void \
|
||||
basic_dyngen::gen_##OP##_32_##REG##_im(int32 value) \
|
||||
{ \
|
||||
if (value == 0) return; \
|
||||
else if (value == 1) gen_op_##OP##_32_##REG##_1(); \
|
||||
else if (value == 2) gen_op_##OP##_32_##REG##_2(); \
|
||||
else if (value == 4) gen_op_##OP##_32_##REG##_4(); \
|
||||
else if (value == 8) gen_op_##OP##_32_##REG##_8(); \
|
||||
else gen_op_##OP##_32_##REG##_im(value); \
|
||||
}
|
||||
|
||||
DEFINE_OP(add,A0);
|
||||
DEFINE_OP(add,T0);
|
||||
DEFINE_OP(add,T1);
|
||||
DEFINE_OP(sub,A0);
|
||||
DEFINE_OP(sub,T0);
|
||||
DEFINE_OP(sub,T1);
|
||||
|
||||
#undef DEFINE_OP
|
||||
|
||||
#define DEFINE_OP(NAME,REG,SIZE) \
|
||||
inline void \
|
||||
basic_dyngen::gen_##NAME##_##SIZE##_##REG##_A0_im(int32 offset) \
|
||||
{ \
|
||||
if (offset == 0) \
|
||||
gen_op_##NAME##_##SIZE##_##REG##_A0_0(); \
|
||||
else \
|
||||
gen_op_##NAME##_##SIZE##_##REG##_A0_im(offset); \
|
||||
}
|
||||
|
||||
DEFINE_OP(load,T0,u32);
|
||||
DEFINE_OP(load,T0,s32);
|
||||
DEFINE_OP(store,T0,32);
|
||||
DEFINE_OP(load,T0,u16);
|
||||
DEFINE_OP(load,T0,s16);
|
||||
DEFINE_OP(store,T0,16);
|
||||
DEFINE_OP(load,T0,u8);
|
||||
DEFINE_OP(load,T0,s8);
|
||||
DEFINE_OP(store,T0,8);
|
||||
|
||||
#undef DEFINE_OP
|
||||
|
||||
#endif /* BASIC_DYNGEN_H */
|
32
SheepShaver/src/kpx_cpu/src/cpu/jit/cxxdemangle.cpp
Normal file
32
SheepShaver/src/kpx_cpu/src/cpu/jit/cxxdemangle.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* cxxdemangle.cpp - C++ demangler
|
||||
*
|
||||
* 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 "cxxdemangle.h"
|
||||
|
||||
#if defined(__GNUC__) && (__GXX_ABI_VERSION > 0)
|
||||
#include <cxxabi.h>
|
||||
|
||||
char *
|
||||
cxx_demangle(const char *mangled_name, char *buf, size_t *n, int *status)
|
||||
{
|
||||
return abi::__cxa_demangle(mangled_name, buf, n, status);
|
||||
}
|
||||
#endif
|
55
SheepShaver/src/kpx_cpu/src/cpu/jit/cxxdemangle.h
Normal file
55
SheepShaver/src/kpx_cpu/src/cpu/jit/cxxdemangle.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* cxxdemangle.h - C++ demangler
|
||||
*
|
||||
* 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 CXX_DEMANGLE_H
|
||||
#define CXX_DEMANGLE_H
|
||||
|
||||
/**
|
||||
* cxx_demangle
|
||||
*
|
||||
* Following GCC 3.0 ABI:
|
||||
* <http://www.codesourcery.com/cxx-abi/abi.html>
|
||||
*
|
||||
* - MANGLED-NAME is a pointer to a null-terminated array of
|
||||
* characters
|
||||
*
|
||||
* - BUF may be null. If it is non-null, then N must also be
|
||||
* nonnull, and BUF is a pointer to an array, of at least *N
|
||||
* characters, that was allocated using malloc().
|
||||
*
|
||||
* - STATUS points to an int that is used as an error indicator. It
|
||||
* is permitted to be null, in which case the user just doesn't
|
||||
* get any detailed error information.
|
||||
*
|
||||
* Codes: 0: success
|
||||
* -1: memory allocation failure
|
||||
* -2: invalid mangled name
|
||||
* -3: invalid arguments (e.g. BUG nonnull and N null)
|
||||
**/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char *cxx_demangle(const char *mangled_name,
|
||||
char *buf,
|
||||
size_t *n,
|
||||
int *status);
|
||||
|
||||
#endif /* CXX_DEMANGLE_H */
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* jit-target-cache.hpp - Target specific code to invalidate cache
|
||||
*
|
||||
* 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 JIT_TARGET_CACHE_H
|
||||
#define JIT_TARGET_CACHE_H
|
||||
|
||||
static inline void flush_icache_range(unsigned long, unsigned long)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* JIT_TARGET_CACHE_H */
|
103
SheepShaver/src/kpx_cpu/src/cpu/jit/dyngen-exec.h
Normal file
103
SheepShaver/src/kpx_cpu/src/cpu/jit/dyngen-exec.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* dyngen defines for micro operation code
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef DYNGEN_EXEC_H
|
||||
#define DYNGEN_EXEC_H
|
||||
|
||||
#include "cpu/jit/jit-config.hpp"
|
||||
#include JIT_TARGET_INCLUDE(dyngen-target-exec.h)
|
||||
|
||||
/* define virtual register set */
|
||||
#define REG_A0 AREG0
|
||||
#define REG_A0_ID AREG0_ID
|
||||
#define REG_T0 AREG1
|
||||
#define REG_T0_ID AREG1_ID
|
||||
#define REG_T1 AREG2
|
||||
#define REG_T1_ID AREG2_ID
|
||||
#ifdef AREG3
|
||||
#define REG_T2 AREG3
|
||||
#define REG_T2_ID AREG3_ID
|
||||
#endif
|
||||
#ifdef AREG4
|
||||
#define REG_T3 AREG4
|
||||
#define REG_T3_ID AREG4_ID
|
||||
#endif
|
||||
#ifdef AREG5
|
||||
#define REG_CPU AREG5
|
||||
#define REG_CPU_ID AREG5_ID
|
||||
#endif
|
||||
#ifdef FREG0
|
||||
#define REG_F0 FREG0
|
||||
#define REG_F0_ID FREG0_ID
|
||||
#endif
|
||||
#ifdef FREG1
|
||||
#define REG_F1 FREG1
|
||||
#define REG_F1_ID FREG1_ID
|
||||
#endif
|
||||
#ifdef FREG2
|
||||
#define REG_F2 FREG2
|
||||
#define REG_F2_ID FREG2_ID
|
||||
#endif
|
||||
#ifdef FREG3
|
||||
#define REG_F3 FREG3
|
||||
#define REG_F3_ID FREG3_ID
|
||||
#endif
|
||||
|
||||
// Force only one return point
|
||||
#define dyngen_barrier() asm volatile ("")
|
||||
|
||||
#ifndef OPPROTO
|
||||
#define OPPROTO
|
||||
#endif
|
||||
|
||||
#ifdef __alpha__
|
||||
/* the symbols are considered non exported so a br immediate is generated */
|
||||
#define __hidden __attribute__((visibility("hidden")))
|
||||
#else
|
||||
#define __hidden
|
||||
#endif
|
||||
|
||||
#ifdef __alpha__
|
||||
/* Suggested by Richard Henderson. This will result in code like
|
||||
ldah $0,__op_param1($29) !gprelhigh
|
||||
lda $0,__op_param1($0) !gprellow
|
||||
We can then conveniently change $29 to $31 and adapt the offsets to
|
||||
emit the appropriate constant. */
|
||||
#define PARAM1 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param1)); _r; })
|
||||
#define PARAM2 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param2)); _r; })
|
||||
#define PARAM3 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param3)); _r; })
|
||||
extern int __op_param1 __hidden;
|
||||
extern int __op_param2 __hidden;
|
||||
extern int __op_param3 __hidden;
|
||||
#else
|
||||
extern int __op_param1, __op_param2, __op_param3;
|
||||
#define PARAM1 ((long)(&__op_param1))
|
||||
#define PARAM2 ((long)(&__op_param2))
|
||||
#define PARAM3 ((long)(&__op_param3))
|
||||
#endif
|
||||
|
||||
#ifndef REG_CPU
|
||||
extern int __op_cpuparam;
|
||||
#define CPUPARAM ((long)(&__op_cpuparam))
|
||||
#endif
|
||||
|
||||
extern int __op_jmp0, __op_jmp1;
|
||||
|
||||
#endif /* DYNGEN_EXEC_H */
|
1414
SheepShaver/src/kpx_cpu/src/cpu/jit/dyngen.c
Normal file
1414
SheepShaver/src/kpx_cpu/src/cpu/jit/dyngen.c
Normal file
File diff suppressed because it is too large
Load Diff
71
SheepShaver/src/kpx_cpu/src/cpu/jit/jit-cache.cpp
Normal file
71
SheepShaver/src/kpx_cpu/src/cpu/jit/jit-cache.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* jit-cache.cpp - Translation 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
|
||||
*/
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "vm_alloc.h"
|
||||
#include "cpu/jit/jit-cache.hpp"
|
||||
|
||||
#define DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
basic_jit_cache::basic_jit_cache(uint32 init_cache_size)
|
||||
: tcode_start(NULL), code_start(NULL), code_p(NULL), code_end(NULL)
|
||||
{
|
||||
init_translation_cache(init_cache_size);
|
||||
}
|
||||
|
||||
basic_jit_cache::~basic_jit_cache()
|
||||
{
|
||||
kill_translation_cache();
|
||||
}
|
||||
|
||||
bool
|
||||
basic_jit_cache::init_translation_cache(uint32 size)
|
||||
{
|
||||
// Round up translation cache size to next 16 KB boundaries
|
||||
const uint32 roundup = 16 * 1024;
|
||||
cache_size = (size + JIT_CACHE_SIZE_GUARD + roundup - 1) & -roundup;
|
||||
|
||||
tcode_start = (uint8 *)vm_acquire(cache_size, VM_MAP_PRIVATE | VM_MAP_32BIT);
|
||||
if (tcode_start == VM_MAP_FAILED) {
|
||||
tcode_start = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vm_protect(tcode_start, cache_size,
|
||||
VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE) < 0) {
|
||||
vm_release(tcode_start, cache_size);
|
||||
tcode_start = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
D(bug("basic_jit_cache: Translation cache: %d KB at %p\n", cache_size / 1024, tcode_start));
|
||||
code_start = tcode_start;
|
||||
code_p = code_start;
|
||||
code_end = code_p + cache_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
basic_jit_cache::kill_translation_cache()
|
||||
{
|
||||
if (tcode_start)
|
||||
vm_release(tcode_start, cache_size);
|
||||
}
|
112
SheepShaver/src/kpx_cpu/src/cpu/jit/jit-cache.hpp
Normal file
112
SheepShaver/src/kpx_cpu/src/cpu/jit/jit-cache.hpp
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* jit-cache.hpp - Translation 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 JIT_CACHE_H
|
||||
#define JIT_CACHE_H
|
||||
|
||||
/**
|
||||
* Basic translation cache
|
||||
**/
|
||||
|
||||
class basic_jit_cache
|
||||
{
|
||||
// Default cache size (2 MB)
|
||||
static const uint32 JIT_CACHE_SIZE = 2 * 1024 * 1024;
|
||||
static const uint32 JIT_CACHE_SIZE_GUARD = 4096;
|
||||
uint32 cache_size;
|
||||
|
||||
// Translation cache (allocated base, current pointer, end pointer)
|
||||
uint8 *tcode_start;
|
||||
uint8 *code_start;
|
||||
uint8 *code_p;
|
||||
uint8 *code_end;
|
||||
|
||||
protected:
|
||||
|
||||
// Initialize translation cache
|
||||
bool init_translation_cache(uint32 size);
|
||||
void kill_translation_cache();
|
||||
|
||||
// Initialize user code start
|
||||
void set_code_start(uint8 *ptr);
|
||||
|
||||
// Get & increase current position
|
||||
void inc_code_ptr(int offset) { code_p += offset; }
|
||||
public:
|
||||
uint8 *code_ptr() const { return code_p; }
|
||||
|
||||
public:
|
||||
|
||||
// Default constructor & destructor
|
||||
basic_jit_cache(uint32 init_cache_size = JIT_CACHE_SIZE);
|
||||
~basic_jit_cache();
|
||||
|
||||
// Invalidate translation cache
|
||||
void invalidate_cache();
|
||||
bool full_translation_cache() const
|
||||
{ return code_p >= code_end; }
|
||||
|
||||
// Emit code to translation cache
|
||||
template< typename T >
|
||||
void emit_generic(T v);
|
||||
void emit_8(uint8 v) { emit_generic<uint8>(v); }
|
||||
void emit_16(uint16 v) { emit_generic<uint16>(v); }
|
||||
void emit_32(uint32 v) { emit_generic<uint32>(v); }
|
||||
void emit_64(uint64 v) { emit_generic<uint64>(v); }
|
||||
void emit_ptr(uintptr v) { emit_generic<uintptr>(v); }
|
||||
void copy_block(const uint8 *block, uint32 size);
|
||||
void emit_block(const uint8 *block, uint32 size);
|
||||
};
|
||||
|
||||
inline void
|
||||
basic_jit_cache::set_code_start(uint8 *ptr)
|
||||
{
|
||||
assert(ptr >= tcode_start && ptr < code_end);
|
||||
code_start = ptr;
|
||||
}
|
||||
|
||||
inline void
|
||||
basic_jit_cache::invalidate_cache()
|
||||
{
|
||||
code_p = code_start;
|
||||
}
|
||||
|
||||
template< class T >
|
||||
inline void
|
||||
basic_jit_cache::emit_generic(T v)
|
||||
{
|
||||
*((T *)code_ptr()) = v;
|
||||
inc_code_ptr(sizeof(T));
|
||||
}
|
||||
|
||||
inline void
|
||||
basic_jit_cache::copy_block(const uint8 *block, uint32 size)
|
||||
{
|
||||
memcpy(code_ptr(), block, size);
|
||||
}
|
||||
|
||||
inline void
|
||||
basic_jit_cache::emit_block(const uint8 *block, uint32 size)
|
||||
{
|
||||
copy_block(block, size);
|
||||
inc_code_ptr(size);
|
||||
}
|
||||
|
||||
#endif /* JIT_CACHE_H */
|
86
SheepShaver/src/kpx_cpu/src/cpu/jit/jit-config.hpp
Normal file
86
SheepShaver/src/kpx_cpu/src/cpu/jit/jit-config.hpp
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* jit-config.hpp - JIT config utils
|
||||
*
|
||||
* 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 JIT_CONFIG_H
|
||||
#define JIT_CONFIG_H
|
||||
|
||||
/**
|
||||
* ENABLE_DYNGEN
|
||||
*
|
||||
* Define to enable the portable "JIT1" engine based on code
|
||||
* inlining technique as implemented in QEMU.
|
||||
**/
|
||||
|
||||
#ifndef ENABLE_DYNGEN
|
||||
#define ENABLE_DYNGEN 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* DYNGEN_ASM_OPTS
|
||||
*
|
||||
* Define to permit host inline asm optimizations. This is
|
||||
* particularly useful to compute emulated condition code
|
||||
* registers.
|
||||
**/
|
||||
|
||||
#ifndef DYNGEN_ASM_OPTS
|
||||
#define DYNGEN_ASM_OPTS 1
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Helpers to reach JIT backends headers
|
||||
**/
|
||||
|
||||
#if defined(__powerpc__) || defined(__ppc__)
|
||||
#define JIT_TARGET ppc
|
||||
#endif
|
||||
#if defined(__i386__)
|
||||
#define JIT_TARGET x86
|
||||
#endif
|
||||
#if defined(__x86_64__)
|
||||
#define JIT_TARGET amd64
|
||||
#endif
|
||||
#if defined(__s390__)
|
||||
#define JIT_TARGET s390
|
||||
#endif
|
||||
#if defined(__alpha__)
|
||||
#define JIT_TARGET alpha
|
||||
#endif
|
||||
#if defined(__ia64__)
|
||||
#define JIT_TARGET ia64
|
||||
#endif
|
||||
#if defined(__sparc__)
|
||||
#define JIT_TARGET sparc
|
||||
#endif
|
||||
#if defined(__arm__)
|
||||
#define JIT_TARGET arm
|
||||
#endif
|
||||
#if defined(__mc68000)
|
||||
#define JIT_TARGET m68k
|
||||
#endif
|
||||
#ifndef JIT_TARGET
|
||||
#error "Unsupport architecture for JIT1"
|
||||
#endif
|
||||
|
||||
#define JIT_PATH_CONCAT(X, Y) X/Y
|
||||
#define JIT_MAKE_HEADER(PATH, HEADER) <JIT_PATH_CONCAT(PATH,HEADER)>
|
||||
#define JIT_TARGET_INCLUDE(HEADER) JIT_MAKE_HEADER(JIT_PATH_CONCAT(cpu/jit,JIT_TARGET),HEADER)
|
||||
|
||||
#endif /* JIT_CONFIG_H */
|
71
SheepShaver/src/kpx_cpu/src/cpu/jit/ppc/dyngen-target-exec.h
Normal file
71
SheepShaver/src/kpx_cpu/src/cpu/jit/ppc/dyngen-target-exec.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* dyngen defines for micro operation code
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef DYNGEN_TARGET_EXEC_H
|
||||
#define DYNGEN_TARGET_EXEC_H
|
||||
|
||||
enum {
|
||||
#define AREG0 "r27"
|
||||
AREG0_ID = 27,
|
||||
|
||||
#define AREG1 "r24"
|
||||
AREG1_ID = 24,
|
||||
|
||||
#define AREG2 "r25"
|
||||
AREG2_ID = 25,
|
||||
|
||||
#define AREG3 "r26"
|
||||
AREG3_ID = 26,
|
||||
|
||||
#define AREG4 "r16"
|
||||
AREG4_ID = 16,
|
||||
|
||||
#define AREG5 "r17"
|
||||
AREG5_ID = 17,
|
||||
|
||||
#define AREG6 "r18"
|
||||
AREG6_ID = 18,
|
||||
|
||||
#define AREG7 "r19"
|
||||
AREG7_ID = 19,
|
||||
|
||||
#define AREG8 "r20"
|
||||
AREG8_ID = 20,
|
||||
|
||||
#define AREG9 "r21"
|
||||
AREG9_ID = 21,
|
||||
|
||||
#define AREG10 "r22"
|
||||
AREG10_ID = 22,
|
||||
|
||||
#define AREG11 "r23"
|
||||
AREG11_ID = 23,
|
||||
|
||||
#define FREG0 "f1"
|
||||
FREG0_ID = 1,
|
||||
|
||||
#define FREG1 "f2"
|
||||
FREG1_ID = 2,
|
||||
|
||||
#define FREG2 "f3"
|
||||
FREG2_ID = 3,
|
||||
};
|
||||
|
||||
#endif /* DYNGEN_TARGET_EXEC_H */
|
44
SheepShaver/src/kpx_cpu/src/cpu/jit/ppc/jit-target-cache.hpp
Normal file
44
SheepShaver/src/kpx_cpu/src/cpu/jit/ppc/jit-target-cache.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* jit-target-cache.hpp - Target specific code to invalidate cache
|
||||
*
|
||||
* 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 JIT_TARGET_CACHE_H
|
||||
#define JIT_TARGET_CACHE_H
|
||||
|
||||
static inline void flush_icache_range(unsigned long start, unsigned long stop)
|
||||
{
|
||||
const int MIN_CACHE_LINE_SIZE = 8; /* conservative value */
|
||||
|
||||
unsigned long p;
|
||||
|
||||
p = start & ~(MIN_CACHE_LINE_SIZE - 1);
|
||||
stop = (stop + MIN_CACHE_LINE_SIZE - 1) & ~(MIN_CACHE_LINE_SIZE - 1);
|
||||
|
||||
for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
|
||||
asm volatile ("dcbst 0,%0" : : "r"(p) : "memory");
|
||||
}
|
||||
asm volatile ("sync" : : : "memory");
|
||||
for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
|
||||
asm volatile ("icbi 0,%0" : : "r"(p) : "memory");
|
||||
}
|
||||
asm volatile ("sync" : : : "memory");
|
||||
asm volatile ("isync" : : : "memory");
|
||||
}
|
||||
|
||||
#endif /* JIT_TARGET_CACHE_H */
|
39
SheepShaver/src/kpx_cpu/src/cpu/jit/x86/dyngen-target-exec.h
Normal file
39
SheepShaver/src/kpx_cpu/src/cpu/jit/x86/dyngen-target-exec.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* dyngen defines for micro operation code
|
||||
*
|
||||
* Copyright (c) 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef DYNGEN_TARGET_EXEC_H
|
||||
#define DYNGEN_TARGET_EXEC_H
|
||||
|
||||
enum {
|
||||
/* callee save registers */
|
||||
#define AREG0 "ebp"
|
||||
AREG0_ID = 5,
|
||||
|
||||
#define AREG1 "ebx"
|
||||
AREG1_ID = 3,
|
||||
|
||||
#define AREG2 "esi"
|
||||
AREG2_ID = 6,
|
||||
|
||||
#define AREG3 "edi"
|
||||
AREG3_ID = 7,
|
||||
};
|
||||
|
||||
#endif /* DYNGEN_TARGET_EXEC_H */
|
@ -0,0 +1 @@
|
||||
#include "cpu/jit/t-dummy/jit-target-cache.hpp"
|
@ -37,7 +37,12 @@ struct powerpc_block_info
|
||||
uint32 opcode;
|
||||
};
|
||||
|
||||
#if PPC_DECODE_CACHE
|
||||
decode_info * di;
|
||||
#endif
|
||||
#if PPC_ENABLE_JIT
|
||||
uint8 * entry_point;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* PPC_BLOCKINFO_H */
|
||||
|
@ -90,6 +90,18 @@
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* PPC_ENABLE_JIT
|
||||
*
|
||||
* Define to 1 if dynamic translation is used. This requires
|
||||
* dyngen to be enabled first.
|
||||
**/
|
||||
|
||||
#ifndef PPC_ENABLE_JIT
|
||||
#define PPC_ENABLE_JIT ENABLE_DYNGEN
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* PPC_EXECUTE_DUMP_STATE
|
||||
*
|
||||
@ -115,6 +127,19 @@
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* PPC_PROFILE_COMPILE_TIME
|
||||
*
|
||||
* Define to enable some compile time statistics. This concerns
|
||||
* time spent into the decoder (PPC_DECODE_CACHE case) or total
|
||||
* time spent into the dynamic translator (PPC_ENABLE_JIT case).
|
||||
**/
|
||||
|
||||
#ifndef PPC_PROFILE_COMPILE_TIME
|
||||
#define PPC_PROFILE_COMPILE_TIME 0
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Sanity checks and features enforcements
|
||||
**/
|
||||
@ -124,4 +149,8 @@
|
||||
#undef PPC_NO_STATIC_II_INDEX_TABLE
|
||||
#endif
|
||||
|
||||
#if PPC_ENABLE_JIT
|
||||
#undef PPC_DECODE_CACHE
|
||||
#endif
|
||||
|
||||
#endif /* PPC_CONFIG_H */
|
||||
|
@ -22,6 +22,13 @@
|
||||
#include "vm_alloc.h"
|
||||
#include "cpu/vm.hpp"
|
||||
#include "cpu/ppc/ppc-cpu.hpp"
|
||||
#ifndef SHEEPSHAVER
|
||||
#include "basic-kernel.hpp"
|
||||
#endif
|
||||
|
||||
#if PPC_ENABLE_JIT
|
||||
#include "cpu/jit/dyngen-exec.h"
|
||||
#endif
|
||||
|
||||
#if ENABLE_MON
|
||||
#include "mon.h"
|
||||
@ -31,9 +38,6 @@
|
||||
#define DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
// Define to gather some compile time statistics
|
||||
#define PROFILE_COMPILE_TIME 1
|
||||
|
||||
void powerpc_cpu::set_register(int id, any_register const & value)
|
||||
{
|
||||
if (id >= powerpc_registers::GPR(0) && id <= powerpc_registers::GPR(31)) {
|
||||
@ -198,6 +202,19 @@ void powerpc_cpu::dump_log(const char *filename)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_MON
|
||||
static uint32 mon_read_byte_ppc(uintptr addr)
|
||||
{
|
||||
return *((uint8 *)addr);
|
||||
}
|
||||
|
||||
static void mon_write_byte_ppc(uintptr addr, uint32 b)
|
||||
{
|
||||
uint8 *m = (uint8 *)addr;
|
||||
*m = b;
|
||||
}
|
||||
#endif
|
||||
|
||||
void powerpc_cpu::initialize()
|
||||
{
|
||||
init_flight_recorder();
|
||||
@ -210,6 +227,7 @@ void powerpc_cpu::initialize()
|
||||
|
||||
// Init syscalls handler
|
||||
execute_do_syscall = NULL;
|
||||
syscall_exit_code = -1;
|
||||
|
||||
// Init field2mask
|
||||
for (int i = 0; i < 256; i++) {
|
||||
@ -227,9 +245,11 @@ void powerpc_cpu::initialize()
|
||||
|
||||
#if ENABLE_MON
|
||||
mon_init();
|
||||
mon_read_byte = mon_read_byte_ppc;
|
||||
mon_write_byte = mon_write_byte_ppc;
|
||||
#endif
|
||||
|
||||
#if PROFILE_COMPILE_TIME
|
||||
#if PPC_PROFILE_COMPILE_TIME
|
||||
compile_count = 0;
|
||||
compile_time = 0;
|
||||
emul_start_time = clock();
|
||||
@ -238,10 +258,13 @@ void powerpc_cpu::initialize()
|
||||
|
||||
powerpc_cpu::~powerpc_cpu()
|
||||
{
|
||||
#if PROFILE_COMPILE_TIME
|
||||
#if PPC_PROFILE_COMPILE_TIME
|
||||
clock_t emul_end_time = clock();
|
||||
|
||||
const char *type = NULL;
|
||||
#if PPC_ENABLE_JIT
|
||||
type = "compile";
|
||||
#endif
|
||||
#if PPC_DECODE_CACHE
|
||||
type = "predecode";
|
||||
#endif
|
||||
@ -312,6 +335,12 @@ bool powerpc_cpu::check_spcflags()
|
||||
#endif
|
||||
if (spcflags().test(SPCFLAG_CPU_EXEC_RETURN)) {
|
||||
spcflags().clear(SPCFLAG_CPU_EXEC_RETURN);
|
||||
#ifndef SHEEPSHAVER
|
||||
// FIXME: add unwind info to the translation cache? Otherwise
|
||||
// we have to manually handle the exit syscall here
|
||||
if (syscall_exit_code >= 0)
|
||||
throw kernel_syscall_exit(syscall_exit_code);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
if (spcflags().test(SPCFLAG_CPU_ENTER_MON)) {
|
||||
@ -338,10 +367,37 @@ void powerpc_cpu::execute(uint32 entry, bool enable_cache)
|
||||
#if PPC_EXECUTE_DUMP_STATE
|
||||
const bool dump_state = true;
|
||||
#endif
|
||||
#if PPC_ENABLE_JIT
|
||||
if (enable_cache) {
|
||||
for (;;) {
|
||||
block_info *bi = compile_block(pc());
|
||||
|
||||
// Execute all cached blocks
|
||||
for (;;) {
|
||||
codegen.execute(bi->entry_point);
|
||||
|
||||
if (!spcflags().empty()) {
|
||||
if (!check_spcflags())
|
||||
return;
|
||||
|
||||
// Force redecoding if cache was invalidated
|
||||
if (spcflags().test(SPCFLAG_JIT_EXEC_RETURN)) {
|
||||
spcflags().clear(SPCFLAG_JIT_EXEC_RETURN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((bi->pc != pc()) && ((bi = block_cache.find(pc())) == NULL))
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if PPC_DECODE_CACHE
|
||||
if (enable_cache) {
|
||||
for (;;) {
|
||||
#if PROFILE_COMPILE_TIME
|
||||
#if PPC_PROFILE_COMPILE_TIME
|
||||
compile_count++;
|
||||
clock_t start_time = clock();
|
||||
#endif
|
||||
@ -359,6 +415,7 @@ void powerpc_cpu::execute(uint32 entry, bool enable_cache)
|
||||
if (dump_state) {
|
||||
di->opcode = opcode;
|
||||
di->execute = nv_mem_fun(&powerpc_cpu::dump_instruction);
|
||||
di++;
|
||||
}
|
||||
#endif
|
||||
#if PPC_FLIGHT_RECORDER
|
||||
@ -369,7 +426,7 @@ void powerpc_cpu::execute(uint32 entry, bool enable_cache)
|
||||
}
|
||||
#endif
|
||||
di->opcode = opcode;
|
||||
di->execute = ii->decode ? ii->decode(this, opcode) : ii->execute;
|
||||
di->execute = ii->decode.ptr() ? ii->decode(this, opcode) : ii->execute;
|
||||
di++;
|
||||
#if PPC_EXECUTE_DUMP_STATE
|
||||
if (dump_state) {
|
||||
@ -392,7 +449,7 @@ void powerpc_cpu::execute(uint32 entry, bool enable_cache)
|
||||
block_cache.add_to_cl_list(bi);
|
||||
block_cache.add_to_active_list(bi);
|
||||
decode_cache_p += bi->size;
|
||||
#if PROFILE_COMPILE_TIME
|
||||
#if PPC_PROFILE_COMPILE_TIME
|
||||
compile_time += (clock() - start_time);
|
||||
#endif
|
||||
|
||||
@ -440,7 +497,7 @@ void powerpc_cpu::execute(uint32 entry, bool enable_cache)
|
||||
if (is_logging())
|
||||
record_step(opcode);
|
||||
#endif
|
||||
assert(ii->execute != 0);
|
||||
assert(ii->execute.ptr() != 0);
|
||||
ii->execute(this, opcode);
|
||||
#if PPC_EXECUTE_DUMP_STATE
|
||||
if (dump_state)
|
||||
@ -476,7 +533,9 @@ void powerpc_cpu::init_decode_cache()
|
||||
// Leave enough room to last calls to dump state functions
|
||||
decode_cache_end_p -= 2;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if PPC_DECODE_CACHE || PPC_ENABLE_JIT
|
||||
block_cache.initialize();
|
||||
#endif
|
||||
}
|
||||
@ -490,18 +549,23 @@ void powerpc_cpu::kill_decode_cache()
|
||||
|
||||
void powerpc_cpu::invalidate_cache()
|
||||
{
|
||||
#if PPC_DECODE_CACHE
|
||||
#if PPC_DECODE_CACHE || PPC_ENABLE_JIT
|
||||
block_cache.clear();
|
||||
block_cache.initialize();
|
||||
decode_cache_p = decode_cache;
|
||||
spcflags().set(SPCFLAG_JIT_EXEC_RETURN);
|
||||
#endif
|
||||
#if PPC_ENABLE_JIT
|
||||
codegen.invalidate_cache();
|
||||
#endif
|
||||
#if PPC_DECODE_CACHE
|
||||
decode_cache_p = decode_cache;
|
||||
#endif
|
||||
}
|
||||
|
||||
void powerpc_cpu::invalidate_cache_range(uintptr start, uintptr end)
|
||||
{
|
||||
D(bug("Invalidate cache block [%08x - %08x]\n", start, end));
|
||||
#if PPC_DECODE_CACHE
|
||||
#if PPC_DECODE_CACHE || PPC_ENABLE_JIT
|
||||
block_cache.clear_range(start, end);
|
||||
#endif
|
||||
}
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "cpu/ppc/ppc-bitfields.hpp"
|
||||
#include "cpu/ppc/ppc-blockinfo.hpp"
|
||||
#include "cpu/ppc/ppc-registers.hpp"
|
||||
#include "cpu/ppc/ppc-dyngen.hpp"
|
||||
#include <vector>
|
||||
|
||||
class powerpc_cpu
|
||||
@ -139,12 +140,13 @@ protected:
|
||||
|
||||
// Instruction information structure
|
||||
struct instr_info_t {
|
||||
char name[8]; // Mnemonic
|
||||
char name[8]; // Instruction name
|
||||
execute_fn execute; // Semantic routine for this instruction
|
||||
decode_fn decode; // Specialized instruction decoder
|
||||
uint16 mnemo; // Mnemonic
|
||||
uint16 format; // Instruction format (XO-form, D-form, etc.)
|
||||
uint16 opcode; // Primary opcode
|
||||
uint16 xo; // Extended opcode
|
||||
uint32 opcode:6; // Primary opcode
|
||||
uint32 xo:10; // Extended opcode
|
||||
uint16 cflow; // Mask of control flow information
|
||||
};
|
||||
|
||||
@ -170,6 +172,7 @@ private:
|
||||
// Syscall callback must return TRUE if no error occurred
|
||||
typedef bool (*syscall_fn)(powerpc_cpu *cpu);
|
||||
syscall_fn execute_do_syscall;
|
||||
int syscall_exit_code;
|
||||
|
||||
#ifdef PPC_NO_STATIC_II_INDEX_TABLE
|
||||
#define PPC_STATIC_II_TABLE
|
||||
@ -202,9 +205,15 @@ public:
|
||||
// Initialization & finalization
|
||||
#ifdef PPC_NO_BASIC_CPU_BASE
|
||||
powerpc_cpu()
|
||||
#if PPC_ENABLE_JIT
|
||||
: codegen(this)
|
||||
#endif
|
||||
#else
|
||||
powerpc_cpu(task_struct *parent_task)
|
||||
: basic_cpu(parent_task)
|
||||
#if PPC_ENABLE_JIT
|
||||
, codegen(this)
|
||||
#endif
|
||||
#endif
|
||||
{ initialize(); }
|
||||
void initialize();
|
||||
@ -256,6 +265,22 @@ protected:
|
||||
// Init decoder with one instruction info
|
||||
void init_decoder_entry(const instr_info_t * ii);
|
||||
|
||||
#if PPC_ENABLE_JIT
|
||||
// Dynamic translation engine
|
||||
struct codegen_context_t {
|
||||
powerpc_dyngen & codegen;
|
||||
uint32 entry_point;
|
||||
uint32 pc;
|
||||
uint32 opcode;
|
||||
const instr_info_t *instr_info;
|
||||
|
||||
codegen_context_t(powerpc_dyngen & codegen_init)
|
||||
: codegen(codegen_init)
|
||||
{ }
|
||||
};
|
||||
virtual bool compile1(codegen_context_t & cg_context) { return false; }
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
// Initializers & destructors
|
||||
@ -280,6 +305,25 @@ private:
|
||||
block_info::decode_info * decode_cache_p;
|
||||
block_info::decode_info * decode_cache_end_p;
|
||||
|
||||
#if PPC_ENABLE_JIT
|
||||
// Dynamic translation engine
|
||||
friend class powerpc_dyngen_helper;
|
||||
friend class powerpc_dyngen;
|
||||
powerpc_dyngen codegen;
|
||||
block_info *compile_block(uint32 entry);
|
||||
powerpc_dyngen *codegen_ptr() { return &codegen; }
|
||||
#endif
|
||||
|
||||
// Semantic action templates
|
||||
template< bool SB, bool OE >
|
||||
uint32 do_execute_divide(uint32, uint32);
|
||||
template< bool EX, bool CA, bool OE >
|
||||
uint32 do_execute_addition(uint32, uint32);
|
||||
template< bool CA, bool OE >
|
||||
uint32 do_execute_subtract(uint32, uint32);
|
||||
template< bool OE >
|
||||
uint32 do_execute_subtract_extended(uint32, uint32);
|
||||
|
||||
// Instruction handlers
|
||||
void execute_nop(uint32 opcode);
|
||||
void execute_illegal(uint32 opcode);
|
||||
|
File diff suppressed because it is too large
Load Diff
942
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-dyngen-ops.cpp
Normal file
942
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-dyngen-ops.cpp
Normal file
@ -0,0 +1,942 @@
|
||||
/*
|
||||
* ppc-dyngen-ops.hpp - PowerPC synthetic instructions
|
||||
*
|
||||
* 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 "cpu/vm.hpp"
|
||||
#include "cpu/jit/dyngen-exec.h"
|
||||
#define NO_DEFINE_ALIAS 1
|
||||
#include "cpu/ppc/ppc-cpu.hpp"
|
||||
#include "cpu/ppc/ppc-bitfields.hpp"
|
||||
#include "cpu/ppc/ppc-registers.hpp"
|
||||
#include "cpu/ppc/ppc-operations.hpp"
|
||||
|
||||
// We need at least 4 general purpose registers
|
||||
#ifdef REG_CPU
|
||||
register struct powerpc_cpu *CPU asm(REG_CPU);
|
||||
#else
|
||||
#define CPU ((powerpc_cpu *)CPUPARAM)
|
||||
#endif
|
||||
register uint32 A0 asm(REG_A0);
|
||||
register uint32 T0 asm(REG_T0);
|
||||
register uint32 T1 asm(REG_T1);
|
||||
#ifdef REG_T2
|
||||
register uint32 CC_LHS asm(REG_T2);
|
||||
#else
|
||||
#define CC_LHS powerpc_dyngen_helper::cc_lhs()
|
||||
#endif
|
||||
#ifdef REG_T3
|
||||
register uint32 CC_RHS asm(REG_T3);
|
||||
#else
|
||||
#define CC_RHS powerpc_dyngen_helper::cc_rhs()
|
||||
#endif
|
||||
|
||||
// Semantic action templates
|
||||
#define DYNGEN_OPS
|
||||
#include "ppc-execute.hpp"
|
||||
|
||||
|
||||
/**
|
||||
* Helper class to access protected CPU context
|
||||
**/
|
||||
|
||||
struct powerpc_dyngen_helper {
|
||||
static inline uint32 get_pc() { return CPU->pc(); }
|
||||
static inline void set_pc(uint32 value) { CPU->pc() = value; }
|
||||
static inline void inc_pc(int32 offset) { CPU->pc() += offset; }
|
||||
static inline uint32 get_lr() { return CPU->lr(); }
|
||||
static inline void set_lr(uint32 value) { CPU->lr() = value; }
|
||||
static inline uint32 get_ctr() { return CPU->ctr(); }
|
||||
static inline void set_ctr(uint32 value) { CPU->ctr() = value; }
|
||||
static inline uint32 get_cr() { return CPU->cr().get(); }
|
||||
static inline void set_cr(uint32 value) { CPU->cr().set(value); }
|
||||
static inline uint32 get_xer() { return CPU->xer().get(); }
|
||||
static inline void set_xer(uint32 value) { CPU->xer().set(value); }
|
||||
static inline void record(int crf, int32 v) { CPU->record_cr(crf, v); }
|
||||
static inline powerpc_cr_register & cr() { return CPU->cr(); }
|
||||
static inline powerpc_xer_register & xer() { return CPU->xer(); }
|
||||
static inline uint32 & cc_lhs() { return CPU->codegen_ptr()->rc_cache.cc_lhs; }
|
||||
static inline uint32 & cc_rhs() { return CPU->codegen_ptr()->rc_cache.cc_rhs; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Load/store general purpose registers
|
||||
**/
|
||||
|
||||
#define DEFINE_OP(REG, N) \
|
||||
void OPPROTO op_load_##REG##_GPR##N(void) \
|
||||
{ \
|
||||
REG = CPU->gpr(N); \
|
||||
} \
|
||||
void OPPROTO op_store_##REG##_GPR##N(void) \
|
||||
{ \
|
||||
CPU->gpr(N) = REG; \
|
||||
}
|
||||
#define DEFINE_REG(N) \
|
||||
DEFINE_OP(A0,N); \
|
||||
DEFINE_OP(T0,N); \
|
||||
DEFINE_OP(T1,N);
|
||||
|
||||
DEFINE_REG(0);
|
||||
DEFINE_REG(1);
|
||||
DEFINE_REG(2);
|
||||
DEFINE_REG(3);
|
||||
DEFINE_REG(4);
|
||||
DEFINE_REG(5);
|
||||
DEFINE_REG(6);
|
||||
DEFINE_REG(7);
|
||||
DEFINE_REG(8);
|
||||
DEFINE_REG(9);
|
||||
DEFINE_REG(10);
|
||||
DEFINE_REG(11);
|
||||
DEFINE_REG(12);
|
||||
DEFINE_REG(13);
|
||||
DEFINE_REG(14);
|
||||
DEFINE_REG(15);
|
||||
DEFINE_REG(16);
|
||||
DEFINE_REG(17);
|
||||
DEFINE_REG(18);
|
||||
DEFINE_REG(19);
|
||||
DEFINE_REG(20);
|
||||
DEFINE_REG(21);
|
||||
DEFINE_REG(22);
|
||||
DEFINE_REG(23);
|
||||
DEFINE_REG(24);
|
||||
DEFINE_REG(25);
|
||||
DEFINE_REG(26);
|
||||
DEFINE_REG(27);
|
||||
DEFINE_REG(28);
|
||||
DEFINE_REG(29);
|
||||
DEFINE_REG(30);
|
||||
DEFINE_REG(31);
|
||||
|
||||
#undef DEFINE_REG
|
||||
#undef DEFINE_OP
|
||||
|
||||
|
||||
/**
|
||||
* Condition Registers
|
||||
**/
|
||||
|
||||
void OPPROTO op_load_T0_CR(void)
|
||||
{
|
||||
T0 = powerpc_dyngen_helper::get_cr();
|
||||
}
|
||||
|
||||
void OPPROTO op_store_T0_CR(void)
|
||||
{
|
||||
powerpc_dyngen_helper::set_cr(T0);
|
||||
}
|
||||
|
||||
void OPPROTO op_load_T0_XER(void)
|
||||
{
|
||||
T0 = powerpc_dyngen_helper::get_xer();
|
||||
}
|
||||
|
||||
void OPPROTO op_store_T0_XER(void)
|
||||
{
|
||||
powerpc_dyngen_helper::set_xer(T0);
|
||||
}
|
||||
|
||||
#define DEFINE_OP(REG, N) \
|
||||
void OPPROTO op_load_##REG##_crb##N(void) \
|
||||
{ \
|
||||
REG = bit_field<N,N>::extract(powerpc_dyngen_helper::get_cr()); \
|
||||
} \
|
||||
void OPPROTO op_store_##REG##_crb##N(void) \
|
||||
{ \
|
||||
uint32 cr = powerpc_dyngen_helper::get_cr(); \
|
||||
bit_field<N,N>::insert(cr, REG); \
|
||||
powerpc_dyngen_helper::set_cr(cr); \
|
||||
}
|
||||
#define DEFINE_REG(N) \
|
||||
DEFINE_OP(T0, N); \
|
||||
DEFINE_OP(T1, N);
|
||||
|
||||
DEFINE_REG(0);
|
||||
DEFINE_REG(1);
|
||||
DEFINE_REG(2);
|
||||
DEFINE_REG(3);
|
||||
DEFINE_REG(4);
|
||||
DEFINE_REG(5);
|
||||
DEFINE_REG(6);
|
||||
DEFINE_REG(7);
|
||||
DEFINE_REG(8);
|
||||
DEFINE_REG(9);
|
||||
DEFINE_REG(10);
|
||||
DEFINE_REG(11);
|
||||
DEFINE_REG(12);
|
||||
DEFINE_REG(13);
|
||||
DEFINE_REG(14);
|
||||
DEFINE_REG(15);
|
||||
DEFINE_REG(16);
|
||||
DEFINE_REG(17);
|
||||
DEFINE_REG(18);
|
||||
DEFINE_REG(19);
|
||||
DEFINE_REG(20);
|
||||
DEFINE_REG(21);
|
||||
DEFINE_REG(22);
|
||||
DEFINE_REG(23);
|
||||
DEFINE_REG(24);
|
||||
DEFINE_REG(25);
|
||||
DEFINE_REG(26);
|
||||
DEFINE_REG(27);
|
||||
DEFINE_REG(28);
|
||||
DEFINE_REG(29);
|
||||
DEFINE_REG(30);
|
||||
DEFINE_REG(31);
|
||||
|
||||
#undef DEFINE_REG
|
||||
#undef DEFINE_OP
|
||||
|
||||
#define DEFINE_OP(CRF, REG) \
|
||||
void OPPROTO op_load_##REG##_cr##CRF(void) \
|
||||
{ \
|
||||
T0 = powerpc_dyngen_helper::cr().get(CRF); \
|
||||
} \
|
||||
void OPPROTO op_store_##REG##_cr##CRF(void) \
|
||||
{ \
|
||||
powerpc_dyngen_helper::cr().set(CRF, REG); \
|
||||
}
|
||||
|
||||
DEFINE_OP(0, T0);
|
||||
DEFINE_OP(1, T0);
|
||||
DEFINE_OP(2, T0);
|
||||
DEFINE_OP(3, T0);
|
||||
DEFINE_OP(4, T0);
|
||||
DEFINE_OP(5, T0);
|
||||
DEFINE_OP(6, T0);
|
||||
DEFINE_OP(7, T0);
|
||||
|
||||
#undef DEFINE_OP
|
||||
|
||||
#define DEFINE_OP(CRF) \
|
||||
void OPPROTO op_commit_so_cache_cr##CRF(void) \
|
||||
{ \
|
||||
int so = powerpc_dyngen_helper::xer().get_so(); \
|
||||
powerpc_dyngen_helper::cr().set_so(CRF, so); \
|
||||
}
|
||||
|
||||
DEFINE_OP(0);
|
||||
DEFINE_OP(1);
|
||||
DEFINE_OP(2);
|
||||
DEFINE_OP(3);
|
||||
DEFINE_OP(4);
|
||||
DEFINE_OP(5);
|
||||
DEFINE_OP(6);
|
||||
DEFINE_OP(7);
|
||||
|
||||
#undef DEFINE_OP
|
||||
|
||||
#define DEFINE_OP_1(NAME,TYPE,CRF) \
|
||||
void OPPROTO op_##NAME##_rc_cache_cr##CRF(void) \
|
||||
{ \
|
||||
uint32 cr = powerpc_dyngen_helper::get_cr(); \
|
||||
cr &= ~(CR_LT_field<CRF>::mask() | \
|
||||
CR_GT_field<CRF>::mask() | \
|
||||
CR_EQ_field<CRF>::mask()); \
|
||||
\
|
||||
if ((TYPE)CC_LHS < (TYPE)CC_RHS) \
|
||||
cr |= CR_LT_field<CRF>::mask(); \
|
||||
else if ((TYPE)CC_LHS > (TYPE)CC_RHS) \
|
||||
cr |= CR_GT_field<CRF>::mask(); \
|
||||
else \
|
||||
cr |= CR_EQ_field<CRF>::mask(); \
|
||||
\
|
||||
powerpc_dyngen_helper::set_cr(cr); \
|
||||
dyngen_barrier(); \
|
||||
}
|
||||
#define DEFINE_OP(CRF) \
|
||||
DEFINE_OP_1(commit,int32,CRF); \
|
||||
DEFINE_OP_1(commit_logical,uint32,CRF)
|
||||
|
||||
DEFINE_OP(0);
|
||||
DEFINE_OP(1);
|
||||
DEFINE_OP(2);
|
||||
DEFINE_OP(3);
|
||||
DEFINE_OP(4);
|
||||
DEFINE_OP(5);
|
||||
DEFINE_OP(6);
|
||||
DEFINE_OP(7);
|
||||
|
||||
#undef DEFINE_OP
|
||||
#undef DEFINE_OP_1
|
||||
|
||||
|
||||
/**
|
||||
* Special purpose registers
|
||||
**/
|
||||
|
||||
void OPPROTO op_load_T0_PC(void)
|
||||
{
|
||||
T0 = powerpc_dyngen_helper::get_pc();
|
||||
}
|
||||
|
||||
void OPPROTO op_store_T0_PC(void)
|
||||
{
|
||||
powerpc_dyngen_helper::set_pc(T0);
|
||||
}
|
||||
|
||||
void OPPROTO op_set_PC_im(void)
|
||||
{
|
||||
powerpc_dyngen_helper::set_pc(PARAM1);
|
||||
}
|
||||
|
||||
void OPPROTO op_set_PC_T1(void)
|
||||
{
|
||||
powerpc_dyngen_helper::set_pc(T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_inc_PC(void)
|
||||
{
|
||||
powerpc_dyngen_helper::inc_pc(PARAM1);
|
||||
}
|
||||
|
||||
void OPPROTO op_load_T0_LR(void)
|
||||
{
|
||||
T0 = powerpc_dyngen_helper::get_lr();
|
||||
}
|
||||
|
||||
void OPPROTO op_store_T0_LR(void)
|
||||
{
|
||||
powerpc_dyngen_helper::set_lr(T0);
|
||||
}
|
||||
|
||||
void OPPROTO op_load_T0_CTR(void)
|
||||
{
|
||||
T0 = powerpc_dyngen_helper::get_ctr();
|
||||
}
|
||||
|
||||
void OPPROTO op_load_T1_CTR(void)
|
||||
{
|
||||
T1 = powerpc_dyngen_helper::get_ctr();
|
||||
}
|
||||
|
||||
void OPPROTO op_store_T0_CTR(void)
|
||||
{
|
||||
powerpc_dyngen_helper::set_ctr(T0);
|
||||
}
|
||||
|
||||
void OPPROTO op_store_T1_CTR(void)
|
||||
{
|
||||
powerpc_dyngen_helper::set_ctr(T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_load_T1_PC(void)
|
||||
{
|
||||
T1 = powerpc_dyngen_helper::get_pc();
|
||||
}
|
||||
|
||||
void OPPROTO op_load_T1_LR(void)
|
||||
{
|
||||
T1 = powerpc_dyngen_helper::get_lr();
|
||||
}
|
||||
|
||||
void OPPROTO op_store_im_LR(void)
|
||||
{
|
||||
powerpc_dyngen_helper::set_lr(PARAM1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Branch instructions
|
||||
**/
|
||||
|
||||
void OPPROTO op_decrement_ctr_T1(void)
|
||||
{
|
||||
T1 = powerpc_dyngen_helper::get_ctr() - 1;
|
||||
powerpc_dyngen_helper::set_ctr(T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_branch_if_T0(void)
|
||||
{
|
||||
if (T0)
|
||||
powerpc_dyngen_helper::set_pc(PARAM1);
|
||||
else
|
||||
powerpc_dyngen_helper::set_pc(PARAM2);
|
||||
dyngen_barrier();
|
||||
}
|
||||
|
||||
void OPPROTO op_branch_if_not_T0(void)
|
||||
{
|
||||
if (!T0)
|
||||
powerpc_dyngen_helper::set_pc(PARAM1);
|
||||
else
|
||||
powerpc_dyngen_helper::set_pc(PARAM2);
|
||||
dyngen_barrier();
|
||||
}
|
||||
|
||||
void OPPROTO op_branch_if_T1(void)
|
||||
{
|
||||
if (T1)
|
||||
powerpc_dyngen_helper::set_pc(PARAM1);
|
||||
else
|
||||
powerpc_dyngen_helper::set_pc(PARAM2);
|
||||
dyngen_barrier();
|
||||
}
|
||||
|
||||
void OPPROTO op_branch_if_not_T1(void)
|
||||
{
|
||||
if (!T1)
|
||||
powerpc_dyngen_helper::set_pc(PARAM1);
|
||||
else
|
||||
powerpc_dyngen_helper::set_pc(PARAM2);
|
||||
dyngen_barrier();
|
||||
}
|
||||
|
||||
void OPPROTO op_branch_if_T0_T1(void)
|
||||
{
|
||||
if (T0 && T1)
|
||||
powerpc_dyngen_helper::set_pc(PARAM1);
|
||||
else
|
||||
powerpc_dyngen_helper::set_pc(PARAM2);
|
||||
dyngen_barrier();
|
||||
}
|
||||
|
||||
void OPPROTO op_branch_T1_if_T0(void)
|
||||
{
|
||||
if (T0)
|
||||
powerpc_dyngen_helper::set_pc(T1);
|
||||
else
|
||||
powerpc_dyngen_helper::set_pc(PARAM1);
|
||||
dyngen_barrier();
|
||||
}
|
||||
|
||||
template< class branch_cond, class ctr_cond >
|
||||
static inline void do_execute_branch(uint32 tpc, uint32 npc)
|
||||
{
|
||||
if (branch_cond::test() && ctr_cond::test())
|
||||
powerpc_dyngen_helper::set_pc(PARAM1);
|
||||
else
|
||||
powerpc_dyngen_helper::set_pc(PARAM2);
|
||||
dyngen_barrier();
|
||||
}
|
||||
|
||||
struct LT_comparator { static inline bool test() { return T0 < 0; } };
|
||||
struct GT_comparator { static inline bool test() { return T0 > 0; } };
|
||||
struct EQ_comparator { static inline bool test() { return T0 == 0; } };
|
||||
|
||||
template< int crb >
|
||||
struct CR_comparator { static inline bool test() { return (T0 & crb); } };
|
||||
|
||||
template< bool br_true, class comparator >
|
||||
struct bool_condition {
|
||||
static inline bool test() {
|
||||
return br_true ? comparator::test() : !comparator::test();
|
||||
}
|
||||
};
|
||||
|
||||
typedef bool_condition< true, CR_comparator<8> > blt_condition;
|
||||
typedef bool_condition<false, CR_comparator<8> > bnlt_condition;
|
||||
typedef bool_condition< true, CR_comparator<4> > bgt_condition;
|
||||
typedef bool_condition<false, CR_comparator<4> > bngt_condition;
|
||||
typedef bool_condition< true, CR_comparator<2> > beq_condition;
|
||||
typedef bool_condition<false, CR_comparator<2> > bneq_condition;
|
||||
typedef bool_condition< true, CR_comparator<1> > bso_condition;
|
||||
typedef bool_condition<false, CR_comparator<1> > bnso_condition;
|
||||
|
||||
struct ctr_0x_condition {
|
||||
static inline bool test() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct ctr_10_condition {
|
||||
static inline bool test() {
|
||||
uint32 ctr = powerpc_dyngen_helper::get_ctr() - 1;
|
||||
powerpc_dyngen_helper::set_ctr(ctr);
|
||||
return ctr != 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct ctr_11_condition {
|
||||
static inline bool test() {
|
||||
uint32 ctr = powerpc_dyngen_helper::get_ctr() - 1;
|
||||
powerpc_dyngen_helper::set_ctr(ctr);
|
||||
return ctr == 0;
|
||||
}
|
||||
};
|
||||
|
||||
#define DEFINE_OP_CTR(COND,CTR) \
|
||||
void OPPROTO op_##COND##_##CTR(void) \
|
||||
{ \
|
||||
do_execute_branch<COND##_condition, ctr_##CTR##_condition>(PARAM1, PARAM2); \
|
||||
}
|
||||
#define DEFINE_OP(COND) \
|
||||
DEFINE_OP_CTR(COND,0x); \
|
||||
DEFINE_OP_CTR(COND,10); \
|
||||
DEFINE_OP_CTR(COND,11)
|
||||
|
||||
DEFINE_OP(blt);
|
||||
DEFINE_OP(bgt);
|
||||
DEFINE_OP(beq);
|
||||
DEFINE_OP(bso);
|
||||
DEFINE_OP(bnlt);
|
||||
DEFINE_OP(bngt);
|
||||
DEFINE_OP(bneq);
|
||||
DEFINE_OP(bnso);
|
||||
|
||||
#undef DEFINE_OP
|
||||
#undef DEFINE_OP_CTR
|
||||
|
||||
|
||||
/**
|
||||
* Compare & Record instructions
|
||||
**/
|
||||
|
||||
void OPPROTO op_record_nego_T0(void)
|
||||
{
|
||||
powerpc_dyngen_helper::xer().set_ov(T0 == 0x80000000);
|
||||
dyngen_barrier();
|
||||
}
|
||||
|
||||
void OPPROTO op_record_cr0_T0(void)
|
||||
{
|
||||
uint32 cr = powerpc_dyngen_helper::get_cr();
|
||||
cr &= ~(CR_LT_field<0>::mask() |
|
||||
CR_GT_field<0>::mask() |
|
||||
CR_EQ_field<0>::mask() |
|
||||
CR_SO_field<0>::mask());
|
||||
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
uint32 v;
|
||||
asm volatile ("cmpwi 0,%1,0 ; mfcr %0" : "=r" (v) : "r" (T0) : "cr0");
|
||||
cr |= (v & (0xe0000000));
|
||||
cr |= (powerpc_dyngen_helper::xer().get_so() << (31 - 3));
|
||||
goto end;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (powerpc_dyngen_helper::xer().get_so())
|
||||
cr |= CR_SO_field<0>::mask();
|
||||
if ((int32)T0 < 0)
|
||||
cr |= CR_LT_field<0>::mask();
|
||||
else if ((int32)T0 > 0)
|
||||
cr |= CR_GT_field<0>::mask();
|
||||
else
|
||||
cr |= CR_EQ_field<0>::mask();
|
||||
|
||||
end:
|
||||
powerpc_dyngen_helper::set_cr(cr);
|
||||
dyngen_barrier();
|
||||
}
|
||||
|
||||
#define im PARAM1
|
||||
|
||||
#define DEFINE_OP(LHS, RHS) \
|
||||
void OPPROTO op_compare_##LHS##_##RHS(void) \
|
||||
{ \
|
||||
CC_LHS = LHS; \
|
||||
CC_RHS = RHS; \
|
||||
}
|
||||
DEFINE_OP(T0,T1);
|
||||
DEFINE_OP(T0,im);
|
||||
DEFINE_OP(T0,0);
|
||||
#undef DEFINE_OP
|
||||
|
||||
#if DYNGEN_ASM_OPTS && defined(__powerpc__)
|
||||
|
||||
#define DEFINE_OP(NAME, COMP, LHS, RHST, RHS) \
|
||||
void OPPROTO op_##NAME##_##LHS##_##RHS(void) \
|
||||
{ \
|
||||
uint32 cr = powerpc_dyngen_helper::xer().get_so(); \
|
||||
uint32 v; \
|
||||
asm volatile (COMP " 7,%1,%2 ; mfcr %0" : "=r" (v) : "r" (LHS), RHST (RHS) : "cr7"); \
|
||||
T0 = cr | (v & 0xe); \
|
||||
}
|
||||
|
||||
DEFINE_OP(do_compare,"cmpw",T0,"r",T1);
|
||||
DEFINE_OP(do_compare,"cmpw",T0,"r",im);
|
||||
DEFINE_OP(do_compare,"cmpwi",T0,"i",0);
|
||||
DEFINE_OP(do_compare_logical,"cmplw",T0,"r",T1);
|
||||
DEFINE_OP(do_compare_logical,"cmplw",T0,"r",im);
|
||||
DEFINE_OP(do_compare_logical,"cmplwi",T0,"i",0);
|
||||
|
||||
#else
|
||||
|
||||
#define DEFINE_OP(NAME, TYPE, LHS, RHS) \
|
||||
void OPPROTO op_##NAME##_##LHS##_##RHS(void) \
|
||||
{ \
|
||||
uint32 cr = powerpc_dyngen_helper::xer().get_so(); \
|
||||
if ((TYPE)LHS < (TYPE)RHS) \
|
||||
cr |= standalone_CR_LT_field::mask(); \
|
||||
else if ((TYPE)LHS > (TYPE)RHS) \
|
||||
cr |= standalone_CR_GT_field::mask(); \
|
||||
else \
|
||||
cr |= standalone_CR_EQ_field::mask(); \
|
||||
T0 = cr; \
|
||||
dyngen_barrier(); \
|
||||
}
|
||||
|
||||
DEFINE_OP(do_compare,int32,T0,T1);
|
||||
DEFINE_OP(do_compare,int32,T0,im);
|
||||
DEFINE_OP(do_compare,int32,T0,0);
|
||||
DEFINE_OP(do_compare_logical,uint32,T0,T1);
|
||||
DEFINE_OP(do_compare_logical,uint32,T0,im);
|
||||
DEFINE_OP(do_compare_logical,uint32,T0,0);
|
||||
|
||||
#endif
|
||||
|
||||
#undef im
|
||||
#undef DEFINE_OP
|
||||
|
||||
|
||||
/**
|
||||
* Divide instructions
|
||||
**/
|
||||
|
||||
#if DYNGEN_ASM_OPTS && defined(__powerpc__)
|
||||
#define get_ov() ({ uint32 xer; asm volatile ("mfxer %0" : "=r" (xer)); XER_OV_field::extract(xer); })
|
||||
#endif
|
||||
|
||||
void OPPROTO op_divw_T0_T1(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
asm volatile ("divw %0,%0,%1" : "=r" (T0) : "r" (T1));
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
T0 = do_execute_divide<true, false>(T0, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_divwo_T0_T1(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
asm volatile ("divwo %0,%0,%1" : "=r" (T0) : "r" (T1));
|
||||
powerpc_dyngen_helper::xer().set_ov(get_ov());
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
T0 = do_execute_divide<true, true>(T0, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_divwu_T0_T1(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
asm volatile ("divwu %0,%0,%1" : "=r" (T0) : "r" (T1));
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
T0 = do_execute_divide<false, false>(T0, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_divwuo_T0_T1(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
asm volatile ("divwuo %0,%0,%1" : "=r" (T0) : "r" (T1));
|
||||
powerpc_dyngen_helper::xer().set_ov(get_ov());
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
T0 = do_execute_divide<false, true>(T0, T1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply instructions
|
||||
**/
|
||||
|
||||
void OPPROTO op_mulhw_T0_T1(void)
|
||||
{
|
||||
T0 = (((int64)(int32)T0) * ((int64)(int32)T1)) >> 32;
|
||||
}
|
||||
|
||||
void OPPROTO op_mulhwu_T0_T1(void)
|
||||
{
|
||||
T0 = (((uint64)T0) * ((uint64)T1)) >> 32;
|
||||
}
|
||||
|
||||
void OPPROTO op_mulli_T0_im(void)
|
||||
{
|
||||
T0 = (int32)T0 * (int32)PARAM1;
|
||||
}
|
||||
|
||||
void OPPROTO op_mullwo_T0_T1(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
asm volatile ("mullwo %0,%0,%1" : "=r" (T0) : "r" (T1));
|
||||
powerpc_dyngen_helper::xer().set_ov(get_ov());
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
int64 RD = (int64)(int32)T0 * (int64)(int32)T1;
|
||||
powerpc_dyngen_helper::xer().set_ov((int32)RD != RD);
|
||||
T0 = RD;
|
||||
dyngen_barrier();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shift/Rotate instructions
|
||||
**/
|
||||
|
||||
void OPPROTO op_slw_T0_T1(void)
|
||||
{
|
||||
T0 = T0 << (T1 & 0x3f);
|
||||
}
|
||||
|
||||
void OPPROTO op_srw_T0_T1(void)
|
||||
{
|
||||
T0 = T0 >> (T1 & 0x3f);
|
||||
}
|
||||
|
||||
void OPPROTO op_sraw_T0_T1(void)
|
||||
{
|
||||
const uint32 n = T1 & 0x3f;
|
||||
const uint32 RD = ((int32)T0) >> n;
|
||||
const bool ca = (((int32)T0) < 0) && (T0 & ~(0xffffffff << n));
|
||||
powerpc_dyngen_helper::xer().set_ca(ca);
|
||||
T0 = RD;
|
||||
dyngen_barrier();
|
||||
}
|
||||
|
||||
void OPPROTO op_sraw_T0_im(void)
|
||||
{
|
||||
const uint32 n = PARAM1;
|
||||
const uint32 RD = ((int32)T0) >> n;
|
||||
const bool ca = (((int32)T0) < 0) && (T0 & ~(0xffffffff << n));
|
||||
powerpc_dyngen_helper::xer().set_ca(ca);
|
||||
T0 = RD;
|
||||
dyngen_barrier();
|
||||
}
|
||||
|
||||
void OPPROTO op_rlwimi_T0_T1(void)
|
||||
{
|
||||
T0 = op_ppc_rlwimi::apply(T1, PARAM1, PARAM2, T0);
|
||||
}
|
||||
|
||||
void OPPROTO op_rlwinm_T0_T1(void)
|
||||
{
|
||||
T0 = op_rotl::apply(T0, PARAM1) & PARAM2;
|
||||
}
|
||||
|
||||
void OPPROTO op_rlwnm_T0_T1(void)
|
||||
{
|
||||
T0 = op_rotl::apply(T0, T1) & PARAM1;
|
||||
}
|
||||
|
||||
void OPPROTO op_cntlzw_32_T0(void)
|
||||
{
|
||||
uint32 n;
|
||||
uint32 m = 0x80000000;
|
||||
for (n = 0; n < 32; n++, m >>= 1)
|
||||
if (T0 & m)
|
||||
break;
|
||||
T0 = n;
|
||||
dyngen_barrier();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Addition/Subtraction
|
||||
**/
|
||||
|
||||
void OPPROTO op_addo_T0_T1(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
uint32 xer;
|
||||
asm volatile ("addo %0,%0,%2 ; mfxer %1" : "=r" (T0), "=r" (xer) : "r" (T1));
|
||||
powerpc_dyngen_helper::xer().set_ov(XER_OV_field::extract(xer));
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
T0 = do_execute_addition<false, false, true>(T0, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_addc_T0_im(void)
|
||||
{
|
||||
T0 = do_execute_addition<false, true, false>(T0, PARAM1);
|
||||
}
|
||||
|
||||
void OPPROTO op_addc_T0_T1(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
uint32 xer;
|
||||
asm volatile ("addc %0,%0,%2 ; mfxer %1" : "=r" (T0), "=r" (xer) : "r" (T1));
|
||||
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
T0 = do_execute_addition<false, true, false>(T0, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_addco_T0_T1(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
uint32 xer;
|
||||
asm volatile ("addco %0,%0,%2 ; mfxer %1" : "=r" (T0), "=r" (xer) : "r" (T1));
|
||||
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
|
||||
powerpc_dyngen_helper::xer().set_ov(XER_OV_field::extract(xer));
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
T0 = do_execute_addition<false, true, true>(T0, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_adde_T0_T1(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
uint32 xer, ca = powerpc_dyngen_helper::xer().get_ca();
|
||||
asm volatile ("li 0,-1 ; addc 0,%0,0" : : "r" (ca) : "r0");
|
||||
asm volatile ("adde %0,%0,%2 ; mfxer %1" : "=r" (T0), "=r" (xer) : "r" (T1));
|
||||
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
T0 = do_execute_addition<true, false, false>(T0, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_addeo_T0_T1(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
uint32 xer, ca = powerpc_dyngen_helper::xer().get_ca();
|
||||
asm volatile ("li 0,-1 ; addc 0,%0,0" : : "r" (ca) : "r0");
|
||||
asm volatile ("addeo %0,%0,%2 ; mfxer %1" : "=r" (T0), "=r" (xer) : "r" (T1));
|
||||
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
|
||||
powerpc_dyngen_helper::xer().set_ov(XER_OV_field::extract(xer));
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
T0 = do_execute_addition<true, false, true>(T0, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_addme_T0(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
uint32 xer, ca = powerpc_dyngen_helper::xer().get_ca();
|
||||
asm volatile ("li 0,-1 ; addc 0,%0,0" : : "r" (ca) : "r0");
|
||||
asm volatile ("addme %0,%0 ; mfxer %1" : "=r" (T0), "=r" (xer));
|
||||
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
T0 = do_execute_addition<true, false, false>(T0, 0xffffffff);
|
||||
}
|
||||
|
||||
void OPPROTO op_addmeo_T0(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
uint32 xer, ca = powerpc_dyngen_helper::xer().get_ca();
|
||||
asm volatile ("li 0,-1 ; addc 0,%0,0" : : "r" (ca) : "r0");
|
||||
asm volatile ("addmeo %0,%0 ; mfxer %1" : "=r" (T0), "=r" (xer));
|
||||
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
|
||||
powerpc_dyngen_helper::xer().set_ov(XER_OV_field::extract(xer));
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
T0 = do_execute_addition<true, false, true>(T0, 0xffffffff);
|
||||
}
|
||||
|
||||
void OPPROTO op_addze_T0(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
uint32 xer, ca = powerpc_dyngen_helper::xer().get_ca();
|
||||
asm volatile ("li 0,-1 ; addc 0,%0,0" : : "r" (ca) : "r0");
|
||||
asm volatile ("addze %0,%0 ; mfxer %1" : "=r" (T0), "=r" (xer));
|
||||
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
T0 = do_execute_addition<true, false, false>(T0, 0);
|
||||
}
|
||||
|
||||
void OPPROTO op_addzeo_T0(void)
|
||||
{
|
||||
#if DYNGEN_ASM_OPTS
|
||||
#if defined(__powerpc__)
|
||||
uint32 xer, ca = powerpc_dyngen_helper::xer().get_ca();
|
||||
asm volatile ("li 0,-1 ; addc 0,%0,0" : : "r" (ca) : "r0");
|
||||
asm volatile ("addzeo %0,%0 ; mfxer %1" : "=r" (T0), "=r" (xer));
|
||||
powerpc_dyngen_helper::xer().set_ca(XER_CA_field::extract(xer));
|
||||
powerpc_dyngen_helper::xer().set_ov(XER_OV_field::extract(xer));
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
T0 = do_execute_addition<true, false, true>(T0, 0);
|
||||
}
|
||||
|
||||
void OPPROTO op_subf_T0_T1(void)
|
||||
{
|
||||
T0 = T1 - T0;
|
||||
}
|
||||
|
||||
void OPPROTO op_subfo_T0_T1(void)
|
||||
{
|
||||
T0 = do_execute_subtract<false, true>(T0, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_subfc_T0_im(void)
|
||||
{
|
||||
T0 = do_execute_subtract<true, false>(T0, PARAM1);
|
||||
}
|
||||
|
||||
void OPPROTO op_subfc_T0_T1(void)
|
||||
{
|
||||
T0 = do_execute_subtract<true, false>(T0, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_subfco_T0_T1(void)
|
||||
{
|
||||
T0 = do_execute_subtract<true, true>(T0, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_subfe_T0_T1(void)
|
||||
{
|
||||
T0 = do_execute_subtract_extended<false>(T0, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_subfeo_T0_T1(void)
|
||||
{
|
||||
T0 = do_execute_subtract_extended<true>(T0, T1);
|
||||
}
|
||||
|
||||
void OPPROTO op_subfme_T0(void)
|
||||
{
|
||||
T0 = do_execute_subtract_extended<false>(T0, 0xffffffff);
|
||||
}
|
||||
|
||||
void OPPROTO op_subfmeo_T0(void)
|
||||
{
|
||||
T0 = do_execute_subtract_extended<true>(T0, 0xffffffff);
|
||||
}
|
||||
|
||||
void OPPROTO op_subfze_T0(void)
|
||||
{
|
||||
T0 = do_execute_subtract_extended<false>(T0, 0);
|
||||
}
|
||||
|
||||
void OPPROTO op_subfzeo_T0(void)
|
||||
{
|
||||
T0 = do_execute_subtract_extended<true>(T0, 0);
|
||||
}
|
254
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-dyngen.cpp
Normal file
254
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-dyngen.cpp
Normal file
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* ppc-dyngen.cpp - PowerPC dynamic translation
|
||||
*
|
||||
* 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 "cpu/ppc/ppc-dyngen.hpp"
|
||||
#include "cpu/ppc/ppc-bitfields.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DYNGEN_IMPL 1
|
||||
#define DEFINE_GEN(NAME,ARGS) void powerpc_dyngen::NAME ARGS
|
||||
#include "ppc-dyngen-ops.hpp"
|
||||
|
||||
void powerpc_dyngen::invalidate_so_cache()
|
||||
{
|
||||
rc_cache.so_status = RC_cache::STATUS_TRASH;
|
||||
}
|
||||
|
||||
void powerpc_dyngen::invalidate_cr_cache()
|
||||
{
|
||||
invalidate_so_cache();
|
||||
rc_cache.val_status = RC_cache::STATUS_TRASH;
|
||||
rc_cache.crf = -1;
|
||||
}
|
||||
|
||||
void powerpc_dyngen::do_gen_commit_cr()
|
||||
{
|
||||
gen_commit_so();
|
||||
switch (rc_cache.val_status) {
|
||||
case RC_cache::STATUS_VALID:
|
||||
gen_commit_rc_cache_cr(rc_cache.crf);
|
||||
break;
|
||||
case RC_cache::STATUS_VALID_LOGICAL:
|
||||
gen_commit_logical_rc_cache_cr(rc_cache.crf);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
invalidate_cr_cache();
|
||||
}
|
||||
|
||||
void powerpc_dyngen::do_gen_commit_so()
|
||||
{
|
||||
gen_commit_so_cache_cr(rc_cache.crf);
|
||||
invalidate_so_cache();
|
||||
}
|
||||
|
||||
void powerpc_dyngen::gen_commit_cr()
|
||||
{
|
||||
if (rc_cache.val_status != RC_cache::STATUS_TRASH) {
|
||||
assert(rc_cache.crf != -1);
|
||||
do_gen_commit_cr();
|
||||
}
|
||||
}
|
||||
|
||||
void powerpc_dyngen::gen_commit_so()
|
||||
{
|
||||
if (rc_cache.so_status != RC_cache::STATUS_TRASH) {
|
||||
assert(rc_cache.crf != -1);
|
||||
do_gen_commit_so();
|
||||
}
|
||||
}
|
||||
|
||||
void powerpc_dyngen::gen_compare_T0_T1(int crf)
|
||||
{
|
||||
if (!rc_cache.has_field(crf))
|
||||
gen_commit_cr();
|
||||
gen_op_compare_T0_T1();
|
||||
rc_cache.cache_field(crf);
|
||||
}
|
||||
|
||||
void powerpc_dyngen::gen_compare_T0_im(int crf, int32 value)
|
||||
{
|
||||
if (!rc_cache.has_field(crf))
|
||||
gen_commit_cr();
|
||||
if (value == 0)
|
||||
gen_op_compare_T0_0();
|
||||
else
|
||||
gen_op_compare_T0_im(value);
|
||||
rc_cache.cache_field(crf);
|
||||
}
|
||||
|
||||
void powerpc_dyngen::gen_compare_logical_T0_T1(int crf)
|
||||
{
|
||||
if (!rc_cache.has_field(crf))
|
||||
gen_commit_cr();
|
||||
gen_op_compare_T0_T1();
|
||||
rc_cache.cache_field(crf, RC_cache::STATUS_VALID_LOGICAL);
|
||||
}
|
||||
|
||||
void powerpc_dyngen::gen_compare_logical_T0_im(int crf, int32 value)
|
||||
{
|
||||
if (!rc_cache.has_field(crf))
|
||||
gen_commit_cr();
|
||||
if (value == 0)
|
||||
gen_op_compare_T0_0();
|
||||
else
|
||||
gen_op_compare_T0_im(value);
|
||||
rc_cache.cache_field(crf, RC_cache::STATUS_VALID_LOGICAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load/store registers
|
||||
**/
|
||||
|
||||
#define DEFINE_INSN(OP, REG, REGT) \
|
||||
void powerpc_dyngen::gen_##OP##_##REG##_##REGT(int i) \
|
||||
{ \
|
||||
switch (i) { \
|
||||
case 0: gen_op_##OP##_##REG##_##REGT##0(); break; \
|
||||
case 1: gen_op_##OP##_##REG##_##REGT##1(); break; \
|
||||
case 2: gen_op_##OP##_##REG##_##REGT##2(); break; \
|
||||
case 3: gen_op_##OP##_##REG##_##REGT##3(); break; \
|
||||
case 4: gen_op_##OP##_##REG##_##REGT##4(); break; \
|
||||
case 5: gen_op_##OP##_##REG##_##REGT##5(); break; \
|
||||
case 6: gen_op_##OP##_##REG##_##REGT##6(); break; \
|
||||
case 7: gen_op_##OP##_##REG##_##REGT##7(); break; \
|
||||
case 8: gen_op_##OP##_##REG##_##REGT##8(); break; \
|
||||
case 9: gen_op_##OP##_##REG##_##REGT##9(); break; \
|
||||
case 10: gen_op_##OP##_##REG##_##REGT##10(); break; \
|
||||
case 11: gen_op_##OP##_##REG##_##REGT##11(); break; \
|
||||
case 12: gen_op_##OP##_##REG##_##REGT##12(); break; \
|
||||
case 13: gen_op_##OP##_##REG##_##REGT##13(); break; \
|
||||
case 14: gen_op_##OP##_##REG##_##REGT##14(); break; \
|
||||
case 15: gen_op_##OP##_##REG##_##REGT##15(); break; \
|
||||
case 16: gen_op_##OP##_##REG##_##REGT##16(); break; \
|
||||
case 17: gen_op_##OP##_##REG##_##REGT##17(); break; \
|
||||
case 18: gen_op_##OP##_##REG##_##REGT##18(); break; \
|
||||
case 19: gen_op_##OP##_##REG##_##REGT##19(); break; \
|
||||
case 20: gen_op_##OP##_##REG##_##REGT##20(); break; \
|
||||
case 21: gen_op_##OP##_##REG##_##REGT##21(); break; \
|
||||
case 22: gen_op_##OP##_##REG##_##REGT##22(); break; \
|
||||
case 23: gen_op_##OP##_##REG##_##REGT##23(); break; \
|
||||
case 24: gen_op_##OP##_##REG##_##REGT##24(); break; \
|
||||
case 25: gen_op_##OP##_##REG##_##REGT##25(); break; \
|
||||
case 26: gen_op_##OP##_##REG##_##REGT##26(); break; \
|
||||
case 27: gen_op_##OP##_##REG##_##REGT##27(); break; \
|
||||
case 28: gen_op_##OP##_##REG##_##REGT##28(); break; \
|
||||
case 29: gen_op_##OP##_##REG##_##REGT##29(); break; \
|
||||
case 30: gen_op_##OP##_##REG##_##REGT##30(); break; \
|
||||
case 31: gen_op_##OP##_##REG##_##REGT##31(); break; \
|
||||
default: abort(); \
|
||||
} \
|
||||
}
|
||||
|
||||
// General purpose registers
|
||||
DEFINE_INSN(load, A0, GPR);
|
||||
DEFINE_INSN(load, T0, GPR);
|
||||
DEFINE_INSN(load, T1, GPR);
|
||||
DEFINE_INSN(store, A0, GPR);
|
||||
DEFINE_INSN(store, T0, GPR);
|
||||
DEFINE_INSN(store, T1, GPR);
|
||||
|
||||
// Condition register bitfield
|
||||
DEFINE_INSN(load, T0, crb);
|
||||
DEFINE_INSN(load, T1, crb);
|
||||
DEFINE_INSN(store, T0, crb);
|
||||
DEFINE_INSN(store, T1, crb);
|
||||
|
||||
#undef DEFINE_INSN
|
||||
|
||||
#define DEFINE_INSN(OP, REG) \
|
||||
void powerpc_dyngen::gen_##OP##_##REG##_cr(int crf) \
|
||||
{ \
|
||||
switch (crf) { \
|
||||
case 0: gen_op_##OP##_##REG##_cr0(); break; \
|
||||
case 1: gen_op_##OP##_##REG##_cr1(); break; \
|
||||
case 2: gen_op_##OP##_##REG##_cr2(); break; \
|
||||
case 3: gen_op_##OP##_##REG##_cr3(); break; \
|
||||
case 4: gen_op_##OP##_##REG##_cr4(); break; \
|
||||
case 5: gen_op_##OP##_##REG##_cr5(); break; \
|
||||
case 6: gen_op_##OP##_##REG##_cr6(); break; \
|
||||
case 7: gen_op_##OP##_##REG##_cr7(); break; \
|
||||
default: abort(); \
|
||||
} \
|
||||
}
|
||||
|
||||
DEFINE_INSN(load, T0);
|
||||
DEFINE_INSN(store, T0);
|
||||
DEFINE_INSN(commit, so_cache);
|
||||
DEFINE_INSN(commit, rc_cache);
|
||||
DEFINE_INSN(commit_logical, rc_cache);
|
||||
|
||||
#undef DEFINE_INSN
|
||||
|
||||
void powerpc_dyngen::gen_record_cr0_T0(void)
|
||||
{
|
||||
gen_compare_T0_im(0, 0);
|
||||
}
|
||||
|
||||
void powerpc_dyngen::gen_bc(int bo, int bi, uint32 tpc, uint32 npc)
|
||||
{
|
||||
gen_commit_cr();
|
||||
if (BO_CONDITIONAL_BRANCH(bo)) {
|
||||
enum { lt, gt, eq, so };
|
||||
gen_load_T0_cr(bi / 4);
|
||||
const int n = ((bi % 4) << 2) | ((bo >> 1) & 3);
|
||||
#define _(CR,DCTR,CTR0) (((CR) << 2) | ((DCTR) ? 0 : 2) | ((CTR0) ? 1 : 0))
|
||||
if (BO_BRANCH_IF_TRUE(bo)) {
|
||||
switch (n) {
|
||||
#define C(CR) \
|
||||
case _(CR,0,0): gen_b##CR##_0x(tpc, npc); break; \
|
||||
case _(CR,0,1): gen_b##CR##_0x(tpc, npc); break; \
|
||||
case _(CR,1,0): gen_b##CR##_10(tpc, npc); break; \
|
||||
case _(CR,1,1): gen_b##CR##_11(tpc, npc); break;
|
||||
C(lt); C(gt); C(eq); C(so);
|
||||
#undef C
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (n) {
|
||||
#define C(CR) \
|
||||
case _(CR,0,0): gen_bn##CR##_0x(tpc, npc); break; \
|
||||
case _(CR,0,1): gen_bn##CR##_0x(tpc, npc); break; \
|
||||
case _(CR,1,0): gen_bn##CR##_10(tpc, npc); break; \
|
||||
case _(CR,1,1): gen_bn##CR##_11(tpc, npc); break;
|
||||
C(lt); C(gt); C(eq); C(so);
|
||||
#undef C
|
||||
}
|
||||
}
|
||||
#undef _
|
||||
}
|
||||
else {
|
||||
if (BO_DECREMENT_CTR(bo)) {
|
||||
gen_decrement_ctr_T1();
|
||||
if (BO_BRANCH_IF_CTR_ZERO(bo))
|
||||
gen_branch_if_not_T1(tpc, npc);
|
||||
else
|
||||
gen_branch_if_T1(tpc, npc);
|
||||
}
|
||||
else {
|
||||
// Branch always
|
||||
gen_set_PC_im(tpc);
|
||||
}
|
||||
}
|
||||
}
|
231
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-dyngen.hpp
Normal file
231
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-dyngen.hpp
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* ppc-dyngen.hpp - PowerPC dynamic translation
|
||||
*
|
||||
* 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_DYNGEN_H
|
||||
#define PPC_DYNGEN_H
|
||||
|
||||
#include "sysdeps.h"
|
||||
#include "cpu/ppc/ppc-config.hpp"
|
||||
|
||||
#if PPC_ENABLE_JIT
|
||||
#include "cpu/jit/jit-config.hpp"
|
||||
#include "cpu/jit/basic-dyngen.hpp"
|
||||
|
||||
class powerpc_dyngen
|
||||
: public basic_dyngen
|
||||
{
|
||||
// Code generators for PowerPC synthetic instructions
|
||||
#ifndef NO_DEFINE_ALIAS
|
||||
# define DEFINE_GEN(NAME,ARGS) void NAME ARGS;
|
||||
# include "ppc-dyngen-ops.hpp"
|
||||
#endif
|
||||
|
||||
struct RC_cache {
|
||||
enum {
|
||||
STATUS_TRASH,
|
||||
STATUS_VALID,
|
||||
STATUS_VALID_LOGICAL
|
||||
};
|
||||
int val_status;
|
||||
int so_status;
|
||||
int crf;
|
||||
|
||||
// Used only by generated code if not enough native registers
|
||||
// are available to cache them all
|
||||
uint32 cc_lhs;
|
||||
uint32 cc_rhs;
|
||||
|
||||
RC_cache()
|
||||
: val_status(STATUS_TRASH), so_status(STATUS_TRASH), crf(-1)
|
||||
{ }
|
||||
|
||||
bool has_field(int test_crf)
|
||||
{ return val_status != STATUS_TRASH && crf == test_crf; }
|
||||
|
||||
void cache_field(int new_crf, int new_status = STATUS_VALID)
|
||||
{ val_status = so_status = new_status; crf = new_crf; }
|
||||
};
|
||||
RC_cache rc_cache;
|
||||
|
||||
public:
|
||||
|
||||
// Make rc_cache accessible to codegen helper
|
||||
friend class powerpc_dyngen_helper;
|
||||
|
||||
// Default constructor
|
||||
powerpc_dyngen(dyngen_cpu_base cpu)
|
||||
: basic_dyngen(cpu)
|
||||
{ }
|
||||
|
||||
// Load/store registers
|
||||
void gen_load_A0_GPR(int i);
|
||||
void gen_load_T0_GPR(int i);
|
||||
void gen_load_T1_GPR(int i);
|
||||
void gen_store_A0_GPR(int i);
|
||||
void gen_store_T0_GPR(int i);
|
||||
void gen_store_T1_GPR(int i);
|
||||
|
||||
// Raw aliases
|
||||
#define DEFINE_ALIAS_RAW(NAME, PRE, POST, ARGLIST, ARGS) \
|
||||
void gen_##NAME ARGLIST { PRE; gen_op_##NAME ARGS; POST; }
|
||||
|
||||
#define DEFINE_ALIAS_0(NAME,PRE,POST) DEFINE_ALIAS_RAW(NAME,PRE,POST,(),())
|
||||
#define DEFINE_ALIAS_1(NAME,PRE,POST) DEFINE_ALIAS_RAW(NAME,PRE,POST,(long p1),(p1))
|
||||
#define DEFINE_ALIAS_2(NAME,PRE,POST) DEFINE_ALIAS_RAW(NAME,PRE,POST,(long p1,long p2),(p1,p2))
|
||||
#define DEFINE_ALIAS_3(NAME,PRE,POST) DEFINE_ALIAS_RAW(NAME,PRE,POST,(long p1,long p2,long p3),(p1,p2,p3))
|
||||
#ifdef NO_DEFINE_ALIAS
|
||||
#define DEFINE_ALIAS(NAME,N)
|
||||
#define DEFINE_ALIAS_CLOBBER_SO(NAME,N)
|
||||
#define DEFINE_ALIAS_CLOBBER_CR(NAME,N)
|
||||
#else
|
||||
#define DEFINE_ALIAS(NAME,N) DEFINE_ALIAS_##N(NAME,,)
|
||||
#define DEFINE_ALIAS_CLOBBER_CR(NAME,N) DEFINE_ALIAS_##N(NAME,gen_commit_cr(),)
|
||||
#define DEFINE_ALIAS_CLOBBER_SO(NAME,N) DEFINE_ALIAS_##N(NAME,gen_commit_so(),)
|
||||
#endif
|
||||
|
||||
// Condition registers
|
||||
private:
|
||||
void do_gen_commit_so();
|
||||
void do_gen_commit_cr();
|
||||
void gen_commit_so_cache_cr(int crf);
|
||||
void gen_commit_rc_cache_cr(int crf);
|
||||
void gen_commit_logical_rc_cache_cr(int crf);
|
||||
public:
|
||||
void invalidate_so_cache();
|
||||
void invalidate_cr_cache();
|
||||
void gen_commit_so();
|
||||
void gen_commit_cr();
|
||||
DEFINE_ALIAS_CLOBBER_CR(load_T0_CR,0);
|
||||
DEFINE_ALIAS_CLOBBER_CR(store_T0_CR,0);
|
||||
DEFINE_ALIAS(load_T0_XER,0);
|
||||
DEFINE_ALIAS(store_T0_XER,0);
|
||||
void gen_load_T0_crb(int i);
|
||||
void gen_load_T1_crb(int i);
|
||||
void gen_store_T0_crb(int i);
|
||||
void gen_store_T1_crb(int i);
|
||||
void gen_load_T0_cr(int crf);
|
||||
void gen_store_T0_cr(int crf);
|
||||
|
||||
// Special purpose registers
|
||||
DEFINE_ALIAS(load_T0_PC,0);
|
||||
DEFINE_ALIAS(store_T0_PC,0);
|
||||
DEFINE_ALIAS(set_PC_T1,0);
|
||||
DEFINE_ALIAS(set_PC_im,1);
|
||||
DEFINE_ALIAS(inc_PC,1);
|
||||
DEFINE_ALIAS(load_T0_LR,0);
|
||||
DEFINE_ALIAS(store_T0_LR,0);
|
||||
DEFINE_ALIAS(load_T0_CTR,0);
|
||||
DEFINE_ALIAS(load_T1_CTR,0);
|
||||
DEFINE_ALIAS(store_T0_CTR,0);
|
||||
DEFINE_ALIAS(store_T1_CTR,0);
|
||||
DEFINE_ALIAS(load_T1_PC,0);
|
||||
DEFINE_ALIAS(load_T1_LR,0);
|
||||
DEFINE_ALIAS(store_im_LR,1);
|
||||
|
||||
// Control Flow
|
||||
DEFINE_ALIAS(decrement_ctr_T1,0);
|
||||
DEFINE_ALIAS(branch_if_T0,2);
|
||||
DEFINE_ALIAS(branch_if_T1,2);
|
||||
DEFINE_ALIAS(branch_if_not_T0,2);
|
||||
DEFINE_ALIAS(branch_if_not_T1,2);
|
||||
DEFINE_ALIAS(branch_if_T0_T1,2);
|
||||
DEFINE_ALIAS(branch_T1_if_T0,1);
|
||||
|
||||
// Compare & Record instructions
|
||||
DEFINE_ALIAS_CLOBBER_SO(record_nego_T0,0);
|
||||
void gen_record_cr0_T0();
|
||||
DEFINE_ALIAS(compare_T0_T1,0);
|
||||
DEFINE_ALIAS(compare_T0_0,0);
|
||||
DEFINE_ALIAS(compare_T0_im,1);
|
||||
void gen_compare_T0_T1(int crf);
|
||||
void gen_compare_T0_im(int crf, int32 value);
|
||||
void gen_compare_logical_T0_T1(int crf);
|
||||
void gen_compare_logical_T0_im(int crf, int32 value);
|
||||
|
||||
// Multiply/Divide instructions
|
||||
DEFINE_ALIAS(mulhw_T0_T1,0);
|
||||
DEFINE_ALIAS(mulhwu_T0_T1,0);
|
||||
DEFINE_ALIAS(mulli_T0_im,1);
|
||||
DEFINE_ALIAS_CLOBBER_SO(mullwo_T0_T1,0);
|
||||
DEFINE_ALIAS(divw_T0_T1,0);
|
||||
DEFINE_ALIAS_CLOBBER_SO(divwo_T0_T1,0);
|
||||
DEFINE_ALIAS(divwu_T0_T1,0);
|
||||
DEFINE_ALIAS_CLOBBER_SO(divwuo_T0_T1,0);
|
||||
|
||||
// Shift/Rotate instructions
|
||||
DEFINE_ALIAS(slw_T0_T1,0);
|
||||
DEFINE_ALIAS(srw_T0_T1,0);
|
||||
DEFINE_ALIAS(sraw_T0_T1,0);
|
||||
DEFINE_ALIAS(sraw_T0_im,1);
|
||||
DEFINE_ALIAS(rlwimi_T0_T1,2);
|
||||
DEFINE_ALIAS(rlwinm_T0_T1,2);
|
||||
DEFINE_ALIAS(rlwnm_T0_T1,1);
|
||||
DEFINE_ALIAS(cntlzw_32_T0,0);
|
||||
|
||||
// Add/Sub related instructions
|
||||
DEFINE_ALIAS(addo_T0_T1,0);
|
||||
DEFINE_ALIAS(addc_T0_im,1);
|
||||
DEFINE_ALIAS(addc_T0_T1,0);
|
||||
DEFINE_ALIAS_CLOBBER_SO(addco_T0_T1,0);
|
||||
DEFINE_ALIAS(adde_T0_T1,0);
|
||||
DEFINE_ALIAS_CLOBBER_SO(addeo_T0_T1,0);
|
||||
DEFINE_ALIAS(addme_T0,0);
|
||||
DEFINE_ALIAS_CLOBBER_SO(addmeo_T0,0);
|
||||
DEFINE_ALIAS(addze_T0,0);
|
||||
DEFINE_ALIAS_CLOBBER_SO(addzeo_T0,0);
|
||||
DEFINE_ALIAS(subf_T0_T1,0);
|
||||
DEFINE_ALIAS_CLOBBER_SO(subfo_T0_T1,0);
|
||||
DEFINE_ALIAS(subfc_T0_im,1);
|
||||
DEFINE_ALIAS(subfc_T0_T1,0);
|
||||
DEFINE_ALIAS_CLOBBER_SO(subfco_T0_T1,0);
|
||||
DEFINE_ALIAS(subfe_T0_T1,0);
|
||||
DEFINE_ALIAS_CLOBBER_SO(subfeo_T0_T1,0);
|
||||
DEFINE_ALIAS(subfme_T0,0);
|
||||
DEFINE_ALIAS_CLOBBER_SO(subfmeo_T0,0);
|
||||
DEFINE_ALIAS(subfze_T0,0);
|
||||
DEFINE_ALIAS_CLOBBER_SO(subfzeo_T0,0);
|
||||
|
||||
// Branch instructions
|
||||
void gen_bc(int bo, int bi, uint32 tpc, uint32 npc);
|
||||
#define DEFINE_ALIAS_GRP_1(CR,CTR) \
|
||||
DEFINE_ALIAS(b##CR##_##CTR,2); \
|
||||
DEFINE_ALIAS(bn##CR##_##CTR,2);
|
||||
#define DEFINE_ALIAS_GRP_2(CR) \
|
||||
DEFINE_ALIAS_GRP_1(CR,0x); \
|
||||
DEFINE_ALIAS_GRP_1(CR,10); \
|
||||
DEFINE_ALIAS_GRP_1(CR,11);
|
||||
DEFINE_ALIAS_GRP_2(lt);
|
||||
DEFINE_ALIAS_GRP_2(gt);
|
||||
DEFINE_ALIAS_GRP_2(eq);
|
||||
DEFINE_ALIAS_GRP_2(so);
|
||||
#undef DEFINE_ALAIS_GRP_2
|
||||
#undef DEFINE_ALIAS_GRP_1
|
||||
|
||||
#undef DEFINE_ALIAS
|
||||
#undef DEFINE_ALIAS_0
|
||||
#undef DEFINE_ALIAS_1
|
||||
#undef DEFINE_ALIAS_2
|
||||
#undef DEFINE_ALIAS_3
|
||||
#undef DEFINE_ALIAS_RAW
|
||||
};
|
||||
|
||||
#endif /* PPC_ENABLE_JIT */
|
||||
|
||||
#endif /* PPC_DYNGEN_H */
|
@ -28,6 +28,11 @@
|
||||
#include "cpu/ppc/ppc-bitfields.hpp"
|
||||
#include "cpu/ppc/ppc-operands.hpp"
|
||||
#include "cpu/ppc/ppc-operations.hpp"
|
||||
#include "cpu/ppc/ppc-execute.hpp"
|
||||
|
||||
#ifndef SHEEPSHAVER
|
||||
#include "basic-kernel.hpp"
|
||||
#endif
|
||||
|
||||
#if ENABLE_MON
|
||||
#include "mon.h"
|
||||
@ -378,22 +383,11 @@ void powerpc_cpu::execute_divide(uint32 opcode)
|
||||
const uint32 b = operand_RB::get(this, opcode);
|
||||
uint32 d;
|
||||
|
||||
if (b == 0 || (SB && a == 0x80000000 && b == 0xffffffff)) {
|
||||
// Reference manual says rD is undefined
|
||||
d = 0;
|
||||
if (SB) {
|
||||
// However, checking against a real PowerPC (7410) yields
|
||||
// that rD gets all bits set to rA MSB
|
||||
d = -(a >> 31);
|
||||
}
|
||||
if (OE::test(opcode))
|
||||
xer().set_ov(1);
|
||||
}
|
||||
else {
|
||||
d = SB ? (int32)a/(int32)b : a/b;
|
||||
if (OE::test(opcode))
|
||||
xer().set_ov(0);
|
||||
}
|
||||
// Specialize divide semantic action
|
||||
if (OE::test(opcode))
|
||||
d = do_execute_divide<SB, true>(a, b);
|
||||
else
|
||||
d = do_execute_divide<SB, false>(a, b);
|
||||
|
||||
// Set CR0 (LT, GT, EQ, SO) if instruction has Rc set
|
||||
if (Rc::test(opcode))
|
||||
@ -926,8 +920,21 @@ void powerpc_cpu::execute_fp_round(uint32 opcode)
|
||||
|
||||
void powerpc_cpu::execute_syscall(uint32 opcode)
|
||||
{
|
||||
cr().set_so(0, execute_do_syscall && !execute_do_syscall(this));
|
||||
#ifdef SHEEPSHAVER
|
||||
D(bug("syscall\n"));
|
||||
increment_pc(4);
|
||||
#else
|
||||
try {
|
||||
cr().set_so(0, execute_do_syscall && !execute_do_syscall(this));
|
||||
increment_pc(4);
|
||||
}
|
||||
catch (kernel_syscall_exit & sc_exit) {
|
||||
// FIXME: add unwind info to the translation cache? Otherwise
|
||||
// we have to manually forward the exception to execution loop
|
||||
syscall_exit_code = sc_exit.status;
|
||||
spcflags().set(SPCFLAG_CPU_EXEC_RETURN);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1026,17 +1033,15 @@ void powerpc_cpu::execute_mfmsr(uint32 opcode)
|
||||
template< class SPR >
|
||||
void powerpc_cpu::execute_mfspr(uint32 opcode)
|
||||
{
|
||||
uint32 spr = SPR::get(this, opcode);
|
||||
const uint32 spr = SPR::get(this, opcode);
|
||||
uint32 d;
|
||||
switch (spr) {
|
||||
case 1: d = xer().get(); break;
|
||||
case 8: d = lr(); break;
|
||||
case 9: d = ctr(); break;
|
||||
case powerpc_registers::SPR_XER: d = xer().get();break;
|
||||
case powerpc_registers::SPR_LR: d = lr(); break;
|
||||
case powerpc_registers::SPR_CTR: d = ctr(); break;
|
||||
#ifdef SHEEPSHAVER
|
||||
case 25: // SDR1
|
||||
d = 0xdead001f;
|
||||
break;
|
||||
case 287: { // PVR
|
||||
case powerpc_registers::SPR_SDR1: d = 0xdead001f; break;
|
||||
case powerpc_registers::SPR_PVR: {
|
||||
extern uint32 PVR;
|
||||
d = PVR;
|
||||
break;
|
||||
@ -1053,13 +1058,13 @@ void powerpc_cpu::execute_mfspr(uint32 opcode)
|
||||
template< class SPR >
|
||||
void powerpc_cpu::execute_mtspr(uint32 opcode)
|
||||
{
|
||||
uint32 spr = SPR::get(this, opcode);
|
||||
const uint32 spr = SPR::get(this, opcode);
|
||||
const uint32 s = operand_RS::get(this, opcode);
|
||||
|
||||
switch (spr) {
|
||||
case 1: xer().set(s); break;
|
||||
case 8: lr() = s; break;
|
||||
case 9: ctr() = s; break;
|
||||
case powerpc_registers::SPR_XER: xer().set(s); break;
|
||||
case powerpc_registers::SPR_LR: lr() = s; break;
|
||||
case powerpc_registers::SPR_CTR: ctr() = s; break;
|
||||
#ifndef SHEEPSHAVER
|
||||
default: execute_illegal(opcode);
|
||||
#endif
|
||||
|
134
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-execute.hpp
Normal file
134
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-execute.hpp
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* ppc-execute.hpp - PowerPC semantic action templates
|
||||
*
|
||||
* 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_EXECUTE_H
|
||||
#define PPC_EXECUTE_H
|
||||
|
||||
// This file is designed to be included from implementation files only.
|
||||
#ifdef DYNGEN_OPS
|
||||
#define PPC_CPU powerpc_dyngen_helper
|
||||
#define DEFINE_HELPER(NAME, ARGS) static inline uint32 NAME ARGS
|
||||
#define RETURN(VAL) dyngen_barrier(); return (VAL)
|
||||
#else
|
||||
#define PPC_CPU powerpc_cpu
|
||||
#define DEFINE_HELPER(NAME, ARGS) inline uint32 powerpc_cpu::NAME ARGS
|
||||
#define RETURN(VAL) return (VAL)
|
||||
#endif
|
||||
|
||||
|
||||
template< bool SB > struct register_value { typedef uint32 type; };
|
||||
template< > struct register_value< true > { typedef int32 type; };
|
||||
|
||||
/**
|
||||
* Add instruction templates
|
||||
**/
|
||||
|
||||
template< bool EX, bool CA, bool OE >
|
||||
DEFINE_HELPER(do_execute_addition, (uint32 RA, uint32 RB))
|
||||
{
|
||||
uint32 RD = RA + RB + (EX ? PPC_CPU::xer().get_ca() : 0);
|
||||
|
||||
const bool _RA = ((int32)RA) < 0;
|
||||
const bool _RB = ((int32)RB) < 0;
|
||||
const bool _RD = ((int32)RD) < 0;
|
||||
|
||||
if (EX) {
|
||||
const bool ca = _RB ^ ((_RB ^ _RA) & (_RA ^ _RD));
|
||||
PPC_CPU::xer().set_ca(ca);
|
||||
}
|
||||
else if (CA) {
|
||||
const bool ca = (uint32)RD < (uint32)RA;
|
||||
PPC_CPU::xer().set_ca(ca);
|
||||
}
|
||||
|
||||
if (OE)
|
||||
PPC_CPU::xer().set_ov((_RB ^ _RD) & (_RA ^ _RD));
|
||||
|
||||
RETURN(RD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract instruction templates
|
||||
**/
|
||||
|
||||
template< bool CA, bool OE >
|
||||
DEFINE_HELPER(do_execute_subtract, (uint32 RA, uint32 RB))
|
||||
{
|
||||
uint32 RD = RB - RA;
|
||||
|
||||
const bool _RA = ((int32)RA) < 0;
|
||||
const bool _RB = ((int32)RB) < 0;
|
||||
const bool _RD = ((int32)RD) < 0;
|
||||
|
||||
if (CA)
|
||||
PPC_CPU::xer().set_ca((uint32)RD <= (uint32)RB);
|
||||
|
||||
if (OE)
|
||||
PPC_CPU::xer().set_ov((_RA ^ _RB) & (_RD ^ _RB));
|
||||
|
||||
RETURN(RD);
|
||||
}
|
||||
|
||||
template< bool OE >
|
||||
DEFINE_HELPER(do_execute_subtract_extended, (uint32 RA, uint32 RB))
|
||||
{
|
||||
const uint32 RD = ~RA + RB + PPC_CPU::xer().get_ca();
|
||||
|
||||
const bool _RA = ((int32)RA) < 0;
|
||||
const bool _RB = ((int32)RB) < 0;
|
||||
const bool _RD = ((int32)RD) < 0;
|
||||
|
||||
const bool ca = !_RA ^ ((_RA ^ _RD) & (_RB ^ _RD));
|
||||
PPC_CPU::xer().set_ca(ca);
|
||||
|
||||
if (OE)
|
||||
PPC_CPU::xer().set_ov((_RA ^ _RB) & (_RD ^ _RB));
|
||||
|
||||
RETURN(RD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Divide instruction templates
|
||||
**/
|
||||
|
||||
template< bool SB, bool OE >
|
||||
DEFINE_HELPER(do_execute_divide, (uint32 RA, uint32 RB))
|
||||
{
|
||||
typename register_value<SB>::type a = RA;
|
||||
typename register_value<SB>::type b = RB;
|
||||
uint32 RD;
|
||||
|
||||
if (b == 0 || (SB && a == 0x80000000 && b == -1)) {
|
||||
// Reference manual says result is undefined but it gets all
|
||||
// bits set to MSB on a real processor
|
||||
RD = SB ? ((int32)RA >> 31) : 0;
|
||||
if (OE)
|
||||
PPC_CPU::xer().set_ov(1);
|
||||
}
|
||||
else {
|
||||
RD = a / b;
|
||||
if (OE)
|
||||
PPC_CPU::xer().set_ov(0);
|
||||
}
|
||||
|
||||
RETURN(RD);
|
||||
}
|
||||
|
||||
#endif /* PPC_EXECUTE_H */
|
204
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-instructions.hpp
Normal file
204
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-instructions.hpp
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* ppc-instructions.hpp - PowerPC instructions IDs
|
||||
*
|
||||
* 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_INSTRUCTIONS_H
|
||||
#define PPC_INSTRUCTIONS_H
|
||||
|
||||
/**
|
||||
* Define PowerPC instruction types
|
||||
**/
|
||||
|
||||
#define PPC_I(X) powerpc_instruction_##X
|
||||
|
||||
enum powerpc_instruction {
|
||||
PPC_I(INVALID),
|
||||
PPC_I(ADD),
|
||||
PPC_I(ADDC),
|
||||
PPC_I(ADDE),
|
||||
PPC_I(ADDI),
|
||||
PPC_I(ADDIC),
|
||||
PPC_I(ADDIC_),
|
||||
PPC_I(ADDIS),
|
||||
PPC_I(ADDME),
|
||||
PPC_I(ADDZE),
|
||||
PPC_I(AND),
|
||||
PPC_I(ANDC),
|
||||
PPC_I(ANDI),
|
||||
PPC_I(ANDIS),
|
||||
PPC_I(B),
|
||||
PPC_I(BC),
|
||||
PPC_I(BCCTR),
|
||||
PPC_I(BCLR),
|
||||
PPC_I(CMP),
|
||||
PPC_I(CMPI),
|
||||
PPC_I(CMPL),
|
||||
PPC_I(CMPLI),
|
||||
PPC_I(CNTLZW),
|
||||
PPC_I(CRAND),
|
||||
PPC_I(CRANDC),
|
||||
PPC_I(CREQV),
|
||||
PPC_I(CRNAND),
|
||||
PPC_I(CRNOR),
|
||||
PPC_I(CROR),
|
||||
PPC_I(CRORC),
|
||||
PPC_I(CRXOR),
|
||||
PPC_I(DCBA),
|
||||
PPC_I(DCBF),
|
||||
PPC_I(DCBI),
|
||||
PPC_I(DCBST),
|
||||
PPC_I(DCBT),
|
||||
PPC_I(DCBTST),
|
||||
PPC_I(DCBZ),
|
||||
PPC_I(DIVW),
|
||||
PPC_I(DIVWU),
|
||||
PPC_I(ECIWX),
|
||||
PPC_I(ECOWX),
|
||||
PPC_I(EIEIO),
|
||||
PPC_I(EQV),
|
||||
PPC_I(EXTSB),
|
||||
PPC_I(EXTSH),
|
||||
PPC_I(FABS),
|
||||
PPC_I(FADD),
|
||||
PPC_I(FADDS),
|
||||
PPC_I(FCMPO),
|
||||
PPC_I(FCMPU),
|
||||
PPC_I(FCTIW),
|
||||
PPC_I(FCTIWZ),
|
||||
PPC_I(FDIV),
|
||||
PPC_I(FDIVS),
|
||||
PPC_I(FMADD),
|
||||
PPC_I(FMADDS),
|
||||
PPC_I(FMR),
|
||||
PPC_I(FMSUB),
|
||||
PPC_I(FMSUBS),
|
||||
PPC_I(FMUL),
|
||||
PPC_I(FMULS),
|
||||
PPC_I(FNABS),
|
||||
PPC_I(FNEG),
|
||||
PPC_I(FNMADD),
|
||||
PPC_I(FNMADDS),
|
||||
PPC_I(FNMSUB),
|
||||
PPC_I(FNMSUBS),
|
||||
PPC_I(FRSP),
|
||||
PPC_I(FSUB),
|
||||
PPC_I(FSUBS),
|
||||
PPC_I(ICBI),
|
||||
PPC_I(ISYNC),
|
||||
PPC_I(LBZ),
|
||||
PPC_I(LBZU),
|
||||
PPC_I(LBZUX),
|
||||
PPC_I(LBZX),
|
||||
PPC_I(LFD),
|
||||
PPC_I(LFDU),
|
||||
PPC_I(LFDUX),
|
||||
PPC_I(LFDX),
|
||||
PPC_I(LFS),
|
||||
PPC_I(LFSU),
|
||||
PPC_I(LFSUX),
|
||||
PPC_I(LFSX),
|
||||
PPC_I(LHA),
|
||||
PPC_I(LHAU),
|
||||
PPC_I(LHAUX),
|
||||
PPC_I(LHAX),
|
||||
PPC_I(LHBRX),
|
||||
PPC_I(LHZ),
|
||||
PPC_I(LHZU),
|
||||
PPC_I(LHZUX),
|
||||
PPC_I(LHZX),
|
||||
PPC_I(LMW),
|
||||
PPC_I(LSWI),
|
||||
PPC_I(LSWX),
|
||||
PPC_I(LWARX),
|
||||
PPC_I(LWBRX),
|
||||
PPC_I(LWZ),
|
||||
PPC_I(LWZU),
|
||||
PPC_I(LWZUX),
|
||||
PPC_I(LWZX),
|
||||
PPC_I(MCRF),
|
||||
PPC_I(MFCR),
|
||||
PPC_I(MFFS),
|
||||
PPC_I(MFMSR),
|
||||
PPC_I(MFSPR),
|
||||
PPC_I(MFTB),
|
||||
PPC_I(MTCRF),
|
||||
PPC_I(MTFSB0),
|
||||
PPC_I(MTFSB1),
|
||||
PPC_I(MTFSF),
|
||||
PPC_I(MTFSFI),
|
||||
PPC_I(MTSPR),
|
||||
PPC_I(MULHW),
|
||||
PPC_I(MULHWU),
|
||||
PPC_I(MULLI),
|
||||
PPC_I(MULLW),
|
||||
PPC_I(NAND),
|
||||
PPC_I(NEG),
|
||||
PPC_I(NOR),
|
||||
PPC_I(OR),
|
||||
PPC_I(ORC),
|
||||
PPC_I(ORI),
|
||||
PPC_I(ORIS),
|
||||
PPC_I(RLWIMI),
|
||||
PPC_I(RLWINM),
|
||||
PPC_I(RLWNM),
|
||||
PPC_I(SC),
|
||||
PPC_I(SLW),
|
||||
PPC_I(SRAW),
|
||||
PPC_I(SRAWI),
|
||||
PPC_I(SRW),
|
||||
PPC_I(STB),
|
||||
PPC_I(STBU),
|
||||
PPC_I(STBUX),
|
||||
PPC_I(STBX),
|
||||
PPC_I(STFD),
|
||||
PPC_I(STFDU),
|
||||
PPC_I(STFDUX),
|
||||
PPC_I(STFDX),
|
||||
PPC_I(STFS),
|
||||
PPC_I(STFSU),
|
||||
PPC_I(STFSUX),
|
||||
PPC_I(STFSX),
|
||||
PPC_I(STH),
|
||||
PPC_I(STHBRX),
|
||||
PPC_I(STHU),
|
||||
PPC_I(STHUX),
|
||||
PPC_I(STHX),
|
||||
PPC_I(STMW),
|
||||
PPC_I(STSWI),
|
||||
PPC_I(STSWX),
|
||||
PPC_I(STW),
|
||||
PPC_I(STWBRX),
|
||||
PPC_I(STWCX),
|
||||
PPC_I(STWU),
|
||||
PPC_I(STWUX),
|
||||
PPC_I(STWX),
|
||||
PPC_I(SUBF),
|
||||
PPC_I(SUBFC),
|
||||
PPC_I(SUBFE),
|
||||
PPC_I(SUBFIC),
|
||||
PPC_I(SUBFME),
|
||||
PPC_I(SUBFZE),
|
||||
PPC_I(SYNC),
|
||||
PPC_I(XOR),
|
||||
PPC_I(XORI),
|
||||
PPC_I(XORIS),
|
||||
PPC_I(MAX) // Total number of instruction types
|
||||
};
|
||||
|
||||
#endif /* PPC_INSTRUCTIONS_H */
|
@ -120,13 +120,16 @@ struct immediate_value {
|
||||
};
|
||||
|
||||
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);
|
||||
static inline uint32 compute(uint32 mb, uint32 me) {
|
||||
return ((mb > me) ?
|
||||
~(((uint32)-1 >> mb) ^ ((me >= 31) ? 0 : (uint32)-1 >> (me + 1))) :
|
||||
(((uint32)-1 >> mb) ^ ((me >= 31) ? 0 : (uint32)-1 >> (me + 1))));
|
||||
}
|
||||
static inline uint32 get(powerpc_cpu *, uint32 opcode) {
|
||||
const uint32 mb = MB_field::extract(opcode);
|
||||
const uint32 me = ME_field::extract(opcode);
|
||||
return compute(mb, me);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -105,27 +105,27 @@ powerpc_cr_register::test(int condition) const
|
||||
|
||||
class powerpc_xer_register
|
||||
{
|
||||
bool so;
|
||||
bool ov;
|
||||
bool ca;
|
||||
uint32 byte_count;
|
||||
uint8 so;
|
||||
uint8 ov;
|
||||
uint8 ca;
|
||||
uint8 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; }
|
||||
void set_so(int v) { so = v; }
|
||||
int get_so() const { return so; }
|
||||
void set_ov(int v) { ov = v; so |= v; }
|
||||
int get_ov() const { return ov; }
|
||||
void set_ca(int v) { ca = v; }
|
||||
int get_ca() const { return ca; }
|
||||
void set_count(int v) { byte_count = v; }
|
||||
int get_count() const { return byte_count; }
|
||||
};
|
||||
|
||||
inline
|
||||
powerpc_xer_register::powerpc_xer_register()
|
||||
: so(false), ov(false), ca(false), byte_count(0)
|
||||
: so(0), ov(0), ca(0), byte_count(0)
|
||||
{ }
|
||||
|
||||
inline uint32
|
||||
@ -178,7 +178,15 @@ struct powerpc_registers
|
||||
PC,
|
||||
SP = GPR_BASE + 1
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
SPR_XER = 1,
|
||||
SPR_LR = 8,
|
||||
SPR_CTR = 9,
|
||||
SPR_SDR1 = 25,
|
||||
SPR_PVR = 287,
|
||||
};
|
||||
|
||||
static inline int GPR(int r) { return GPR_BASE + r; }
|
||||
static inline int FPR(int r) { return FPR_BASE + r; }
|
||||
|
||||
|
1031
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-translate.cpp
Normal file
1031
SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-translate.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -21,10 +21,13 @@
|
||||
#include <stdlib.h>
|
||||
#include "sysdeps.h"
|
||||
#include "cpu/ppc/ppc-cpu.hpp"
|
||||
#include "cpu/ppc/ppc-instructions.hpp"
|
||||
|
||||
// Disassemblers needed for debugging purposes
|
||||
#if ENABLE_MON
|
||||
#include "mon.h"
|
||||
#include "mon_disass.h"
|
||||
#endif
|
||||
|
||||
#define TEST_ADD 1
|
||||
#define TEST_SUB 1
|
||||
@ -86,6 +89,10 @@ static void inline flush_icache_range(uint32 *start_p, uint32 length)
|
||||
asm volatile ("sync" : : : "memory");
|
||||
asm volatile ("isync" : : : "memory");
|
||||
}
|
||||
#else
|
||||
static void inline flush_icache_range(uint32 *start_p, uint32 length)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
struct exec_return { };
|
||||
@ -105,6 +112,7 @@ class powerpc_test_cpu
|
||||
void emul_set_cr(uint32 value)
|
||||
{ cr().set(value); }
|
||||
|
||||
#if defined(__powerpc__)
|
||||
uint32 native_get_xer() const
|
||||
{ uint32 xer; asm volatile ("mfxer %0" : "=r" (xer)); return xer; }
|
||||
|
||||
@ -116,6 +124,10 @@ class powerpc_test_cpu
|
||||
|
||||
void native_set_cr(uint32 cr) const
|
||||
{ asm volatile ("mtcr %0" : : "r" (cr)); }
|
||||
#endif
|
||||
|
||||
void flush_icache_range(uint32 *start, uint32 size)
|
||||
{ invalidate_cache(); ::flush_icache_range(start, size); }
|
||||
|
||||
void init_decoder();
|
||||
void print_flags(uint32 cr, uint32 xer, int crf = 0) const;
|
||||
@ -124,12 +136,8 @@ class powerpc_test_cpu
|
||||
|
||||
public:
|
||||
|
||||
powerpc_test_cpu()
|
||||
: powerpc_cpu(NULL)
|
||||
{ mon_init(); init_decoder(); }
|
||||
|
||||
~powerpc_test_cpu()
|
||||
{ mon_exit(); }
|
||||
powerpc_test_cpu();
|
||||
~powerpc_test_cpu();
|
||||
|
||||
bool test(void);
|
||||
|
||||
@ -154,11 +162,13 @@ private:
|
||||
static const uint32 msk_values[];
|
||||
|
||||
void test_one(uint32 *code, const char *insn, uint32 a1, uint32 a2, uint32 a3, uint32 a0 = 0);
|
||||
void test_instruction_CNTLZ(const char *insn, uint32 opcode);
|
||||
void test_instruction_RR___(const char *insn, uint32 opcode);
|
||||
void test_instruction_RRI__(const char *insn, uint32 opcode);
|
||||
#define test_instruction_RRK__ test_instruction_RRI__
|
||||
void test_instruction_RRS__(const char *insn, uint32 opcode);
|
||||
void test_instruction_RRR__(const char *insn, uint32 opcode);
|
||||
void test_instruction_RRRSH(const char *insn, uint32 opcode);
|
||||
void test_instruction_RRIII(const char *insn, uint32 opcode);
|
||||
void test_instruction_RRRII(const char *insn, uint32 opcode);
|
||||
void test_instruction_CRR__(const char *insn, uint32 opcode);
|
||||
@ -178,9 +188,25 @@ private:
|
||||
void test_load_multiple(void);
|
||||
};
|
||||
|
||||
powerpc_test_cpu::powerpc_test_cpu()
|
||||
: powerpc_cpu(NULL)
|
||||
{
|
||||
#if ENABLE_MON
|
||||
mon_init();
|
||||
#endif
|
||||
init_decoder();
|
||||
}
|
||||
|
||||
powerpc_test_cpu::~powerpc_test_cpu()
|
||||
{
|
||||
#if ENABLE_MON
|
||||
mon_exit();
|
||||
#endif
|
||||
}
|
||||
|
||||
void powerpc_test_cpu::execute_return(uint32 opcode)
|
||||
{
|
||||
throw exec_return();
|
||||
spcflags().set(SPCFLAG_CPU_EXEC_RETURN);
|
||||
}
|
||||
|
||||
void powerpc_test_cpu::init_decoder()
|
||||
@ -196,7 +222,8 @@ void powerpc_test_cpu::init_decoder()
|
||||
{ "return",
|
||||
(execute_pmf)&powerpc_test_cpu::execute_return,
|
||||
NULL,
|
||||
D_form, 6, 0, CFLOW_TRAP
|
||||
PPC_I(MAX),
|
||||
D_form, 6, 0, CFLOW_JUMP
|
||||
}
|
||||
};
|
||||
|
||||
@ -211,16 +238,11 @@ void powerpc_test_cpu::init_decoder()
|
||||
void powerpc_test_cpu::execute(uint32 *code_p)
|
||||
{
|
||||
static uint32 code[] = { POWERPC_BLR | 1, POWERPC_EMUL_OP };
|
||||
lr() = (uint32)code_p;
|
||||
assert((uintptr)code <= INT_MAX);
|
||||
lr() = (uintptr)code_p;
|
||||
|
||||
try {
|
||||
invalidate_cache();
|
||||
pc() = (uintptr)code;
|
||||
powerpc_cpu::execute();
|
||||
}
|
||||
catch (exec_return const &) {
|
||||
// Nothing, simply return
|
||||
}
|
||||
pc() = (uintptr)code;
|
||||
powerpc_cpu::execute();
|
||||
}
|
||||
|
||||
void powerpc_test_cpu::print_flags(uint32 cr, uint32 xer, int crf) const
|
||||
@ -242,6 +264,7 @@ void powerpc_test_cpu::print_flags(uint32 cr, uint32 xer, int crf) const
|
||||
|
||||
void powerpc_test_cpu::test_one(uint32 *code, const char *insn, uint32 a1, uint32 a2, uint32 a3, uint32 a0)
|
||||
{
|
||||
#if defined(__powerpc__)
|
||||
// Invoke native code
|
||||
const uint32 save_xer = native_get_xer();
|
||||
const uint32 save_cr = native_get_cr();
|
||||
@ -254,6 +277,12 @@ void powerpc_test_cpu::test_one(uint32 *code, const char *insn, uint32 a1, uint3
|
||||
const uint32 native_cr = native_get_cr();
|
||||
native_set_xer(save_xer);
|
||||
native_set_cr(save_cr);
|
||||
#else
|
||||
// FIXME: Restore context from results file
|
||||
const uint32 native_rd = 0;
|
||||
const uint32 native_xer = 0;
|
||||
const uint32 native_cr = 0;
|
||||
#endif
|
||||
|
||||
// Invoke emulated code
|
||||
emul_set_xer(init_xer);
|
||||
@ -284,7 +313,9 @@ void powerpc_test_cpu::test_one(uint32 *code, const char *insn, uint32 a1, uint3
|
||||
}
|
||||
|
||||
if (!ok || verbose) {
|
||||
#if ENABLE_MON
|
||||
disass_ppc(stdout, (uintptr)code, code[0]);
|
||||
#endif
|
||||
#define PRINT_OPERANDS(PREFIX) do { \
|
||||
printf(" %08x, %08x, %08x, %08x => %08x [", \
|
||||
a0, a1, a2, a3, PREFIX##_rd); \
|
||||
@ -333,6 +364,34 @@ const uint32 powerpc_test_cpu::msk_values[] = {
|
||||
30, 31
|
||||
};
|
||||
|
||||
void powerpc_test_cpu::test_instruction_CNTLZ(const char *insn, uint32 opcode)
|
||||
{
|
||||
// Test code
|
||||
static uint32 code[] = {
|
||||
POWERPC_ILLEGAL, POWERPC_BLR,
|
||||
POWERPC_MR(0, RA), POWERPC_ILLEGAL, POWERPC_BLR
|
||||
};
|
||||
|
||||
// Input values
|
||||
const int n_values = sizeof(reg_values)/sizeof(reg_values[0]);
|
||||
|
||||
code[0] = code[3] = opcode; // <op> RD,RA,RB
|
||||
rA_field::insert(code[3], 0); // <op> RD,R0,RB
|
||||
flush_icache_range(code, sizeof(code));
|
||||
|
||||
for (uint32 mask = 0x800000000; mask != 0; mask >>= 1) {
|
||||
uint32 ra = mask;
|
||||
test_one(&code[0], insn, ra, 0, 0);
|
||||
test_one(&code[2], insn, ra, 0, 0);
|
||||
}
|
||||
// random values (including zero)
|
||||
for (int i = 0; i < n_values; i++) {
|
||||
uint32 ra = reg_values[i];
|
||||
test_one(&code[0], insn, ra, 0, 0);
|
||||
test_one(&code[2], insn, ra, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void powerpc_test_cpu::test_instruction_RR___(const char *insn, uint32 opcode)
|
||||
{
|
||||
// Test code
|
||||
@ -431,6 +490,31 @@ void powerpc_test_cpu::test_instruction_RRR__(const char *insn, uint32 opcode)
|
||||
}
|
||||
}
|
||||
|
||||
void powerpc_test_cpu::test_instruction_RRRSH(const char *insn, uint32 opcode)
|
||||
{
|
||||
// Test code
|
||||
static uint32 code[] = {
|
||||
POWERPC_ILLEGAL, POWERPC_BLR,
|
||||
POWERPC_MR(0, RA), POWERPC_ILLEGAL, POWERPC_BLR
|
||||
};
|
||||
|
||||
// Input values
|
||||
const int n_values = sizeof(reg_values)/sizeof(reg_values[0]);
|
||||
|
||||
code[0] = code[3] = opcode; // <op> RD,RA,RB
|
||||
rA_field::insert(code[3], 0); // <op> RD,R0,RB
|
||||
flush_icache_range(code, sizeof(code));
|
||||
|
||||
for (int i = 0; i < n_values; i++) {
|
||||
const uint32 ra = reg_values[i];
|
||||
for (int j = 0; j <= 64; j++) {
|
||||
const uint32 rb = j;
|
||||
test_one(&code[0], insn, ra, rb, 0);
|
||||
test_one(&code[2], insn, ra, rb, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void powerpc_test_cpu::test_instruction_RRIII(const char *insn, uint32 opcode)
|
||||
{
|
||||
// Test code
|
||||
@ -590,55 +674,81 @@ void powerpc_test_cpu::test_instruction_CCC__(const char *insn, uint32 opcode)
|
||||
void powerpc_test_cpu::test_add(void)
|
||||
{
|
||||
#if TEST_ADD
|
||||
TEST_INSTRUCTION(RRR__,"add", _XO(31,RD,RA,RB,0,266,0));
|
||||
TEST_INSTRUCTION(RRR__,"add.", _XO(31,RD,RA,RB,0,266,1));
|
||||
TEST_INSTRUCTION(RRR__,"addo", _XO(31,RD,RA,RB,1,266,0));
|
||||
TEST_INSTRUCTION(RRR__,"addo." , _XO(31,RD,RA,RB,1,266,1));
|
||||
TEST_INSTRUCTION(RRR__,"addc.", _XO(31,RD,RA,RB,0, 10,1));
|
||||
TEST_INSTRUCTION(RRR__,"addco.", _XO(31,RD,RA,RB,1, 10,1));
|
||||
TEST_INSTRUCTION(RRR__,"adde.", _XO(31,RD,RA,RB,0,138,1));
|
||||
TEST_INSTRUCTION(RRR__,"addeo.", _XO(31,RD,RA,RB,1,138,1));
|
||||
TEST_INSTRUCTION(RRI__,"addi", _D (14,RD,RA,00));
|
||||
TEST_INSTRUCTION(RRI__,"addic", _D (12,RD,RA,00));
|
||||
TEST_INSTRUCTION(RRI__,"addic.", _D (13,RD,RA,00));
|
||||
TEST_INSTRUCTION(RRI__,"addis", _D (15,RD,RA,00));
|
||||
TEST_INSTRUCTION(RR___,"addme.", _XO(31,RD,RA,00,0,234,1));
|
||||
TEST_INSTRUCTION(RR___,"addmeo.", _XO(31,RD,RA,00,1,234,1));
|
||||
TEST_INSTRUCTION(RR___,"addze.", _XO(31,RD,RA,00,0,202,1));
|
||||
TEST_INSTRUCTION(RR___,"addzeo.", _XO(31,RD,RA,00,1,202,1));
|
||||
init_xer |= XER_CA_field::mask();
|
||||
TEST_INSTRUCTION(RRR__,"adde.", _XO(31,RD,RA,RB,0,138,1));
|
||||
TEST_INSTRUCTION(RRR__,"addeo.", _XO(31,RD,RA,RB,1,138,1));
|
||||
TEST_INSTRUCTION(RR___,"addme.", _XO(31,RD,RA,00,0,234,1));
|
||||
TEST_INSTRUCTION(RR___,"addmeo.", _XO(31,RD,RA,00,1,234,1));
|
||||
TEST_INSTRUCTION(RR___,"addze.", _XO(31,RD,RA,00,0,202,1));
|
||||
TEST_INSTRUCTION(RR___,"addzeo.", _XO(31,RD,RA,00,1,202,1));
|
||||
init_xer &= ~XER_CA_field::mask();
|
||||
const int n_xer_values = 3;
|
||||
uint32 xer_values[n_xer_values];
|
||||
xer_values[0] = init_xer;
|
||||
xer_values[1] = init_xer | XER_OV_field::mask();
|
||||
xer_values[2] = init_xer | XER_CA_field::mask();
|
||||
|
||||
// Iterate over some specific XER values so that we make sure we
|
||||
// only update them when actually needed
|
||||
for (int i = 0; i < n_xer_values; i++) {
|
||||
const uint32 saved_xer = init_xer;
|
||||
init_xer = xer_values[i];
|
||||
TEST_INSTRUCTION(RRR__,"add", _XO(31,RD,RA,RB,0,266,0));
|
||||
TEST_INSTRUCTION(RRR__,"add.", _XO(31,RD,RA,RB,0,266,1));
|
||||
TEST_INSTRUCTION(RRR__,"addo", _XO(31,RD,RA,RB,1,266,0));
|
||||
TEST_INSTRUCTION(RRR__,"addo." , _XO(31,RD,RA,RB,1,266,1));
|
||||
TEST_INSTRUCTION(RRR__,"addc.", _XO(31,RD,RA,RB,0, 10,1));
|
||||
TEST_INSTRUCTION(RRR__,"addco.", _XO(31,RD,RA,RB,1, 10,1));
|
||||
TEST_INSTRUCTION(RRR__,"adde", _XO(31,RD,RA,RB,0,138,0));
|
||||
TEST_INSTRUCTION(RRR__,"adde.", _XO(31,RD,RA,RB,0,138,1));
|
||||
TEST_INSTRUCTION(RRR__,"addeo", _XO(31,RD,RA,RB,1,138,0));
|
||||
TEST_INSTRUCTION(RRR__,"addeo.", _XO(31,RD,RA,RB,1,138,1));
|
||||
TEST_INSTRUCTION(RRI__,"addi", _D (14,RD,RA,00));
|
||||
TEST_INSTRUCTION(RRI__,"addic", _D (12,RD,RA,00));
|
||||
TEST_INSTRUCTION(RRI__,"addic.", _D (13,RD,RA,00));
|
||||
TEST_INSTRUCTION(RRI__,"addis", _D (15,RD,RA,00));
|
||||
TEST_INSTRUCTION(RR___,"addme", _XO(31,RD,RA,00,0,234,0));
|
||||
TEST_INSTRUCTION(RR___,"addme.", _XO(31,RD,RA,00,0,234,1));
|
||||
TEST_INSTRUCTION(RR___,"addmeo", _XO(31,RD,RA,00,1,234,0));
|
||||
TEST_INSTRUCTION(RR___,"addmeo.", _XO(31,RD,RA,00,1,234,1));
|
||||
TEST_INSTRUCTION(RR___,"addze", _XO(31,RD,RA,00,0,202,0));
|
||||
TEST_INSTRUCTION(RR___,"addze.", _XO(31,RD,RA,00,0,202,1));
|
||||
TEST_INSTRUCTION(RR___,"addzeo", _XO(31,RD,RA,00,1,202,0));
|
||||
TEST_INSTRUCTION(RR___,"addzeo.", _XO(31,RD,RA,00,1,202,1));
|
||||
init_xer = saved_xer;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void powerpc_test_cpu::test_sub(void)
|
||||
{
|
||||
#if TEST_SUB
|
||||
TEST_INSTRUCTION(RRR__,"subf.", _XO(31,RD,RA,RB,0, 40,1));
|
||||
TEST_INSTRUCTION(RRR__,"subfo.", _XO(31,RD,RA,RB,1, 40,1));
|
||||
TEST_INSTRUCTION(RRR__,"subfc.", _XO(31,RD,RA,RB,0, 8,1));
|
||||
TEST_INSTRUCTION(RRR__,"subfco.", _XO(31,RD,RA,RB,1, 8,1));
|
||||
TEST_INSTRUCTION(RRR__,"subfe.", _XO(31,RD,RA,RB,0,136,1));
|
||||
TEST_INSTRUCTION(RRR__,"subfeo.", _XO(31,RD,RA,RB,1,136,1));
|
||||
TEST_INSTRUCTION(RRI__,"subfic", _D ( 8,RD,RA,00));
|
||||
TEST_INSTRUCTION(RR___,"subfme.", _XO(31,RD,RA,00,0,232,1));
|
||||
TEST_INSTRUCTION(RR___,"subfmeo.", _XO(31,RD,RA,00,1,232,1));
|
||||
TEST_INSTRUCTION(RR___,"subfze.", _XO(31,RD,RA,00,0,200,1));
|
||||
TEST_INSTRUCTION(RR___,"subfzeo.", _XO(31,RD,RA,00,1,200,1));
|
||||
init_xer |= XER_CA_field::mask();
|
||||
TEST_INSTRUCTION(RRR__,"subfe.", _XO(31,RD,RA,RB,0,136,1));
|
||||
TEST_INSTRUCTION(RRR__,"subfeo.", _XO(31,RD,RA,RB,1,136,1));
|
||||
TEST_INSTRUCTION(RR___,"subfme.", _XO(31,RD,RA,00,0,232,1));
|
||||
TEST_INSTRUCTION(RR___,"subfmeo.", _XO(31,RD,RA,00,1,232,1));
|
||||
TEST_INSTRUCTION(RR___,"subfze.", _XO(31,RD,RA,00,0,200,1));
|
||||
TEST_INSTRUCTION(RR___,"subfzeo.", _XO(31,RD,RA,00,1,200,1));
|
||||
init_xer &= ~XER_CA_field::mask();
|
||||
const int n_xer_values = 3;
|
||||
uint32 xer_values[n_xer_values];
|
||||
xer_values[0] = init_xer;
|
||||
xer_values[1] = init_xer | XER_OV_field::mask();
|
||||
xer_values[2] = init_xer | XER_CA_field::mask();
|
||||
|
||||
// Iterate over some specific XER values so that we make sure we
|
||||
// only update them when actually needed
|
||||
for (int i = 0; i < n_xer_values; i++) {
|
||||
const uint32 saved_xer = init_xer;
|
||||
init_xer = xer_values[i];
|
||||
TEST_INSTRUCTION(RRR__,"subf", _XO(31,RD,RA,RB,0, 40,0));
|
||||
TEST_INSTRUCTION(RRR__,"subf.", _XO(31,RD,RA,RB,0, 40,1));
|
||||
TEST_INSTRUCTION(RRR__,"subfo", _XO(31,RD,RA,RB,1, 40,0));
|
||||
TEST_INSTRUCTION(RRR__,"subfo.", _XO(31,RD,RA,RB,1, 40,1));
|
||||
TEST_INSTRUCTION(RRR__,"subfc", _XO(31,RD,RA,RB,0, 8,0));
|
||||
TEST_INSTRUCTION(RRR__,"subfc.", _XO(31,RD,RA,RB,0, 8,1));
|
||||
TEST_INSTRUCTION(RRR__,"subfco", _XO(31,RD,RA,RB,1, 8,0));
|
||||
TEST_INSTRUCTION(RRR__,"subfco.", _XO(31,RD,RA,RB,1, 8,1));
|
||||
TEST_INSTRUCTION(RRR__,"subfe", _XO(31,RD,RA,RB,0,136,0));
|
||||
TEST_INSTRUCTION(RRR__,"subfe.", _XO(31,RD,RA,RB,0,136,1));
|
||||
TEST_INSTRUCTION(RRR__,"subfeo", _XO(31,RD,RA,RB,1,136,0));
|
||||
TEST_INSTRUCTION(RRR__,"subfeo.", _XO(31,RD,RA,RB,1,136,1));
|
||||
TEST_INSTRUCTION(RRI__,"subfic", _D ( 8,RD,RA,00));
|
||||
TEST_INSTRUCTION(RR___,"subfme", _XO(31,RD,RA,00,0,232,0));
|
||||
TEST_INSTRUCTION(RR___,"subfme.", _XO(31,RD,RA,00,0,232,1));
|
||||
TEST_INSTRUCTION(RR___,"subfmeo", _XO(31,RD,RA,00,1,232,0));
|
||||
TEST_INSTRUCTION(RR___,"subfmeo.", _XO(31,RD,RA,00,1,232,1));
|
||||
TEST_INSTRUCTION(RR___,"subfze", _XO(31,RD,RA,00,0,200,0));
|
||||
TEST_INSTRUCTION(RR___,"subfze.", _XO(31,RD,RA,00,0,200,1));
|
||||
TEST_INSTRUCTION(RR___,"subfzeo", _XO(31,RD,RA,00,1,200,0));
|
||||
TEST_INSTRUCTION(RR___,"subfzeo.", _XO(31,RD,RA,00,1,200,1));
|
||||
init_xer = saved_xer;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -674,22 +784,35 @@ void powerpc_test_cpu::test_div(void)
|
||||
void powerpc_test_cpu::test_logical(void)
|
||||
{
|
||||
#if TEST_LOGICAL
|
||||
TEST_INSTRUCTION(RRR__,"and", _X (31,RA,RD,RB,28,0));
|
||||
TEST_INSTRUCTION(RRR__,"and.", _X (31,RA,RD,RB,28,1));
|
||||
TEST_INSTRUCTION(RRR__,"andc", _X (31,RA,RD,RB,60,0));
|
||||
TEST_INSTRUCTION(RRR__,"andc.", _X (31,RA,RD,RB,60,1));
|
||||
TEST_INSTRUCTION(RRK__,"andi.", _D (28,RA,RD,00));
|
||||
TEST_INSTRUCTION(RRK__,"andis.", _D (29,RA,RD,00));
|
||||
TEST_INSTRUCTION(RR___,"cntlzw.", _X (31,RA,RD,00,26,1));
|
||||
TEST_INSTRUCTION(CNTLZ,"cntlzw", _X (31,RA,RD,00,26,0));
|
||||
TEST_INSTRUCTION(CNTLZ,"cntlzw.", _X (31,RA,RD,00,26,1));
|
||||
TEST_INSTRUCTION(RRR__,"eqv", _X (31,RA,RD,RB,284,0));
|
||||
TEST_INSTRUCTION(RRR__,"eqv.", _X (31,RA,RD,RB,284,1));
|
||||
TEST_INSTRUCTION(RR___,"extsb", _X (31,RA,RD,00,954,0));
|
||||
TEST_INSTRUCTION(RR___,"extsb.", _X (31,RA,RD,00,954,1));
|
||||
TEST_INSTRUCTION(RR___,"extsh", _X (31,RA,RD,00,922,0));
|
||||
TEST_INSTRUCTION(RR___,"extsh.", _X (31,RA,RD,00,922,1));
|
||||
TEST_INSTRUCTION(RRR__,"nand", _X (31,RA,RD,RB,476,0));
|
||||
TEST_INSTRUCTION(RRR__,"nand.", _X (31,RA,RD,RB,476,1));
|
||||
TEST_INSTRUCTION(RR___,"neg", _XO(31,RD,RA,RB,0,104,0));
|
||||
TEST_INSTRUCTION(RR___,"neg.", _XO(31,RD,RA,RB,0,104,1));
|
||||
TEST_INSTRUCTION(RR___,"nego", _XO(31,RD,RA,RB,1,104,0));
|
||||
TEST_INSTRUCTION(RR___,"nego.", _XO(31,RD,RA,RB,1,104,1));
|
||||
TEST_INSTRUCTION(RRR__,"nor", _X (31,RA,RD,RB,124,0));
|
||||
TEST_INSTRUCTION(RRR__,"nor.", _X (31,RA,RD,RB,124,1));
|
||||
TEST_INSTRUCTION(RRR__,"or", _X (31,RA,RD,RB,444,0));
|
||||
TEST_INSTRUCTION(RRR__,"or.", _X (31,RA,RD,RB,444,1));
|
||||
TEST_INSTRUCTION(RRR__,"orc", _X (31,RA,RD,RB,412,0));
|
||||
TEST_INSTRUCTION(RRR__,"orc.", _X (31,RA,RD,RB,412,1));
|
||||
TEST_INSTRUCTION(RRK__,"ori", _D (24,RA,RD,00));
|
||||
TEST_INSTRUCTION(RRK__,"oris", _D (25,RA,RD,00));
|
||||
TEST_INSTRUCTION(RRR__,"xor", _X (31,RA,RD,RB,316,0));
|
||||
TEST_INSTRUCTION(RRR__,"xor.", _X (31,RA,RD,RB,316,1));
|
||||
TEST_INSTRUCTION(RRK__,"xori", _D (26,RA,RD,00));
|
||||
TEST_INSTRUCTION(RRK__,"xoris", _D (27,RA,RD,00));
|
||||
@ -699,22 +822,25 @@ void powerpc_test_cpu::test_logical(void)
|
||||
void powerpc_test_cpu::test_shift(void)
|
||||
{
|
||||
#if TEST_SHIFT
|
||||
TEST_INSTRUCTION(RRR__,"slw", _X (31,RA,RD,RB, 24,0));
|
||||
TEST_INSTRUCTION(RRR__,"slw.", _X (31,RA,RD,RB, 24,1));
|
||||
TEST_INSTRUCTION(RRR__,"sraw", _X (31,RA,RD,RB,792,0));
|
||||
TEST_INSTRUCTION(RRR__,"sraw.", _X (31,RA,RD,RB,792,1));
|
||||
TEST_INSTRUCTION(RRRSH,"slw", _X (31,RA,RD,RB, 24,0));
|
||||
TEST_INSTRUCTION(RRRSH,"slw.", _X (31,RA,RD,RB, 24,1));
|
||||
TEST_INSTRUCTION(RRRSH,"sraw", _X (31,RA,RD,RB,792,0));
|
||||
TEST_INSTRUCTION(RRRSH,"sraw.", _X (31,RA,RD,RB,792,1));
|
||||
TEST_INSTRUCTION(RRS__,"srawi", _X (31,RA,RD,00,824,0));
|
||||
TEST_INSTRUCTION(RRS__,"srawi.", _X (31,RA,RD,00,824,1));
|
||||
TEST_INSTRUCTION(RRR__,"srw", _X (31,RA,RD,RB,536,0));
|
||||
TEST_INSTRUCTION(RRR__,"srw.", _X (31,RA,RD,RB,536,1));
|
||||
TEST_INSTRUCTION(RRRSH,"srw", _X (31,RA,RD,RB,536,0));
|
||||
TEST_INSTRUCTION(RRRSH,"srw.", _X (31,RA,RD,RB,536,1));
|
||||
#endif
|
||||
}
|
||||
|
||||
void powerpc_test_cpu::test_rotate(void)
|
||||
{
|
||||
#if TEST_ROTATE
|
||||
TEST_INSTRUCTION(RRIII,"rlwimi", _M (20,RA,RD,00,00,00,0));
|
||||
TEST_INSTRUCTION(RRIII,"rlwimi.", _M (20,RA,RD,00,00,00,1));
|
||||
TEST_INSTRUCTION(RRIII,"rlwinm", _M (21,RA,RD,00,00,00,0));
|
||||
TEST_INSTRUCTION(RRIII,"rlwinm.", _M (21,RA,RD,00,00,00,1));
|
||||
TEST_INSTRUCTION(RRRII,"rlwnm", _M (23,RA,RD,RB,00,00,0));
|
||||
TEST_INSTRUCTION(RRRII,"rlwnm.", _M (23,RA,RD,RB,00,00,1));
|
||||
#endif
|
||||
}
|
||||
@ -759,8 +885,12 @@ bool powerpc_test_cpu::test(void)
|
||||
{
|
||||
// Tests initialization
|
||||
tests = errors = 0;
|
||||
#if defined(__powerpc__)
|
||||
init_cr = native_get_cr() & ~CR_field<0>::mask();
|
||||
init_xer = native_get_xer() & ~(XER_OV_field::mask() | XER_CA_field::mask());
|
||||
#else
|
||||
init_cr = init_xer = 0;
|
||||
#endif
|
||||
|
||||
// Tests execution
|
||||
test_add();
|
||||
|
Loading…
Reference in New Issue
Block a user