ref: d7f507b5b937e7d64f337d435c395d36e990395c
parent: 8ebb846fb2b3b6ba6e828be2ba1ed37d6b4847f2
author: aiju <[email protected]>
date: Sat Apr 7 12:03:16 EDT 2012
games/gb: better audio, scaling, fixed serious MMC5 bug
--- a/sys/src/games/gb/audio.c
+++ b/sys/src/games/gb/audio.c
@@ -6,7 +6,7 @@
#include "fns.h"
static int fd;
-int ch1c, ch2c, ch3c, ch4c, ch4sr = 1;
+static int sc, ch1c, ch2c, ch3c, ch4c, ch4sr = 1, ch1vec, ch2vec, ch4vec, ch1v, ch2v, ch4v;
enum { SAMPLE = 44100 };
@@ -33,13 +33,74 @@
}
static void
+soundlen(int len, int ctrl, int n)
+{
+ if(mem[ctrl] & 128){
+ mem[0xFF26] |= (1<<n);
+ mem[ctrl] &= ~128;
+ switch(n){
+ case 0:
+ ch1v = mem[0xFF12];
+ break;
+ case 1:
+ ch2v = mem[0xFF17];
+ break;
+ case 3:
+ ch4v = mem[0xFF21];
+ break;
+ }
+ }
+ if((mem[ctrl] & 64) == 0){
+ mem[0xFF26] |= (1<<n);
+ return;
+ }
+ if((mem[0xFF26] & (1<<n)) == 0)
+ return;
+ if(mem[len] == ((n == 2) ? 255 : 63)){
+ mem[0xFF26] &= ~(1<<n);
+ return;
+ }
+ mem[len]++;
+}
+
+static void
+envelope(int *v, int *c)
+{
+ int f;
+
+ f = (*v & 7) * SAMPLE / 64;
+ if(f == 0)
+ return;
+ if(*c >= f){
+ if(*v & 8){
+ if((*v >> 4) < 0xF)
+ *v += 0x10;
+ }else
+ if((*v >> 4) > 0)
+ *v -= 0x10;
+ *c = 0;
+ }
+ (*c)++;
+}
+
+static void
dosample(short *smp)
{
int ch1s, ch2s, ch3s, ch4s, ch1f, ch2f, ch3f, ch4f, k, r, s;
u8int f;
-
- ch4s = 0;
+ if(sc >= SAMPLE/256){
+ soundlen(0xFF11, 0xFF14, 0);
+ soundlen(0xFF16, 0xFF19, 1);
+ soundlen(0xFF1B, 0xFF1E, 2);
+ soundlen(0xFF20, 0xFF23, 3);
+ sc = 0;
+ }
+ sc++;
+ envelope(&ch1v, &ch1vec);
+ envelope(&ch2v, &ch2vec);
+ envelope(&ch4v, &ch4vec);
+
ch1f = freq(0xFF13);
if(ch1c >= ch1f)
ch1c = 0;
@@ -47,8 +108,8 @@
ch1s = 1;
else
ch1s = -1;
- ch1s *= mem[0xFF12] >> 4;
- ch1s *= 10000 / 0xF;
+ ch1s *= ch1v >> 4;
+ ch1s *= 8000 / 0xF;
ch1c++;
ch2f = freq(0xFF18);
@@ -58,8 +119,8 @@
ch2s = 1;
else
ch2s = -1;
- ch2s *= mem[0xFF17] >> 4;
- ch2s *= 10000 / 0xF;
+ ch2s *= ch2v >> 4;
+ ch2s *= 8000 / 0xF;
ch2c++;
ch3f = freq(0xFF1D) * 100 / 32;
@@ -86,7 +147,7 @@
ch3s >>= 2;
break;
}
- ch3s *= 10000 / 0xF;
+ ch3s *= 8000 / 0xF;
ch3c++;
}
@@ -113,12 +174,15 @@
ch4s = -1;
else
ch4s = 1;
- ch4s *= mem[0xFF21] >> 4;
- ch4s *= 10000 / 0xF;
+ ch4s *= ch4v >> 4;
+ ch4s *= 8000 / 0xF;
smp[0] = 0;
smp[1] = 0;
f = mem[0xFF25];
+ r = mem[0xFF26] & 15;
+ r = r | (r << 4);
+ f &= r;
if(f & 0x01) smp[0] += ch1s;
if(f & 0x02) smp[0] += ch2s;
if(f & 0x04) smp[0] += ch3s;
@@ -145,6 +209,10 @@
void
initaudio(void)
{
+ mem[0xFF26] = 0xF;
+ ch1v = 0xF0;
+ ch2v = 0xF0;
+ ch4v = 0xF0;
fd = open("/dev/audio", OWRITE);
if(fd < 0)
return;
--- a/sys/src/games/gb/dat.h
+++ b/sys/src/games/gb/dat.h
@@ -9,6 +9,8 @@
extern uchar *cart;
extern int mbc, rombanks, rambanks;
+extern int scale;
+
enum {
rB,
rC,
--- a/sys/src/games/gb/gb.c
+++ b/sys/src/games/gb/gb.c
@@ -9,7 +9,7 @@
#include "fns.h"
uchar *cart, *ram;
-int mbc, rombanks, rambanks, clock, ppuclock, divclock, timerclock, syncclock, syncfreq, sleeps, checkclock, msgclock, timerfreq, timer, keys, savefd, savereq, loadreq;
+int mbc, rombanks, rambanks, clock, ppuclock, divclock, timerclock, syncclock, syncfreq, sleeps, checkclock, msgclock, timerfreq, timer, keys, savefd, savereq, loadreq, scale;
Rectangle picr;
Image *bg, *tmp;
Mousectl *mc;
@@ -140,10 +140,10 @@
initdraw(nil, nil, title);
originwindow(screen, Pt(0, 0), screen->r.min);
p = divpt(addpt(screen->r.min, screen->r.max), 2);
- picr = (Rectangle){subpt(p, Pt(80, 72)), addpt(p, Pt(80, 72))};
+ picr = (Rectangle){subpt(p, Pt(scale * 80, scale * 72)), addpt(p, Pt(scale * 80, scale * 72))};
bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
if(screen->chan != XRGB32 || screen->chan != XBGR32)
- tmp = allocimage(display, Rect(0, 0, 160, 144), XRGB32, 0, 0);
+ tmp = allocimage(display, Rect(0, 0, scale * 160, scale * 144), XRGB32, 0, 0);
draw(screen, screen->r, bg, nil, ZP);
if(ram && battery){
@@ -230,10 +230,17 @@
Mouse m;
Point p;
+ scale = 1;
ARGBEGIN{
case 'a':
initaudio();
break;
+ case '2':
+ scale = 2;
+ break;
+ case '3':
+ scale = 3;
+ break;
default:
sysfatal("unknown flag -%c", ARGC());
}ARGEND;
@@ -279,7 +286,7 @@
if(getwindow(display, Refnone) < 0)
sysfatal("resize failed: %r");
p = divpt(addpt(screen->r.min, screen->r.max), 2);
- picr = (Rectangle){subpt(p, Pt(80, 72)), addpt(p, Pt(80, 72))};
+ picr = (Rectangle){subpt(p, Pt(scale * 80, scale * 72)), addpt(p, Pt(scale * 80, scale * 72))};
bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
}
}
--- a/sys/src/games/gb/mem.c
+++ b/sys/src/games/gb/mem.c
@@ -118,20 +118,20 @@
}
return;
case 5:
- switch(p >> 13){
- case 0:
+ switch(p >> 12){
+ case 0: case 1:
if((v & 0x0F) == 0x0A)
ramswitch(1, rambank);
else
ramswitch(0, rambank);
return;
- case 1:
+ case 2:
romswitch((rombank & 0x100) | v);
return;
- case 2:
+ case 3:
romswitch((((int)v & 1) << 8) | (rombank & 0xFF));
return;
- case 3:
+ case 4:
ramswitch(ramen, v & 15);
return;
@@ -162,6 +162,8 @@
timerfreq = 256;
}
break;
+ case 0xFF26:
+ v = (v & 0xF0) | (mem[p] & 0x0F);
case 0xFF41:
v &= ~7;
v |= mem[p] & 7;
--- a/sys/src/games/gb/ppu.c
+++ b/sys/src/games/gb/ppu.c
@@ -5,7 +5,7 @@
#include "dat.h"
#include "fns.h"
-uchar pic[160*144*4];
+uchar pic[160*144*4*9];
static void
resolvetile(u8int tx, u8int ty, u8int toy, int window, u8int* tnl1, u8int *tnl2)
@@ -32,17 +32,32 @@
static void
pixel(int x, int y, int val, int back)
{
+ int X, Y;
+ uchar *p;
+
val = (3 - val) * 0x55;
- pic[y*160*4 + x*4] = val;
- pic[y*160*4 + x*4 + 1] = val;
- pic[y*160*4 + x*4 + 2] = val;
- pic[y*160*4 + x*4 + 3] = back ? 0 : 0xFF;
+ if(scale > 1){
+ for(X = scale * x; X < scale * (x+1); X++)
+ for(Y = scale * y; Y < scale * (y+1); Y++){
+ p = pic + Y * scale * 160 * 4 + X * 4;
+ p[0] = val;
+ p[1] = val;
+ p[2] = val;
+ p[3] = back ? 0 : 0xFF;
+ }
+ }else{
+ p = pic + y*160*4 + x*4;
+ p[0] = val;
+ p[1] = val;
+ p[2] = val;
+ p[3] = back ? 0 : 0xFF;
+ }
}
static void
pixelbelow(int x, int y, int val)
{
- if(pic[y*160*4 + x*4 + 3] == 0)
+ if(pic[y*scale*scale*160*4 + x*scale*4 + 3] == 0)
pixel(x, y, val, 0);
}
@@ -191,10 +206,10 @@
mem[LY] = 0;
if(mem[LCDC] & LCDOP){
if(tmp){
- loadimage(tmp, tmp->r, pic, sizeof(pic));
+ loadimage(tmp, tmp->r, pic, 160*144*4*scale*scale);
draw(screen, picr, tmp, nil, ZP);
}else
- loadimage(screen, picr, pic, sizeof(pic));
+ loadimage(screen, picr, pic, 160*144*4*scale*scale);
flushimage(display, 1);
memset(pic, sizeof pic, 0);
}