diff options
-rw-r--r-- | sys/src/games/gb/apu.c | 214 | ||||
-rw-r--r-- | sys/src/games/gb/dat.h | 4 | ||||
-rw-r--r-- | sys/src/games/gb/ev.c | 19 | ||||
-rw-r--r-- | sys/src/games/gb/fns.h | 1 | ||||
-rw-r--r-- | sys/src/games/gb/mem.c | 41 |
5 files changed, 185 insertions, 94 deletions
diff --git a/sys/src/games/gb/apu.c b/sys/src/games/gb/apu.c index 007a1612f..e995ce83d 100644 --- a/sys/src/games/gb/apu.c +++ b/sys/src/games/gb/apu.c @@ -4,17 +4,17 @@ #include "dat.h" #include "fns.h" -Event evsamp, evenv; +Event evsamp; +extern Event evenv, evwave; s16int sbuf[2*4000], *sbufp; enum { Freq = 44100, - SRATEDIV = 8388608 / Freq, - ENVDIV = 8388608 / 512 + SRATEDIV = FREQ / Freq }; static int fd; u16int envmod; -u8int sweepen, sweepctr; +u8int sweepen, sweepcalc, sweepctr; u16int sweepfreq; typedef struct chan chan; struct chan { @@ -25,9 +25,11 @@ struct chan { u32int finc; u8int vol; }; -u8int wave[32]; u8int wpos; u16int lfsr; +u8int apustatus; +ulong waveclock; +u8int wavebuf; chan sndch[4] = { { @@ -51,8 +53,8 @@ chan sndch[4] = { }; Var apuvars[] = { - VAR(envmod), VAR(sweepen), - VAR(sweepctr), VAR(sweepfreq), ARR(wave), VAR(wpos), VAR(lfsr), + VAR(apustatus), VAR(envmod), VAR(sweepen), VAR(sweepcalc), + VAR(sweepctr), VAR(sweepfreq), VAR(wpos), VAR(lfsr), VAR(waveclock), VAR(wavebuf), VAR(sndch[0].ectr), VAR(sndch[0].len), VAR(sndch[0].fctr), VAR(sndch[0].fthr), VAR(sndch[0].finc), VAR(sndch[0].vol), VAR(sndch[1].ectr), VAR(sndch[1].len), VAR(sndch[1].fctr), VAR(sndch[1].fthr), VAR(sndch[1].finc), VAR(sndch[1].vol), VAR(sndch[2].ectr), VAR(sndch[2].len), VAR(sndch[2].fctr), VAR(sndch[2].fthr), VAR(sndch[2].finc), VAR(sndch[2].vol), @@ -68,7 +70,7 @@ rate(int i, u16int v) sndch[i].finc = 131072ULL * 65536 / (Freq * (2048 - (v & 0x7ff))); break; case 2: - sndch[2].finc = 2097152ULL * 65536 / (Freq * (2048 - (v & 0x7ff))); + sndch[2].finc = 4 * (2048 - (v & 0x7ff)); break; case 3: sndch[3].finc = 524288ULL * 65536 / Freq; @@ -84,12 +86,12 @@ void env(chan *c) { if((envmod & 1) == 0 && c->len > 0 && (*c->freq & 1<<6) != 0) - --c->len; - if(c->len == 0){ - c->vol = 0; - return; - } - if((envmod & 7) != 7 || c->ectr == 0 || --c->ectr != 0) + if(--c->len == 0){ + apustatus &= ~(1<<c->n); + c->vol = 0; + return; + } + if((apustatus & 1<<c->n) == 0 || (envmod & 7) != 7 || c->ectr == 0 || --c->ectr != 0) return; c->ectr = *c->env & 7; if((*c->env & 1<<3) != 0){ @@ -100,22 +102,27 @@ env(chan *c) c->vol--; } +void +wavetick(void *) +{ + addevent(&evwave, sndch[2].finc); + wpos = wpos + 1 & 31; + wavebuf = reg[WAVE + (wpos >> 1)]; + waveclock = clock; +} + s8int wavesamp(void) { - s8int x; - int v; + u8int x; - sndch[2].fctr = v = sndch[2].fctr + sndch[2].finc; - if(sndch[2].len == 0 || (reg[NR30] & 1<<7) == 0) + if((apustatus & 1<<4) == 0) return 0; - for(;;){ - x = wave[wpos]; - v -= 0x10000; - if(v < 0) - break; - wpos = wpos + 1 & 31; - } + x = wavebuf; + if((wpos & 1) == 0) + x >>= 4; + else + x &= 0xf; if((reg[NR32] & 3<<5) == 0) x = 0; else @@ -137,13 +144,13 @@ lfsrsamp(void) break; lfsr >>= 1; if(((l ^ lfsr) & 1) != 0) - if((reg[0x7c/2] & 1<<3) != 0) + if((reg[NR43] & 1<<3) != 0) lfsr |= 0x40; else lfsr |= 0x4000; } if((l & 1) != 0) - return -sndch[3].vol; + return 0; else return sndch[3].vol; } @@ -160,11 +167,13 @@ sweep(int wb) if((cnt & 1<<3) != 0) d = -d; fr = sweepfreq + d; + sweepcalc |= cnt; if(fr > 2047){ sndch[0].len = 0; sndch[0].vol = 0; + apustatus &= ~1; sweepen = 0; - }else if(wb){ + }else if(wb && (cnt & 7) != 0){ sweepfreq = fr; reg[NR13] = fr; reg[NR14] = reg[NR14] & 0xf8 | fr >> 8; @@ -174,7 +183,7 @@ sweep(int wb) } void -sndstart(chan *c, u16int v) +sndstart(chan *c, u8int v) { u8int cnt; @@ -182,29 +191,41 @@ sndstart(chan *c, u16int v) c->ectr = *c->env & 7; if(c->len == 0) c->len = 64; + apustatus |= 1<<c->n; if(c == sndch){ cnt = reg[NR10]; - sweepen = (cnt & 0x07) != 0 && (cnt & 0x70) != 0; + sweepen = (cnt & 0x07) != 0 || (cnt & 0x70) != 0; sweepctr = cnt >> 4 & 7; - sweepfreq = v & 0x7ff; + sweepctr += sweepctr - 1 & 8; + sweepfreq = v << 8 & 0x700 | reg[NR13]; + sweepcalc = 0; if((cnt & 0x07) != 0) sweep(0); } + if((*c->freq & 0x40) == 0 && (v & 0x40) != 0 && (envmod & 1) != 0 && --c->len == 0 || (*c->env & 0xf8) == 0){ + apustatus &= ~(1<<c->n); + c->vol = 0; + } } void envtick(void *) { - addevent(&evenv, ENVDIV); + addevent(&evenv, FREQ / 512); env(&sndch[0]); env(&sndch[1]); if((envmod & 1) == 0 && sndch[2].len > 0 && (reg[NR34] & 0x40) != 0) - sndch[2].len--; + if(--sndch[2].len == 0){ + apustatus &= ~4; + delevent(&evwave); + } env(&sndch[3]); if((envmod & 3) == 2 && sweepen && --sweepctr == 0){ sweepctr = reg[NR10] >> 4 & 7; - sweep(1); + sweepctr += sweepctr - 1 & 8; + if((reg[NR10] & 0x70) != 0) + sweep(1); } envmod++; } @@ -223,12 +244,12 @@ sampletick(void *) if(sndch[0].fctr >= sndch[0].fthr) ch[0] = sndch[0].vol; else - ch[0] = -sndch[0].vol; + ch[0] = 0; 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[1] = 0; ch[2] = wavesamp(); ch[3] = lfsrsamp(); @@ -237,19 +258,20 @@ sampletick(void *) s[0] = 0; s[1] = 0; for(i = 0; i < 4; i++){ + if(i == 2 ? ((reg[NR30] & 0x80) == 0) : ((*sndch[i].env & 0xf8) == 0)) + continue; + ch[i] = ch[i] * 2 - 15; if((cnth & 1<<i) != 0) - s[1] += ch[i] * (1 + (cntl & 7)); + s[0] += ch[i]; if((cnth & 1<<4<<i) != 0) - s[0] += ch[i] * (1 + (cntl >> 4 & 7)); + s[1] += ch[i]; } - 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; + s[0] *= 1 + (cntl & 7); + s[1] *= 1 + (cntl >> 4 & 7); if(sbufp < sbuf + nelem(sbuf)){ - sbufp[0] = s[0] << 6; - sbufp[1] = s[1] << 6; + sbufp[0] = s[0] * 60; + sbufp[1] = s[1] * 60; sbufp += 2; } } @@ -258,14 +280,34 @@ void sndwrite(u8int a, u8int v) { static u16int thr[4] = {0x2000, 0x4000, 0x8000, 0xC000}; + static u8int clrreg[] = { + 0x80, 0x3f, 0x00, 0xff, 0xbf, + 0xff, 0x3f, 0x00, 0xff, 0xbf, + 0x7f, 0xff, 0x9f, 0xff, 0xbf, + 0xff, 0xff, 0x00, 0x00, 0xbf, + 0x00, 0x00 + }; - if((reg[NR52] & 0x80) == 0 && a != NR52) + if((reg[NR52] & 0x80) == 0 && a != NR52 && ((mode & CGB) != 0 || a != NR11 && a != NR21 && a != NR31 && a != NR41)) return; switch(a){ + case NR10: + if((sweepcalc & 0x08) != 0 && (reg[NR10] & ~v & 0x08) != 0){ + sndch[0].vol = 0; + apustatus &= ~1; + sweepcalc = 0; + } + break; case NR11: sndch[0].fthr = thr[v >> 6 & 3]; sndch[0].len = 64 - (v & 63); break; + case NR12: + if((v & 0xf8) == 0){ + sndch[0].vol = 0; + apustatus &= ~1; + } + break; case NR13: rate(0, reg[NR14] << 8 & 0x700 | v); break; @@ -276,6 +318,13 @@ sndwrite(u8int a, u8int v) break; case NR21: sndch[1].fthr = thr[v >> 6 & 3]; + sndch[1].len = 64 - (v & 63); + break; + case NR22: + if((v & 0xf8) == 0){ + sndch[1].vol = 0; + apustatus &= ~2; + } break; case NR23: rate(1, reg[NR24] << 8 & 0x700 | v); @@ -286,8 +335,10 @@ sndwrite(u8int a, u8int v) sndstart(&sndch[1], v); break; case NR30: - if((v & 1<<7) != 0 && sndch[2].len == 0) - sndch[2].len = 256; + if((v & 0x80) == 0){ + apustatus &= ~4; + delevent(&evwave); + } break; case NR31: sndch[2].len = 256 - (v & 0xff); @@ -297,6 +348,25 @@ sndwrite(u8int a, u8int v) break; case NR34: rate(2, v << 8 & 0x700 | reg[NR33]); + if((v & 0x80) != 0){ + if(sndch[2].len == 0) + sndch[2].len = 256; + wpos = 0; + if((reg[NR30] & 0x80) != 0){ + apustatus |= 4; + delevent(&evwave); + addevent(&evwave, sndch[2].finc); + } + } + break; + case NR41: + sndch[3].len = 64 - (v & 63); + break; + case NR42: + if((v & 0xf8) == 0){ + sndch[3].vol = 0; + apustatus &= ~8; + } break; case NR43: rate(3, v); @@ -311,42 +381,44 @@ sndwrite(u8int a, u8int v) } break; case NR52: + apustatus = v & 0xf0 | apustatus & 0x0f; if((v & 0x80) == 0){ - memset(reg + NR10, 0, NR52 - NR10); - sndch[0].len = 0; - sndch[1].len = 0; - sndch[2].len = 0; - sndch[3].len = 0; + memcpy(reg + NR10, clrreg, NR52 - NR10); + if((mode & CGB) != 0){ + sndch[0].len = 0; + sndch[1].len = 0; + sndch[2].len = 0; + sndch[3].len = 0; + apustatus = 0; + delevent(&evwave); + } + }else if((reg[NR52] & 0x80) == 0){ + envmod = 0; + delevent(&evenv); + addevent(&evenv, FREQ / 512); + sndch[0].fctr = 0; + sndch[1].fctr = 0; + sndch[3].fctr = 0; } } -} - -int -apuread(void) -{ - u8int v; - - v = reg[NR52] & 0xf0; - if(sndch[0].len != 0) v |= 1; - if(sndch[1].len != 0) v |= 2; - if(sndch[2].len != 0) v |= 4; - if(sndch[3].len != 0) v |= 8; - return v; + reg[a] = v; } u8int waveread(u8int a) { - a <<= 1; - return wave[a + wpos & 31] << 4 | wave[a + wpos + 1 & 31]; + if((apustatus & 4) != 0) + if((mode & CGB) != 0 || clock - waveclock == 0) + return wavebuf; + else + return 0xff; + return reg[WAVE + a]; } void wavewrite(u8int a, u8int v) { - a <<= 1; - wave[a + wpos & 31] = v >> 4; - wave[a + wpos + 1 & 31] = v & 0x0f; + reg[WAVE + a] = v; } void @@ -358,8 +430,6 @@ audioinit(void) sbufp = sbuf; evsamp.f = sampletick; addevent(&evsamp, SRATEDIV); - evenv.f = envtick; - addevent(&evenv, ENVDIV); } int diff --git a/sys/src/games/gb/dat.h b/sys/src/games/gb/dat.h index 787c8f839..4bff393e5 100644 --- a/sys/src/games/gb/dat.h +++ b/sys/src/games/gb/dat.h @@ -20,6 +20,8 @@ extern ulong clock; extern u8int ppuy, ppustate; +extern u8int apustatus; + extern u8int mode; extern u8int mbc, feat; extern int keys, scale; @@ -69,6 +71,7 @@ enum { WX = 0x4B, KEY1 = 0x4D, VBK = 0x4F, + RP = 0x56, HDMASH = 0x51, HDMASL = 0x52, HDMADH = 0x53, @@ -128,6 +131,7 @@ enum { PICH = 144, MILLION = 1000000, BILLION = 1000000000, + FREQ = 1<<23 }; struct Event { diff --git a/sys/src/games/gb/ev.c b/sys/src/games/gb/ev.c index b70cb974e..20d84876e 100644 --- a/sys/src/games/gb/ev.c +++ b/sys/src/games/gb/ev.c @@ -4,9 +4,9 @@ #include "dat.h" #include "fns.h" -Event evhblank, evtimer, evnever; -extern Event evsamp, evenv; -Event *events[NEVENT] = {&evhblank, &evtimer, &evnever, &evsamp, &evenv}; +Event evhblank, evtimer, evenv, evwave; +extern Event evsamp; +Event *events[NEVENT] = {&evhblank, &evtimer, &evenv, &evsamp, &evwave}; Event *elist; static int timshtab[4] = {12, 4, 6, 8}, timsh; ulong timclock; @@ -104,19 +104,16 @@ timertick(void *) } void -nevertick(void *) -{ - addevent(&evnever, 10000000); -} - -void eventinit(void) { extern void hblanktick(void *); + extern void envtick(void *); + extern void wavetick(void *); evhblank.f = hblanktick; addevent(&evhblank, 240*4); evtimer.f = timertick; - evnever.f = nevertick; - addevent(&evnever, 10000000); + evenv.f = envtick; + addevent(&evenv, FREQ / 512); + evwave.f = wavetick; } diff --git a/sys/src/games/gb/fns.h b/sys/src/games/gb/fns.h index 237327057..be9b24960 100644 --- a/sys/src/games/gb/fns.h +++ b/sys/src/games/gb/fns.h @@ -23,7 +23,6 @@ int dmastep(void); void audioinit(void); int audioout(void); void sndwrite(u8int, u8int); -int apuread(void); u8int waveread(u8int); void wavewrite(u8int, u8int); u8int timread(void); diff --git a/sys/src/games/gb/mem.c b/sys/src/games/gb/mem.c index 7f2894571..7dc58fc56 100644 --- a/sys/src/games/gb/mem.c +++ b/sys/src/games/gb/mem.c @@ -27,7 +27,7 @@ regread(u8int a) switch(a){ case JOYP: - v = keys & 0x30 | 0xcf; + v = reg[a] & 0xff; if((reg[a] & 0x10) == 0) v &= 0xf0 | ~keys; if((reg[a] & 0x20) == 0) @@ -50,10 +50,13 @@ regread(u8int a) case NR14: case NR24: case NR34: case NR44: return reg[a] | 0xbf; case NR52: - return apuread(); + return apustatus; + case NR11: case NR21: + return reg[a] | 0x3f; + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: + case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f: + return waveread(a & 0xf); default: - if((a & 0xf0) == 0x30) - return waveread(a & 0xf); return reg[a]; } } @@ -78,6 +81,8 @@ regwrite(u8int a, u8int v) u8int u; switch(a){ + case JOYP: v |= 0xcf; break; + case SC: v |= 0x7c; break; case DIV: divclock = clock; v = 0; @@ -111,13 +116,13 @@ regwrite(u8int a, u8int v) break; case VBK: if((mode & COL) == 0) - break; + goto ff; vramb = vram + ((v & 1) << 13); v |= 0xfe; break; case SVBK: if((mode & COL) == 0) - break; + goto ff; v &= 7; wramb = wram + (v + (v - 1 >> 3 & 1) << 12); v |= 0xf8; @@ -161,29 +166,45 @@ regwrite(u8int a, u8int v) reg[OCPS] = reg[OCPS] + 1 - ((reg[OCPS] & 0x3f) + 1 & 0x40); break; case IF: v |= 0xe0; break; + case IE: v &= 0x1f; break; case KEY1: v |= 0x7e; break; case HDMAC: if((mode & COL) == 0) - break; + goto ff; dma = (v & 0x80) != 0 ? -1 : 1; break; case NR10: v |= 0x80; goto snd; case NR14: case NR24: v |= 0x38; goto snd; + case NR30: v |= 0x7f; goto snd; case NR32: v |= 0x9f; goto snd; case NR41: v |= 0xc0; goto snd; case NR44: v |= 0x3f; goto snd; case NR52: v |= 0x70; goto snd; case NR11: case NR12: case NR13: case NR21: case NR22: case NR23: - case NR30: case NR31: case NR33: case NR34: + case NR31: case NR33: case NR34: case NR42: case NR43: case NR50: case NR51: snd: sndwrite(a, v); + return; + case SB: + case TMA: break; + case HDMASL: case HDMASH: case HDMADL: case HDMADH: case RP: + if((mode & COL) == 0) + goto ff; + break; + case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: + case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f: + wavewrite(a & 0xf, v); + return; + default: + if(a >= 0x80) + break; + ff: + v = 0xff; } - if((a & 0xf0) == 0x30) - wavewrite(a, v); reg[a] = v; } |