ref: 8ebb846fb2b3b6ba6e828be2ba1ed37d6b4847f2
parent: 4950b5468bd60e9821e3aa9c93ec80a16c194230
author: aiju <[email protected]>
date: Fri Apr 6 16:57:45 EDT 2012
more games/gb fun
--- /dev/null
+++ b/sys/src/games/gb/audio.c
@@ -1,0 +1,152 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+static int fd;
+int ch1c, ch2c, ch3c, ch4c, ch4sr = 1;
+
+enum { SAMPLE = 44100 };
+
+static int
+thresh(int f, int b)
+{
+ switch(b){
+ case 0: return f/8;
+ case 1: return f/4;
+ case 2: return f/2;
+ default: return 3*f/4;
+ }
+}
+
+static int
+freq(int lower)
+{
+ int f;
+
+ f = mem[lower+1] & 7;
+ f = (f << 8) | mem[lower];
+ f = muldiv(2048 - f, SAMPLE, 131072);
+ return f;
+}
+
+static void
+dosample(short *smp)
+{
+ int ch1s, ch2s, ch3s, ch4s, ch1f, ch2f, ch3f, ch4f, k, r, s;
+ u8int f;
+
+ ch4s = 0;
+
+ ch1f = freq(0xFF13);
+ if(ch1c >= ch1f)
+ ch1c = 0;
+ if(ch1c >= thresh(ch1f, mem[0xFF11] >> 6))
+ ch1s = 1;
+ else
+ ch1s = -1;
+ ch1s *= mem[0xFF12] >> 4;
+ ch1s *= 10000 / 0xF;
+ ch1c++;
+
+ ch2f = freq(0xFF18);
+ if(ch2c >= ch2f)
+ ch2c = 0;
+ if(ch2c >= thresh(ch1f, mem[0xFF16] >> 6))
+ ch2s = 1;
+ else
+ ch2s = -1;
+ ch2s *= mem[0xFF17] >> 4;
+ ch2s *= 10000 / 0xF;
+ ch2c++;
+
+ ch3f = freq(0xFF1D) * 100 / 32;
+ if(ch3f == 0)
+ ch3f = 1;
+ ch3s = 0;
+ if(mem[0xFF1A] & 0x80){
+ if(ch3c >= freq(0xFF1D))
+ ch3c = 0;
+ k = ch3c * 100 / ch3f;
+ ch3s = mem[0xFF30 + (k >> 1)];
+ if(k & 1)
+ ch3s &= 0xF;
+ else
+ ch3s >>= 4;
+ switch(mem[0xFF1C]){
+ case 0:
+ ch3s = 0;
+ break;
+ case 2:
+ ch3s >>= 1;
+ break;
+ case 3:
+ ch3s >>= 2;
+ break;
+ }
+ ch3s *= 10000 / 0xF;
+ ch3c++;
+ }
+
+ r = mem[0xFF22] & 7;
+ s = mem[0xFF22] >> 4;
+ if(r != 0)
+ ch4f = 524288 / r;
+ else
+ ch4f = 524288 * 2;
+ ch4f >>= s+1;
+ if(ch4f == 0)
+ ch4f = 1;
+ ch4f = SAMPLE / ch4f;
+ if(ch4c >= ch4f){
+ ch4sr <<= 1;
+ if(mem[0xFF22] & 4)
+ k = ((ch4sr >> 6) ^ (ch4sr >> 7)) & 1;
+ else
+ k = ((ch4sr >> 14) ^ (ch4sr >> 15)) & 1;
+ ch4sr |= k;
+ ch4c = 0;
+ }
+ if(ch4sr & 1)
+ ch4s = -1;
+ else
+ ch4s = 1;
+ ch4s *= mem[0xFF21] >> 4;
+ ch4s *= 10000 / 0xF;
+
+ smp[0] = 0;
+ smp[1] = 0;
+ f = mem[0xFF25];
+ if(f & 0x01) smp[0] += ch1s;
+ if(f & 0x02) smp[0] += ch2s;
+ if(f & 0x04) smp[0] += ch3s;
+ if(f & 0x08) smp[0] += ch4s;
+ if(f & 0x10) smp[1] += ch1s;
+ if(f & 0x20) smp[1] += ch2s;
+ if(f & 0x40) smp[1] += ch3s;
+ if(f & 0x80) smp[1] += ch4s;
+}
+
+void
+audioproc(void *)
+{
+ short samples[10 * 2];
+ int i;
+
+ for(;;){
+ for(i = 0; i < sizeof samples/4; i++)
+ dosample(samples + 2 * i);
+ write(fd, samples, sizeof samples);
+ }
+}
+
+void
+initaudio(void)
+{
+ fd = open("/dev/audio", OWRITE);
+ if(fd < 0)
+ return;
+ proccreate(audioproc, nil, 8192);
+}
--- a/sys/src/games/gb/cpu.c
+++ b/sys/src/games/gb/cpu.c
@@ -793,14 +793,14 @@
memwrite(lohi(R[rC], 0xFF), R[rA]);
return 8;
case 0xE8:
- val = (schar)fetch8();
- val32 = (int)sp + val;
+ val = (short)(schar)fetch8();
+ val32 = (uint)sp + (uint)val;
Fl = 0;
- if(val32 > 0xFFFF || val32 < 0)
+ if(((sp & 0xFF) + (val & 0xFF)) > 0xFF)
Fl |= FLAGC;
- if(((sp & 0xFFF) + (val & 0xFFF)) > 0xFFF)
+ if(((sp & 0xF) + (val & 0xF)) > 0xF)
Fl |= FLAGH;
- sp = val;
+ sp = val32;
return 16;
case 0xE9:
pc = lohi(R[rL], R[rH]);
@@ -823,12 +823,15 @@
IME= 0;
return 4;
case 0xF8:
- val32 = sp + (schar)fetch8();
+ val = (short)(schar)fetch8();
+ val32 = (uint)sp + (uint)val;
+ Fl = 0;
+ if(((sp & 0xFF) + (val & 0xFF)) > 0xFF)
+ Fl |= FLAGC;
+ if(((sp & 0xF) + (val & 0xF)) > 0xF)
+ Fl |= FLAGH;
R[rL] = val32;
R[rH] = val32 >> 8;
- Fl = 0;
- if(val32 < 0 || val32 > 0xFFFF)
- Fl = FLAGC;
return 12;
case 0xF9:
sp = lohi(R[rL], R[rH]);
--- a/sys/src/games/gb/dat.h
+++ b/sys/src/games/gb/dat.h
@@ -1,7 +1,7 @@
extern u16int pc, curpc, sp;
extern u8int R[8], Fl;
extern int halt, IME, keys;
-extern int clock, ppuclock, divclock, timerclock, syncclock, timerfreq, timer;
+extern int clock, ppuclock, divclock, timerclock, timerfreq, timer;
extern int rombank, rambank, ramen, battery, ramrom;
extern uchar mem[], *ram;
@@ -72,4 +72,7 @@
IF = 0xFF0F,
IE = 0xFFFF,
CPUFREQ = 4194304,
+
+ MILLION = 1000000,
+ BILLION = 1000000000,
};
--- a/sys/src/games/gb/fns.h
+++ b/sys/src/games/gb/fns.h
@@ -8,3 +8,4 @@
void flushram(void);
void savestate(char *);
void loadstate(char *);
+void initaudio(void);
\ No newline at end of file
--- 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, 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;
Rectangle picr;
Image *bg, *tmp;
Mousectl *mc;
@@ -87,6 +87,11 @@
case 0x11: case 0x12:
mbc = 3;
break;
+ case 0x1B: case 0x1E:
+ battery = 1;
+ case 0x19: case 0x1A: case 0x1C: case 0x1D:
+ mbc = 5;
+ break;
default:
sysfatal("%s: unknown cartridge type %.2x", file, cart[0x147]);
}
@@ -226,6 +231,9 @@
Point p;
ARGBEGIN{
+ case 'a':
+ initaudio();
+ break;
default:
sysfatal("unknown flag -%c", ARGC());
}ARGEND;
@@ -244,6 +252,7 @@
if(mc == nil)
sysfatal("init mouse: %r");
proccreate(keyproc, nil, 8192);
+ syncfreq = CPUFREQ / 50;
old = nsec();
for(;;){
if(savereq){
@@ -260,6 +269,7 @@
divclock += t;
timerclock += t;
syncclock += t;
+ checkclock += t;
if(ppuclock >= 456){
ppustep();
ppuclock -= 456;
@@ -285,15 +295,22 @@
}
timerclock = 0;
}
- if(syncclock >= CPUFREQ / 100){
+ if(syncclock >= syncfreq){
+ sleep(10);
+ sleeps++;
+ syncclock = 0;
+ }
+ if(checkclock >= CPUFREQ){
new = nsec();
- diff = new - old;
- diff = 10000000 - diff;
- diff /= 1000000;
- if(diff > 0)
- sleep(diff);
+ diff = new - old - sleeps * 10 * MILLION;
+ diff = BILLION - diff;
+ if(diff <= 0)
+ syncfreq = CPUFREQ;
+ else
+ syncfreq = ((vlong)CPUFREQ) * 10 * MILLION / diff;
old = new;
- syncclock = 0;
+ checkclock = 0;
+ sleeps = 0;
}
if(msgclock > 0){
msgclock -= t;
--- a/sys/src/games/gb/mem.c
+++ b/sys/src/games/gb/mem.c
@@ -117,6 +117,26 @@
return;
}
return;
+ case 5:
+ switch(p >> 13){
+ case 0:
+ if((v & 0x0F) == 0x0A)
+ ramswitch(1, rambank);
+ else
+ ramswitch(0, rambank);
+ return;
+ case 1:
+ romswitch((rombank & 0x100) | v);
+ return;
+ case 2:
+ romswitch((((int)v & 1) << 8) | (rombank & 0xFF));
+ return;
+ case 3:
+ ramswitch(ramen, v & 15);
+ return;
+
+ }
+ return;
default:
sysfatal("mbc %d unimplemented", mbc);
}
--- a/sys/src/games/gb/mkfile
+++ b/sys/src/games/gb/mkfile
@@ -10,6 +10,7 @@
ppu.$O\
daa.$O\
state.$O\
+ audio.$O\
HFILES=dat.h fns.h
--- a/sys/src/games/gb/ppu.c
+++ b/sys/src/games/gb/ppu.c
@@ -79,24 +79,33 @@
static void
drawsprites(void)
{
- u8int x, y, tnl1, tnl2, dx, ddx, val, pal;
+ u8int y, t, tnl1, tnl2, dx, ddx, val, pal;
schar dy;
u16int tnli;
- int i;
+ int i, x, big;
struct { u8int y, x, t, f; } *s;
y = mem[LY];
+ big = mem[LCDC] & SPRITE16;
s = (void*)(mem + 0xFE00);
for(i = 0; i < 40; i++, s++){
- if(s->y == 0 && s->x == 0)
+ if(s->y == 0 || s->x == 0)
continue;
dy = y - s->y + 16;
- if(dy < 0 || dy >= 8)
+ if(dy < 0 || dy >= (big ? 16 : 8))
continue;
pal = (s->f & (1<<4)) ? mem[OBP1] : mem[OBP0];
if(s->f & (1<<6))
- dy = 7 - dy;
- tnli = 0x8000 + 2 * (u16int)dy + 16 * (u16int) s->t;
+ dy = (big ? 15 : 7) - dy;
+ t = s->t;
+ if(big){
+ if(dy >= 8){
+ t |= 1;
+ dy -= 8;
+ }else
+ t &= ~1;
+ }
+ tnli = 0x8000 + 2 * (u16int)dy + 16 * (u16int) t;
tnl1 = mem[tnli];
tnl2 = mem[tnli + 1];
x = s->x - 9;
@@ -105,14 +114,14 @@
if((s->f & (1<<5)) == 0)
ddx = 7 - dx;
val = ((tnl1 >> ddx) & 1) | (((tnl2 >> ddx) & 1) << 1);
- if(val == 0)
+ if(x < 0 || val == 0)
continue;
val = (pal >> (2 * val)) & 3;
if(x >= 160)
break;
- if(s->f & (1<<7)){
+ if(s->f & (1<<7))
pixelbelow(x, y, val);
- }else
+ else
pixel(x, y, val, 0);
}
}
@@ -167,8 +176,9 @@
interrupt(INTLCDC);
}else
mem[STAT] &= ~4;
- if(mem[LY] < 144 && (mem[LCDC] & LCDOP)){
+ if(mem[LY] < 144)
mem[STAT] &= ~3;
+ if(mem[LY] < 144 && (mem[LCDC] & LCDOP)){
if(mem[LCDC] & BGDISP)
drawbg();
if(mem[LCDC] & WINDOWDISP)
--- a/sys/src/games/gb/state.c
+++ b/sys/src/games/gb/state.c
@@ -81,7 +81,6 @@
clock = get32();
ppuclock = get32();
divclock = get32();
- syncclock = get32();
timerfreq = get32();
timer = get32();
rombank = get32();
@@ -114,7 +113,6 @@
put32(ppuclock);
put32(divclock);
put32(timerclock);
- put32(syncclock);
put32(timerfreq);
put32(timer);
put32(rombank);