diff options
author | aiju <devnull@localhost> | 2014-10-03 16:52:56 +0200 |
---|---|---|
committer | aiju <devnull@localhost> | 2014-10-03 16:52:56 +0200 |
commit | 5de71b116a9daf647952a84006cc481a6ce259bd (patch) | |
tree | a359afe8a886331f7a8598253014abcd4c62757b | |
parent | 02ea56dbdad9e2b3145ac57ee1765aa74d5dd4f8 (diff) | |
download | plan9front-5de71b116a9daf647952a84006cc481a6ce259bd.tar.xz |
games/gba: new faster ppu code, audio support
-rw-r--r-- | sys/src/games/gba/apu.c | 370 | ||||
-rw-r--r-- | sys/src/games/gba/dat.h | 34 | ||||
-rw-r--r-- | sys/src/games/gba/ev.c | 211 | ||||
-rw-r--r-- | sys/src/games/gba/fns.h | 12 | ||||
-rw-r--r-- | sys/src/games/gba/gba.c | 21 | ||||
-rw-r--r-- | sys/src/games/gba/mem.c | 98 | ||||
-rw-r--r-- | sys/src/games/gba/mkfile | 2 | ||||
-rw-r--r-- | sys/src/games/gba/ppu.c | 990 |
8 files changed, 1230 insertions, 508 deletions
diff --git a/sys/src/games/gba/apu.c b/sys/src/games/gba/apu.c new file mode 100644 index 000000000..39a0972ff --- /dev/null +++ b/sys/src/games/gba/apu.c @@ -0,0 +1,370 @@ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include "dat.h" +#include "fns.h" + +Event evsamp; +int srate, sratediv; +s16int sbuf[2*4000], *sbufp, bias; +enum { + Freq = 44100, +}; +static int stime; +s8int snddma[2]; +static int fd; + +u16int envctr, envrel, envmod; +u8int sweepen, sweepctr; +u16int sweepfreq; +typedef struct chan chan; +struct chan { + u8int n, ectr; + u16int len; + u16int *env, *freq; + u16int fctr, fthr; + u32int finc; + u8int vol; +}; +u8int wave[64], wpos, wbank; +u16int lfsr; + +chan sndch[4] = { + { + .n = 0, + .env = reg + 0x62/2, + .freq = reg + 0x64/2, + }, + { + .n = 1, + .env = reg + 0x68/2, + .freq = reg + 0x6c/2, + }, + { + .n = 2, + }, + { + .n = 3, + .env = reg + 0x78/2, + .freq = reg + 0x7c/2, + } +}; + +void +rate(int i, u16int v) +{ + switch(i){ + case 0: case 1: + sndch[i].finc = 131072ULL * 65536 / (srate * (2048 - (v & 0x7ff))); + break; + case 2: + sndch[2].finc = 2097152ULL * 65536 / (srate * (2048 - (v & 0x7ff))); + break; + case 3: + sndch[3].finc = 524288ULL * 65536 / srate; + if((v & 7) != 0) + sndch[3].finc /= v & 7; + else + sndch[3].finc <<= 1; + sndch[3].finc >>= (v >> 4 & 15) + 1; + } +} + +void +env(chan *c) +{ + if((envmod & 1) == 0 && c->len > 0 && (*c->freq & 1<<14) != 0) + --c->len; + if(c->len == 0){ + c->vol = 0; + return; + } + if((envmod & 7) != 7 || c->ectr == 0 || --c->ectr != 0) + return; + c->ectr = *c->env >> 8 & 7; + if((*c->env & 1<<11) != 0){ + if(c->vol < 15) + c->vol++; + }else + if(c->vol > 0) + c->vol--; +} + +s8int +wavesamp(void) +{ + s8int x; + u16int vol, cnt; + int v; + + sndch[2].fctr = v = sndch[2].fctr + sndch[2].finc; + if(sndch[2].len == 0 || (reg[0x70/2] & 1<<7) == 0) + return 0; + vol = reg[0x72/2]; + cnt = reg[0x70/2]; + for(;;){ + x = wave[wbank ^ wpos]; + v -= 0x10000; + if(v < 0) + break; + wpos++; + if((cnt & 1<<5) != 0) + wpos &= 63; + else + wpos &= 31; + } + if((vol & 1<<15) != 0) + x = (x >> 2) + (x >> 3); + else if((vol & 3<<14) == 0) + x = 0; + else + x = x >> (vol >> 14 & 3); + return x; +} + +s8int +lfsrsamp(void) +{ + int v; + u16int l; + + sndch[3].fctr = v = sndch[3].fctr + sndch[3].finc; + for(;;){ + l = lfsr; + v -= 0x10000; + if(v < 0) + break; + lfsr >>= 1; + if(((l ^ lfsr) & 1) != 0) + if((reg[0x7c/2] & 1<<3) != 0) + lfsr |= 0x40; + else + lfsr |= 0x4000; + } + if((l & 1) != 0) + return -sndch[3].vol; + else + return sndch[3].vol; +} + +void +sweep(int wb) +{ + u16int fr; + int d; + u16int cnt; + + cnt = reg[0x60/2]; + d = sweepfreq >> (cnt & 7); + if((cnt & 1<<3) != 0) + d = -d; + fr = sweepfreq + d; + print("%d %d %d\n", d, sweepfreq, fr); + if(fr > 2047){ + sndch[0].len = 0; + sndch[0].vol = 0; + sweepen = 0; + }else if(wb){ + sweepfreq = fr; + reg[0x64/2] = reg[0x64/2] & 0xfc00 | fr; + rate(0, fr); + sweep(0); + } +} + +void +sndstart(chan *c, u16int v) +{ + u16int cnt; + + c->vol = *c->env >> 12; + c->ectr = *c->env >> 8 & 7; + if(c->len == 0) + c->len = 64; + if(c == sndch){ + cnt = reg[0x60/2]; + sweepen = (cnt & 0x07) != 0 && (cnt & 0x70) != 0; + sweepctr = cnt >> 4 & 7; + sweepfreq = v & 0x7ff; + if((cnt & 0x07) != 0) + sweep(0); + } +} + +void +sampletick(void *) +{ + u16int cntl, cnth; + s16int ch[6]; + s16int s[2]; + int i; + + addevent(&evsamp, sratediv + evsamp.time); + + if(--envctr == 0){ + envctr = envrel; + env(&sndch[0]); + env(&sndch[1]); + if((envmod & 1) == 0 && sndch[2].len > 0 && (reg[0x74/2] & 1<<14) != 0) + sndch[2].len--; + env(&sndch[3]); + if((envmod & 3) == 2 && sweepen && --sweepctr == 0){ + sweepctr = reg[0x60/2] >> 4 & 7; + sweep(1); + } + envmod++; + } + + sndch[0].fctr += sndch[0].finc; + if(sndch[0].fctr >= sndch[0].fthr) + ch[0] = sndch[0].vol; + else + ch[0] = -sndch[0].vol; + sndch[1].fctr += sndch[1].finc; + if(sndch[1].fctr >= sndch[1].fthr) + ch[1] = sndch[1].vol; + else + ch[1] = -sndch[1].vol; + ch[2] = wavesamp(); + ch[3] = lfsrsamp(); + + cntl = reg[SOUNDCNTL]; + cnth = reg[SOUNDCNTH]; + for(i = 0; i < 4; i++) + ch[i] = ch[i] >> (cnth & 3); + ch[5] = snddma[0] << 1 + (cnth >> 2 & 1); + ch[6] = snddma[1] << 1 + (cnth >> 3 & 1); + + s[0] = 0; + s[1] = 0; + for(i = 0; i < 4; i++){ + if((cntl & 1<<8<<i) != 0) + s[1] += ch[i] * (1 + (cntl & 7)); + if((cntl & 1<<12<<i) != 0) + s[0] += ch[i] * (1 + (cntl >> 4 & 7)); + } + for(i = 5; i < 6; i++){ + if((cnth & 1<<3<<i) != 0) + s[1] += ch[i]; + if((cnth & 1<<4<<i) != 0) + s[0] += ch[i]; + } + s[0] += bias; + s[1] += bias; + if(s[0] < -0x200) s[0] = -0x200; + else if(s[0] > 0x1ff) s[0] = 0x1ff; + if(s[1] < -0x200) s[1] = -0x200; + else if(s[1] > 0x1ff) s[1] = 0x1ff; + + stime -= Freq; + while(stime < 0){ + if(sbufp < sbuf + nelem(sbuf)){ + sbufp[0] = s[0] << 6; + sbufp[1] = s[1] << 6; + sbufp += 2; + } + stime += srate; + } +} + + +void +sndwrite(u16int a, u16int v) +{ + int sh, p, i; + static u16int thr[4] = {0x2000, 0x4000, 0x8000, 0xC000}; + + switch(a){ + case 0x62: + sndch[0].fthr = thr[v >> 6 & 3]; + sndch[0].len = 64 - (v & 63); + break; + case 0x64: + rate(0, v); + if((v & 1<<15) != 0) + sndstart(&sndch[0], v); + break; + case 0x68: + sndch[1].fthr = thr[v >> 6 & 3]; + break; + case 0x6c: + rate(1, v); + if((v & 1<<15) != 0) + sndstart(&sndch[1], v); + break; + case 0x70: + wbank = v >> 1 & 32; + break; + case 0x72: + sndch[2].len = 256 - (v & 0xff); + break; + case 0x74: + rate(2, v); + if((v & 1<<15) != 0 && sndch[2].len == 0) + sndch[2].len = 256; + break; + case 0x7c: + rate(3, v); + if((v & 1<<15) != 0){ + if((v & 1<<3) != 0) + lfsr = 0x7f; + else + lfsr = 0x7fff; + sndstart(&sndch[3], v); + } + break; + case SOUNDBIAS*2: + sh = 9 - (v >> 14 & 3); + if(sratediv != 1<<sh){ + srate = 1 << 24 - sh; + sratediv = 1 << sh; + envrel = srate / 512; + rate(0, reg[0x64/2]); + rate(1, reg[0x6c/2]); + rate(2, reg[0x74/2]); + rate(3, reg[0x7c/2]); + } + bias = (v & 0x3ff) - 0x200; + break; + case 0x90: case 0x92: case 0x94: case 0x96: + case 0x98: case 0x9a: case 0x9c: case 0x9e: + p = ~reg[0x70/2] >> 1 & 32; + for(i = a - 0x90; i < a - 0x90 + 2; i++){ + wave[(wpos + 2 * i) & 31 + p] = v >> 4 & 0xf; + wave[(wpos + 2 * i + 1) & 31 + p] = v & 0xf; + v >>= 8; + } + break; + } +} + +void +audioinit(void) +{ + fd = open("/dev/audio", OWRITE); + if(fd < 0) + sysfatal("open: %r"); + sbufp = sbuf; + sndwrite(SOUNDBIAS*2, 0x200); + evsamp.f = sampletick; + addevent(&evsamp, sratediv); +} + +int +audioout(void) +{ + int rc; + static int cl; + + if(sbufp == nil) + return -1; + if(sbufp == sbuf) + return 0; + cl = clock; + rc = write(fd, sbuf, (sbufp - sbuf) * 2); + if(rc > 0) + sbufp -= (rc+1)/2; + if(sbufp < sbuf) + sbufp = sbuf; + return 0; +} diff --git a/sys/src/games/gba/dat.h b/sys/src/games/gba/dat.h index 7e673cf8c..2e180d68b 100644 --- a/sys/src/games/gba/dat.h +++ b/sys/src/games/gba/dat.h @@ -15,11 +15,20 @@ extern u16int reg[]; extern uchar *rom, *back; extern int nrom, nback, backup; -extern int ppux, ppuy; -extern u8int bldy, blda, bldb; +extern int hblank, ppuy; +extern int clock; extern int scale; +typedef struct Event Event; +struct Event { + int time; + void (*f)(void *); + Event *next; + void *aux; +}; +extern Event *elist; + enum { DISPCNT = 0x0/2, DISPSTAT = 0x4/2, @@ -35,6 +44,10 @@ enum { BG2XH = 0x2a/2, BG2YL = 0x2c/2, BG2YH = 0x2e/2, + BG3XL = 0x38/2, + BG3XH = 0x3a/2, + BG3YL = 0x3c/2, + BG3YH = 0x3e/2, WIN0H = 0x40/2, WIN1H = 0x42/2, @@ -42,15 +55,24 @@ enum { WIN1V = 0x46/2, WININ = 0x48/2, WINOUT = 0x4a/2, + MOSAIC = 0x4c/2, BLDCNT = 0x50/2, BLDALPHA = 0x52/2, BLDY = 0x54/2, + SOUNDCNTL = 0x80/2, + SOUNDCNTH = 0x82/2, + SOUNDBIAS = 0x88/2, + + FIFOAH = 0xa2/2, + FIFOBH = 0xa6/2, + DMA0CNTH = 0xba/2, DMA1CNTH = 0xc6/2, DMA2CNTH = 0xd2/2, DMA3CNTH = 0xde/2, + TM0CNTH = 0x102/2, KEYCNT = 0x132/2, IE = 0x200/2, @@ -73,9 +95,16 @@ enum { IRQVCTREN = 1<<5, /* BGnCNT */ + BGMOSAIC = 1<<6, BG8 = 1<<7, DISPWRAP = 1<<13, + /* TIMERnCNTH */ + PRESC = 3, + COUNTUP = 1<<2, + TIMERIRQ = 1<<6, + TIMERON = 1<<7, + /* DMAnCNTH */ DMADCNT = 5, DMASCNT = 7, @@ -111,4 +140,5 @@ enum { KB = 1024, BACKTYPELEN = 64, + HZ = 16777216, }; diff --git a/sys/src/games/gba/ev.c b/sys/src/games/gba/ev.c new file mode 100644 index 000000000..d4110a30a --- /dev/null +++ b/sys/src/games/gba/ev.c @@ -0,0 +1,211 @@ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include "dat.h" +#include "fns.h" + +typedef struct { + u16int *cnt; + Event; + u16int val; + int clock; + u8int i, sh, snd; +} Timer; + +typedef struct fifo fifo; +struct fifo { + u32int d[8]; + u8int head, level, headpos; +}; +fifo sndfifo[2]; + +Event *elist; +Timer timers[4]; +Event evhblank; + +void +addevent(Event *ev, int time) +{ + Event **p, *e; + int t; + + t = time; + for(p = &elist; (e = *p) != nil; p = &e->next){ + if(t < e->time){ + e->time -= t; + break; + } + t -= e->time; + } + ev->next = e; + ev->time = t; + *p = ev; +} + +void +delevent(Event *ev) +{ + Event **p, *e; + + for(p = &elist; (e = *p) != nil; p = &e->next) + if(e == ev){ + *p = e->next; + if(e->next != nil) + e->next->time += e->time; + return; + } +} + +void +popevent(void) +{ + Event *e; + int t; + + do{ + e = elist; + t = e->time; + elist = e->next; + e->f(e->aux); + }while((elist->time += t) <= 0); +} + + +void +fifoput(int i, u32int s) +{ + fifo *f; + + f = sndfifo + i; + if(f->level < 8) + f->d[(f->head + f->level++) & 7] = s; +} + +void +fifotimer(int b, int n) +{ + fifo *f; + int i, j; + extern s8int snddma[2]; + + for(i = 0; i < 2; i++){ + if((b & 1<<i) == 0) + continue; + f = &sndfifo[i]; + for(j = 0; j < n && f->level > 0; j++){ + snddma[i] = f->d[f->head] & 0xff; + f->d[f->head] >>= 8; + if(++f->headpos == 4){ + f->head = (f->head + 1) & 7; + f->level--; + f->headpos = 0; + } + } + if(f->level <= 4) + dmastart(DMASOUND); + } +} + +void +soundcnth(u16int v) +{ + timers[0].snd = 0; + timers[1].snd = 0; + if((v & 3<<8) != 0) + timers[(v >> 10) & 1].snd |= 1; + if((v & 3<<12) != 0) + timers[(v >> 14) & 1].snd |= 2; + if((v & 1<<11) != 0){ + sndfifo[0].level = 0; + sndfifo[0].head = 0; + sndfifo[0].headpos = 0; + } + if((v & 1<<15) != 0){ + sndfifo[1].level = 0; + sndfifo[1].head = 0; + sndfifo[1].headpos = 0; + } +} + +u16int +timerget(int i) +{ + Timer *t; + + t = &timers[i]; + if((*t->cnt & (COUNTUP|TIMERON)) != TIMERON) + return t->val; + return t->val + (clock - t->clock >> t->sh); +} + +void +timerset(int i, u16int nc) +{ + u32int v; + u16int oc; + Timer *t; + + t = &timers[i]; + oc = *t->cnt; + if((oc & (PRESC|COUNTUP|TIMERON)) == (nc & (PRESC|COUNTUP|TIMERON))) + return; + if((oc & (COUNTUP|TIMERON)) == TIMERON){ + v = t->val + (clock - t->clock >> t->sh); + delevent(t); + }else + v = t->val; + if((oc & TIMERON) == 0 && (nc & TIMERON) != 0) + v = t->cnt[-1]; + if((nc & 3) != 0) + t->sh = 4 + (nc & 3) * 2; + else + t->sh = 0; + t->val = v; + t->clock = clock & -(1 << t->sh); + if((nc & (COUNTUP|TIMERON)) == TIMERON) + addevent(t, (0x10000 - t->val << t->sh) + (-clock & (1 << t->sh) - 1)); +} + +void +timertick(void *aux) +{ + Timer *t; + u32int v; + int to; + + t = aux; + t->clock = clock + t->time & -(1 << t->sh); + t->val = -t->time >> t->sh; + do{ + to = 0; + do{ + t->val = v = t->val + t->cnt[-1]; + to++; + }while(v >= 0x10000); + if(t == aux) + addevent(t, (0x10000 - t->val << t->sh) + (-clock & (1 << t->sh) - 1)); + if((*t->cnt & TIMERIRQ) != 0) + setif(IRQTIM0 << t->i); + if(t->snd) + fifotimer(t->snd, to); + if(++t >= timers + 4 || (*t->cnt & (COUNTUP | TIMERON)) != (COUNTUP|TIMERON)) + break; + t->val = v = t->val + to; + }while(v >= 0x10000); +} + +void +eventinit(void) +{ + int i; + extern void hblanktick(void *); + + for(i = 0; i < 4; i++){ + timers[i].f = timertick; + timers[i].aux = &timers[i]; + timers[i].i = i; + timers[i].cnt = ®[TM0CNTH + i * 2]; + } + evhblank.f = hblanktick; + addevent(&evhblank, 240*4); +} diff --git a/sys/src/games/gba/fns.h b/sys/src/games/gba/fns.h index 2074228ca..84baeb5ee 100644 --- a/sys/src/games/gba/fns.h +++ b/sys/src/games/gba/fns.h @@ -11,3 +11,15 @@ int dmastep(void); void dmastart(int); void flushback(void); void writeback(void); +void eventinit(void); +void popevent(void); +u16int timerget(int); +void timerset(int, u16int); +void addevent(Event *, int); +void ppuwrite(u16int, u16int); +void fifoput(int, u32int); +void soundcnth(u16int); +void soundbias(u16int); +void audioinit(void); +int audioout(void); +void sndwrite(u16int, u16int); diff --git a/sys/src/games/gba/gba.c b/sys/src/games/gba/gba.c index d7b889ff9..94612c06f 100644 --- a/sys/src/games/gba/gba.c +++ b/sys/src/games/gba/gba.c @@ -15,11 +15,10 @@ Mousectl *mc; int keys, paused, framestep, backup; QLock pauselock; int savefd, saveframes; +int clock; char *biosfile = "/sys/games/lib/gbabios.bin"; -int ppuclock; - void * emalloc(ulong sz) { @@ -347,6 +346,7 @@ flush(void) flushimage(display, 1); if(profile) timing(); + audioout(); if(framestep){ paused = 1; qlock(&pauselock); @@ -370,7 +370,7 @@ flush(void) void usage(void) { - fprint(2, "usage: %s [-23T] [-s savetype] [-b biosfile] rom\n", argv0); + fprint(2, "usage: %s [-23aT] [-s savetype] [-b biosfile] rom\n", argv0); exits("usage"); } @@ -388,6 +388,9 @@ threadmain(int argc, char **argv) case '3': scale = 3; break; + case 'a': + audioinit(); + break; case 's': s = EARGF(usage()); backup = parsetype(s, &nback); @@ -416,7 +419,8 @@ threadmain(int argc, char **argv) sysfatal("initmouse: %r"); proccreate(keyproc, nil, mainstacksize); screeninit(); - + + eventinit(); memreset(); reset(); for(;;){ @@ -430,11 +434,8 @@ threadmain(int argc, char **argv) t = 8; else t = step(); - ppuclock += t; - while(ppuclock >= 4){ - ppustep(); - ppuclock -= 4; - } - timerstep(t); + clock += t; + if((elist->time -= t) <= 0) + popevent(); } } diff --git a/sys/src/games/gba/mem.c b/sys/src/games/gba/mem.c index dc09aaf91..be6203678 100644 --- a/sys/src/games/gba/mem.c +++ b/sys/src/games/gba/mem.c @@ -10,8 +10,6 @@ u16int pram[512], oam[512]; uchar *rom, *back; int nrom, nback; u16int reg[512]; -u16int tim[4]; -int timerclock; int dmaact; enum { DMASRC, @@ -100,7 +98,7 @@ regread(u32int a) if(ppuy >= 160 && ppuy != 227) v |= 1; - if(ppux >= 240) + if(hblank) v |= 2; if(ppuy == v >> 8) v |= 4; @@ -108,7 +106,7 @@ regread(u32int a) case 0x006: return ppuy; case 0x100: case 0x104: case 0x108: case 0x10c: - return tim[(a - 0x100) / 4]; + return timerget((a - 0x100) / 4); case 0x130: return keys ^ 0x3ff; default: @@ -122,7 +120,11 @@ regwrite16(u32int a, u16int v) u16int *p; int i; static u8int ws0[4] = {5,4,3,9}; - + + if(a < 0x56) + ppuwrite(a, v); + else if(a < 0xa0) + sndwrite(a, v); p = ®[a/2]; switch(a){ case IF*2: @@ -133,19 +135,6 @@ regwrite16(u32int a, u16int v) *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: i = (a - DMA0CNTH*2) / 12; if((v & DMAEN) != 0){ @@ -159,9 +148,14 @@ regwrite16(u32int a, u16int v) }else dmaact &= ~1<<i; break; + case SOUNDCNTH*2: + soundcnth(v); + break; + case FIFOAH*2: case FIFOBH*2: + fifoput(a >> 2 & 1, p[-1] | v << 16); + break; case 0x102: case 0x106: case 0x10a: case 0x10e: - if((*p & 1<<7) == 0 && (v & 1<<7) != 0) - tim[(a-0x102)/4] = p[-1]; + timerset((a - 0x102) / 4, v); break; case WAITCNT*2: waitst[3] = waitst[7] = ws0[v & 3]; @@ -196,7 +190,7 @@ regwrite(u32int a, u32int v, int n) w = w & 0xff00 | (u8int)v; else w = w & 0xff | v << 8; - regwrite16(a, w); + regwrite16(a & ~1, w); break; default: regwrite16(a, v); @@ -242,6 +236,11 @@ memread(u32int a, int n, int seq) cyc++; if(n == 4) return regread(b) | regread(b+2) << 16; + else if(n == 1) + if((b & 1) != 0) + return regread(b) >> 8; + else + return regread(b) & 0xff; return regread(b); case 5: b = a & sizeof(pram) - 1; @@ -353,6 +352,7 @@ void memreset(void) { reg[0x88/2] = 0x200; + reg[BG2PA] = reg[BG2PD] = 0x100; if(backup == EEPROM) if(nrom <= 16*KB*KB) eepstart = 0x1000000; @@ -362,50 +362,6 @@ memreset(void) eepstart = -1; } -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) { @@ -413,7 +369,7 @@ dmastep(void) u16int *cntp, cnt; u32int *dr; u32int v; - int sz; + int sz, snd; cyc = 0; for(i = 0; i < 4; i++) @@ -425,6 +381,9 @@ dmastep(void) cntp = reg + DMA0CNTH + i * 6; cnt = *cntp; dr = dmar + 4 * i; + snd = (cnt >> DMAWHEN & 3) == 3 && (i == 1 || i == 2); + if(snd) + cnt = cnt & ~(3 << DMADCNT) | DMAFIX << DMADCNT | DMAWIDE; sz = (cnt & DMAWIDE) != 0 ? 4 : 2; if(i == 0) @@ -468,15 +427,18 @@ dmastart(int cond) u16int *cntp, cnt, c; cntp = reg + DMA0CNTH; - for(i = 0; i < 3; i++, cntp += 6){ + for(i = 0; i < 4; i++, cntp += 6){ cnt = *cntp; if((cnt & DMAEN) == 0) continue; c = cnt >> DMAWHEN & 3; if(c == 3) c += (i + 1) / 2; - if(c == cond) + if(c == cond){ dmaact |= 1<<i; + if(c == DMASOUND) + dmar[i * 4 + DMACNT] = 4; + } } } diff --git a/sys/src/games/gba/mkfile b/sys/src/games/gba/mkfile index 19e77437d..73855b71e 100644 --- a/sys/src/games/gba/mkfile +++ b/sys/src/games/gba/mkfile @@ -7,6 +7,8 @@ OFILES=\ mem.$O\ gba.$O\ ppu.$O\ + ev.$O\ + apu.$O\ HFILES=dat.h fns.h diff --git a/sys/src/games/gba/ppu.c b/sys/src/games/gba/ppu.c index 3eb829c3b..dee1c6a80 100644 --- a/sys/src/games/gba/ppu.c +++ b/sys/src/games/gba/ppu.c @@ -4,29 +4,28 @@ #include "dat.h" #include "fns.h" -int ppux, ppuy; -uchar pic[240*160*2*3*3]; +int hblank, ppuy; u8int bldy, blda, bldb; +u32int hblclock; +int ppux0; +u32int pixcol[480]; +u8int pixpri[480]; +u8int pixwin[240]; +uchar pic[240*160*3*2]; +int objalpha; typedef struct bg bg; struct bg { uchar n; - uchar depth; - - s32int rpx0, rpy0, rpx, rpy; - s32int sx, sy; - + s32int rpx0, rpy0, rpx1, rpy1, rpx, rpy; u16int tx, ty; u8int tnx, tny; - u16int t; - u8int *chr; - u16int *pal; + + u8int mosaic, mctr, lasti; + u32int curc; + u8int curpri; }; -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 { @@ -44,140 +43,296 @@ struct sprite { s32int rx, ry; s16int dx, dy; + + u8int mctr, mcol; }; static sprite sprt[128], *sp = sprt; enum { SPRROT = 1<<8, SPRDIS = 1<<9, SPRDOUB = 1<<9, + SPRMOSA = 1<<12, SPR8 = 1<<13, SPRWIDE = 1<<14, SPRTALL = 1<<15, SPRHFLIP = 1<<28, SPRVFLIP = 1<<29, SPRSIZE0 = 1<<30, - SPRSIZE1 = 1<<31 + SPRSIZE1 = 1<<31, + + NOWIN = 0, + OBJWIN = 1, + WIN2 = 2, + WIN1 = 4, + + OBJALPHA = 1<<16, + SRCOBJ = 4<<17, + SRCBACK = 5<<17, + + VACANT = 0x10, + BACKDROP = 8, }; +#define SRCBG(n) ((n)<<17) void -pixeldraw(int x, int y, u16int v) +sprinit(void) { - uchar *p; - u16int *q; - union { u16int w; u8int b[2]; } u; + 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}; - 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; + 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; + if((t0 & SPRMOSA) != 0){ + dy = dy - dy % ((reg[MOSAIC] >> 12 & 15) + 1); + sp->mctr = 0; + } + 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 -pixel(u16int c, int n, int p) +spr(int x1) { - 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; + int x0, i, dx, sx0, sx1; + u8int pri, v, d, *b; + u16int x, y; + u32int c, t0; + sprite *s; + + x0 = ppux0; + for(s = sprt; s < sp; s++){ + if(s->x >= x1 || s->x + s->wb <= x0) + continue; + t0 = s->t0; + pri = s->t1 >> 10 & 3; + sx0 = s->x >= x0 ? s->x : x0; + sx1 = s->x + s->wb; + if(x1 < sx1) + sx1 = x1; + dx = sx0 - s->x; + for(i = sx0; i < sx1; i++, dx++){ + if((t0 & SPRROT) != 0){ + d = (t0 & SPR8) != 0; + x = s->rx >> 8; + y = s->ry >> 8; + s->rx += s->dx; + s->ry += s->dy; + if(x < s->w && y < s->h){ + b = s->base; + 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; + }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((t0 & SPRMOSA) != 0) + if(s->mctr == 0){ + s->mctr = reg[MOSAIC] >> 8 & 15; + s->mcol = v; + }else{ + --s->mctr; + v = s->mcol; + } + if(v != 0){ + c = s->pal[v] | SRCOBJ; + switch(t0 >> 10 & 3){ + case 1: + c |= OBJALPHA; + objalpha++; + case 0: + if(pri < pixpri[i]){ + pixcol[i] = c; + pixpri[i] = pri; + } + break; + case 2: + if((reg[DISPCNT] & 1<<15) != 0) + pixwin[i] |= OBJWIN; + break; + } + } + } } } void -tile(bg *b) +bgpixel(bg *b, int i, u32int c, int pri) { - 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; + u8int *p; + u32int *q; + int j; + + if(b != nil){ + c |= SRCBG(b->n); + if(b->mosaic){ + for(j = (u8int)(b->lasti+1); j <= i; j++){ + if(b->mctr == 0){ + if(j == i){ + b->curc = c; + b->curpri = pri; + }else + b->curpri = VACANT; + b->mctr = reg[MOSAIC] & 15; + }else + b->mctr--; + if(b->curpri != VACANT && (pixwin[j] & 1<<b->n) == 0) + bgpixel(nil, j, b->curc, b->curpri); + } + b->lasti = i; + return; + } + } + p = pixpri + i; + q = pixcol + i; + if(pri < p[0]){ + p[240] = p[0]; + p[0] = pri; + q[240] = q[0]; + q[0] = c; + }else if(pri < p[240]){ + p[240] = pri; + q[240] = c; } - 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) { - u16int cnt, x, y; + u16int x, y; u16int *rr; - - cnt = reg[DISPCNT]; + int msz; + + b->mosaic = (reg[BG0CNT + b->n] & BGMOSAIC) != 0; + if(b->mosaic){ + b->mctr = 0; + b->lasti = -1; + } 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; + if(!b->mosaic || ppuy % ((reg[MOSAIC] >> 4 & 15) + 1) == 0){ + b->rpx1 = b->rpx0; + b->rpy1 = b->rpy0; + } + b->rpx = b->rpx1; + b->rpy = b->rpy1; 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; + y = ppuy; + if(b->mosaic){ + msz = (reg[MOSAIC] >> 4 & 15) + 1; + y = y - y % msz; + } + y += (rr[BG0VOFS] & 0x1ff); 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){ + switch(reg[DISPCNT] & 7){ case 0: bginit(&bgst[0], 0, 0); bginit(&bgst[1], 0, 0); @@ -202,289 +357,200 @@ bgsinit(void) } void -bitbg(bg *b) +bitbg(bg *b, int x1) { - u16int cnt; - int v; - uchar *p; - u16int *rr; - uchar *base; + u8int *base, *p, pri, d; + u16int cnt, *rr, sx, sy; + int i, v; 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; + if((cnt & 1<<8 + b->n) == 0) + return; + rr = reg + (b->n - 2 << 3); + if((cnt & 7) != 5){ + sx = 240 << 8; + sy = 160 << 8; + d = (cnt & 7) == 3; + }else{ + sx = 160 << 8; + sy = 128 << 8; + d = 1; + } + base = vram; + if((cnt & FRAME) != 0 && (cnt & 7) != 3) + base += 0xa000; + pri = reg[BG0CNT + b->n] & 3; + for(i = ppux0; i < x1; i++){ + if(((pixwin[i] & 1<<b->n) == 0 || b->mosaic) && (u32int)b->rpx < sx && (u32int)b->rpy < sy){ + if(d){ + 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; + } + if(v >= 0) + bgpixel(b, i, v, pri); + } - }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]; + b->rpx += (s16int) rr[BG2PA]; + b->rpy += (s16int) rr[BG2PC]; + } } void -txtbg(bg *b) +txtbg(bg *b, int x1) { - u16int bgcnt; - u8int x, v; - + u8int y, v, d, *cp; + u16int bgcnt, ta0, ta, tx, ty, t, *pal; + u32int ca; + int i, x, mx; + + if((reg[DISPCNT] & 1<<8 + b->n) == 0) + return; 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); + d = bgcnt >> 7 & 1; + tx = b->tx; + ty = b->ty; + ta0 = (bgcnt << 3 & 0xf800) + ((ty & 0x1f) << 6); + switch(bgcnt >> 14){ + case 2: ta0 += ty << 6 & 0x800; break; + case 3: ta0 += ty << 7 & 0x1000; break; } -} - -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; + x = ppux0; + i = b->tnx; + for(; x < x1; tx++, i = 0){ + ta = ta0 + ((tx & 0x1f) << 1); + if((bgcnt & 1<<14) != 0) + ta += tx << 6 & 0x800; + t = vram[ta] | vram[ta+1] << 8; + if(d) + pal = pram; + else + pal = pram + (t >> 8 & 0xf0); + ca = (bgcnt << 12 & 0xc000) + ((t & 0x3ff) << 5+d); + if(ca >= 0x10000) + continue; + y = b->tny; + if((t & 1<<11) != 0) + y ^= 7; + ca += y << 2+d; + cp = vram + ca; + for(; i < 8; i++, x++){ + if(x >= x1) + goto out; + if((pixwin[x] & 1<<b->n) != 0 && !b->mosaic) + continue; + mx = i; + if((t & 1<<10) != 0) + mx ^= 7; + v = cp[mx >> 1-d]; + if(!d) + if((mx & 1) != 0) + v >>= 4; + else + v &= 0xf; + if(v != 0) + bgpixel(b, x, pal[v], bgcnt & 3); + } } +out: + b->tx = tx; + b->tnx = i; } void -sprinit(void) +rotbg(bg *b, int x1) { - 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}; + uchar *p, v; + u16int bgcnt, *rr, ta; + int i, row, sz, x, y; - 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) + rr = reg + (b->n - 2 << 3); + if((reg[DISPCNT] & 1<<8 + b->n) == 0) + return; + bgcnt = reg[BG0CNT + b->n]; + row = (bgcnt >> 14) + 4; + sz = 1 << 3 + row; + for(i = ppux0; i < x1; i++){ + x = b->rpx >> 8; + y = b->rpy >> 8; + b->rpx += (s16int) rr[BG2PA]; + b->rpy += (s16int) rr[BG2PC]; + if((pixwin[i] & 1<<b->n) != 0 && !b->mosaic) 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++; + if((bgcnt & DISPWRAP) != 0){ + x &= sz - 1; + y &= sz - 1; + }else if((uint)x >= sz || (uint)y >= sz) + continue; + 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) + bgpixel(b, i, pram[v], bgcnt & 3); + } } void -spr(void) +windows(int x1) { - sprite *s; - ushort dx; - u32int t0; - uchar v; - ushort x, y; - u16int c; - int pv, ppri, pri; - uchar d; - uchar *b; + static u8int wintab[8] = {2, 3, 1, 1, 0, 0, 0, 0}; + int i, sx0, sx1; + u16int v, h; + u16int cnt; - 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; + cnt = reg[DISPCNT]; + if((cnt >> 13) != 0){ + if((cnt & 1<<13) != 0){ + v = reg[WIN0V]; + h = reg[WIN0H]; + if(ppuy < (u8int)v && ppuy >= v >> 8){ + sx1 = (u8int)h; + sx0 = h >> 8; + if(sx0 < ppux0) + sx0 = ppux0; + if(sx1 > x1) + sx1 = x1; + for(i = sx0; i < sx1; i++) + pixwin[i] |= WIN1; + } } - 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((cnt & 1<<14) != 0){ + v = reg[WIN1V]; + h = reg[WIN1H]; + if(ppuy < (u8int)v && ppuy >= v >> 8){ + sx1 = (u8int)h; + sx0 = h >> 8; + if(sx0 < ppux0) + sx0 = ppux0; + if(sx1 > x1) + sx1 = x1; + for(i = sx0; i < sx1; i++) + pixwin[i] |= WIN2; } } + for(i = ppux0; i < x1; i++){ + v = wintab[pixwin[i]]; + h = reg[WININ + (v & 2) / 2]; + if((v & 1) != 0) + h >>= 8; + pixwin[i] = ~h; + } } - if(pv >= 0){ - pixel(pv, 4, ppri); - if(pv >> 16 != 0) - objtrans = 1; - } + for(i = ppux0; i < x1; i++) + if(pixpri[i] == VACANT || (pixwin[i] & 1<<4) != 0){ + pixcol[i] = pram[0] | SRCBACK; + pixpri[i] = BACKDROP; + }else{ + pixcol[i+240] = pram[0] | SRCBACK; + pixpri[i+240] = BACKDROP; + } + objalpha = 0; } u16int @@ -537,117 +603,185 @@ darken(u16int c1) } void -windows(void) +colormath(int x1) { - 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; - } + u16int bldcnt; + u32int *p; + int i; + + bldcnt = reg[BLDCNT]; + if((bldcnt & 3<<6) == 0 && objalpha == 0) + return; + p = pixcol + ppux0; + for(i = ppux0; i < x1; i++, p++){ + if((*p & OBJALPHA) != 0) + goto alpha; + if((pixwin[i] & 1<<5) != 0 || (bldcnt & 1<<(*p >> 17)) == 0) + continue; + switch(bldcnt >> 6 & 3){ + case 1: + alpha: + if((bldcnt & 1<<8+(p[240] >> 17)) == 0) + continue; + *p = mix(*p, p[240]); + break; + case 2: + *p = brighten(*p); + break; + case 3: + *p = darken(*p); + break; } - 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; - } + } +} + +void +linecopy(void) +{ + u32int *p; + uchar *q; + u16int *r; + u16int v; + union { u16int w; u8int b[2]; } u; + int n; + + p = pixcol; + q = pic + ppuy * 240 * 2 * scale; + r = (u16int*)q; + n = 240; + while(n--){ + v = *p++; + if(scale == 1){ + *q++ = v; + *q++ = v >> 8; + continue; } - if((dispcnt & 1<<15) != 0 && objwin != 0){ - bgmask &= reg[WINOUT] >> 8; - goto windone; + u.b[0] = v; + u.b[1] = v >> 8; + if(scale == 2){ + *r++ = u.w; + *r++ = u.w; + }else{ + *r++ = u.w; + *r++ = u.w; + *r++ = u.w; } - bgmask &= reg[WINOUT]; - } -windone: - if(pixelpri[0] != 8 && (bgmask & 1<<4) == 0){ - pixelpri[0] = 8; - pixeldat[0] = pram[0] | 5 << 16; } } void -colormath(void) +syncppu(int x1) { - u8int src0; - u16int bldcnt; - - if((bgmask & 1<<5) == 0) + int i; + u16int cnt; + + if(hblank || ppuy >= 160) return; - bldcnt = reg[BLDCNT]; - src0 = pixeldat[0] >> 16; - if(objtrans && src0 == 4) - goto alpha; - if((bldcnt & 3<<6) == 0 || (bldcnt & 1<<src0) == 0) + if(x1 >= 240) + x1 = 240; + else if(x1 <= ppux0) return; - switch(bldcnt >> 6 & 3){ + cnt = reg[DISPCNT]; + if((cnt & FBLANK) != 0){ + for(i = ppux0; i < x1; i++) + pixcol[i] = 0xffff; + ppux0 = x1; + return; + } + + if((cnt & 1<<12) != 0) + spr(x1); + windows(x1); + switch(cnt & 7){ + case 0: + txtbg(&bgst[0], x1); + txtbg(&bgst[1], x1); + txtbg(&bgst[2], x1); + txtbg(&bgst[3], x1); + break; case 1: - alpha: - if((bldcnt & 1<<8+(pixeldat[1]>>16)) == 0) - return; - pixeldat[0] = mix(pixeldat[0], pixeldat[1]); + txtbg(&bgst[0], x1); + txtbg(&bgst[1], x1); + rotbg(&bgst[2], x1); break; case 2: - pixeldat[0] = brighten(pixeldat[0]); + rotbg(&bgst[2], x1); + rotbg(&bgst[3], x1); break; case 3: - pixeldat[0] = darken(pixeldat[0]); - break; + case 4: + case 5: + bitbg(&bgst[2], x1); } + colormath(x1); + ppux0 = x1; } void -ppustep(void) +hblanktick(void *) { + extern Event evhblank; 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(hblank){ + hblclock = clock + evhblank.time; + addevent(&evhblank, 240*4 + evhblank.time); + hblank = 0; + ppux0 = 0; + memset(pixpri, VACANT, sizeof(pixpri)); + memset(pixwin, 0, 240); if(++ppuy >= 228){ ppuy = 0; flush(); } - if((stat & IRQVCTREN) != 0 && ppuy == stat >> 8) - setif(IRQVCTR); if(ppuy < 160){ - bgsinit(); sprinit(); + bgsinit(); }else if(ppuy == 160){ + dmastart(DMAVBL); if((stat & IRQVBLEN) != 0) setif(IRQVBL); - dmastart(DMAVBL); } + if((stat & IRQVCTREN) != 0 && ppuy == stat >> 8) + setif(IRQVCTR); + }else{ + syncppu(240); + linecopy(); + addevent(&evhblank, 68*4 + evhblank.time); + hblank = 1; + if((stat & IRQHBLEN) != 0) + setif(IRQHBL); + if(ppuy < 160) + dmastart(DMAHBL); + } +} + +void +ppuwrite(u16int a, u16int v) +{ + syncppu((clock - hblclock) / 4); + switch(a){ + 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 BG2XL*2: bgst[2].rpx0 = bgst[2].rpx0 & 0xffff0000 | v; break; + case BG2XH*2: bgst[2].rpx0 = bgst[2].rpx0 & 0xffff | (s32int)(v << 20) >> 4; break; + case BG2YL*2: bgst[2].rpy0 = bgst[2].rpy0 & 0xffff0000 | v; break; + case BG2YH*2: bgst[2].rpy0 = bgst[2].rpy0 & 0xffff | (s32int)(v << 20) >> 4; break; + case BG3XL*2: bgst[3].rpx0 = bgst[3].rpx0 & 0xffff0000 | v; break; + case BG3XH*2: bgst[3].rpx0 = bgst[3].rpx0 & 0xffff | (s32int)(v << 20) >> 4; break; + case BG3YL*2: bgst[3].rpy0 = bgst[3].rpy0 & 0xffff0000 | v; break; + case BG3YH*2: bgst[3].rpy0 = bgst[3].rpy0 & 0xffff | (s32int)(v << 20) >> 4; break; } } |