games/gba: slowly working (no pun intended)

This commit is contained in:
aiju 2014-09-27 20:50:20 +02:00
parent 709e78b9f9
commit 77f3fa19de
8 changed files with 3181 additions and 0 deletions

1269
sys/src/games/gba/cpu.c Normal file

File diff suppressed because it is too large Load diff

114
sys/src/games/gba/dat.h Normal file
View file

@ -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,
};

13
sys/src/games/gba/fns.h Normal file
View file

@ -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);

439
sys/src/games/gba/gba.c Normal file
View file

@ -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);
}
}

665
sys/src/games/gba/mem.c Normal file
View file

@ -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 = &reg[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;
}
}

13
sys/src/games/gba/mkfile Normal file
View file

@ -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

653
sys/src/games/gba/ppu.c Normal file
View file

@ -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);
}
}
}

15
sys/src/games/gba/test.c Normal file
View file

@ -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);
}