#ifndef EMU_COMPILE
#error "Hi I forgot to set EMU_COMPILE"
#endif
#include "arm.h"
static uint8 condtable[256];
static void condtable_init(void) {
uint8 nzcv, cond;
uint8 *table = condtable;
for(nzcv = 0; nzcv < 16; nzcv++) {
for(cond = 0; cond < 16; cond++) {
uint8 n = (nzcv >> 3) & 1;
uint8 z = (nzcv >> 2) & 1;
uint8 c = (nzcv >> 1) & 1;
uint8 v = (nzcv >> 0) & 1;
uint8 truth = 0;
switch(cond & 0xE) {
case 0x0: truth = z; break;
case 0x2: truth = c; break;
case 0x4: truth = n; break;
case 0x6: truth = v; break;
case 0x8: truth = ((!z) && c); break;
case 0xA: truth = (n == v); break;
case 0xC: truth = ((!z) && (n == v)); break;
case 0xE: truth = 1; break;
}
if(cond & 1) { truth = !truth; }
*table++ = truth ? 1 : 0;
}
}
}
sint32 EMU_CALL arm_init(void) {
condtable_init();
return 0;
}
#define ARMSTATE ((struct ARM_STATE*)(state))
#define MODE_USER (0x10)
#define MODE_FIQ (0x11)
#define MODE_IRQ (0x12)
#define MODE_SVC (0x13)
#define MODE_ABT (0x17)
#define MODE_UND (0x1B)
#define MODE_SYSTEM (0x1F)
#define MODE_MASK (0x1F)
#define PSR_IRQMASK (0x80)
#define PSR_FIQMASK (0x40)
#define PSR_POS_N (31)
#define PSR_POS_Z (30)
#define PSR_POS_C (29)
#define PSR_POS_V (28)
#define PSR_NMASK (1<<31)
#define PSR_ZMASK (1<<30)
#define PSR_CMASK (1<<29)
#define PSR_VMASK (1<<28)
#define EXCEPTION_RESET (0)
#define EXCEPTION_UDEF (1)
#define EXCEPTION_SWI (2)
#define EXCEPTION_PABT (3)
#define EXCEPTION_DABT (4)
#define EXCEPTION_RESERVED (5)
#define EXCEPTION_IRQ (6)
#define EXCEPTION_FIQ (7)
struct ARM_STATE {
uint32 r[16];
uint32 rfiq[7];
uint32 rirq[2];
uint32 rsvc[2];
uint32 rabt[2];
uint32 rund[2];
uint32 cpsr; uint32 spsr;
uint32 spsr_fiq;
uint32 spsr_svc;
uint32 spsr_abt;
uint32 spsr_irq;
uint32 spsr_und;
sint32 cycles_remaining;
sint32 cycles_remaining_last_checkpoint;
arm_advance_callback_t advance;
void *hwstate;
struct ARM_MEMORY_MAP *map_load;
struct ARM_MEMORY_MAP *map_store;
uint32 maxpc;
void *fetchbase;
uint32 fetchbox;
int badinsflag;
};
uint32 EMU_CALL arm_get_state_size(void) {
return sizeof(struct ARM_STATE);
}
static EMU_INLINE void pcchanged(struct ARM_STATE *state) { state->maxpc = 0; }
static void regswap(struct ARM_STATE *state) {
uint32 *a, *b, *s, i, n = 0, t;
switch(state->cpsr & MODE_MASK) {
case MODE_FIQ: a = (state->r) + 8; b = state->rfiq; n = 7; s = &(state->spsr_fiq); break; case MODE_IRQ: a = (state->r) + 13; b = state->rirq; n = 2; s = &(state->spsr_irq); break; case MODE_SVC: a = (state->r) + 13; b = state->rsvc; n = 2; s = &(state->spsr_svc); break; case MODE_ABT: a = (state->r) + 13; b = state->rabt; n = 2; s = &(state->spsr_abt); break; case MODE_UND: a = (state->r) + 13; b = state->rund; n = 2; s = &(state->spsr_und); break; default: return;
}
for(i = 0; i < n; i++) { t = a[i]; a[i] = b[i]; b[i] = t; }
t = state->spsr; state->spsr = *s; *s = t;
}
static uint32 getuserreg(struct ARM_STATE *state, uint32 n) {
uint32 mode = state->cpsr & MODE_MASK;
n &= 0xF;
if((n < 8) || (n > 14)) return state->r[n];
if(mode == MODE_FIQ) return state->rfiq[n - 8];
if(n < 13) return state->r[n];
if(mode == MODE_IRQ) return state->rirq[n - 13];
if(mode == MODE_SVC) return state->rsvc[n - 13];
if(mode == MODE_ABT) return state->rabt[n - 13];
if(mode == MODE_UND) return state->rund[n - 13];
return state->r[n];
}
static void setuserreg(struct ARM_STATE *state, uint32 n, uint32 v) {
uint32 mode = state->cpsr & MODE_MASK;
n &= 0xF;
if((n < 8) || (n > 14)) { state->r[n] = v; return; }
if(mode == MODE_FIQ) { state->rfiq[n - 8] = v; return; }
if(n < 13) { state->r[n] = v; return; }
if(mode == MODE_IRQ) { state->rirq[n - 13] = v; return; }
if(mode == MODE_SVC) { state->rsvc[n - 13] = v; return; }
if(mode == MODE_ABT) { state->rabt[n - 13] = v; return; }
if(mode == MODE_UND) { state->rund[n - 13] = v; return; }
state->r[n] = v; return;
}
static void setcpsr(struct ARM_STATE *state, uint32 psr) {
regswap(state);
state->cpsr = psr & 0xF00000FF;
regswap(state);
}
static void exception(struct ARM_STATE *state, uint32 n) {
static const uint32 mode_on_entry[8] = {
MODE_SVC, MODE_UND, MODE_SVC, MODE_ABT,
MODE_ABT, 0, MODE_IRQ, MODE_FIQ
};
static const uint32 lr_offset[8] = {
0, 4, 4, 4, 8, 0, 4, 4
};
uint32 spsr;
n &= 7;
spsr = state->cpsr;
setcpsr(state, ((state->cpsr) & (~(MODE_MASK))) | mode_on_entry[n]);
state->spsr = spsr;
state->r[14] = state->r[15] + lr_offset[n];
state->r[15] = 4 * n;
if(n == EXCEPTION_FIQ) { state->cpsr |= PSR_FIQMASK; }
if(n == EXCEPTION_IRQ) { state->cpsr |= PSR_IRQMASK; }
}
void EMU_CALL arm_clear_state(void *state) {
memset(state, 0, sizeof(struct ARM_STATE));
exception(ARMSTATE, EXCEPTION_RESET);
}
void EMU_CALL arm_set_memory_maps(
void *state,
struct ARM_MEMORY_MAP *map_load,
struct ARM_MEMORY_MAP *map_store
) {
ARMSTATE->map_load = map_load;
ARMSTATE->map_store = map_store;
}
void EMU_CALL arm_set_advance_callback(
void *state,
arm_advance_callback_t advance,
void *hwstate
) {
ARMSTATE->advance = advance;
ARMSTATE->hwstate = hwstate;
}
uint32 EMU_CALL arm_getreg(void *state, sint32 regnum) {
if(regnum >= ARM_REG_GEN && regnum < (ARM_REG_GEN+16)) {
sint32 n = regnum - ARM_REG_GEN;
return ARMSTATE->r[n];
}
switch(regnum) {
case ARM_REG_CPSR: return ARMSTATE->cpsr;
case ARM_REG_SPSR: return ARMSTATE->spsr;
}
return 0;
}
void EMU_CALL arm_break(void *state) {
if(ARMSTATE->cycles_remaining <= 0) return;
ARMSTATE->cycles_remaining_last_checkpoint -= ARMSTATE->cycles_remaining;
ARMSTATE->cycles_remaining = 0;
}
static EMU_INLINE struct ARM_MEMORY_TYPE* mmwalk(struct ARM_MEMORY_MAP *map, uint32 a) {
for(;; map++) {
uint32 x = map->x;
uint32 y = map->y;
if(a < x || a > y) continue;
return &(map->type);
}
}
static void hw_sync(struct ARM_STATE *state) {
sint32 d = state->cycles_remaining_last_checkpoint - state->cycles_remaining;
if(d > 0) { state->advance(state->hwstate, d); }
state->cycles_remaining_last_checkpoint = state->cycles_remaining;
}
static EMU_INLINE void renew_fetch_region(struct ARM_STATE *state) {
struct ARM_MEMORY_TYPE *t;
state->r[15] &= ~3;
t = mmwalk(state->map_load, state->r[15]);
if(t->n == ARM_MAP_TYPE_POINTER) {
uint32 astart = (state->r[15]) & (~(t->mask));
state->maxpc = astart + ((t->mask) + 1);
state->fetchbase = ((uint8*)(t->p)) - astart;
} else {
state->maxpc = state->r[15] + 4;
state->fetchbase = ((uint8*)(&(state->fetchbox)))-(state->r[15]);
state->fetchbox = ((arm_load_callback_t)(t->p))(state->hwstate, state->r[15], 0xFFFFFFFF);
}
}
static EMU_INLINE uint32 lb(struct ARM_STATE *state, uint32 a) {
struct ARM_MEMORY_TYPE *t;
t = mmwalk(state->map_load, a);
a &= t->mask;
if(t->n == ARM_MAP_TYPE_POINTER) {
a ^= EMU_ENDIAN_XOR(3);
return *((uint8*)(((uint8*)(t->p))+a));
} else {
uint32 sh = (a & 3) * 8;
hw_sync(state);
a = ((arm_load_callback_t)(t->p))(state->hwstate, a & (~3), 0xFF << sh);
a >>= sh;
return a & 0xFF;
}
}
static EMU_INLINE uint32 lh(struct ARM_STATE *state, uint32 a) {
struct ARM_MEMORY_TYPE *t;
uint32 sh;
t = mmwalk(state->map_load, a);
sh = (a & 3) * 8;
a &= t->mask & (~3);
if(t->n == ARM_MAP_TYPE_POINTER) {
a = *((uint32*)(((uint8*)(t->p))+a));
} else {
hw_sync(state);
a = ((arm_load_callback_t)(t->p))(state->hwstate, a, 0xFFFF << sh);
}
a >>= sh;
return a & 0xFFFF;
}
static EMU_INLINE uint32 lw(struct ARM_STATE *state, uint32 a) {
struct ARM_MEMORY_TYPE *t;
uint32 sh;
t = mmwalk(state->map_load, a);
sh = (a & 3) * 8;
a &= t->mask & (~3);
if(t->n == ARM_MAP_TYPE_POINTER) {
a = *((uint32*)(((uint8*)(t->p))+a));
} else {
hw_sync(state);
a = ((arm_load_callback_t)(t->p))(state->hwstate, a, 0xFFFFFFFF);
}
return a >> sh;
}
static EMU_INLINE void sb(struct ARM_STATE *state, uint32 a, uint32 d) {
struct ARM_MEMORY_TYPE *t;
t = mmwalk(state->map_store, a);
a &= t->mask;
if(t->n == ARM_MAP_TYPE_POINTER) {
a ^= EMU_ENDIAN_XOR(3);
*((uint8*)(((uint8*)(t->p))+a)) = d;
} else {
uint32 sh = (a & 3) * 8;
hw_sync(state);
d &= 0xFF;
((arm_store_callback_t)(t->p))(state->hwstate, a & (~3), d << sh, 0xFF << sh);
}
}
static EMU_INLINE void sh(struct ARM_STATE *state, uint32 a, uint32 d) {
struct ARM_MEMORY_TYPE *t;
uint32 sh;
t = mmwalk(state->map_store, a);
sh = (a & 3) * 8;
a &= t->mask & (~3);
d &= 0xFFFF;
if(t->n == ARM_MAP_TYPE_POINTER) {
*((uint32*)(((uint8*)(t->p))+a)) &= ~(0xFFFF << sh);
*((uint32*)(((uint8*)(t->p))+a)) |= (d << sh);
} else {
hw_sync(state);
((arm_store_callback_t)(t->p))(state->hwstate, a, d << sh, 0xFFFF << sh);
}
}
static EMU_INLINE void sw(struct ARM_STATE *state, uint32 a, uint32 d) {
struct ARM_MEMORY_TYPE *t;
uint32 sh;
t = mmwalk(state->map_store, a);
sh = (a & 3) * 8;
a &= t->mask & (~3);
if(t->n == ARM_MAP_TYPE_POINTER) {
*((uint32*)(((uint8*)(t->p))+a)) &= ~(0xFFFFFFFF << sh);
*((uint32*)(((uint8*)(t->p))+a)) |= (d << sh);
} else {
hw_sync(state);
((arm_store_callback_t)(t->p))(state->hwstate, a, d << sh, 0xFFFFFFFF << sh);
}
}
static EMU_INLINE uint32 fetch(struct ARM_STATE *state) {
if(state->r[15] >= state->maxpc) renew_fetch_region(state);
return *((uint32*)(((uint8*)(state->fetchbase))+(state->r[15])));
}
static void EMU_CALL badins(struct ARM_STATE *state, uint32 insword) {
state->badinsflag = 1;
arm_break(state);
}
#define IFIELD(B,N) ((insword>>(B))&((1<<(N))-1))
#define WRITESTATUS(N) (((N)&1)!=0)
#define ISLOGIC(N) ((((N)&0x0C)==0x00)||(((N)&0x18)==0x18))
#define C_TO_CPSR { state->cpsr &= ~PSR_CMASK; state->cpsr |= (c&1) << PSR_POS_C; }
#define GET_NZ_TO_CPSR(V) { state->cpsr &= ~((PSR_NMASK)|(PSR_ZMASK)); state->cpsr |= ((V)&0x80000000) | (((uint32)((V)==0))<<(PSR_POS_Z)); }
#define DATAOP(N) (((N)>>1)&0xF)
#define DATA_AND (0x0)
#define DATA_EOR (0x1)
#define DATA_SUB (0x2)
#define DATA_RSB (0x3)
#define DATA_ADD (0x4)
#define DATA_ADC (0x5)
#define DATA_SBC (0x6)
#define DATA_RSC (0x7)
#define DATA_TST (0x8)
#define DATA_TEQ (0x9)
#define DATA_CMP (0xA)
#define DATA_CMN (0xB)
#define DATA_ORR (0xC)
#define DATA_MOV (0xD)
#define DATA_BIC (0xE)
#define DATA_MVN (0xF)
#define INSDATA(N) \
static void EMU_CALL insdata##N(struct ARM_STATE *state, uint32 insword) { \
uint32 v, c; \
uint32 result, operand1, operand2; \
uint32 rd; \
\
\
\
if(!(N & 0x20)) { \
\
\
\
if((insword & 0xF0) == 0x90) { \
\
\
\
if((N & 0xFC) == 0x00) { \
state->r[15] += 8; \
rd = IFIELD(16,4); \
result = state->r[IFIELD(0,4)] * state->r[IFIELD(8,4)]; \
if(N & 2) result += state->r[IFIELD(12,4)]; \
\
if(WRITESTATUS(N)) { GET_NZ_TO_CPSR(result); } \
state->r[15] -= 4; \
state->r[rd] = result; \
if(rd == 15) pcchanged(state); \
return; \
\
\
\
} else if((N & 0xF8) == 0x08) { \
badins(state, insword); return; \
\
\
\
} else if((N & 0xFB) == 0x10) { \
badins(state, insword); return; \
} else { \
badins(state, insword); return; \
} \
\
\
\
} else if((insword & 0x90) == 0x90) { \
badins(state, insword); return; \
} \
} \
\
\
\
if((N & 0x19) == 0x10) { \
if((insword & 0x0FFF0FFF) == 0x010F0000) { \
state->r[15] += 4; \
rd = IFIELD(12, 4); \
if(rd != 15) state->r[rd] = state->cpsr; \
return; \
} \
if((insword & 0x0FFF0FFF) == 0x014F0000) { \
state->r[15] += 4; \
rd = IFIELD(12, 4); \
if(rd != 15) state->r[rd] = state->spsr; \
return; \
} \
if((insword & 0x0FFFFFF0) == 0x0129F000) { \
state->r[15] += 8; \
setcpsr(state, state->r[IFIELD(0,4)]); \
state->r[15] -= 4; \
arm_break(state); \
return; \
} \
if((insword & 0x0FFFFFF0) == 0x0169F000) { \
state->r[15] += 8; \
state->spsr = state->r[IFIELD(0,4)]; \
state->r[15] -= 4; \
return; \
} \
if((insword & 0x0FFFFFF0) == 0x0128F000) { \
uint32 src; \
state->r[15] += 8; \
src = state->r[IFIELD(0,4)]; \
state->r[15] -= 4; \
state->cpsr &= 0x0FFFFFFF; \
state->cpsr |= (src & 0xF0000000); \
return; \
} \
if((insword & 0x0FFFFFF0) == 0x0168F000) { \
uint32 src; \
state->r[15] += 8; \
src = state->r[IFIELD(0,4)]; \
state->r[15] -= 4; \
state->spsr &= 0x0FFFFFFF; \
state->spsr |= (src & 0xF0000000); \
return; \
} \
if((insword & 0x0FFFF000) == 0x0328F000) { \
uint32 src = IFIELD(0,8); \
uint32 ror = IFIELD(8,4) * 2; \
src = (src >> ror) | (src << (32-ror)); \
state->cpsr &= 0x0FFFFFFF; \
state->cpsr |= (src & 0xF0000000); \
return; \
} \
if((insword & 0x0FFFF000) == 0x0368F000) { \
uint32 src = IFIELD(0,8); \
uint32 ror = IFIELD(8,4) * 2; \
src = (src >> ror) | (src << (32-ror)); \
state->spsr &= 0x0FFFFFFF; \
state->spsr |= (src & 0xF0000000); \
return; \
} \
} \
\
\
\
state->r[15] += 8; \
\
\
\
if(!(N & 0x20)) { \
operand2 = state->r[IFIELD(0,4)]; \
\
\
if((insword & 0xFF0) == 0x060) { \
c = operand2 & 1; \
operand2 = (operand2 >> 1) | ((state->cpsr << 2) & 0x80000000); \
if(WRITESTATUS(N) && ISLOGIC(N)) { C_TO_CPSR; } \
} else { \
uint8 shiftby; \
if((insword & 0x10) == 0) { \
shiftby = IFIELD(7,5); \
shiftby |= ((shiftby == 0) & ((insword & 0x60) != 0)) << 5; \
} else { \
shiftby = state->r[IFIELD(8,4)]; \
} \
if(shiftby) { \
switch(IFIELD(5,2)) { \
case 0: \
if(WRITESTATUS(N) && ISLOGIC(N)) { \
if(shiftby > 32) { c = 0; } else { c = operand2 >> (32-shiftby); } \
C_TO_CPSR; \
} \
operand2 <<= shiftby; \
break; \
case 1: \
if(WRITESTATUS(N) && ISLOGIC(N)) { \
if(shiftby > 32) { c = 0; } else { c = operand2 >> (shiftby-1); } \
C_TO_CPSR; \
} \
operand2 >>= shiftby; \
break; \
case 2: \
if(WRITESTATUS(N) && ISLOGIC(N)) { \
if(shiftby >= 32) { c = operand2 >> 31; } else { c = operand2 >> (shiftby-1); } \
C_TO_CPSR; \
} \
operand2 = ((sint32)(((sint32)operand2) >> shiftby)); \
break; \
case 3: \
if(WRITESTATUS(N) && ISLOGIC(N)) { \
c = operand2 >> ((shiftby-1)&31); \
C_TO_CPSR; \
} \
shiftby &= 31; \
operand2 = (operand2 >> shiftby) | (operand2 << (32-shiftby)); \
break; \
} \
} \
} \
} else { \
uint32 ror = IFIELD(8,4) * 2; \
operand2 = IFIELD(0,8); \
operand2 = (operand2 >> ror) | (operand2 << (32 - ror)); \
} \
\
\
\
\
if(DATAOP(N) != DATA_MOV && DATAOP(N) != DATA_MVN) { \
operand1 = state->r[IFIELD(16,4)]; \
} \
if(DATAOP(N) == DATA_MOV) { result = operand2; \
} else if(DATAOP(N) == DATA_MVN) { result = operand2 ^ 0xFFFFFFFF; \
} else if(DATAOP(N) == DATA_AND) { result = operand1 & operand2; \
} else if(DATAOP(N) == DATA_TST) { result = operand1 & operand2; \
} else if(DATAOP(N) == DATA_EOR) { result = operand1 ^ operand2; \
} else if(DATAOP(N) == DATA_TEQ) { result = operand1 ^ operand2; \
} else if(DATAOP(N) == DATA_ORR) { result = operand1 | operand2; \
} else if(DATAOP(N) == DATA_BIC) { result = operand1 & (~operand2); \
\
\
\
\
} else if( \
DATAOP(N)==DATA_ADD || \
DATAOP(N)==DATA_CMN || \
DATAOP(N)==DATA_ADC \
) { \
result = operand1 + operand2; \
if(DATAOP(N)==DATA_ADC) result += (((state->cpsr) >> PSR_POS_C) & 1); \
if(WRITESTATUS(N)) { \
v = ((operand2^result)&(~(operand1^operand2))) >> 31; \
c = (result^((operand1^operand2)|(operand2^result))) >> 31; \
state->cpsr &= ~((PSR_CMASK)|(PSR_VMASK)); \
state->cpsr |= v << PSR_POS_V; \
state->cpsr |= c << PSR_POS_C; \
} \
\
} else if( \
DATAOP(N)==DATA_SUB || \
DATAOP(N)==DATA_RSB || \
DATAOP(N)==DATA_CMP || \
DATAOP(N)==DATA_SBC || \
DATAOP(N)==DATA_RSC \
) { \
\
if(DATAOP(N)==DATA_RSB || DATAOP(N)==DATA_RSC) { \
result = operand1; operand1 = operand2; operand2 = result; } \
result = operand1 - operand2; \
\
if(DATAOP(N)==DATA_SBC || DATAOP(N)==DATA_RSC) { \
result += (((state->cpsr) >> PSR_POS_C) & 1); \
result--; \
} \
if(WRITESTATUS(N)) { \
v = ((operand2^operand1)&(~(operand2^result))) >> 31; \
c = (~(operand1^((operand2^operand1)|(operand1^result)))) >> 31; \
state->cpsr &= ~((PSR_CMASK)|(PSR_VMASK)); \
state->cpsr |= v << PSR_POS_V; \
state->cpsr |= c << PSR_POS_C; \
} \
} \
\
if(WRITESTATUS(N)) { GET_NZ_TO_CPSR(result); } \
\
state->r[15] -= 4; \
\
if((N & 0x18) != 0x10) { \
uint32 rd = IFIELD(12,4); \
state->r[rd] = result; \
\
if(rd == 15) { \
\
pcchanged(state); \
\
if(WRITESTATUS(N)) { \
setcpsr(state, state->spsr); \
arm_break(state); \
} \
} \
} \
}
INSDATA(0x00) INSDATA(0x01) INSDATA(0x02) INSDATA(0x03) INSDATA(0x04) INSDATA(0x05) INSDATA(0x06) INSDATA(0x07)
INSDATA(0x08) INSDATA(0x09) INSDATA(0x0A) INSDATA(0x0B) INSDATA(0x0C) INSDATA(0x0D) INSDATA(0x0E) INSDATA(0x0F)
INSDATA(0x10) INSDATA(0x11) INSDATA(0x12) INSDATA(0x13) INSDATA(0x14) INSDATA(0x15) INSDATA(0x16) INSDATA(0x17)
INSDATA(0x18) INSDATA(0x19) INSDATA(0x1A) INSDATA(0x1B) INSDATA(0x1C) INSDATA(0x1D) INSDATA(0x1E) INSDATA(0x1F)
INSDATA(0x20) INSDATA(0x21) INSDATA(0x22) INSDATA(0x23) INSDATA(0x24) INSDATA(0x25) INSDATA(0x26) INSDATA(0x27)
INSDATA(0x28) INSDATA(0x29) INSDATA(0x2A) INSDATA(0x2B) INSDATA(0x2C) INSDATA(0x2D) INSDATA(0x2E) INSDATA(0x2F)
INSDATA(0x30) INSDATA(0x31) INSDATA(0x32) INSDATA(0x33) INSDATA(0x34) INSDATA(0x35) INSDATA(0x36) INSDATA(0x37)
INSDATA(0x38) INSDATA(0x39) INSDATA(0x3A) INSDATA(0x3B) INSDATA(0x3C) INSDATA(0x3D) INSDATA(0x3E) INSDATA(0x3F)
#define SDT_I(N) (((N)>>5)&1)
#define SDT_P(N) (((N)>>4)&1)
#define SDT_U(N) (((N)>>3)&1)
#define SDT_B(N) (((N)>>2)&1)
#define SDT_W(N) (((N)>>1)&1)
#define SDT_L(N) (((N)>>0)&1)
#define INSSDT(N) \
static void EMU_CALL inssdt##N(struct ARM_STATE *state, uint32 insword) { \
uint32 rn = IFIELD(16,4); \
uint32 rd = IFIELD(12,4); \
uint32 address, offset; \
\
state->r[15] += 8; \
\
address = state->r[rn]; \
\
if(!(SDT_I(N))) { \
offset = insword & 0xFFF; \
} else { \
offset = state->r[IFIELD(0,4)]; \
\
\
if((insword & 0xFF0) == 0x060) { \
offset = (offset >> 1) | ((state->cpsr << 2) & 0x80000000); \
} else { \
uint8 shiftby = IFIELD(7,5); \
shiftby |= ((shiftby == 0) & ((insword & 0x60) != 0)) << 5; \
if(shiftby) { \
switch(IFIELD(5,2)) { \
case 0: offset <<= shiftby; break; \
case 1: offset >>= shiftby; break; \
case 2: offset = ((sint32)(((sint32)offset) >> shiftby)); break; \
case 3: \
shiftby &= 31; \
offset = (offset >> shiftby) | (offset << (32-shiftby)); \
break; \
} \
} \
} \
} \
if(( SDT_P(N))) { if(SDT_U(N)) { address += offset; } else { address -= offset; } } \
if(SDT_L(N)) { \
if(SDT_B(N)) { state->r[rd] = (uint8 )lb(state, address); } \
else { state->r[rd] = (uint32)lw(state, address); } \
if(rd == 15) { state->r[15] += 4; pcchanged(state); } \
} else { \
if(SDT_B(N)) { sb(state, address, state->r[rd] & 0xFF); } \
else { sw(state, address, state->r[rd] ); } \
} \
if((!SDT_P(N))) { if(SDT_U(N)) { address += offset; } else { address -= offset; } } \
\
if((!SDT_P(N)) || SDT_W(N)) { state->r[rn] = address; } \
state->r[15] -= 4; \
}
INSSDT(0x40) INSSDT(0x41) INSSDT(0x42) INSSDT(0x43) INSSDT(0x44) INSSDT(0x45) INSSDT(0x46) INSSDT(0x47)
INSSDT(0x48) INSSDT(0x49) INSSDT(0x4A) INSSDT(0x4B) INSSDT(0x4C) INSSDT(0x4D) INSSDT(0x4E) INSSDT(0x4F)
INSSDT(0x50) INSSDT(0x51) INSSDT(0x52) INSSDT(0x53) INSSDT(0x54) INSSDT(0x55) INSSDT(0x56) INSSDT(0x57)
INSSDT(0x58) INSSDT(0x59) INSSDT(0x5A) INSSDT(0x5B) INSSDT(0x5C) INSSDT(0x5D) INSSDT(0x5E) INSSDT(0x5F)
INSSDT(0x60) INSSDT(0x61) INSSDT(0x62) INSSDT(0x63) INSSDT(0x64) INSSDT(0x65) INSSDT(0x66) INSSDT(0x67)
INSSDT(0x68) INSSDT(0x69) INSSDT(0x6A) INSSDT(0x6B) INSSDT(0x6C) INSSDT(0x6D) INSSDT(0x6E) INSSDT(0x6F)
INSSDT(0x70) INSSDT(0x71) INSSDT(0x72) INSSDT(0x73) INSSDT(0x74) INSSDT(0x75) INSSDT(0x76) INSSDT(0x77)
INSSDT(0x78) INSSDT(0x79) INSSDT(0x7A) INSSDT(0x7B) INSSDT(0x7C) INSSDT(0x7D) INSSDT(0x7E) INSSDT(0x7F)
#define BDT_P(N) (((N)>>4)&1)
#define BDT_U(N) (((N)>>3)&1)
#define BDT_S(N) (((N)>>2)&1)
#define BDT_W(N) (((N)>>1)&1)
#define BDT_L(N) (((N)>>0)&1)
#define INSBDT(N) \
static void EMU_CALL insbdt##N(struct ARM_STATE *state, uint32 insword) { \
uint32 address = state->r[IFIELD(16,4)]; \
uint32 rfe = 0; \
sint32 r = 0; \
sint32 rend = 16; \
sint32 rstep = 1; \
\
state->r[15] += 12; \
\
if(!(BDT_U(N))) { r=15; rend=-1; rstep=-1; } \
for(; r!=rend; r+=rstep) if((insword>>r)&1) { \
if(( BDT_P(N))) { if(BDT_U(N)) { address += 4; } else { address -= 4; } } \
if(BDT_L(N)) { \
if(BDT_S(N)) { \
if((insword&0x8000)==0) { setuserreg(state, r, lw(state, address)); } \
else { state->r[r] = lw(state, address); } \
} else { \
state->r[r] = lw(state, address); \
} \
\
if(r == 15) { \
\
state->r[15] += 8; pcchanged(state); \
\
if(BDT_S(N)) { \
rfe = 1; \
} \
} \
} else { \
if(BDT_S(N)) { sw(state, address, getuserreg(state, r)); } \
else { sw(state, address, state->r[r]); } \
} \
if((!BDT_P(N))) { if(BDT_U(N)) { address += 4; } else { address -= 4; } } \
} \
if(BDT_W(N)) { state->r[IFIELD(16,4)] = address; if(IFIELD(16,4)==15) pcchanged(state); } \
state->r[15] -= 8; \
if(rfe) { \
setcpsr(state, state->spsr); \
arm_break(state); \
} \
}
INSBDT(0x80) INSBDT(0x81) INSBDT(0x82) INSBDT(0x83) INSBDT(0x84) INSBDT(0x85) INSBDT(0x86) INSBDT(0x87)
INSBDT(0x88) INSBDT(0x89) INSBDT(0x8A) INSBDT(0x8B) INSBDT(0x8C) INSBDT(0x8D) INSBDT(0x8E) INSBDT(0x8F)
INSBDT(0x90) INSBDT(0x91) INSBDT(0x92) INSBDT(0x93) INSBDT(0x94) INSBDT(0x95) INSBDT(0x96) INSBDT(0x97)
INSBDT(0x98) INSBDT(0x99) INSBDT(0x9A) INSBDT(0x9B) INSBDT(0x9C) INSBDT(0x9D) INSBDT(0x9E) INSBDT(0x9F)
static void EMU_CALL insbranch(struct ARM_STATE *state, uint32 insword) {
insword <<= 8;
insword = ((sint32)(((sint32)insword) >> 6));
state->r[15] += 8 + insword;
pcchanged(state);
}
static void EMU_CALL insbranchlink(struct ARM_STATE *state, uint32 insword) {
state->r[14] = state->r[15] + 4;
insword <<= 8;
insword = ((sint32)(((sint32)insword) >> 6));
state->r[15] += 8 + insword;
pcchanged(state);
}
typedef void (EMU_CALL *inscallback)(struct ARM_STATE *state, uint32 insword);
static inscallback inscalltable[256] = {
insdata0x00,insdata0x01,insdata0x02,insdata0x03,insdata0x04,insdata0x05,insdata0x06,insdata0x07,
insdata0x08,insdata0x09,insdata0x0A,insdata0x0B,insdata0x0C,insdata0x0D,insdata0x0E,insdata0x0F,
insdata0x10,insdata0x11,insdata0x12,insdata0x13,insdata0x14,insdata0x15,insdata0x16,insdata0x17,
insdata0x18,insdata0x19,insdata0x1A,insdata0x1B,insdata0x1C,insdata0x1D,insdata0x1E,insdata0x1F,
insdata0x20,insdata0x21,insdata0x22,insdata0x23,insdata0x24,insdata0x25,insdata0x26,insdata0x27,
insdata0x28,insdata0x29,insdata0x2A,insdata0x2B,insdata0x2C,insdata0x2D,insdata0x2E,insdata0x2F,
insdata0x30,insdata0x31,insdata0x32,insdata0x33,insdata0x34,insdata0x35,insdata0x36,insdata0x37,
insdata0x38,insdata0x39,insdata0x3A,insdata0x3B,insdata0x3C,insdata0x3D,insdata0x3E,insdata0x3F,
inssdt0x40,inssdt0x41,inssdt0x42,inssdt0x43,inssdt0x44,inssdt0x45,inssdt0x46,inssdt0x47,
inssdt0x48,inssdt0x49,inssdt0x4A,inssdt0x4B,inssdt0x4C,inssdt0x4D,inssdt0x4E,inssdt0x4F,
inssdt0x50,inssdt0x51,inssdt0x52,inssdt0x53,inssdt0x54,inssdt0x55,inssdt0x56,inssdt0x57,
inssdt0x58,inssdt0x59,inssdt0x5A,inssdt0x5B,inssdt0x5C,inssdt0x5D,inssdt0x5E,inssdt0x5F,
inssdt0x60,inssdt0x61,inssdt0x62,inssdt0x63,inssdt0x64,inssdt0x65,inssdt0x66,inssdt0x67,
inssdt0x68,inssdt0x69,inssdt0x6A,inssdt0x6B,inssdt0x6C,inssdt0x6D,inssdt0x6E,inssdt0x6F,
inssdt0x70,inssdt0x71,inssdt0x72,inssdt0x73,inssdt0x74,inssdt0x75,inssdt0x76,inssdt0x77,
inssdt0x78,inssdt0x79,inssdt0x7A,inssdt0x7B,inssdt0x7C,inssdt0x7D,inssdt0x7E,inssdt0x7F,
insbdt0x80,insbdt0x81,insbdt0x82,insbdt0x83,insbdt0x84,insbdt0x85,insbdt0x86,insbdt0x87,
insbdt0x88,insbdt0x89,insbdt0x8A,insbdt0x8B,insbdt0x8C,insbdt0x8D,insbdt0x8E,insbdt0x8F,
insbdt0x90,insbdt0x91,insbdt0x92,insbdt0x93,insbdt0x94,insbdt0x95,insbdt0x96,insbdt0x97,
insbdt0x98,insbdt0x99,insbdt0x9A,insbdt0x9B,insbdt0x9C,insbdt0x9D,insbdt0x9E,insbdt0x9F,
insbranch,insbranch,insbranch,insbranch,insbranch,insbranch,insbranch,insbranch,
insbranch,insbranch,insbranch,insbranch,insbranch,insbranch,insbranch,insbranch,
insbranchlink,insbranchlink,insbranchlink,insbranchlink,insbranchlink,insbranchlink,insbranchlink,insbranchlink,
insbranchlink,insbranchlink,insbranchlink,insbranchlink,insbranchlink,insbranchlink,insbranchlink,insbranchlink,
badins,badins,badins,badins,badins,badins,badins,badins,
badins,badins,badins,badins,badins,badins,badins,badins,
badins,badins,badins,badins,badins,badins,badins,badins,
badins,badins,badins,badins,badins,badins,badins,badins,
badins,badins,badins,badins,badins,badins,badins,badins,
badins,badins,badins,badins,badins,badins,badins,badins,
badins,badins,badins,badins,badins,badins,badins,badins,
badins,badins,badins,badins,badins,badins,badins,badins
};
sint32 EMU_CALL arm_execute(void *state, sint32 cycles, uint8 fiq) {
uint32 instruction;
if(ARMSTATE->badinsflag) { return -1; }
ARMSTATE->cycles_remaining_last_checkpoint = cycles;
ARMSTATE->cycles_remaining = cycles;
if((ARMSTATE->cpsr & PSR_FIQMASK) == 0) {
if(fiq) {
exception(ARMSTATE, EXCEPTION_FIQ);
ARMSTATE->cycles_remaining -= 2;
}
}
ARMSTATE->maxpc = 0;
while(ARMSTATE->cycles_remaining > 0) {
instruction = fetch(ARMSTATE);
if(!condtable[(instruction >> 28) + ((ARMSTATE->cpsr) >> 24)]) {
ARMSTATE->r[15] += 4;
ARMSTATE->cycles_remaining -= 2;
continue;
}
inscalltable[(instruction>>20)&0xFF](ARMSTATE, instruction);
ARMSTATE->cycles_remaining -= 2;
}
hw_sync(state);
if(ARMSTATE->badinsflag) { return -1; }
return 0;
}