ref: e85e9a78a8b587d6b7a8bd84f70dbde33e7de904
dir: /sys/src/games/gba/cpu.c/
#include <u.h>
#include <libc.h>
#include <thread.h>
#include "../eui.h"
#include "dat.h"
#include "fns.h"
enum {
FLAGN = 1<<31,
FLAGZ = 1<<30,
FLAGC = 1<<29,
FLAGV = 1<<28,
FLAGT = 1<<5,
FLAGI = 1<<7,
MUSR = 0x10,
MFIQ = 0x11,
MIRQ = 0x12,
MSVC = 0x13,
MABT = 0x17,
MUND = 0x1b,
MSYS = 0x1f,
MODE = 0x1f,
R13USR = 0, R14USR, R13FIQ, R14FIQ,
R13SVC, R14SVC, R13ABT, R14ABT,
R13IRQ, R14IRQ, R13UND, R14UND,
SPSRFIQ, SPSRSVC, SPSRABT, SPSRIRQ, SPSRUND,
R8USR, R9USR, R10USR, R11USR, R12USR,
R8FIQ, R9FIQ, R10FIQ, R11FIQ, R12FIQ
};
u32int r[16], cpsr, spsr;
u32int saver[R12FIQ+1];
u32int curpc;
int irq;
u32int instr0, instr1, pipel = -1;
int cyc;
Var cpuvars[] = {
ARR(r), VAR(cpsr), VAR(spsr), ARR(saver), VAR(irq),
VAR(instr0), VAR(instr1), VAR(pipel),
{nil, 0, 0},
};
#define pipeflush() {io(); pipel = -1;}
#define io() cyc++
static int steparm(void);
static int stepthumb(void);
int (*step)(void);
void
undefined(u32int instr)
{
if((cpsr & FLAGT) != 0)
sysfatal("undefined opcode %#.4ux (pc=%#.8ux)", (u16int)instr, curpc);
else
sysfatal("undefined opcode %#.8ux (pc=%#.8ux)", instr, curpc);
}
int
cond(int n, u32int instr)
{
switch(n){
case 0: return (cpsr & FLAGZ) != 0;
case 1: return (cpsr & FLAGZ) == 0;
case 2: return (cpsr & FLAGC) != 0;
case 3: return (cpsr & FLAGC) == 0;
case 4: return (cpsr & FLAGN) != 0;
case 5: return (cpsr & FLAGN) == 0;
case 6: return (cpsr & FLAGV) != 0;
case 7: return (cpsr & FLAGV) == 0;
case 8: return (cpsr & (FLAGC|FLAGZ)) == FLAGC;
case 9: return (cpsr & (FLAGC|FLAGZ)) != FLAGC;
case 10: return ((cpsr ^ cpsr << 3) & FLAGN) == 0;
case 11: return ((cpsr ^ cpsr << 3) & FLAGN) != 0;
case 12: return ((cpsr ^ cpsr << 3) & (FLAGN|FLAGZ)) == 0;
case 13: return ((cpsr ^ cpsr << 3) & (FLAGN|FLAGZ)) != 0;
case 14: return 1;
}
undefined(instr);
return 0;
}
static void
setcpsr(int n)
{
if((n & FLAGT) != 0)
step = stepthumb;
else
step = steparm;
if((cpsr & MODE) == (n & MODE)){
cpsr = n;
return;
}
switch(cpsr & MODE){
case MUSR:
case MSYS:
saver[R13USR] = r[13];
saver[R14USR] = r[14];
break;
case MFIQ:
saver[R13FIQ] = r[13];
saver[R14FIQ] = r[14];
saver[SPSRFIQ] = spsr;
break;
case MSVC:
saver[R13SVC] = r[13];
saver[R14SVC] = r[14];
saver[SPSRSVC] = spsr;
break;
case MABT:
saver[R13ABT] = r[13];
saver[R14ABT] = r[14];
saver[SPSRABT] = spsr;
break;
case MIRQ:
saver[R13IRQ] = r[13];
saver[R14IRQ] = r[14];
saver[SPSRIRQ] = spsr;
break;
case MUND:
saver[R13UND] = r[13];
saver[R14UND] = r[14];
saver[SPSRUND] = spsr;
break;
}
switch(n & MODE){
case MUSR:
case MSYS:
r[13] = saver[R13USR];
r[14] = saver[R14USR];
break;
case MFIQ:
r[13] = saver[R13FIQ];
r[14] = saver[R14FIQ];
spsr = saver[SPSRFIQ];
break;
case MSVC:
r[13] = saver[R13SVC];
r[14] = saver[R14SVC];
spsr = saver[SPSRSVC];
break;
case MABT:
r[13] = saver[R13ABT];
r[14] = saver[R14ABT];
spsr = saver[SPSRABT];
break;
case MIRQ:
r[13] = saver[R13IRQ];
r[14] = saver[R14IRQ];
spsr = saver[SPSRIRQ];
break;
case MUND:
r[13] = saver[R13UND];
r[14] = saver[R14UND];
spsr = saver[SPSRUND];
break;
default:
sysfatal("invalid mode switch to %#x (pc=%#.8x)", n, curpc);
}
if((cpsr & MODE) == MFIQ){
memcpy(&saver[R8FIQ], &r[8], 5*4);
memcpy(&r[8], &saver[R8USR], 5*4);
}
if((n & MODE) == MFIQ){
memcpy(&saver[R8USR], &r[8], 5*4);
memcpy(&r[8], &saver[R8FIQ], 5*4);
}
cpsr = n;
}
static void
interrupt(int src)
{
u32int v;
v = cpsr;
setcpsr(cpsr & ~(MODE|FLAGI|FLAGT) | FLAGI | src);
spsr = v;
switch(src){
case MIRQ:
if((v & FLAGT) != 0)
r[14] = r[15];
else
r[14] = r[15] - 4;
r[15] = 0x18;
break;
case MSVC:
if((v & FLAGT) != 0)
r[14] = r[15] - 2;
else
r[14] = r[15] - 4;
r[15] = 0x08;
break;
default:
sysfatal("unknown exception %x\n", src);
}
pipeflush();
}
static void
mulspeed(u32int val)
{
if((int)val < 0) val = ~val;
if((val >> 8) == 0)
cyc += 1;
else if((val >> 16) == 0)
cyc += 2;
else if((val >> 24) == 0)
cyc += 3;
else
cyc += 4;
}
static void
armextra(u32int instr)
{
int Rn, Rd, Rm, Rs, sh;
u32int addr, off, val;
u64int vall;
enum {
SIGN = 1<<6,
HALF = 1<<5,
LOAD = 1<<20,
WRBACK = 1<<21,
IMM = 1<<22,
ADD = 1<<23,
PRE = 1<<24,
BYTE = 1<<22,
LONG = 1<<23,
MSIGN = 1<<22,
ACC = 1<<21,
FLAGS = 1<<20,
};
Rm = instr & 0xf;
Rn = instr >> 16 & 0xf;
Rd = instr >> 12 & 0xf;
if((instr & 0x60) == 0){
if((instr & 1<<24) != 0){
addr = r[Rn];
if((instr & 0x0ffffff0) == 0x012fff10){
r[14] = r[15] - 4;
r[15] = r[Rm] & ~1;
setcpsr(cpsr | FLAGT);
pipeflush();
}else if((instr & BYTE) != 0){
io();
val = (u8int) memread(addr, 1, 0);
memwrite(addr, (u8int) r[Rm], 1);
r[Rd] = val;
}else{
io();
val = memread(addr & ~3, 4, 0);
if((addr & 3) != 0){
sh = (addr & 3) << 2;
val = val >> sh | val << 32 - sh;
}
memwrite(addr & ~3, r[Rm], 4);
r[Rd] = val;
}
}else{
Rs = instr >> 8 & 0xf;
mulspeed(r[Rs]);
if((instr & LONG) != 0){
if((instr & ACC) != 0){
vall = (u64int)r[Rn] << 32 | r[Rd];
io();
}else
vall = 0;
io();
if((instr & MSIGN) == 0)
vall += ((u64int) r[Rs]) * r[Rm];
else
vall += (s64int) ((s32int) r[Rs]) * (s32int) r[Rm];
r[Rn] = vall >> 32;
r[Rd] = vall;
if((instr & FLAGS) != 0){
cpsr &= ~(FLAGN|FLAGZ|FLAGC);
if(vall == 0)
cpsr |= FLAGZ;
if((s64int)vall < 0)
cpsr |= FLAGN;
}
}else{
val = r[Rs] * r[Rm];
if((instr & ACC) != 0){
val += r[Rd];
io();
}
if((instr & FLAGS) != 0){
cpsr &= ~(FLAGN|FLAGZ|FLAGC);
if(val == 0)
cpsr |= FLAGZ;
if((int)val < 0)
cpsr |= FLAGN;
}
r[Rn] = val;
}
}
return;
}
if((instr & IMM) == 0)
off = r[Rm];
else
off = instr & 0xf | instr >> 4 & 0xf0;
if((instr & ADD) == 0)
off = -off;
addr = r[Rn];
if((instr & PRE) != 0)
addr += off;
switch(instr & (HALF|LOAD)){
case 0:
memwrite(addr, (u8int) r[Rd], 1);
break;
case HALF:
memwrite(addr & ~1, (u16int) r[Rd], 2);
break;
case LOAD:
io();
r[Rd] = (u8int) memread(addr, 1, 0);
if((instr & SIGN) != 0)
r[Rd] = (s8int) r[Rd];
break;
case LOAD|HALF:
io();
val = (u16int) memread(addr & ~1, 2, 0);
if((instr & SIGN) != 0)
val = (s16int) val;
if((addr & 1) != 0)
val = val << 8 | val >> 24;
r[Rd] = val;
break;
}
if((instr & PRE) == 0)
addr += off;
if((instr & (WRBACK|PRE)) != PRE && Rn != Rd)
r[Rn] = addr;
}
static void
armsr(u32int instr)
{
int Rd, Rs;
u32int op, op0;
if((instr & 0x0fbf0fff) == 0x010f0000){
Rd = instr >> 12 & 0xf;
r[Rd] = (instr & 1<<22) != 0 ? spsr : cpsr;
return;
}
if((instr & 0x0fb0fff0) == 0x0120f000){
Rs = instr & 0xf;
op = r[Rs];
msr:
op0 = 0;
if((instr & 1<<16) != 0) op0 |= 0xff;
if((instr & 1<<17) != 0) op0 |= 0xff00;
if((instr & 1<<18) != 0) op0 |= 0xff0000;
if((instr & 1<<19) != 0) op0 |= 0xff000000;
if((instr & 1<<22) != 0)
spsr = spsr & ~op0 | op & op0;
else
setcpsr(cpsr & ~op0 | op & op0);
if((cpsr & FLAGT) != 0)
sysfatal("illegal MSR to CPSR (T bit set, val=%#.8ux, pc=%#.8ux)", cpsr, curpc);
return;
}
if((instr & 0x0fb0f000) == 0x0320f000){
op = (u8int) instr;
Rs = instr >> 7 & 0x1e;
op = op >> Rs | op << 32 - Rs;
goto msr;
}
if((instr & 0x0ffffff0) == 0x012fff10){
Rs = instr & 0xf;
op = r[Rs];
if((op & 1) != 0)
setcpsr(cpsr | FLAGT);
r[15] = op & ~1;
pipeflush();
return;
}
undefined(instr);
}
static void
armalu(u32int instr)
{
int Rn, Rd, Rs, Rm, oper, sbit;
u32int op, op0, res;
u64int res64;
int sh;
int cout;
if((instr & (1<<25|0x90)) == 0x90){
armextra(instr);
return;
}
Rn = instr >> 16 & 0xf;
Rd = instr >> 12 & 0xf;
Rs = instr >> 8 & 0xf;
if((instr & 1<<25) == 0){
Rm = instr & 0xf;
op = r[Rm];
if((instr & 1<<4) == 0)
sh = instr >> 7 & 0x1f;
else{
sh = (u8int) r[Rs];
if(Rm == 15)
op += 4; /* undocumented behaviour */
}
switch(instr >> 5 & 3){
default:
if(sh == 0)
cout = cpsr >> 29;
else if(sh < 32){
cout = op >> 32 - sh;
op = op << sh;
}else if(sh == 32){
cout = op;
op = 0;
}else
cout = op = 0;
break;
case 1:
if(sh == 0)
if((instr & 1<<4) != 0)
cout = cpsr >> 29;
else{
cout = op >> 31;
op = 0;
}
else if(sh < 32 && sh != 0){
cout = op >> sh - 1;
op = op >> sh;
}else if(sh == 32){
cout = op >> 31;
op = 0;
}else
cout = op = 0;
break;
case 2:
if(sh == 0){
if((instr & 1<<4) != 0)
cout = cpsr >> 29;
else
cout = op = -((int)op < 0);
}else if(sh < 32){
cout = op >> sh - 1;
op = ((int) op) >> sh;
}else
cout = op = -((int)op < 0);
break;
case 3:
if(sh == 0){
if((instr & 1<<4) != 0)
cout = cpsr >> 29;
else{
cout = op;
op = op >> 1 | (cpsr & FLAGC) << 2;
}
}else{
sh &= 31;
if(sh == 0)
cout = op >> 31;
else{
cout = op >> sh - 1;
op = op << 32 - sh | op >> sh;
}
}
break;
}
cyc++;
}else{
op = (u8int) instr;
Rs <<= 1;
if(Rs != 0){
op = op << 32 - Rs | op >> Rs;
cout = op >> 31;
}else
cout = cpsr >> 29;
}
sbit = instr & 1<<20;
op0 = r[Rn];
oper = instr >> 21 & 0xf;
SET(res64);
switch(oper){
default: case 0: case 8: res = op0 & op; break;
case 1: case 9: res = op0 ^ op; break;
case 2: case 10: res64 = (uvlong) op0 + ~op + 1; res = res64; break;
case 3: res64 = (uvlong) op + ~op0 + 1; res = res64; break;
case 4: case 11: res64 = (uvlong) op0 + op; res = res64; break;
case 5: res64 = (uvlong) op0 + op + (cpsr >> 29 & 1); res = res64; break;
case 6: res64 = (uvlong) op0 + ~op + (cpsr >> 29 & 1); res = res64; break;
case 7: res64 = (uvlong) op + ~op0 + (cpsr >> 29 & 1); res = res64; break;
case 12: res = op0 | op; break;
case 13: res = op; break;
case 14: res = op0 & ~op; break;
case 15: res = ~op; break;
}
if(sbit){
switch(oper){
case 2: case 6: case 10:
cpsr &= ~(FLAGC|FLAGN|FLAGZ|FLAGV);
if(res64 >> 32 != 0)
cpsr |= FLAGC;
if(((op0 ^ op) & (op0 ^ res) & 1<<31) != 0)
cpsr |= FLAGV;
break;
case 3: case 7:
cpsr &= ~(FLAGC|FLAGN|FLAGZ|FLAGV);
if(res64 >> 32 != 0)
cpsr |= FLAGC;
if(((op ^ op0) & (op ^ res) & 1<<31) != 0)
cpsr |= FLAGV;
break;
case 4: case 5: case 11:
cpsr &= ~(FLAGC|FLAGN|FLAGZ|FLAGV);
if(res64 >> 32 != 0)
cpsr |= FLAGC;
if((~(op ^ op0) & (op ^ res) & 1<<31) != 0)
cpsr |= FLAGV;
break;
default:
cpsr &= ~(FLAGC|FLAGN|FLAGZ);
if(cout & 1)
cpsr |= FLAGC;
break;
}
if(res == 0)
cpsr |= FLAGZ;
if((res & 1<<31) != 0)
cpsr |= FLAGN;
}
if(oper < 8 || oper > 11){
r[Rd] = res;
if(Rd == 15){
if(sbit)
setcpsr(spsr);
pipeflush();
}
}else if(!sbit){
if((instr & 1<<25) != 0)
cyc--;
armsr(instr);
}
}
static void
armsingle(u32int instr)
{
int op, Rn, Rd, Rm;
u32int off, addr, val, sh;
enum {
LOAD = 1<<0,
WRBACK = 1<<1,
BYTE = 1<<2,
ADD = 1<<3,
PRE = 1<<4,
REGOFF = 1<<5
};
op = instr >> 20;
Rn = instr >> 16 & 0xf;
Rd = instr >> 12 & 0xf;
if((op & REGOFF) != 0){
Rm = instr & 0xf;
off = r[Rm];
if((instr & 0xfff0) != 0){
sh = instr >> 7 & 0x1f;
switch(instr >> 5 & 3){
case 0: off = off << sh; break;
case 1:
if(sh == 0)
off = 0;
else
off = off >> sh;
break;
case 2:
if(sh == 0)
off = -((int)off < 0);
else
off = ((int)off) >> sh;
break;
case 3:
if(sh == 0)
off = off >> 1 | (cpsr & FLAGC) << 2;
else
off = off >> sh | off << 32 - sh;
break;
}
}
}else
off = instr & 0xfff;
if((op & ADD) == 0)
off = -off;
addr = r[Rn];
if((op & PRE) != 0)
addr += off;
io();
switch(op & (LOAD|BYTE)){
case 0:
memwrite(addr & ~3, r[Rd], 4);
break;
case BYTE:
memwrite(addr, r[Rd], 1);
break;
case LOAD:
val = memread(addr & ~3, 4, 0);
if((addr & 3) != 0){
sh = (addr & 3) << 3;
val = val >> sh | val << 32 - sh;
}
r[Rd] = val;
io();
if(Rd == 15)
pipeflush();
break;
case LOAD|BYTE:
r[Rd] = (u8int) memread(addr, 1, 0);
io();
if(Rd == 15)
pipeflush();
break;
}
if((op & PRE) == 0)
addr += off;
if((op & (WRBACK|PRE)) != PRE && Rn != Rd)
r[Rn] = addr;
}
static void
armmulti(u32int instr)
{
int i, Rn, pop, user;
u32int addr, val, *rp;
u16int bits;
int seq;
enum {
LOAD = 1<<20,
WRBACK = 1<<21,
USER = 1<<22,
UP = 1<<23,
PRE = 1<<24,
};
Rn = instr >> 16 & 0xf;
addr = r[Rn] & ~3;
if((instr & LOAD) != 0)
io();
for(bits = instr, pop = 0; bits != 0; pop++)
bits &= bits - 1;
pop <<= 2;
user = (instr & (USER|1<<15)) == USER;
switch(instr & (PRE|UP)){
default:
val = addr - pop;
addr = val + 4;
break;
case PRE:
addr = val = addr - pop;
break;
case UP:
val = addr + pop;
break;
case UP|PRE:
val = addr + pop;
addr += 4;
break;
}
seq = 0;
for(i = 0; i < 16; i++){
if((instr & 1<<i) == 0)
continue;
if(user)
switch(i){
case 8: case 9: case 10: case 11: case 12:
if((cpsr & MODE) == MFIQ){
rp = &saver[R8USR + i - 8];
break;
}
default: rp = &r[i]; break;
case 13: rp = &saver[R13USR]; break;
case 14: rp = &saver[R14USR]; break;
}
else
rp = &r[i];
if((instr & LOAD) != 0)
*rp = memread(addr, 4, seq);
else
memwrite(addr, *rp, 4);
addr += 4;
seq = 1;
}
/* undocumented: if Rn is the first register set in a load, it's overwritten if writeback is specified */
if((instr & WRBACK) != 0 && ((instr & LOAD) == 0 || (instr & instr-1 & 1<<Rn) == 0))
r[Rn] = val;
if((instr & (LOAD|1<<15)) == (LOAD|1<<15)){
if((instr & USER) != 0)
setcpsr(spsr);
pipeflush();
}
}
static void
armbranch(u32int instr)
{
int a;
a = instr & 0xffffff;
a = (a << 8) >> 6;
if((instr & 1<<24) != 0)
r[14] = r[15] - 4;
r[15] += a;
pipeflush();
}
static int
steparm(void)
{
int s;
u32int instr;
cyc = 0;
if((pipel & 2) != 0)
goto fetch;
if(irq && (cpsr & FLAGI) == 0){
interrupt(MIRQ);
return 1;
}
curpc = r[15] - 8;
instr = instr1;
if(trace)
print("A %.8ux %.8ux %.8ux %.8ux %.8ux | %.8ux %.8ux %.8ux %.8ux | %.8ux %.8ux %.8ux %.8ux\n", curpc, instr, cpsr, r[13], r[14], r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7]);
if(instr >> 28 != 0xe && !cond(instr >> 28, instr))
goto fetch;
switch(instr >> 24 & 0xf){
case 0: case 1: case 2: case 3:
armalu(instr);
break;
case 4: case 5: case 6: case 7:
armsingle(instr);
break;
case 8: case 9:
armmulti(instr);
break;
case 10: case 11:
armbranch(instr);
break;
case 15:
interrupt(MSVC);
break;
default:
undefined(instr);
}
fetch:
instr1 = instr0;
s = step == steparm ? 4 : 2;
instr0 = memread(r[15], s, pipel != -1);
r[15] += s;
pipel <<= 1;
return cyc;
}
static void
addflags(u32int a, u32int b, u32int c)
{
u64int v;
v = (u64int) a + b + c;
cpsr &= ~(FLAGN|FLAGZ|FLAGC|FLAGV);
if((u32int)v == 0)
cpsr |= FLAGZ;
cpsr |= v & FLAGN;
if(v >> 32 != 0)
cpsr |= FLAGC;
if((~(a ^ b) & (a ^ v) & 1<<31) != 0)
cpsr |= FLAGV;
}
static void
nz(u32int v)
{
cpsr &= ~(FLAGN|FLAGZ);
if(v == 0)
cpsr |= FLAGZ;
cpsr |= v & FLAGN;
}
static void
thshadd(u16int instr)
{
int Rd, Rs, off, op;
u32int val, a, b, cout;
Rd = instr & 7;
Rs = instr >> 3 & 7;
off = instr >> 6 & 0x1f;
op = instr >> 11 & 3;
a = r[Rs];
switch(op){
case 0:
if(off == 0){
r[Rd] = val = a;
cout = cpsr >> 29;
}else{
r[Rd] = val = a << off;
cout = a >> 32 - off;
}
goto logflags;
case 1:
if(off == 0){
r[Rd] = val = 0;
cout = a >> 31;
}else{
r[Rd] = val = a >> off;
cout = a >> off - 1;
}
goto logflags;
case 2:
if(off == 0)
cout = r[Rd] = val = -((int)a < 0);
else{
r[Rd] = val = (int)a >> off;
cout = a >> off - 1;
}
goto logflags;
case 3:
break;
}
if((instr & 1<<10) == 0)
b = r[off & 7];
else
b = off & 7;
if((instr & 1<<9) != 0)
b = -b;
r[Rd] = a + b;
addflags(a, b, 0);
return;
logflags:
cpsr &= ~(FLAGN|FLAGZ|FLAGC);
if(val == 0)
cpsr |= FLAGZ;
if((int)val < 0)
cpsr |= FLAGN;
if((cout & 1) != 0)
cpsr |= FLAGC;
}
static void
thaddimm(u16int instr)
{
int Rd, b, op, a;
b = instr & 0xff;
Rd = instr >> 8 & 7;
a = r[Rd];
op = instr >> 11 & 3;
switch(op){
case 0:
r[Rd] = b;
nz(b);
break;
case 1:
addflags(a, ~b, 1);
break;
case 2:
r[Rd] = a + b;
addflags(a, b, 0);
break;
case 3:
r[Rd] = a - b;
addflags(a, ~b, 1);
break;
}
}
static void
thalu(u16int instr)
{
int Rs, Rd;
u32int a, b, v, c;
switch(instr >> 10 & 3){
case 0:
Rd = instr & 7;
Rs = instr >> 3 & 7;
a = r[Rd];
b = r[Rs];
switch(instr >> 6 & 0xf){
case 0:
r[Rd] = v = a & b;
nz(v);
break;
case 1:
r[Rd] = v = a ^ b;
nz(v);
break;
case 2:
io();
v = a;
if(b != 0){
if(b < 32){
c = v >> 32 - b;
v <<= b;
}else if(b == 32){
c = v;
v = 0;
}else
c = v = 0;
cpsr = cpsr & ~FLAGC | c << 29 & FLAGC;
}
r[Rd] = v;
nz(v);
break;
case 3:
io();
v = a;
if(b != 0){
if(b < 32){
c = v >> b - 1;
v >>= b;
}else if(b == 32){
c = v >> 31;
v = 0;
}else
c = v = 0;
cpsr = cpsr & ~FLAGC | c << 29 & FLAGC;
}
r[Rd] = v;
nz(v);
break;
case 4:
io();
v = a;
if(b != 0){
if(b < 32){
c = v >> b - 1;
v = (int)v >> b;
}else
c = v = -((int)v < 0);
cpsr = cpsr & ~FLAGC | c << 29 & FLAGC;
}
r[Rd] = v;
nz(v);
break;
case 5:
c = cpsr >> 29 & 1;
r[Rd] = a + b + c;
addflags(a, b, c);
break;
case 6:
c = cpsr >> 29 & 1;
r[Rd] = a + ~b + c;
addflags(a, ~b, c);
break;
case 7:
io();
b &= 31;
r[Rd] = v = a >> b | a << 32 - b;
if(r[Rs] != 0){
c = a >> (b - 1 & 31);
cpsr = cpsr & ~FLAGC | c << 29 & FLAGC;
}
nz(v);
break;
case 8:
nz(a & b);
break;
case 9:
r[Rd] = -b;
addflags(0, ~b, 1);
break;
case 10:
addflags(a, ~b, 1);
break;
case 11:
addflags(a, b, 0);
break;
case 12:
r[Rd] = v = a | b;
nz(v);
break;
case 13:
r[Rd] = v = a * b;
mulspeed(a);
nz(v);
cpsr &= ~FLAGC;
break;
case 14:
r[Rd] = v = a & ~b;
nz(v);
break;
case 15:
r[Rd] = ~b;
nz(~b);
break;
}
break;
case 1:
Rd = instr & 7 | instr >> 4 & 8;
Rs = instr >> 3 & 15;
switch(instr >> 8 & 3){
case 0:
r[Rd] += r[Rs];
if(Rd == 15){
r[15] &= ~1;
pipeflush();
}
break;
case 1:
addflags(r[Rd], ~r[Rs], 1);
break;
case 2:
r[Rd] = r[Rs];
if(Rd == 15){
r[15] &= ~1;
pipeflush();
}
break;
case 3:
if((r[Rs] & 1) == 0)
setcpsr(cpsr & ~FLAGT);
r[15] = r[Rs] & ~1;
pipeflush();
break;
}
break;
case 2: case 3:
Rd = instr >> 8 & 7;
a = (r[15] & ~3) + (((u8int) instr) << 2);
io();
r[Rd] = memread(a & ~3, 4, 0);
break;
}
}
static void
thldst(u16int instr)
{
int Rd, Rb, Ro, size, sx, load, sh;
u32int v, off;
Rd = instr & 7;
Rb = instr >> 3 & 7;
sx = 0;
switch(instr >> 13){
case 2:
Ro = instr >> 6 & 7;
off = r[Ro];
if((instr & 1<<9) != 0){
load = instr & 3<<10;
sx = instr & 1<<10;
size = load == 1<<10 ? 1 : 2;
}else{
load = instr & 1<<11;
size = (instr & 1<<10) != 0 ? 1 : 4;
}
break;
default:
case 3:
if((instr & 1<<12) != 0){
off = instr >> 6 & 0x1f;
size = 1;
}else{
off = instr >> 4 & 0x7c;
size = 4;
}
load = instr & 1<<11;
break;
case 4:
if((instr & 1<<12) == 0){
off = instr >> 5 & 0x3e;
size = 2;
load = instr & 1<<11;
}else{
Rb = 13;
Rd = instr >> 8 & 7;
off = instr << 2 & 0x3fc;
load = instr & 1<<11;
size = 4;
}
break;
}
off += r[Rb];
if(load){
io();
v = memread(off & -size, size, 0);
if(sx)
if(size == 2)
v = ((int)(v << 16)) >> 16;
else
v = ((int)(v << 24)) >> 24;
if((off & size - 1) != 0){
sh = (off & size - 1) << 3;
v = v >> sh | v << 32 - sh;
}
r[Rd] = v;
}else
memwrite(off & -size, r[Rd], size);
}
static void
thldaddr(u16int instr)
{
int Rd, imm, v;
imm = instr << 2 & 0x3fc;
if((instr & 1<<11) != 0)
v = r[13];
else
v = r[15] & ~3;
Rd = instr >> 8 & 7;
r[Rd] = v + imm;
}
static void
thmulti(u16int instr)
{
int off, lr, Rb;
int i, seq;
u32int addr;
if((instr >> 8) == 0xb0){
off = instr << 2 & 0x1fc;
if((instr & 1<<7) != 0)
off = -off;
r[13] += off;
return;
}
if(instr >> 14 != 3){
Rb = 13;
lr = instr & 1<<8;
}else{
Rb = instr >> 8 & 7;
lr = 0;
}
addr = r[Rb];
seq = 0;
if((instr & 1<<11) != 0){
io();
for(i = 0; i < 8; i++){
if((instr & 1<<i) == 0)
continue;
r[i] = memread(addr & ~3, 4, seq);
addr += 4;
seq = 1;
}
if(lr){
r[15] = memread(addr & ~3, 4, seq) & ~1;
pipeflush();
addr += 4;
}
}else if(Rb == 13){
if(lr){
addr -= 4;
memwrite(addr & ~3, r[14], 4);
}
for(i = 7; i >= 0; i--){
if((instr & 1<<i) == 0)
continue;
addr -= 4;
memwrite(addr & ~3, r[i], 4);
}
}else
for(i = 0; i < 8; i++){
if((instr & 1<<i) == 0)
continue;
memwrite(addr & ~3, r[i], 4);
addr += 4;
}
if(Rb == 13 || (instr & 1<<Rb) == 0)
r[Rb] = addr;
}
static void
thcondbranch(u16int instr)
{
if((instr >> 8 & 15) == 0xf){
interrupt(MSVC);
return;
}
if(!cond(instr >> 8 & 15, instr))
return;
r[15] += ((int)(instr << 24)) >> 23;
pipeflush();
}
static void
thbranch(u16int instr)
{
r[15] += ((int)(instr << 21)) >> 20;
pipeflush();
}
static void
thlbranch(u16int instr)
{
if((instr & 1<<11) != 0){
r[15] = r[14] + (instr << 1 & 0xffe);
r[14] = curpc + 3;
pipeflush();
}else
r[14] = r[15] + ((int)(instr << 21)>>9);
}
static int
stepthumb(void)
{
u16int instr;
int s;
cyc = 0;
if((pipel & 2) != 0)
goto fetch;
if(irq && (cpsr & FLAGI) == 0){
interrupt(MIRQ);
return 1;
}
curpc = r[15] - 4;
instr = instr1;
if(trace)
print("T %.8ux %.4ux %.8ux %.8ux %.8ux | %.8ux %.8ux %.8ux %.8ux | %.8ux %.8ux %.8ux %.8ux\n", curpc, instr, cpsr, r[13], r[14], r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7]);
switch(instr >> 12 & 0xf){
case 0: case 1:
thshadd(instr);
break;
case 2: case 3:
thaddimm(instr);
break;
case 4:
thalu(instr);
break;
case 5: case 6: case 7: case 8: case 9:
thldst(instr);
break;
case 10:
thldaddr(instr);
break;
case 11: case 12:
thmulti(instr);
break;
case 13:
thcondbranch(instr);
break;
case 14:
thbranch(instr);
break;
case 15:
thlbranch(instr);
break;
default:
undefined(instr);
}
fetch:
instr1 = instr0;
s = step == steparm ? 4 : 2;
instr0 = memread(r[15], s, pipel != -1);
r[15] += s;
pipel <<= 1;
return cyc;
}
void
reset(void)
{
setcpsr(0xd3);
r[15] = 0;
pipel = -1;
}
void
cpuload(void)
{
if((cpsr & FLAGT) != 0)
step = stepthumb;
else
step = steparm;
}