From 8ebb846fb2b3b6ba6e828be2ba1ed37d6b4847f2 Mon Sep 17 00:00:00 2001 From: aiju Date: Fri, 6 Apr 2012 20:57:45 +0200 Subject: [PATCH] more games/gb fun --- sys/src/games/gb/audio.c | 152 +++++++++++++++++++++++++++++++++++++++ sys/src/games/gb/cpu.c | 21 +++--- sys/src/games/gb/dat.h | 5 +- sys/src/games/gb/fns.h | 1 + sys/src/games/gb/gb.c | 35 ++++++--- sys/src/games/gb/mem.c | 20 ++++++ sys/src/games/gb/mkfile | 1 + sys/src/games/gb/ppu.c | 30 +++++--- sys/src/games/gb/state.c | 2 - 9 files changed, 236 insertions(+), 31 deletions(-) create mode 100644 sys/src/games/gb/audio.c diff --git a/sys/src/games/gb/audio.c b/sys/src/games/gb/audio.c new file mode 100644 index 000000000..94e08967b --- /dev/null +++ b/sys/src/games/gb/audio.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#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); +} diff --git a/sys/src/games/gb/cpu.c b/sys/src/games/gb/cpu.c index 691527ccc..315cf1336 100644 --- a/sys/src/games/gb/cpu.c +++ b/sys/src/games/gb/cpu.c @@ -793,14 +793,14 @@ step(void) 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 @@ step(void) 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]); diff --git a/sys/src/games/gb/dat.h b/sys/src/games/gb/dat.h index 1e13d918d..1ecf533bb 100644 --- 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 @@ enum { IF = 0xFF0F, IE = 0xFFFF, CPUFREQ = 4194304, + + MILLION = 1000000, + BILLION = 1000000000, }; diff --git a/sys/src/games/gb/fns.h b/sys/src/games/gb/fns.h index 990f794e1..8d43d223b 100644 --- a/sys/src/games/gb/fns.h +++ b/sys/src/games/gb/fns.h @@ -8,3 +8,4 @@ void message(char *, ...); void flushram(void); void savestate(char *); void loadstate(char *); +void initaudio(void); \ No newline at end of file diff --git a/sys/src/games/gb/gb.c b/sys/src/games/gb/gb.c index bed16e2cb..5f2509ab9 100644 --- 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 @@ loadrom(char *file) 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 @@ threadmain(int argc, char** argv) Point p; ARGBEGIN{ + case 'a': + initaudio(); + break; default: sysfatal("unknown flag -%c", ARGC()); }ARGEND; @@ -244,6 +252,7 @@ threadmain(int argc, char** argv) if(mc == nil) sysfatal("init mouse: %r"); proccreate(keyproc, nil, 8192); + syncfreq = CPUFREQ / 50; old = nsec(); for(;;){ if(savereq){ @@ -260,6 +269,7 @@ threadmain(int argc, char** argv) divclock += t; timerclock += t; syncclock += t; + checkclock += t; if(ppuclock >= 456){ ppustep(); ppuclock -= 456; @@ -285,16 +295,23 @@ threadmain(int argc, char** argv) } timerclock = 0; } - if(syncclock >= CPUFREQ / 100){ - new = nsec(); - diff = new - old; - diff = 10000000 - diff; - diff /= 1000000; - if(diff > 0) - sleep(diff); - old = new; + if(syncclock >= syncfreq){ + sleep(10); + sleeps++; syncclock = 0; } + if(checkclock >= CPUFREQ){ + new = nsec(); + diff = new - old - sleeps * 10 * MILLION; + diff = BILLION - diff; + if(diff <= 0) + syncfreq = CPUFREQ; + else + syncfreq = ((vlong)CPUFREQ) * 10 * MILLION / diff; + old = new; + checkclock = 0; + sleeps = 0; + } if(msgclock > 0){ msgclock -= t; if(msgclock <= 0){ diff --git a/sys/src/games/gb/mem.c b/sys/src/games/gb/mem.c index 4348a5567..81f37b201 100644 --- a/sys/src/games/gb/mem.c +++ b/sys/src/games/gb/mem.c @@ -117,6 +117,26 @@ memwrite(u16int p, u8int v) 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); } diff --git a/sys/src/games/gb/mkfile b/sys/src/games/gb/mkfile index c500e64b0..de0152f66 100644 --- a/sys/src/games/gb/mkfile +++ b/sys/src/games/gb/mkfile @@ -10,6 +10,7 @@ OFILES=\ ppu.$O\ daa.$O\ state.$O\ + audio.$O\ HFILES=dat.h fns.h diff --git a/sys/src/games/gb/ppu.c b/sys/src/games/gb/ppu.c index a83ea1980..dde9576c6 100644 --- a/sys/src/games/gb/ppu.c +++ b/sys/src/games/gb/ppu.c @@ -79,24 +79,33 @@ drawbg(void) 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 @@ drawsprites(void) 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 @@ ppustep(void) 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) diff --git a/sys/src/games/gb/state.c b/sys/src/games/gb/state.c index c5db3088a..2b2f57391 100644 --- a/sys/src/games/gb/state.c +++ b/sys/src/games/gb/state.c @@ -81,7 +81,6 @@ loadstate(char *file) clock = get32(); ppuclock = get32(); divclock = get32(); - syncclock = get32(); timerfreq = get32(); timer = get32(); rombank = get32(); @@ -114,7 +113,6 @@ savestate(char *file) put32(ppuclock); put32(divclock); put32(timerclock); - put32(syncclock); put32(timerfreq); put32(timer); put32(rombank);