Cog/Frameworks/AudioOverload/aosdk/eng_dsf/arm7i.c
2009-02-28 22:04:03 -08:00

1339 lines
32 KiB
C

//
// ARM7 processor emulator - interpreter core
// version 1.6 / 2008-02-16
// (c) Radoslaw Balcewicz
//
#include "arm7.h"
#include "arm7i.h"
//--------------------------------------------------------------------------
// definitions and macros
/** PC is being incremented after every instruction fetch, so we adjust for
that on all stores and jumps. */
#define PC_ADJUSTMENT (-4)
/** Memory access routines. */
#include "arm7memil.c"
/** Bit shifts compatible with IA32. */
#define SHL(w, k) (((UINT32)(w)) << (k))
#define SHR(w, k) (((UINT32)(w)) >> (k))
#define SAR(w, k) (((INT32)(w)) >> (k))
#define ROR(w, k) (SHR (w, k) | SHL (w, 32 - (k)))
/** Byte rotation for unaligned 32-bit read. */
#define RBOD(w, i) (ROR (w, (i) * 8))
/** Data processing macros. */
#define NEG(i) ((i) & (1 << 31))
#define POS(i) (~(i) & (1 << 31))
#define ADDCARRY(a, b, c) \
((NEG (a) & NEG (b)) |\
(NEG (a) & POS (c)) |\
(NEG (b) & POS (c))) ? 1 : 0;
#define ADDOVERFLOW(a, b, c) \
((NEG (a) & NEG (b) & POS (c)) |\
(POS (a) & POS (b) & NEG (c))) ? 1 : 0;
#define SUBCARRY(a, b, c) \
((NEG (a) & POS (b)) |\
(NEG (a) & POS (c)) |\
(POS (b) & POS (c))) ? 1 : 0;
#define SUBOVERFLOW(a, b, c)\
((NEG (a) & POS (b) & POS (c)) |\
(POS (a) & NEG (b) & NEG (c))) ? 1 : 0;
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// private functions
/** Condition EQ. */
static int R_WEQ (void);
/** Condition NE. */
static int R_WNE (void);
/** Condition CS. */
static int R_WCS (void);
/** Condition CC. */
static int R_WCC (void);
/** Condition MI. */
static int R_WMI (void);
/** Condition PL. */
static int R_WPL (void);
/** Condition VS. */
static int R_WVS (void);
/** Condition VC. */
static int R_WVC (void);
/** Condition HI. */
static int R_WHI (void);
/** Condition LS. */
static int R_WLS (void);
/** Condition GE. */
static int R_WGE (void);
/** Condition LT. */
static int R_WLT (void);
/** Condition GT. */
static int R_WGT (void);
/** Condition LE. */
static int R_WLE (void);
/** Condition AL. */
static int R_WAL (void);
/** Undefined condition. */
static int R_Wxx (void);
/** Calculates barrel shifter output. */
static UINT32 WyliczPrzes (void);
/** Logical shift left. */
static UINT32 LSL_x (UINT32 w, int i);
/** Logical shift right. */
static UINT32 LSR_x (UINT32 w, int i);
/** Arithmetic shift right. */
static UINT32 ASR_x (UINT32 w, int i);
/** Rotate right. */
static UINT32 ROR_x (UINT32 w, int i);
/** Rotate right extended. */
static UINT32 RRX_1 (UINT32 w);
/** Group 00x opcodes. */
static void R_G00x (void);
/** Multiply instructions. */
static void R_MUL_MLA (void);
/** Single data swap. */
static void R_SWP (void);
/** PSR Transfer. */
static void R_PSR (void);
/** Data processing instructions. */
static void R_DP (void);
/** Data processing result writeback. */
static void R_WynikDP (ARM7_REG w);
/** Data processing flags writeback. */
static void R_FlagiDP (ARM7_REG w);
/** Single data transfer. */
static void R_SDT (void);
/** Rozkaz "Undefined". */
static void R_Und ();
/** Block Data Transfer. */
static void R_BDT ();
/** Block load instructions. */
static void R_LDM (int Rn, UINT32 adres);
/** Block store instructions. */
static void R_STM (int Rn, UINT32 adres);
/** Branch/Branch with link. */
static void R_B_BL (void);
/** Group 110 opcodes. */
static void R_G110 (void);
/** Group 111 opcodes. */
static void R_G111 (void);
#ifdef ARM7_THUMB
/** Halfword and Signed Data Transfer. */
static void R_HSDT ();
#endif
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// private data
/** Flag testing functions for conditional execution. */
static int (*s_tabWar [16]) (void) = {R_WEQ, R_WNE, R_WCS, R_WCC, R_WMI, R_WPL,
R_WVS, R_WVC, R_WHI, R_WLS, R_WGE, R_WLT, R_WGT, R_WLE, R_WAL, R_Wxx};
/** Handler table for instruction groups. */
static void (*s_tabGrup [8]) (void) = {R_G00x, R_G00x, R_SDT, R_SDT, R_BDT,
R_B_BL, R_G110, R_G111};
/** Data processing instructions split to arithmetic and logical. */
static int s_tabAL [16] = {FALSE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE};
/** Cycles it took for current instruction to complete. */
static int s_cykle;
//--------------------------------------------------------------------------
// public functions
//--------------------------------------------------------------------------
/** Single step, returns number of burned cycles. */
int ARM7i_Step ()
{
ARM7.kod = arm7_read_32 (ARM7.Rx [ARM7_PC] & ~3);
// we increment PC here, and if there's a load from memory it will simply
// overwrite it (all PC modyfing code should be aware of this)
ARM7.Rx [ARM7_PC] += 4;
s_cykle = 2;
// condition test and group selection
if (s_tabWar [(ARM7.kod >> 28) & 15] ())
s_tabGrup [(ARM7.kod >> 25) & 7] ();
return s_cykle;
}
//--------------------------------------------------------------------------
// private functions
//--------------------------------------------------------------------------
/** Condition EQ. */
int R_WEQ ()
{
// "Z set"
return ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_Z;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition NE. */
int R_WNE ()
{
// "Z clear"
return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_Z);
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition CS. */
int R_WCS ()
{
// "C set"
return ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition CC. */
int R_WCC ()
{
// "C clear"
return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C);
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition MI. */
int R_WMI ()
{
// "N set"
return ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_N;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition PL. */
int R_WPL ()
{
// "N clear"
return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_N);
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition VS. */
int R_WVS ()
{
// "V set"
return ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_V;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition VC. */
int R_WVC ()
{
// "V clear"
return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_V);
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition HI. */
int R_WHI ()
{
// "C set and Z clear"
return (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) &&\
!(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_Z);
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition LS. */
int R_WLS ()
{
// "C clear or Z set"
return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ||\
(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_Z);
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition GE. */
int R_WGE ()
{
// "N equals V"
return (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_N) &&\
(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_V) || !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_N) &&\
!(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_V);
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition LT. */
int R_WLT ()
{
// "N not equal to V"
return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_N) &&\
(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_V) || (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_N) &&\
!(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_V);
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition GT. */
int R_WGT ()
{
// "Z clear AND (N equals V)"
return !(ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_Z) && R_WGE ();
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition LE. */
int R_WLE ()
{
// "Z set OR (N not equal to V)"
return (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_Z) || R_WLT ();
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Condition AL. */
int R_WAL ()
{
// "(ignored)"
return TRUE;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Undefined condition. */
int R_Wxx ()
{
// behaviour undefined
return FALSE;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Calculates barrel shifter output. */
UINT32 WyliczPrzes ()
{
int Rm, Rs, i;
UINT32 w;
// Rm is source for the shift operation
Rm = ARM7.kod & 15;
if (ARM7.kod & (1 << 4))
{
s_cykle++;
// shift count in Rs (8 lowest bits)
if (Rm != ARM7_PC)
w = ARM7.Rx [Rm];
else
w = (ARM7.Rx [ARM7_PC] & ~3) + 12 + PC_ADJUSTMENT;
// Rs can't be PC
Rs = (ARM7.kod >> 8) & 15;
i = (UINT8)ARM7.Rx [Rs];
if (i == 0)
{
// special case
ARM7.carry = (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ? 1 : 0;
return w;
}
switch ((ARM7.kod >> 5) & 3)
{
case 0:
w = LSL_x (w, i);
break;
case 1:
w = LSR_x (w, i);
break;
case 2:
w = ASR_x (w, i);
break;
case 3:
w = ROR_x (w, i);
break;
}
}
else
{
// shift count as immediate in opcode
if (Rm != ARM7_PC)
w = ARM7.Rx [Rm];
else
w = (ARM7.Rx [ARM7_PC] & ~3) + 8 + PC_ADJUSTMENT;
i = (ARM7.kod >> 7) & 31;
switch ((ARM7.kod >> 5) & 3)
{
case 0:
w = LSL_x (w, i);
break;
case 1:
if (i > 0)
w = LSR_x (w, i);
else
w = LSR_x (w, 32);
break;
case 2:
if (i > 0)
w = ASR_x (w, i);
else
w = ASR_x (w, 32);
break;
case 3:
if (i > 0)
w = ROR_x (w, i);
else
w = RRX_1 (w);
break;
}
}
return w;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Logical shift left. */
UINT32 LSL_x (UINT32 w, int i)
{
// LSL #0 copies C into carry out and returns unmodified value
if (i == 0)
{
ARM7.carry = (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ? 1 : 0;
return w;
}
// LSL #32 copies LSB to carry out and returns zero
if (i == 32)
{
ARM7.carry = w & 1;
return 0;
}
// LSL > #32 returns zero for both carry and output
if (i > 32)
{
ARM7.carry = 0;
return 0;
}
// normal shift
ARM7.carry = (w & (1 << (32 - i))) ? 1 : 0;
w = SHL (w, i);
return w;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Logical shift right. */
UINT32 LSR_x (UINT32 w, int i)
{
// LSR #32 copies MSB to carry out and returns zero
if (i == 32)
{
ARM7.carry = (w & (1 << 31)) ? 1 : 0;
return 0;
}
// LSR > #32 returns zero for both carry and output
if (i > 32)
{
ARM7.carry = 0;
return 0;
}
// normal shift
ARM7.carry = (w & (1 << (i - 1))) ? 1 : 0;
w = SHR (w, i);
return w;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Arithmetic shift right. */
UINT32 ASR_x (UINT32 w, int i)
{
// ASR >= #32 carry out and output value depends on the minus sign
if (i >= 32)
{
if (w & (1 << 31))
{
ARM7.carry = 1;
return ~0;
}
ARM7.carry = 0;
return 0;
}
// normal shift
ARM7.carry = (w & (1 << (i - 1))) ? 1 : 0;
w = SAR (w, i);
return w;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Rotate right. */
UINT32 ROR_x (UINT32 w, int i)
{
// mask count to [0; 31]
i &= 0x1f;
// ROR #32,#64,etc. copies MSB into carry out and returns unmodified value
if (i == 0)
{
ARM7.carry = (w & (1 << 31)) ? 1 : 0;
return w;
}
// normal shift
ARM7.carry = (w & (1 << (i-1))) ? 1 : 0;
w = ROR (w, i);
return w;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Rotate right extended. */
UINT32 RRX_1 (UINT32 w)
{
// same as RCR by 1 in IA32
ARM7.carry = w & 1;
return (w >> 1) | ((ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) << 2);
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Group 00x opcodes. */
void R_G00x ()
{
#ifdef ARM7_THUMB
// 24 constant bits
if ((ARM7.kod & 0x0ffffff0) == 0x012fff10) // BX - branch with possible mode transfer
{
#ifdef ARM7_THUMB
int Rn = ARM7.Rx[ARM7.kod & 0xf];
// switching to Thumb mode?
if (Rn & 1)
{
ARM7_SetCPSR(ARM7.Rx[ARM7_CPSR] | ARM7_CPSR_T);
}
ARM7.Rx[ARM7_PC] = Rn & ~1;
#endif
}
// 15 constant bits
else if ((ARM7.kod & 0x0fb00ff0) == 0x01000090)
R_SWP ();
// 10 constant bits
else if ((ARM7.kod & 0x0fc000f0) == 0x00000090)
R_MUL_MLA ();
// 10 constant bits
else if ((ARM7.kod & 0x0e400f90) == 0x00000090)
R_HSDT ();
// 9 constant bits
else if ((ARM7.kod & 0x0f8000f0) == 0x00800090)
{
// logerror("G00x / Multiply long\n");
}
// 6 constant bits
else if ((ARM7.kod & 0x0e400090) == 0x00400090)
R_HSDT ();
// 2 constant bits
else
{
if ((ARM7.kod & 0x01900000) == 0x01000000)
// TST, TEQ, CMP & CMN without S bit are "PSR Transfer"
R_PSR ();
else
// the rest is "Data processing"
R_DP ();
}
#else
if ((ARM7.kod & 0x03b00090) == 0x01000090)
R_SWP ();
else if ((ARM7.kod & 0x03c00090) == 0x00000090)
R_MUL_MLA ();
else
{
if ((ARM7.kod & 0x01900000) == 0x01000000)
// TST, TEQ, CMP & CMN without S bit are "PSR Transfer"
R_PSR ();
else
// the rest is "Data processing"
R_DP ();
}
#endif
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Single data swap. */
void R_SWP ()
{
int Rn, Rd, Rm;
UINT32 adres, w;
#define BIT_B (ARM7.kod & (1 << 21))
s_cykle += 4;
// none of these can be PC
Rn = (ARM7.kod >> 16) & 15;
Rd = (ARM7.kod >> 12) & 15;
Rm = ARM7.kod & 15;
adres = ARM7.Rx [Rn];
if (BIT_B)
{
// "byte"
w = arm7_read_8 (adres);
arm7_write_8 (adres, (UINT8)ARM7.Rx [Rm]);
}
else
{
// "word"
w = RBOD (arm7_read_32 (adres & ~3), adres & 3);
arm7_write_32 (adres & ~3, ARM7.Rx [Rm]);
}
ARM7.Rx [Rd] = w;
#undef BIT_B
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Multiply instructions. */
void R_MUL_MLA ()
{
int Rm, Rs, Rn, Rd;
UINT32 wynik;
#define BIT_A (ARM7.kod & (1 << 21))
#define BIT_S (ARM7.kod & (1 << 20))
s_cykle += 2;
// none of these can be PC, also Rd != Rm
Rd = (ARM7.kod >> 16) & 15,
Rs = (ARM7.kod >> 8) & 15,
Rm = ARM7.kod & 15;
// MUL
wynik = ARM7.Rx [Rm] * ARM7.Rx [Rs];
if (BIT_A)
{
// MLA
Rn = (ARM7.kod >> 12) & 15;
wynik += ARM7.Rx [Rn];
}
ARM7.Rx [Rd] = wynik;
if (BIT_S)
{
// V remains unchanged, C is undefined
ARM7.Rx [ARM7_CPSR] &= ~(ARM7_CPSR_N | ARM7_CPSR_Z);
if (wynik == 0)
ARM7.Rx [ARM7_CPSR] |= ARM7_CPSR_Z;
ARM7.Rx [ARM7_CPSR] |= wynik & 0x80000000;
}
#undef BIT_S
#undef BIT_A
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** PSR Transfer. */
void R_PSR ()
{
int Rd, Rm;
UINT32 w, arg;
#define BIT_I (ARM7.kod & (1 << 25))
#define BIT_P (ARM7.kod & (1 << 22))
// none of the registers involved can be PC
if (ARM7.kod & (1 << 21))
{
// MSR
Rm = ARM7.kod & 15;
if (BIT_I)
// immediate (lower 12 bits)
arg = ROR (ARM7.kod & 0xff, ((ARM7.kod >> 8) & 0xf) * 2);
else
// register
arg = ARM7.Rx [Rm];
// decode mask bits
if (BIT_P)
{
w = ARM7.Rx [ARM7_SPSR];
if (ARM7_CPSR_M (ARM7.Rx [ARM7_CPSR]) > ARM7_CPSR_M_usr &&\
ARM7_CPSR_M (ARM7.Rx [ARM7_CPSR]) < ARM7_CPSR_M_sys)
{
if (ARM7.kod & (1 << 16))
w = (w & 0xffffff00) | (arg & 0x000000ff);
if (ARM7.kod & (1 << 17))
w = (w & 0xffff00ff) | (arg & 0x0000ff00);
if (ARM7.kod & (1 << 18))
w = (w & 0xff00ffff) | (arg & 0x00ff0000);
if (ARM7.kod & (1 << 19))
// ARMv5E should have 0xf8000000 argument mask
w = (w & 0x00ffffff) | (arg & 0xf0000000);
}
// force valid mode
w |= 0x10;
ARM7.Rx [ARM7_SPSR] = w;
}
else
{
w = ARM7.Rx [ARM7_CPSR];
// only flags can be changed in User mode
if (ARM7_CPSR_M (ARM7.Rx [ARM7_CPSR]) != ARM7_CPSR_M_usr)
{
if (ARM7.kod & (1 << 16))
w = (w & 0xffffff00) | (arg & 0x000000ff);
if (ARM7.kod & (1 << 17))
w = (w & 0xffff00ff) | (arg & 0x0000ff00);
if (ARM7.kod & (1 << 18))
w = (w & 0xff00ffff) | (arg & 0x00ff0000);
}
if (ARM7.kod & (1 << 19))
// ARMv5E should have 0xf8000000 argument mask
w = (w & 0x00ffffff) | (arg & 0xf0000000);
// force valid mode
w |= 0x10;
ARM7_SetCPSR (w);
}
}
else
{
// MRS
Rd = (ARM7.kod >> 12) & 15;
if (BIT_P)
ARM7.Rx [Rd] = ARM7.Rx [ARM7_SPSR];
else
ARM7.Rx [Rd] = ARM7.Rx [ARM7_CPSR];
}
#undef BIT_P
#undef BIT_I
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Data processing instructions. */
void R_DP ()
{
int Rn;
ARM7_REG arg1, arg2, w;
#define BIT_I (ARM7.kod & (1 << 25))
// Rn can be PC, so we need to account for that
Rn = (ARM7.kod >> 16) & 15;
if (BIT_I)
{
if (Rn != ARM7_PC)
arg1 = ARM7.Rx [Rn];
else
arg1 = (ARM7.Rx [ARM7_PC] & ~3) + 8 + PC_ADJUSTMENT;
// immediate in lowest 12 bits
arg2 = ROR (ARM7.kod & 0xff, ((ARM7.kod >> 8) & 0xf) * 2);
// preload carry out from C
ARM7.carry = (ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ? 1 : 0;
}
else
{
if (Rn != ARM7_PC)
arg1 = ARM7.Rx [Rn];
else
// register or immediate shift?
if (ARM7.kod & (1 << 4))
arg1 = (ARM7.Rx [ARM7_PC] & ~3) + 12 + PC_ADJUSTMENT;
else
arg1 = (ARM7.Rx [ARM7_PC] & ~3) + 8 + PC_ADJUSTMENT;
// calculate in barrel shifter
arg2 = WyliczPrzes ();
}
// decode instruction type
switch ((ARM7.kod >> 21) & 15)
{
case 0:
// AND
R_WynikDP (arg1 & arg2);
break;
case 1:
// EOR
R_WynikDP (arg1 ^ arg2);
break;
case 2:
// SUB
w = arg1 - arg2;
ARM7.carry = SUBCARRY (arg1, arg2, w);
ARM7.overflow = SUBOVERFLOW (arg1, arg2, w);
R_WynikDP (w);
break;
case 3:
// RSB
w = arg2 - arg1;
ARM7.carry = SUBCARRY (arg2, arg1, w);
ARM7.overflow = SUBOVERFLOW (arg2, arg1, w);
R_WynikDP (w);
break;
case 4:
// ADD
w = arg1 + arg2;
ARM7.carry = ADDCARRY (arg1, arg2, w);
ARM7.overflow = ADDOVERFLOW (arg1, arg2, w);
R_WynikDP (w);
break;
case 5:
// ADC
w = arg1 + arg2 + ((ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ? 1 : 0);
ARM7.carry = ADDCARRY (arg1, arg2, w);
ARM7.overflow = ADDOVERFLOW (arg1, arg2, w);
R_WynikDP (w);
break;
case 6:
// SBC
w = arg1 - arg2 - ((ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ? 0 : 1);
ARM7.carry = SUBCARRY (arg1, arg2, w);
ARM7.overflow = SUBOVERFLOW (arg1, arg2, w);
R_WynikDP (w);
break;
case 7:
// RSC
w = arg2 - arg1 - ((ARM7.Rx [ARM7_CPSR] & ARM7_CPSR_C) ? 0 : 1);
ARM7.carry = SUBCARRY (arg2, arg1, w);
ARM7.overflow = SUBOVERFLOW (arg2, arg1, w);
R_WynikDP (w);
break;
case 8:
// TST
R_FlagiDP (arg1 & arg2);
break;
case 9:
// TEQ
R_FlagiDP (arg1 ^ arg2);
break;
case 10:
// CMP
w = arg1 - arg2;
ARM7.carry = SUBCARRY (arg1, arg2, w);
ARM7.overflow = SUBOVERFLOW (arg1, arg2, w);
R_FlagiDP (w);
break;
case 11:
// CMN
w = arg1 + arg2;
ARM7.carry = ADDCARRY (arg1, arg2, w);
ARM7.overflow = ADDOVERFLOW (arg1, arg2, w);
R_FlagiDP (w);
break;
case 12:
// ORR
R_WynikDP (arg1 | arg2);
break;
case 13:
// MOV
R_WynikDP (arg2);
break;
case 14:
// BIC
R_WynikDP (arg1 & ~arg2);
break;
case 15:
// MVN
R_WynikDP (~arg2);
break;
}
#undef BIT_I
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Data processing result writeback. */
void R_WynikDP (ARM7_REG w)
{
int Rd;
#define BIT_S (ARM7.kod & (1 << 20))
Rd = (ARM7.kod >> 12) & 15;
ARM7.Rx [Rd] = w;
if (BIT_S)
{
if (Rd == ARM7_PC)
{
s_cykle += 4;
// copy current SPSR to CPSR
ARM7_SetCPSR (ARM7.Rx [ARM7_SPSR]);
}
else
// save new flags
R_FlagiDP (w);
}
#undef BIT_S
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Data processing flags writeback. */
void R_FlagiDP (ARM7_REG w)
{
// arithmetic or logical instruction?
if (s_tabAL [(ARM7.kod >> 21) & 15])
{
ARM7.Rx [ARM7_CPSR] &= ~(ARM7_CPSR_N | ARM7_CPSR_Z | ARM7_CPSR_C |\
ARM7_CPSR_V);
ARM7.Rx [ARM7_CPSR] |= ARM7.overflow << 28;
}
else
ARM7.Rx [ARM7_CPSR] &= ~(ARM7_CPSR_N | ARM7_CPSR_Z | ARM7_CPSR_C);
ARM7.Rx [ARM7_CPSR] |= ARM7.carry << 29;
if (w == 0)
ARM7.Rx [ARM7_CPSR] |= ARM7_CPSR_Z;
ARM7.Rx [ARM7_CPSR] |= w & 0x80000000;
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Single data transfer. */
void R_SDT (void)
{
int Rn, Rd, offset;
UINT32 adres, w = 0;
#define BIT_I (ARM7.kod & (1 << 25))
#define BIT_P (ARM7.kod & (1 << 24))
#define BIT_U (ARM7.kod & (1 << 23))
#define BIT_B (ARM7.kod & (1 << 22))
#define BIT_W (ARM7.kod & (1 << 21))
#define BIT_L (ARM7.kod & (1 << 20))
if (BIT_I && (ARM7.kod & (1 << 4)))
{
R_Und ();
return;
}
Rn = (ARM7.kod >> 16) & 15,
Rd = (ARM7.kod >> 12) & 15;
if (Rn != ARM7_PC)
adres = ARM7.Rx [Rn];
else
adres = ARM7.Rx [ARM7_PC] & ~3;
if (!BIT_L)
if (Rd != ARM7_PC)
w = ARM7.Rx [Rd];
else
w = (ARM7.Rx [ARM7_PC] & ~3) + 12 + PC_ADJUSTMENT;
if (BIT_I)
// calculate value in barrel shifter
offset = WyliczPrzes ();
else
// immediate in lowest 12 bits
offset = ARM7.kod & 0xfff;
if (!BIT_U)
offset = -offset;
if (BIT_P)
{
// "pre-index"
adres += offset;
if (BIT_W)
// "write-back"
ARM7.Rx [Rn] = adres;
}
else
// "post-index"
ARM7.Rx [Rn] += offset;
if (Rn == ARM7_PC)
adres += 8 + PC_ADJUSTMENT;
if (BIT_L)
{
s_cykle += 3;
// "load"
if (BIT_B)
// "byte"
ARM7.Rx [Rd] = arm7_read_8 (adres);
else
// "word"
ARM7.Rx [Rd] = RBOD (arm7_read_32 (adres & ~3), adres & 3);
}
else
{
s_cykle += 2;
// "store"
if (BIT_B)
// "byte"
arm7_write_8 (adres, (UINT8)w);
else
// "word"
arm7_write_32 (adres & ~3, w);
}
#undef BIT_L
#undef BIT_W
#undef BIT_B
#undef BIT_U
#undef BIT_P
#undef BIT_I
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Undefined. */
void R_Und ()
{
UINT32 sr = ARM7.Rx [ARM7_CPSR];
ARM7_SetCPSR (ARM7_CPSR_MX (sr, ARM7_CPSR_M_und) | ARM7_CPSR_I);
ARM7.Rx [ARM7_SPSR] = sr;
ARM7.Rx [ARM7_LR] = ARM7.Rx [ARM7_PC] + 4;
ARM7.Rx [ARM7_PC] = 0x00000004;
}
//--------------------------------------------------------------------------
#define BIT_U (ARM7.kod & (1 << 23))
#define BIT_S (ARM7.kod & (1 << 22))
//--------------------------------------------------------------------------
/** Block Data Transfer. */
void R_BDT ()
{
int Rn, usr = FALSE;
UINT32 adres;
ARM7_REG cpsr = 0;
#define BIT_L (ARM7.kod & (1 << 20))
// Rn can't be PC
Rn = (ARM7.kod >> 16) & 15;
adres = ARM7.Rx [Rn];
// transfer in User mode
if (BIT_S)
if (!BIT_L || !(ARM7.kod & (1 << ARM7_PC)))
usr = TRUE;
if (usr)
{
//EMU_BLAD (BLAD_WEWNETRZNY, "BDT: user transfer");
cpsr = ARM7.Rx [ARM7_CPSR];
ARM7_SetCPSR (ARM7_CPSR_MX (cpsr, ARM7_CPSR_M_usr));
}
if (BIT_L)
// "load"
R_LDM (Rn, adres);
else
// "store"
R_STM (Rn, adres);
if (usr)
ARM7_SetCPSR (cpsr);
#undef BIT_L
}
//--------------------------------------------------------------------------
#define BIT_P (ARM7.kod & (1 << 24))
#define BIT_W (ARM7.kod & (1 << 21))
//--------------------------------------------------------------------------
/** Block load instructions. */
void R_LDM (int Rn, UINT32 adres)
{
int i, n, sp;
// count registers on the list
for (i = 0, n = 0; i < 16; i++)
if (ARM7.kod & (1 << i))
n++;
s_cykle += n * 2 + 1;
n <<= 2;
// transfer type
sp = BIT_P;
if (!BIT_U)
{
// "down"
n = -n;
adres += n;
sp = !sp;
}
if (BIT_W)
// "write-back"
ARM7.Rx [Rn] += n;
// for all registers in mask
if (sp)
for (i = 0; i < 16; i++)
{
if (!(ARM7.kod & (1 << i)))
continue;
adres += 4;
ARM7.Rx [i] = arm7_read_32 (adres);
}
else
for (i = 0; i < 16; i++)
{
if (!(ARM7.kod & (1 << i)))
continue;
ARM7.Rx [i] = arm7_read_32 (adres);
adres += 4;
}
// special case - mode change when PC is written
if ((ARM7.kod & (1 << ARM7_PC)) && BIT_S)
ARM7_SetCPSR (ARM7.Rx [ARM7_SPSR]);
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Block store instructions. */
void R_STM (int Rn, UINT32 adres)
{
int i, n, p, sp;
// count registers on the list and remember the first one
for (i = 0, n = 0, p = -1; i < 16; i++)
if (ARM7.kod & (1 << i))
{
n++;
if (p < 0)
p = i;
}
s_cykle += n * 2;
n <<= 2;
// transfer type
sp = BIT_P;
if (!BIT_U)
{
// "down"
n = -n;
adres += n;
sp = !sp;
}
// if base register is not the first one to transfer, writeback happens here
if (BIT_W && Rn != p)
// "write-back"
ARM7.Rx [Rn] += n;
// registers R0-R14
if (sp)
for (i = 0; i < 15; i++)
{
if (!(ARM7.kod & (1 << i)))
continue;
adres += 4;
arm7_write_32 (adres, ARM7.Rx [i]);
}
else
for (i = 0; i < 15; i++)
{
if (!(ARM7.kod & (1 << i)))
continue;
arm7_write_32 (adres, ARM7.Rx [i]);
adres += 4;
}
// PC is a special case
if (ARM7.kod & (1 << ARM7_PC))
{
if (sp)
{
adres += 4;
arm7_write_32 (adres, (ARM7.Rx [ARM7_PC] & ~3) + 12 + PC_ADJUSTMENT);
}
else
{
arm7_write_32 (adres, (ARM7.Rx [ARM7_PC] & ~3) + 12 + PC_ADJUSTMENT);
adres += 4;
}
}
// if base register is the first one to transfer, writeback happens here
if (BIT_W && Rn == p)
// "write-back"
ARM7.Rx [Rn] += n;
}
//--------------------------------------------------------------------------
#undef BIT_W
#undef BIT_P
#undef BIT_S
#undef BIT_U
//--------------------------------------------------------------------------
/** Branch/Branch with link. */
void R_B_BL ()
{
INT32 offset;
#define BIT_L (ARM7.kod & (1 << 24))
s_cykle += 4;
offset = (ARM7.kod & 0x00ffffff) << 2;
if (offset & 0x02000000)
offset |= 0xfc000000;
offset += 8 + PC_ADJUSTMENT;
if (BIT_L)
// "Branch with link"
ARM7.Rx [ARM7_LR] = (ARM7.Rx [ARM7_PC] & ~3) + 4 + PC_ADJUSTMENT;
// "Branch"
ARM7.Rx [ARM7_PC] += offset;
#undef BIT_L
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Group 110 opcodes. */
void R_G110 ()
{
// logerror("ARM7: G110 / Coprocessor data transfer\n");
}
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
/** Group 111 opcodes. */
void R_G111 ()
{
if ((ARM7.kod & 0xf0000000) == 0xe0000000)
{
/* if (ARM7.kod & (1 << 4))
logerror("ARM7: G111 / Coprocessor register transfer\n");
else
logerror("ARM7: G111 / Coprocessor data operation\n"); */
}
else
{
UINT32 sr = ARM7.Rx [ARM7_CPSR];
ARM7_SetCPSR (ARM7_CPSR_MX (sr, ARM7_CPSR_M_svc) | ARM7_CPSR_I);
ARM7.Rx [ARM7_SPSR] = sr;
ARM7.Rx [ARM7_LR] = ARM7.Rx [ARM7_PC];
ARM7.Rx [ARM7_PC] = 0x00000008;
}
}
//--------------------------------------------------------------------------
#ifdef ARM7_THUMB
//--------------------------------------------------------------------------
/** Halfword and Signed Data Transfer. */
void R_HSDT ()
{
int Rm, Rd, Rn, offset;
uint32_t adres, w;
#define BIT_P (ARM7.kod & (1 << 24))
#define BIT_U (ARM7.kod & (1 << 23))
#define BIT_W (ARM7.kod & (1 << 21))
#define BIT_L (ARM7.kod & (1 << 20))
#define BIT_S (ARM7.kod & (1 << 6))
#define BIT_H (ARM7.kod & (1 << 5))
// Rm can't be PC
Rn = (ARM7.kod >> 16) & 15;
Rd = (ARM7.kod >> 12) & 15;
if (Rn != ARM7_PC)
adres = ARM7.Rx [Rn];
else
adres = ARM7.Rx [ARM7_PC] & ~3;
if (!BIT_L)
if (Rd != ARM7_PC)
w = ARM7.Rx [Rd];
else
w = (ARM7.Rx [ARM7_PC] & ~3) + 12 + POPRAWKA_PC;
if (1 << 22)
// immediate
offset = ((ARM7.kod >> 4) & 0xf0) | (ARM7.kod & 15);
else
{
// register
Rm = ARM7.kod & 15;
offset = ARM7.Rx [Rm];
}
if (!BIT_U)
offset = -offset;
if (BIT_P)
{
// "pre-index"
adres += offset;
if (BIT_W)
// "write-back"
ARM7.Rx [Rn] = adres;
}
else
// "post-index"
ARM7.Rx [Rn] += offset;
if (Rn == ARM7_PC)
adres += 8 + POPRAWKA_PC;
if (BIT_L)
{
// "load"
s_cykle += 3;
if (BIT_S)
{
if (BIT_H)
// "signed halfword"
ARM7.Rx [Rd] = (INT32)(INT16)arm7_read_16 (adres);
else
// "signed byte"
ARM7.Rx [Rd] = (INT32)(INT8)arm7_read_8 (adres);
}
else
// "unsigned halfword"
ARM7.Rx [Rd] = arm7_read_16 (adres);
}
else
{
// store
s_cykle += 2;
if (BIT_H)
// "halfword"
arm7_write_16 (adres, (UINT16)w);
else
// "byte"
arm7_write_8 (adres, (UINT8)w);
}
#undef BIT_H
#undef BIT_S
#undef BIT_L
#undef BIT_W
#undef BIT_U
#undef BIT_P
}
//--------------------------------------------------------------------------
#endif