shithub: riscv

Download patch

ref: d9af840ca248f27e6b9d2a135e537c41b3578d52
parent: ec8d0d9e800000ef137893bdb49c84fd76527cb2
author: aiju <devnull@localhost>
date: Thu Apr 2 11:30:12 EDT 2015

games/gb: improved audio code

--- 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 @@
 	u32int finc;
 	u8int vol;
 };
-u8int wave[32];
 u8int wpos;
 u16int lfsr;
+u8int apustatus;
+ulong waveclock;
+u8int wavebuf;
 
 chan sndch[4] = {
 	{
@@ -51,8 +53,8 @@
 };
 
 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 @@
 		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,13 +86,13 @@
 env(chan *c)
 {
 	if((envmod & 1) == 0 && c->len > 0 && (*c->freq & 1<<6) != 0)
-		--c->len;
-	if(c->len == 0){
-		c->vol = 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;
-	}
-	if((envmod & 7) != 7 || c->ectr == 0 || --c->ectr != 0)
-		return;
 	c->ectr = *c->env & 7;
 	if((*c->env & 1<<3) != 0){
 		if(c->vol < 15)
@@ -100,22 +102,27 @@
 			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 @@
 			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 @@
 	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 @@
 }
 
 void
-sndstart(chan *c, u16int v)
+sndstart(chan *c, u8int v)
 {
 	u8int cnt;
 
@@ -182,29 +191,41 @@
 	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 @@
 	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 @@
 	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 @@
 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,7 +318,14 @@
 		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);
 		break;
@@ -286,8 +335,10 @@
 			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,7 +348,26 @@
 		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);
 		break;
@@ -311,42 +381,44 @@
 		}
 		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;
 		}
 	}
+	reg[a] = v;
 }
 
-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;
-}
-
 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 @@
 	sbufp = sbuf;
 	evsamp.f = sampletick;
 	addevent(&evsamp, SRATEDIV);
-	evenv.f = envtick;
-	addevent(&evenv, ENVDIV);
 }
 
 int
--- a/sys/src/games/gb/dat.h
+++ b/sys/src/games/gb/dat.h
@@ -20,6 +20,8 @@
 
 extern u8int ppuy, ppustate;
 
+extern u8int apustatus;
+
 extern u8int mode;
 extern u8int mbc, feat;
 extern int keys, scale;
@@ -69,6 +71,7 @@
 	WX = 0x4B,
 	KEY1 = 0x4D,
 	VBK = 0x4F,
+	RP = 0x56,
 	HDMASH = 0x51,
 	HDMASL = 0x52,
 	HDMADH = 0x53,
@@ -128,6 +131,7 @@
 	PICH = 144,
 	MILLION = 1000000,
 	BILLION = 1000000000,
+	FREQ = 1<<23
 };
 
 struct Event {
--- 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 @@
 }
 
 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;
 }
--- a/sys/src/games/gb/fns.h
+++ b/sys/src/games/gb/fns.h
@@ -23,7 +23,6 @@
 void audioinit(void);
 int audioout(void);
 void sndwrite(u8int, u8int);
-int apuread(void);
 u8int waveread(u8int);
 void wavewrite(u8int, u8int);
 u8int timread(void);
--- a/sys/src/games/gb/mem.c
+++ b/sys/src/games/gb/mem.c
@@ -27,7 +27,7 @@
 
 	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 @@
 	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 @@
 	u8int u;
 
 	switch(a){
+	case JOYP: v |= 0xcf; break;
+	case SC: v |= 0x7c; break;
 	case DIV:
 		divclock = clock;
 		v = 0;
@@ -111,13 +116,13 @@
 		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,14 +166,16 @@
 			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;
@@ -175,15 +182,29 @@
 	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;
 }