ref: 39bf5b987e8ab53a74adec5a15b30f4eda85b357
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; }