diff options
author | aiju <devnull@localhost> | 2014-09-27 20:50:20 +0200 |
---|---|---|
committer | aiju <devnull@localhost> | 2014-09-27 20:50:20 +0200 |
commit | 77f3fa19de87be8ded57a038cab60b240b191dab (patch) | |
tree | 569b0565fb6b1caf71da7c1bcffb2c0c73c7ec6c /sys/src/games | |
parent | 709e78b9f9a76658cc6c704176ebf8f5821ee9d2 (diff) |
games/gba: slowly working (no pun intended)
Diffstat (limited to 'sys/src/games')
-rw-r--r-- | sys/src/games/gba/cpu.c | 1269 | ||||
-rw-r--r-- | sys/src/games/gba/dat.h | 114 | ||||
-rw-r--r-- | sys/src/games/gba/fns.h | 13 | ||||
-rw-r--r-- | sys/src/games/gba/gba.c | 439 | ||||
-rw-r--r-- | sys/src/games/gba/mem.c | 665 | ||||
-rw-r--r-- | sys/src/games/gba/mkfile | 13 | ||||
-rw-r--r-- | sys/src/games/gba/ppu.c | 653 | ||||
-rw-r--r-- | sys/src/games/gba/test.c | 15 |
8 files changed, 3181 insertions, 0 deletions
diff --git a/sys/src/games/gba/cpu.c b/sys/src/games/gba/cpu.c new file mode 100644 index 000000000..eb861ab55 --- /dev/null +++ b/sys/src/games/gba/cpu.c @@ -0,0 +1,1269 @@ +#include <u.h> +#include <libc.h> +#include <thread.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, trace; + +#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]; + 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, 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 >>= 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 - 1), 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, 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; +} diff --git a/sys/src/games/gba/dat.h b/sys/src/games/gba/dat.h new file mode 100644 index 000000000..7e673cf8c --- /dev/null +++ b/sys/src/games/gba/dat.h @@ -0,0 +1,114 @@ +typedef char s8int; +typedef short s16int; +typedef long s32int; +typedef vlong s64int; + +extern int cpuhalt, trace, keys; + +extern u32int curpc; +extern int irq; + +extern int dmaact; +extern uchar vram[]; +extern u16int pram[], oam[]; +extern u16int reg[]; +extern uchar *rom, *back; +extern int nrom, nback, backup; + +extern int ppux, ppuy; +extern u8int bldy, blda, bldb; + +extern int scale; + +enum { + DISPCNT = 0x0/2, + DISPSTAT = 0x4/2, + BG0CNT = 0x8/2, + BG0HOFS = 0x10/2, + BG0VOFS = 0x12/2, + + BG2PA = 0x20/2, + BG2PB = 0x22/2, + BG2PC = 0x24/2, + BG2PD = 0x26/2, + BG2XL = 0x28/2, + BG2XH = 0x2a/2, + BG2YL = 0x2c/2, + BG2YH = 0x2e/2, + + WIN0H = 0x40/2, + WIN1H = 0x42/2, + WIN0V = 0x44/2, + WIN1V = 0x46/2, + WININ = 0x48/2, + WINOUT = 0x4a/2, + BLDCNT = 0x50/2, + BLDALPHA = 0x52/2, + BLDY = 0x54/2, + + DMA0CNTH = 0xba/2, + DMA1CNTH = 0xc6/2, + DMA2CNTH = 0xd2/2, + DMA3CNTH = 0xde/2, + + KEYCNT = 0x132/2, + + IE = 0x200/2, + IF = 0x202/2, + WAITCNT = 0x204/2, + IME = 0x208/2, + +}; + +enum { + /* DISPCNT */ + FRAME = 1<<4, + HBLFREE = 1<<5, + OBJNOMAT = 1<<6, + FBLANK = 1<<7, + + /* DISPSTAT */ + IRQVBLEN = 1<<3, + IRQHBLEN = 1<<4, + IRQVCTREN = 1<<5, + + /* BGnCNT */ + BG8 = 1<<7, + DISPWRAP = 1<<13, + + /* DMAnCNTH */ + DMADCNT = 5, + DMASCNT = 7, + DMAREP = 1<<9, + DMAWIDE = 1<<10, + DMAWHEN = 12, + DMAIRQ = 1<<14, + DMAEN = 1<<15, + + DMAINC = 0, + DMADEC = 1, + DMAFIX = 2, + DMAINCREL = 3, + + DMANOW = 0, + DMAVBL = 1, + DMAHBL = 2, + DMASPEC = 3, + DMASOUND = 4, + DMAVIDEO = 5, + + IRQVBL = 1<<0, + IRQHBL = 1<<1, + IRQVCTR = 1<<2, + IRQTIM0 = 1<<3, + IRQDMA0 = 1<<8, + IRQKEY = 1<<12, + + NOBACK = 0, + SRAM = 1, + EEPROM = 2, + FLASH = 3, + + KB = 1024, + BACKTYPELEN = 64, +}; diff --git a/sys/src/games/gba/fns.h b/sys/src/games/gba/fns.h new file mode 100644 index 000000000..2074228ca --- /dev/null +++ b/sys/src/games/gba/fns.h @@ -0,0 +1,13 @@ +u32int memread(u32int, int, int); +void memwrite(u32int, u32int, int); +extern int (*step)(void); +void reset(void); +void memreset(void); +void setif(u16int); +void ppustep(void); +void timerstep(int t); +void flush(void); +int dmastep(void); +void dmastart(int); +void flushback(void); +void writeback(void); diff --git a/sys/src/games/gba/gba.c b/sys/src/games/gba/gba.c new file mode 100644 index 000000000..f4b187c07 --- /dev/null +++ b/sys/src/games/gba/gba.c @@ -0,0 +1,439 @@ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include <draw.h> +#include <mouse.h> +#include <keyboard.h> +#include "dat.h" +#include "fns.h" + +int cpuhalt; +int scale, profile; +Rectangle picr; +Image *bg, *tmp; +Mousectl *mc; +int keys, paused, framestep, backup; +QLock pauselock; +int savefd, saveframes; + +char *biosfile = "/sys/games/lib/gbabios.bin"; + +int ppuclock; + +void * +emalloc(ulong sz) +{ + void *v; + + v = malloc(sz); + if(v == nil) + sysfatal("malloc: %r"); + setmalloctag(v, getcallerpc(&sz)); + return v; +} + +void +writeback(void) +{ + if(saveframes == 0) + saveframes = 15; +} + +void +flushback(void) +{ + if(savefd >= 0) + pwrite(savefd, back, nback, BACKTYPELEN); + saveframes = 0; +} + +void +loadbios(void) +{ + extern uchar bios[16384]; + + int fd; + + fd = open(biosfile, OREAD); + if(fd < 0) + sysfatal("open: %r"); + readn(fd, bios, 16384); + close(fd); +} + +int +romtype(int *size) +{ + u32int *p, n, v; + union {char a[4]; u32int u;} s1 = {"EEPR"}, s2 = {"SRAM"}, s3 = {"FLAS"}; + + p = (u32int *) rom; + n = nrom / 4; + do{ + v = *p++; + if(v == s1.u && memcmp(p - 1, "EEPROM_V", 8) == 0){ + print("backup type is either eeprom4 or eeprom64 -- can't detect which one\n"); + return NOBACK; + } + if(v == s2.u && memcmp(p - 1, "SRAM_V", 6) == 0){ + *size = 32*KB; + return SRAM; + } + if(v == s3.u){ + if(memcmp(p - 1, "FLASH_V", 7) == 0 || memcmp(p - 1, "FLASH512_V", 10) == 0){ + *size = 64*KB; + return FLASH; + } + if(memcmp(p - 1, "FLASH1M_V", 9) == 0){ + *size = 128*KB; + return FLASH; + } + } + }while(--n); + return NOBACK; +} + +int +parsetype(char *s, int *size) +{ + if(strcmp(s, "eeprom4") == 0){ + *size = 512; + return EEPROM; + }else if(strcmp(s, "eeprom64") == 0){ + *size = 8*KB; + return EEPROM; + }else if(strcmp(s, "sram256") == 0){ + *size = 32*KB; + return SRAM; + }else if(strcmp(s, "flash512") == 0){ + *size = 64*KB; + return FLASH; + }else if(strcmp(s, "flash1024") == 0){ + *size = 128*KB; + return FLASH; + }else + return NOBACK; +} + +void +typename(char *s, int type, int size) +{ + char *st; + switch(type){ + case EEPROM: + st = "eeprom"; + break; + case FLASH: + st = "flash"; + break; + case SRAM: + st = "sram"; + break; + default: + sysfatal("typestr: unknown type %d -- shouldn't happen", type); + return; + } + snprint(s, BACKTYPELEN, "%s%d", st, size/128); +} + +void +loadsave(char *file) +{ + char *buf, *p; + char tstr[BACKTYPELEN]; + int type, size; + + buf = emalloc(strlen(file) + 4); + strcpy(buf, file); + p = strchr(buf, '.'); + if(p == nil) + p = buf + strlen(buf); + strcpy(p, ".sav"); + savefd = open(buf, ORDWR); + if(savefd < 0){ + if(backup == NOBACK){ + backup = romtype(&nback); + if(backup == NOBACK){ + fprint(2, "failed to autodetect save format\n"); + free(buf); + return; + } + } + savefd = create(buf, OWRITE, 0664); + if(savefd < 0){ + fprint(2, "create: %r"); + free(buf); + return; + } + memset(tstr, 0, sizeof(tstr)); + typename(tstr, backup, nback); + write(savefd, tstr, sizeof(tstr)); + back = emalloc(nback); + memset(back, 0, nback); + write(savefd, back, nback); + free(buf); + atexit(flushback); + return; + } + readn(savefd, tstr, sizeof(tstr)); + tstr[31] = 0; + type = parsetype(tstr, &size); + if(type == NOBACK || backup != NOBACK && (type != backup || nback != size)) + sysfatal("%s: invalid format", buf); + backup = type; + nback = size; + back = emalloc(nback); + readn(savefd, back, nback); + atexit(flushback); + free(buf); +} + +void +loadrom(char *file) +{ + int fd; + vlong sz; + + fd = open(file, OREAD); + if(fd < 0) + sysfatal("open: %r"); + sz = seek(fd, 0, 2); + if(sz <= 0 || sz >= 32*1024*1024) + sysfatal("nope.jpg"); + seek(fd, 0, 0); + nrom = sz; + rom = emalloc(nrom); + if(readn(fd, rom, sz) < sz) + sysfatal("read: %r"); + close(fd); + loadsave(file); + if(nrom == 32*KB*KB && backup == EEPROM) + nrom -= 256; +} + +void +screeninit(void) +{ + Point p; + + p = divpt(addpt(screen->r.min, screen->r.max), 2); + picr = (Rectangle){subpt(p, Pt(scale * 120, scale * 80)), addpt(p, Pt(scale * 120, scale * 80))}; + tmp = allocimage(display, Rect(0, 0, scale * 240, scale > 1 ? 1 : scale * 160), CHAN4(CIgnore, 1, CBlue, 5, CGreen, 5, CRed, 5), scale > 1, 0); + bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF); + draw(screen, screen->r, bg, nil, ZP); +} + +void +keyproc(void *) +{ + int fd, k; + static char buf[256]; + char *s; + Rune r; + + fd = open("/dev/kbd", OREAD); + if(fd < 0) + sysfatal("open: %r"); + for(;;){ + if(read(fd, buf, sizeof(buf) - 1) <= 0) + sysfatal("read /dev/kbd: %r"); + if(buf[0] == 'c'){ + /*if(utfrune(buf, KF|5)) + savereq = 1; + if(utfrune(buf, KF|6)) + loadreq = 1;*/ + if(utfrune(buf, Kdel)){ + close(fd); + threadexitsall(nil); + } + if(utfrune(buf, 't')) + trace = !trace; + } + if(buf[0] != 'k' && buf[0] != 'K') + continue; + s = buf + 1; + k = 0; + while(*s != 0){ + s += chartorune(&r, s); + switch(r){ + case Kdel: close(fd); threadexitsall(nil); + case 'z': k |= 1<<1; break; + case 'x': k |= 1<<0; break; + case 'a': k |= 1<<9; break; + case 's': k |= 1<<8; break; + case Kshift: k |= 1<<2; break; + case 10: k |= 1<<3; break; + case Kup: k |= 1<<6; break; + case Kdown: k |= 1<<7; break; + case Kleft: k |= 1<<5; break; + case Kright: k |= 1<<4; break; + case Kesc: + if(paused) + qunlock(&pauselock); + else + qlock(&pauselock); + paused = !paused; + break; + case KF|1: + if(paused){ + qunlock(&pauselock); + paused=0; + } + framestep = !framestep; + break; + } + } + keys = k; + } + +} + +void +timing(void) +{ + static int fcount; + static vlong old; + static char buf[32]; + vlong new; + + if(++fcount == 60) + fcount = 0; + else + return; + new = nsec(); + if(new != old) + sprint(buf, "%6.2f%%", 1e11 / (new - old)); + else + buf[0] = 0; + draw(screen, rectaddpt(Rect(10, 10, 200, 30), screen->r.min), bg, nil, ZP); + string(screen, addpt(screen->r.min, Pt(10, 10)), display->black, ZP, display->defaultfont, buf); + old = nsec(); +} + +void +flush(void) +{ + extern uchar pic[]; + Mouse m; + int x; + + if(nbrecvul(mc->resizec) > 0){ + if(getwindow(display, Refnone) < 0) + sysfatal("resize failed: %r"); + screeninit(); + } + while(nbrecv(mc->c, &m) > 0) + ; + if(scale == 1){ + loadimage(tmp, tmp->r, pic, 240*160*2); + draw(screen, picr, tmp, nil, ZP); + } else { + Rectangle r; + uchar *s; + int w; + + s = pic; + r = picr; + w = 240*2*scale; + while(r.min.y < picr.max.y){ + loadimage(tmp, tmp->r, s, w); + s += w; + r.max.y = r.min.y+scale; + draw(screen, r, tmp, nil, ZP); + r.min.y = r.max.y; + } + } + flushimage(display, 1); + if(profile) + timing(); + if(framestep){ + paused = 1; + qlock(&pauselock); + framestep = 0; + } + + if(saveframes > 0 && --saveframes == 0) + flushback(); + + if((reg[KEYCNT] & 1<<14) != 0){ + x = reg[KEYCNT] & keys; + if((reg[KEYCNT] & 1<<15) != 0){ + if(x == (reg[KEYCNT] & 0x3ff)) + setif(IRQKEY); + }else + if(x != 0) + setif(IRQKEY); + } +} + +void +usage(void) +{ + fprint(2, "usage: %s [-23T] [-s savetype] [-b biosfile] rom\n", argv0); + exits("usage"); +} + +void +threadmain(int argc, char **argv) +{ + char *s; + int t; + + scale = 1; + ARGBEGIN { + case '2': + scale = 2; + break; + case '3': + scale = 3; + break; + case 's': + s = EARGF(usage()); + backup = parsetype(s, &nback); + if(backup == NOBACK) + sysfatal("unknown save type '%s'", s); + break; + case 'b': + biosfile = strdup(EARGF(usage())); + break; + case 'T': + profile++; + break; + default: + usage(); + } ARGEND; + if(argc < 1) + usage(); + + loadbios(); + loadrom(argv[0]); + + if(initdraw(nil, nil, nil) < 0) + sysfatal("initdraw: %r"); + mc = initmouse(nil, screen); + if(mc == nil) + sysfatal("initmouse: %r"); + proccreate(keyproc, nil, mainstacksize); + screeninit(); + + memreset(); + reset(); + for(;;){ + if(paused){ + qlock(&pauselock); + qunlock(&pauselock); + } + if(dmaact) + t = dmastep(); + else if(cpuhalt) + t = 8; + else + t = step(); + ppuclock += t; + while(ppuclock >= 4){ + ppustep(); + ppuclock -= 4; + } + timerstep(t); + } +} diff --git a/sys/src/games/gba/mem.c b/sys/src/games/gba/mem.c new file mode 100644 index 000000000..f4c95f559 --- /dev/null +++ b/sys/src/games/gba/mem.c @@ -0,0 +1,665 @@ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include "dat.h" +#include "fns.h" + +uchar bios[16*KB], wram0[32*KB], wram1[256*KB]; +uchar vram[96*KB]; +u16int pram[512], oam[512]; +uchar *rom, *back; +int nrom, nback; +u16int reg[512]; +u16int tim[4]; +int timerclock; +int dmaact; +enum { + DMASRC, + DMADST, + DMACNT +}; +u32int dmar[16]; +u8int waitst[16] = {5, 5, 5, 5, 3, 5, 5, 9, 8, 10, 10, 14}; + +extern int cyc; + +static int eepromread(void); +static void eepromwrite(int); +static u8int flashread(u16int); +static void flashwrite(u16int, u8int); + +static u32int +arread(uchar *c, int n) +{ + switch(n){ + default: + return c[0]; + case 2: + return c[0] | c[1] << 8; + case 4: + return c[0] | c[1] << 8 | c[2] << 16 | c[3] << 24; + } +} + +static void +arwrite(uchar *c, u32int v, int n) +{ + switch(n){ + case 4: + c[3] = v >> 24; + c[2] = v >> 16; + case 2: + c[1] = v >> 8; + default: + c[0] = v; + } +} + +static u32int +ar16read(u16int *c, int h, int n) +{ + switch(n){ + case 1: + return c[0] >> (h << 3); + default: + return c[0]; + case 4: + return c[0] | c[1] << 16; + } +} + +static void +ar16write(u16int *c, int h, u32int v, int n) +{ + switch(n){ + case 1: + if(h) + c[0] = c[0] & 0xff | ((u8int)v) << 8; + else + c[0] = c[0] & 0xff00 | (u8int)v; + break; + case 2: + c[0] = v; + break; + case 4: + c[0] = v; + c[1] = v >> 16; + break; + } +} + +static u32int +regread(u32int a) +{ + u32int v; + + switch(a){ + case DISPSTAT*2: + v = reg[a/2] & ~7; + + if(ppuy >= 160 && ppuy != 227) + v |= 1; + if(ppux >= 240) + v |= 2; + if(ppuy == v >> 8) + v |= 4; + return v; + case 0x006: + return ppuy; + case 0x100: case 0x104: case 0x108: case 0x10c: + return tim[(a - 0x100) / 4]; + case 0x130: + return keys ^ 0x3ff; + default: + return reg[a/2]; + } +} + +static void +regwrite16(u32int a, u16int v) +{ + u16int *p; + int i; + static u8int ws0[4] = {5,4,3,9}; + + p = ®[a/2]; + switch(a){ + case IF*2: + *p &= ~v; + setif(0); + return; + case BLDALPHA*2: + blda = v & 0x1f; + if(blda > 16) + blda = 16; + bldb = v >> 8 & 0x1f; + if(bldb > 16) + bldb = 16; + break; + case BLDY*2: + bldy = v & 0x1f; + if(bldy > 16) + bldy = 16; + break; + case DMA0CNTH*2: case DMA1CNTH*2: case DMA2CNTH*2: case DMA3CNTH*2: + if((*p & DMAEN) == 0 && (v & DMAEN) != 0){ + i = (a - DMA0CNTH*2) / 12; + if((v >> DMAWHEN & 3) == 0) + dmaact |= 1<<i; + if(i == 3 && (v >> DMAWHEN & 3) == 3) + print("DMA video capture mode\n"); + dmar[4*i + DMASRC] = p[-5] | p[-4] << 16; + dmar[4*i + DMADST] = p[-3] | p[-2] << 16; + dmar[4*i + DMACNT] = p[-1]; + } + break; + case 0x102: case 0x106: case 0x10a: case 0x10e: + if((*p & 1<<7) == 0 && (v & 1<<7) != 0) + tim[(a-0x102)/4] = p[-1]; + break; + case IME*2: case IE*2: + setif(0); + break; + case WAITCNT*2: + waitst[3] = waitst[7] = ws0[v & 3]; + waitst[0] = ws0[v >> 2 & 3]; + waitst[4] = ((v & 1<<4) == 0) + 2; + waitst[1] = ws0[v >> 5 & 3]; + waitst[5] = (v & 1<<7) == 0 ? 5 : 2; + waitst[2] = ws0[v >> 8 & 3]; + waitst[6] = (v & 1<<10) == 0 ? 9 : 2; + for(i = 0; i < 8; i++) + waitst[8 + i] = waitst[i] + waitst[i | 4]; + break; + case 0x301: + cpuhalt = 1; + break; + } + *p = v; +} + +static void +regwrite(u32int a, u32int v, int n) +{ + u16int w; + + switch(n){ + case 1: + if((a & ~1) == IF) + w = 0; + else + w = regread(a); + if((a & 1) == 0) + w = w & 0xff00 | (u8int)v; + else + w = w & 0xff | v << 8; + regwrite16(a, w); + break; + default: + if((a & 1) != 0) + sysfatal("unaligned register access"); + regwrite16(a, v); + break; + case 4: + if((a & 1) != 0) + sysfatal("unaligned register access"); + regwrite16(a, v); + regwrite16(a + 2, v >> 16); + break; + } +} + +void +setif(u16int v) +{ + reg[IF] |= v; + irq = (reg[IME] & 1) != 0 && (reg[IF] & reg[IE]) != 0; + if(irq) + cpuhalt = 0; +} + +u32int +memread(u32int a, int n, int seq) +{ + u32int b; + assert((a & n-1) == 0); + + switch(a >> 24){ + case 0: + b = a & sizeof(bios) - 1; + cyc++; + return arread(bios + b, n); + case 2: + b = a & sizeof(wram1) - 1; + cyc += n > 2 ? 6 : 3; + return arread(wram1 + b, n); + case 3: + b = a & sizeof(wram0) - 1; + cyc++; + return arread(wram0 + b, n); + case 4: + b = a & 0xffffff; + if(b >= sizeof(reg)) goto fault; + cyc++; + if(n == 4) + return regread(b) | regread(b+2) << 16; + return regread(b); + case 5: + b = a & sizeof(pram) - 1; + cyc += (n+1) >> 1; + return ar16read(pram + b/2, b & 1, n); + case 6: + b = a & 128*KB - 1; + if(b >= 64*KB) + b &= ~(32*KB); + cyc += (n+1) >> 1; + return arread(vram + b, n); + case 7: + b = a & sizeof(oam) - 1; + cyc++; + return ar16read(oam + b/2, b & 1, n); + case 8: case 9: case 10: case 11: case 12: case 13: + b = a & 0x1ffffff; + cyc += waitst[(a >> 25) - 4 | seq << 2 | (n > 2) << 3]; + if(b >= nrom){ + if(backup == EEPROM && b >= 0x1000000 && (nrom < 16*KB*KB || b >= 0x1ffff00)) + return eepromread(); + return 0; + } + return arread(rom + b, n); + case 14: + if(backup == SRAM){ + b = a & nback - 1; + return arread(back + b, n); + } + if(backup == FLASH) + return flashread(a); + return 0; + default: + fault: + sysfatal("read from %#.8ux (pc=%#.8ux)", a, curpc); + return 0; + } +} + +void +memwrite(u32int a, u32int v, int n) +{ + u32int b; + assert((a & n-1) == 0); + + switch(a >> 24){ + case 0: + return; + case 2: + b = a & sizeof(wram1) - 1; + cyc += n > 2 ? 6 : 3; + arwrite(wram1 + b, v, n); + return; + case 3: + b = a & sizeof(wram0) - 1; + cyc++; + arwrite(wram0 + b, v, n); + return; + case 4: + cyc++; + b = a & 0xffffff; + if(b == 0x410) return; + if(b >= sizeof(reg)) goto fault; + regwrite(b, v, n); + return; + case 5: + b = a & sizeof(pram) - 1; + cyc += (n+1) >> 1; + ar16write(pram + b/2, b & 1, v, n); + return; + case 6: + b = a & 128*KB - 1; + if(b >= 64*KB) + b &= ~(32*KB); + cyc += (n+1) >> 1; + arwrite(vram + b, v, n); + return; + case 7: + b = a & sizeof(oam) - 1; + cyc++; + ar16write(oam + b/2, b & 1, v, n); + return; + case 8: case 9: case 10: case 11: case 12: case 13: + if(backup == EEPROM){ + b = a & 0x01ffffff; + if(b >= 0x1000000 && (nrom < 16*KB*KB || b >= 0x1ffff00)) + eepromwrite(v & 1); + } + return; + case 14: + if(backup == SRAM){ + b = a & nback - 1; + arwrite(back + b, v, n); + writeback(); + return; + } + if(backup == FLASH){ + flashwrite(a, v); + return; + } + return; + default: + fault: + sysfatal("write to %#.8ux, value %#.8ux (pc=%#.8ux)", a, v, curpc); + } +} + +void +memreset(void) +{ + reg[0x88/2] = 0x200; +} + +void +timerstep(int t) +{ + int i, carry; + u16int c; + u16int nt; + + nt = -t; + carry = 0; + timerclock += t; + for(i = 0; i < 4; i++){ + c = reg[0x102/2 + i*2]; + if((c & 1<<7) == 0) + goto next; + if((c & 1<<2) == 0) + switch(c & 3){ + case 1: + if((timerclock & 63) != 0) + goto next; + break; + case 2: + if((timerclock & 255) != 0) + goto next; + break; + case 3: + if((timerclock & 1023) != 0) + goto next; + break; + } + else + if(!carry) + goto next; + if(carry = tim[i] >= nt){ + tim[i] += reg[0x100/2 + i*2]; + if((c & 1<<6) != 0) + setif(IRQTIM0 << i); + } + tim[i] += t; + continue; + next: + carry = 0; + } +} + +int +dmastep(void) +{ + int i; + u16int *cntp, cnt; + u32int *dr; + u32int v; + int sz; + + cyc = 0; + for(i = 0; i < 4; i++) + if((dmaact & 1<<i) != 0) + break; + if(i == 4) + return cyc; + curpc = -1; + cntp = reg + DMA0CNTH + i * 6; + cnt = *cntp; + dr = dmar + 4 * i; + + sz = (cnt & DMAWIDE) != 0 ? 4 : 2; + if(i == 0) + dr[DMASRC] &= 0x07FFFFFF; + else + dr[DMASRC] &= 0x0FFFFFFF; + if(i != 3) + dr[DMADST] &= 0x7FFFFFFF; + else + dr[DMADST] &= 0x0FFFFFFF; + v = memread(dr[DMASRC] & -sz, sz, 1); + memwrite(dr[DMADST] & -sz, v, sz); + switch(cnt >> DMADCNT & 3){ + case DMAINC: case DMAINCREL: dr[DMADST] += sz; break; + case DMADEC: dr[DMADST] -= sz; break; + } + switch(cnt >> DMASCNT & 3){ + case DMAINC: dr[DMASRC] += sz; break; + case DMADEC: dr[DMASRC] -= sz; break; + } + if(dr[DMACNT] == 0) + dr[DMACNT] = i != 3 ? 0x4000 : 0x10000; + if(--dr[DMACNT] == 0){ + dmaact &= ~(1<<i); + if((cnt & DMAREP) != 0){ + dmar[DMACNT] = cntp[-1]; + if((cnt >> DMADCNT & 3) == DMAINCREL) + dmar[DMADST] = cntp[-3] | cntp[-2] << 16; + }else + *cntp &= ~DMAEN; + if((cnt & DMAIRQ) != 0) + setif(IRQDMA0 << i); + } + return cyc; +} + +void +dmastart(int cond) +{ + int i; + u16int *cntp, cnt, c; + + cntp = reg + DMA0CNTH; + for(i = 0; i < 3; i++, cntp += 6){ + cnt = *cntp; + if((cnt & DMAEN) == 0) + continue; + c = cnt >> DMAWHEN & 3; + if(c == 3) + c += (i + 1) / 2; + if(c == cond) + dmaact |= 1<<i; + } +} + +int eepromstate, eeprompos, eepromaddr; +u64int eepromdata; + +enum { + EEPROMCMD, + EEPROMRDCMD, + EEPROMRDRESP, + EEPROMWRCMD, + EEPROMWRDATA, + EEPROMWRRESP, +}; + +static int +eepromread(void) +{ + int v; + + switch(eepromstate){ + case EEPROMRDRESP: + eeprompos++; + if(eeprompos <= 4) + return 0; + if(eeprompos == 68){ + eepromstate = EEPROMCMD; + eeprompos = 0; + } + v = eepromdata >> 63; + eepromdata <<= 1; + return v; + case EEPROMWRRESP: + if(++eeprompos == 1000){ + eepromstate = EEPROMCMD; + eeprompos = 0; + return 1; + } + return 0; + default: + return 0; + } +} + +static void +eepromwrite(int n) +{ + uchar *p; + + switch(eepromstate){ + case EEPROMCMD: + eepromaddr = eepromaddr << 1 | n; + if(++eeprompos >= 2){ + switch(eepromaddr & 3){ + case 2: + eepromstate = EEPROMWRCMD; + break; + case 3: + eepromstate = EEPROMRDCMD; + break; + } + eeprompos = 0; + } + break; + case EEPROMRDCMD: + case EEPROMWRCMD: + eepromaddr = eepromaddr << 1 | n; + eeprompos++; + if(nback == 512){ + if(eeprompos >= 7) + eepromaddr = eepromaddr >> 1 & 0x3f; + else + break; + }else{ + if(eeprompos >= 15) + eepromaddr = eepromaddr >> 1 & 0x3fff; + else + break; + } + if(eepromstate == EEPROMRDCMD){ + p = back + eepromaddr * 8; + eepromdata = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24 | (u64int)p[4] << 32 | + (u64int)p[5] << 40 | (u64int)p[6] << 48 | (u64int)p[7] << 56; + eeprompos = 0; + eepromstate = EEPROMRDRESP; + break; + }else{ + eepromdata = n; + eeprompos = 1; + eepromstate = EEPROMWRDATA; + break; + } + case EEPROMWRDATA: + if(eeprompos == 64){ + p = back + eepromaddr * 8; + p[0] = eepromdata; + p[1] = eepromdata >> 8; + p[2] = eepromdata >> 16; + p[3] = eepromdata >> 24; + p[4] = eepromdata >> 32; + p[5] = eepromdata >> 40; + p[6] = eepromdata >> 48; + p[7] = eepromdata >> 56; + eepromstate = EEPROMWRRESP; + eeprompos = 0; + writeback(); + break; + } + eepromdata = eepromdata << 1 | n; + eeprompos++; + break; + } +} + +int flashstate, flashmode, flashbank; + +enum { + FLASHCMD0, + FLASHCMD1, + FLASHCMD2, + FLASHBANK, + FLASHWRITE, + + FLASHID = 1, + FLASHERASE = 2, +}; + +static u8int +flashread(u16int a) +{ + if((flashmode & FLASHID) != 0) + return (a & 1) != 0 ? 0xd4 : 0xbf; + return back[(flashbank << 16) + a]; +} + +static void +flashwrite(u16int a, u8int v) +{ + int erase; + + switch(flashstate){ + case FLASHCMD0: + if(a == 0x5555 && v == 0xaa) + flashstate = FLASHCMD1; + break; + case FLASHCMD1: + if(a == 0x2aaa && v == 0x55) + flashstate = FLASHCMD2; + else + flashstate = FLASHCMD0; + break; + case FLASHCMD2: + flashstate = FLASHCMD0; + erase = flashmode & FLASHERASE; + flashmode &= ~FLASHERASE; + switch(v){ + case 0x90: flashmode |= FLASHID; break; + case 0xF0: flashmode &= ~FLASHID; break; + case 0x80: flashmode |= FLASHERASE; break; + case 0x10: + if(erase){ + memset(back, 0xff, nback); + writeback(); + } + break; + case 0x30: + if(erase){ + memset(back + (a & 0xf000) + (flashbank << 16), 0xff, 4096); + writeback(); + } + break; + case 0xA0: + writeback(); + flashstate = FLASHWRITE; + break; + case 0xB0: flashstate = FLASHBANK; break; + default: + print("unknown flash cmd %x\n", v); + } + break; + case FLASHBANK: + flashbank = v % (nback >> 16); + flashstate = FLASHCMD0; + break; + case FLASHWRITE: + back[(flashbank << 16) + a] &= v; + writeback(); + flashstate = FLASHCMD0; + break; + } +} + diff --git a/sys/src/games/gba/mkfile b/sys/src/games/gba/mkfile new file mode 100644 index 000000000..19e77437d --- /dev/null +++ b/sys/src/games/gba/mkfile @@ -0,0 +1,13 @@ +</$objtype/mkfile + +BIN=/$objtype/bin/games +TARG=gba +OFILES=\ + cpu.$O\ + mem.$O\ + gba.$O\ + ppu.$O\ + +HFILES=dat.h fns.h + +</sys/src/cmd/mkone diff --git a/sys/src/games/gba/ppu.c b/sys/src/games/gba/ppu.c new file mode 100644 index 000000000..c25b96e01 --- /dev/null +++ b/sys/src/games/gba/ppu.c @@ -0,0 +1,653 @@ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include "dat.h" +#include "fns.h" + +int ppux, ppuy; +uchar pic[240*160*2*3*3]; +u8int bldy, blda, bldb; + +typedef struct bg bg; +struct bg { + uchar n; + uchar depth; + + s32int rpx0, rpy0, rpx, rpy; + s32int sx, sy; + + u16int tx, ty; + u8int tnx, tny; + u16int t; + u8int *chr; + u16int *pal; +}; +static u8int mode=-1; +static bg bgst[4] = {{.n = 0}, {.n = 1}, {.n = 2}, {.n = 3}}; +static u32int pixeldat[2], pixelpri[2]; +static u16int bgmask; +static u8int objwin, objtrans; + +typedef struct sprite sprite; +struct sprite { + uchar w, wb, h; + s16int x; + uchar ysh; + + uchar *base; + u16int *pal; + u16int inc; + + u32int t0; + u16int t1; + uchar depth; + + s32int rx, ry; + s16int dx, dy; +}; +static sprite sprt[128], *sp = sprt; +enum { + SPRROT = 1<<8, + SPRDIS = 1<<9, + SPRDOUB = 1<<9, + SPR8 = 1<<13, + SPRWIDE = 1<<14, + SPRTALL = 1<<15, + SPRHFLIP = 1<<28, + SPRVFLIP = 1<<29, + SPRSIZE0 = 1<<30, + SPRSIZE1 = 1<<31 +}; + +void +pixeldraw(int x, int y, u16int v) +{ + uchar *p; + u16int *q; + union { u16int w; u8int b[2]; } u; + + if(scale == 1){ + p = pic + (x + y * 240) * 2; + p[0] = v; + p[1] = v >> 8; + return; + } + u.b[0] = v; + u.b[1] = v >> 8; + if(scale == 2){ + q = (u16int*)pic + (x + y * 240) * 2; + q[0] = u.w; + q[1] = u.w; + }else{ + q = (u16int*)pic + (x + y * 240) * 3; + q[0] = u.w; + q[1] = u.w; + q[2] = u.w; + } +} + +void +pixel(u16int c, int n, int p) +{ + if(p < pixelpri[0]){ + pixeldat[1] = pixeldat[0]; + pixelpri[1] = pixelpri[0]; + pixelpri[0] = p; + pixeldat[0] = c | n << 16; + }else if(p < pixelpri[1]){ + pixelpri[1] = p; + pixeldat[1] = c | n << 16; + } +} + +void +tile(bg *b) +{ + u16int bgcnt, ta, tx, ty, y, t; + u8int d; + u8int *chr; + + bgcnt = reg[BG0CNT + b->n]; + d = bgcnt >> 7 & 1; + tx = b->tx; + ty = b->ty; + ta = (bgcnt << 3 & 0xf800) + ((tx & 0x1f) << 1) + ((ty & 0x1f) << 6); + switch(bgcnt >> 14){ + case 1: ta += tx << 6 & 0x800; break; + case 2: ta += ty << 6 & 0x800; break; + case 3: ta += tx << 6 & 0x800 | ty << 7 & 0x1000; break; + } + t = vram[ta] | vram[ta+1] << 8; + b->t = t; + chr = vram + (bgcnt << 12 & 0xc000) + ((t & 0x3ff) << 5+d); + y = b->tny; + if((t & 1<<11) != 0) + y ^= 7; + chr = chr + (y << 2+d); + b->chr = chr; + if(d != 0) + b->pal = pram; + else + b->pal = pram + (t >> 8 & 0xf0); +} + +void +bginit(bg *b, int scal, int bit) +{ + u16int cnt, x, y; + u16int *rr; + + cnt = reg[DISPCNT]; + if(scal){ + rr = reg + (b->n - 2 << 3); + if(ppuy == 0){ + b->rpx0 = (s32int)(rr[BG2XL] | rr[BG2XH] << 16) << 4 >> 4; + b->rpy0 = (s32int)(rr[BG2YL] | rr[BG2YH] << 16) << 4 >> 4; + } + b->rpx = b->rpx0; + b->rpy = b->rpy0; + b->rpx0 += (s16int)rr[BG2PB]; + b->rpy0 += (s16int)rr[BG2PD]; + switch(cnt & 7){ + case 3: + case 4: + b->sx = 240 << 8; + b->sy = 160 << 8; + b->depth = (cnt & 7) == 3; + break; + case 5: + b->sx = 160 << 8; + b->sy = 128 << 8; + b->depth = 1; + break; + } + }else{ + rr = reg + (b->n << 1); + x = rr[BG0HOFS] & 0x1ff; + y = (rr[BG0VOFS] & 0x1ff) + ppuy; + b->tx = x >> 3; + b->ty = y >> 3; + b->tnx = x & 7; + b->tny = y & 7; + tile(b); + } +} + +void +bgsinit(void) +{ + mode = reg[DISPCNT] & 7; + switch(mode){ + case 0: + bginit(&bgst[0], 0, 0); + bginit(&bgst[1], 0, 0); + bginit(&bgst[2], 0, 0); + bginit(&bgst[3], 0, 0); + break; + case 1: + bginit(&bgst[0], 0, 0); + bginit(&bgst[1], 0, 0); + bginit(&bgst[2], 1, 0); + break; + case 2: + bginit(&bgst[2], 1, 0); + bginit(&bgst[3], 1, 0); + break; + case 3: + case 4: + case 5: + bginit(&bgst[2], 1, 1); + break; + } +} + +void +bitbg(bg *b) +{ + u16int cnt; + int v; + uchar *p; + u16int *rr; + uchar *base; + + cnt = reg[DISPCNT]; + rr = reg - 8 + (b->n << 3); + if((bgmask & 1<<b->n) == 0) + goto next; + if(b->rpx >= 0 && b->rpy >= 0 && b->rpx <= b->sx && b->rpy <= b->sy){ + base = vram; + if((cnt & FRAME) != 0 && (cnt & 7) != 3) + base += 0xa000; + if(b->depth){ + p = base + 2 * (b->rpx >> 8) + 480 * (b->rpy >> 8); + v = p[0] | p[1] << 8; + }else{ + v = base[(b->rpx >> 8) + 240 * (b->rpy >> 8)]; + if(v != 0) + v = pram[v]; + else + v = -1; + } + }else + v = -1; + if(v >= 0) + pixel(v, b->n, reg[BG0CNT + b->n] & 3); +next: + b->rpx += (s16int) rr[BG2PA]; + b->rpy += (s16int) rr[BG2PC]; +} + +void +rotbg(bg *b) +{ + u16int *rr, ta; + u16int bgcnt; + int row, sz, x, y; + uchar *p, v; + + rr = reg - 8 + (b->n << 3); + if((bgmask & 1<<b->n) == 0) + goto next; + bgcnt = reg[BG0CNT + b->n]; + row = (bgcnt >> 14) + 4; + sz = 1 << 3 + row; + x = b->rpx >> 8; + y = b->rpy >> 8; + if((bgcnt & DISPWRAP) != 0){ + x &= sz - 1; + y &= sz - 1; + }else if((uint)x >= sz || (uint)y >= sz) + goto next; + ta = (bgcnt << 3 & 0xf800) + ((y >> 3) << row) + (x >> 3); + p = vram + (bgcnt << 12 & 0xc000) + (vram[ta] << 6); + p += (x & 7) + ((y & 7) << 3); + if((v = *p) != 0) + pixel(pram[v], b->n, bgcnt & 3); +next: + b->rpx += (s16int) rr[BG2PA]; + b->rpy += (s16int) rr[BG2PC]; +} + +void +txtbg(bg *b) +{ + u16int bgcnt; + u8int x, v; + + bgcnt = reg[BG0CNT + b->n]; + if((bgmask & 1<<b->n) == 0) + goto next; + x = b->tnx; + if((b->t & 1<<10) != 0) + x ^= 7; + if((bgcnt & BG8) != 0) + v = b->chr[x]; + else{ + v = b->chr[x>>1]; + if((x & 1) != 0) + v >>= 4; + else + v &= 0xf; + } + if(v != 0) + pixel(b->pal[v], b->n, bgcnt & 3); +next: + if(++b->tnx == 8){ + b->tnx = 0; + b->tx++; + tile(b); + } +} + +void +bgs(void) +{ + switch(mode){ + case 0: + txtbg(&bgst[0]); + txtbg(&bgst[1]); + txtbg(&bgst[2]); + txtbg(&bgst[3]); + break; + case 1: + txtbg(&bgst[0]); + txtbg(&bgst[1]); + rotbg(&bgst[2]); + break; + case 2: + rotbg(&bgst[2]); + rotbg(&bgst[3]); + break; + case 3: + case 4: + case 5: + bitbg(&bgst[2]); + break; + } +} + +void +sprinit(void) +{ + u16int *p, *pp; + u16int cnt, t1; + u32int t0; + int budg; + uchar ws, h, hb, d, dy, s; + static uchar wss[16] = {3, 4, 5, 6, 4, 5, 5, 6, 3, 3, 4, 5}; + static uchar hss[16] = {3, 4, 5, 6, 3, 3, 4, 5, 4, 5, 5, 6}; + + sp = sprt; + cnt = reg[DISPCNT]; + budg = (cnt & HBLFREE) != 0 ? 954 : 1210; + for(p = oam; p < oam + 512; p += 4){ + t0 = p[0]; + if((t0 & (SPRROT|SPRDIS)) == SPRDIS) + continue; + t0 |= p[1] << 16; + s = t0 >> 30 & 3 | t0 >> 12 & 12; + hb = h = 1 << hss[s]; + dy = ppuy - (u8int) t0; + if((t0 & (SPRROT|SPRDOUB)) == (SPRROT|SPRDOUB)) + hb <<= 1; + if(dy >= hb || (u8int)t0 + hb > 256 && ppuy + 256 - (u8int)t0 >= hb) + continue; + sp->x = (s32int)(t0 << 7) >> 23; + sp->t0 = t0; + ws = wss[s]; + sp->wb = sp->w = 1<<ws; + sp->h = h; + sp->t1 = t1 = p[2]; + sp->base = vram + 0x10000 + ((t1 & 0x3ff) << 5); + d = (t0 & SPR8) != 0; + sp->ysh = (cnt & OBJNOMAT) != 0 ? 2 + d + ws : 10; + if((t0 & SPRROT) != 0){ + if((t0 & SPRDOUB) != 0) + sp->wb <<= 1; + budg -= 10 + sp->w*2; + pp = oam + 3 + (t0 >> 21 & 0x1f0); + sp->dx = pp[0]; + sp->dy = pp[8]; + sp->rx = (dy - hb/2) * (s16int) pp[4] + (sp->w << 7) - sp->dx * sp->wb/2; + sp->ry = (dy - hb/2) * (s16int)pp[12] + (sp->h << 7) - sp->dy * sp->wb/2; + if(sp->x < 0){ + sp->rx -= sp->x * sp->dx; + sp->ry -= sp->x * sp->dy; + } + }else{ + budg -= sp->w; + if((t0 & SPRVFLIP) != 0) + dy = h - 1 - dy; + sp->base += (dy & 7) << 2 + d; + sp->base += dy >> 3 << sp->ysh; + if((t0 & SPRHFLIP) != 0) + sp->base += sp->w - 7 << 2 + d; + sp->inc = (1 << 5 + d) - (1 << 2 + d); + if(sp->x < 0) + if((t0 & SPRHFLIP) != 0){ + sp->base -= ((-sp->x & 7) >> 1 - d) + (-sp->x >> 3 << 5 + d); + if((t0 & SPR8) == 0 && (sp->x & 1) != 0) + sp->base--; + }else + sp->base += ((-sp->x & 7) >> 1 - d) + (-sp->x >> 3 << 5 + d); + } + if((t0 & SPR8) != 0) + sp->pal = pram + 0x100; + else + sp->pal = pram + 0x100 + (t1 >> 8 & 0xf0); + if(budg < 0) + break; + sp++; + } +} + +void +spr(void) +{ + sprite *s; + ushort dx; + u32int t0; + uchar v; + ushort x, y; + u16int c; + int pv, ppri, pri; + uchar d; + uchar *b; + + pv = -1; + ppri = 6;; + for(s = sprt; s < sp; s++){ + dx = ppux - s->x; + if(dx >= s->wb) + continue; + t0 = s->t0; + if((t0 & SPRROT) != 0){ + x = s->rx >> 8; + y = s->ry >> 8; + if(x < s->w && y < s->h){ + b = s->base; + d = (t0 & SPR8) != 0; + b += (y & 7) << 2 + d; + b += y >> 3 << s->ysh; + b += (x & 7) >> 1 - d; + b += x >> 3 << 5 + d; + v = *b; + if(!d) + if((x & 1) != 0) + v >>= 4; + else + v &= 0xf; + }else + v = 0; + s->rx += s->dx; + s->ry += s->dy; + }else if((t0 & SPRHFLIP) != 0){ + if((t0 & SPR8) != 0) + v = *--s->base; + else if((dx & 1) != 0) + v = *s->base & 0x0f; + else + v = *--s->base >> 4; + if((dx & 7) == 7) + s->base -= s->inc; + }else{ + v = *s->base; + if((t0 & SPR8) != 0) + s->base++; + else if((dx & 1) != 0){ + v >>= 4; + s->base++; + }else + v &= 0xf; + if((dx & 7) == 7) + s->base += s->inc; + } + if(v != 0){ + pri = s->t1 >> 10 & 3; + c = s->pal[v]; + switch(s->t0 >> 10 & 3){ + case 1: + c |= 1<<16; + case 0: + if(ppri > pri){ + pv = c; + ppri = pri; + } + break; + case 2: + objwin = 1; + break; + } + } + } + if(pv >= 0){ + pixel(pv, 4, ppri); + if(pv >> 16 != 0) + objtrans = 1; + } +} + +u16int +mix(u16int c1, u16int c2) +{ + u16int r, g, b, eva, evb; + + eva = blda; + evb = bldb; + b = ((c1 & 0x7c00) * eva + (c2 & 0x7c00) * evb) >> 4; + g = ((c1 & 0x03e0) * eva + (c2 & 0x03e0) * evb) >> 4; + r = ((c1 & 0x001f) * eva + (c2 & 0x001f) * evb) >> 4; + if(b > 0x7c00) b = 0x7c00; + if(g > 0x03e0) g = 0x03e0; + if(r > 0x001f) r = 0x001f; + return b & 0x7c00 | g & 0x03e0 | r; +} + +u16int +brighten(u16int c1) +{ + u16int r, g, b, y; + + y = bldy; + b = c1 & 0x7c00; + b = b + (0x7c00 - b) * y / 16; + g = c1 & 0x03e0; + g = g + (0x03e0 - g) * y / 16; + r = c1 & 0x001f; + r = r + (0x001f - r) * y / 16; + if(b > 0x7c00) b = 0x7c00; + if(g > 0x03e0) g = 0x03e0; + if(r > 0x001f) r = 0x001f; + return b & 0x7c00 | g & 0x03e0 | r; +} + +u16int +darken(u16int c1) +{ + u16int r, g, b, y; + + y = 16 - bldy; + b = c1 & 0x7c00; + b = b * y / 16; + g = c1 & 0x03e0; + g = g * y / 16; + r = c1 & 0x001f; + r = r * y / 16; + return b & 0x7c00 | g & 0x03e0 | r; +} + +void +windows(void) +{ + u16int dispcnt; + u16int v, h; + + dispcnt = reg[DISPCNT]; + bgmask = dispcnt >> 8 | 1<<5; + if((dispcnt >> 13) != 0){ + if((dispcnt & 1<<13) != 0){ + v = reg[WIN0V]; + h = reg[WIN0H]; + if(ppuy < (u8int)v && ppuy >= v >> 8 && + ppux < (u8int)h && ppux >= h >> 8){ + bgmask &= reg[WININ]; + goto windone; + } + } + if((dispcnt & 1<<14) != 0){ + v = reg[WIN1V]; + h = reg[WIN1H]; + if(ppuy < (u8int)v && ppuy >= v >> 8 && + ppux < (u8int)h && ppux >= h >> 8){ + bgmask &= reg[WININ] >> 8; + goto windone; + } + } + if((dispcnt & 1<<15) != 0 && objwin != 0){ + bgmask &= reg[WINOUT] >> 8; + goto windone; + } + bgmask &= reg[WINOUT]; + } +windone: + if(pixelpri[0] != 8 && (bgmask & 1<<4) == 0){ + pixelpri[0] = 8; + pixeldat[0] = pram[0] | 5 << 16; + } +} + +void +colormath(void) +{ + u8int src0; + u16int bldcnt; + + if((bgmask & 1<<5) == 0) + return; + bldcnt = reg[BLDCNT]; + src0 = pixeldat[0] >> 16; + if(objtrans && src0 == 4) + goto alpha; + if((bldcnt & 3<<6) == 0 || (bldcnt & 1<<src0) == 0) + return; + switch(bldcnt >> 6 & 3){ + case 1: + alpha: + if((bldcnt & 1<<8+(pixeldat[1]>>16)) == 0) + return; + pixeldat[0] = mix(pixeldat[0], pixeldat[1]); + break; + case 2: + pixeldat[0] = brighten(pixeldat[0]); + break; + case 3: + pixeldat[0] = darken(pixeldat[0]); + break; + } +} + +void +ppustep(void) +{ + u16int stat; + u16int cnt; + + stat = reg[DISPSTAT]; + cnt = reg[DISPCNT]; + if(ppuy < 160 && ppux < 240) + if((cnt & FBLANK) == 0){ + objwin = 0; + objtrans = 0; + pixelpri[0] = 8; + pixeldat[0] = pram[0] | 5 << 16; + if((cnt & 1<<12) != 0) + spr(); + windows(); + bgs(); + colormath(); + pixeldraw(ppux, ppuy, pixeldat[0]); + }else + pixeldraw(ppux, ppuy, 0xffff); + if(ppux == 240 && ppuy < 160){ + if((stat & IRQHBLEN) != 0) + setif(IRQHBL); + dmastart(DMAHBL); + } + if(++ppux >= 308){ + ppux = 0; + if(++ppuy >= 228){ + ppuy = 0; + flush(); + } + if((stat & IRQVCTREN) != 0 && ppuy == stat >> 8) + setif(IRQVCTR); + if(ppuy < 160){ + bgsinit(); + sprinit(); + }else if(ppuy == 160){ + if((stat & IRQVBLEN) != 0) + setif(IRQVBL); + dmastart(DMAVBL); + } + } +} diff --git a/sys/src/games/gba/test.c b/sys/src/games/gba/test.c new file mode 100644 index 000000000..d08676c6f --- /dev/null +++ b/sys/src/games/gba/test.c @@ -0,0 +1,15 @@ +#include <u.h> +#include <libc.h> + +void main() +{ + int fd; + Dir *d; + int n, i; + + fd = open(".", OREAD); + n = dirreadall(fd, &d); + for(i = 0; i < n; i++) + if(d[i].name[0] == '\xef') + remove(d[i].name); +}
\ No newline at end of file |