137fb6cb9f
Here's another patch for rockboy that adds automatic frameskip (it's pretty rough as I haven't figured out an accurate timer), fullscreen support on the H300, and a bit of assembly and some IRAM stuff. I'm not sure if I'm doing the IRAM stuff correct though as it doesn't seem to make much of a difference if any. I've also added a statistics option that will show how many frames per second the gameboy is seeing (not what the player is getting) and what the frameskip is at. When you enable stats sometimes you have to go back into the menu and then come out to clear erronous values. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8397 a1c6a512-1295-4272-9138-f99709370657
469 lines
10 KiB
C
469 lines
10 KiB
C
|
|
|
|
|
|
#include "rockmacros.h"
|
|
#include "defs.h"
|
|
#include "pcm.h"
|
|
#include "sound.h"
|
|
#include "cpu-gb.h"
|
|
#include "hw.h"
|
|
#include "regs.h"
|
|
#include "rc.h"
|
|
#include "noise.h"
|
|
|
|
|
|
static const byte dmgwave[16] =
|
|
{
|
|
0xac, 0xdd, 0xda, 0x48,
|
|
0x36, 0x02, 0xcf, 0x16,
|
|
0x2c, 0x04, 0xe5, 0x2c,
|
|
0xac, 0xdd, 0xda, 0x48
|
|
};
|
|
|
|
static const byte cgbwave[16] =
|
|
{
|
|
0x00, 0xff, 0x00, 0xff,
|
|
0x00, 0xff, 0x00, 0xff,
|
|
0x00, 0xff, 0x00, 0xff,
|
|
0x00, 0xff, 0x00, 0xff,
|
|
};
|
|
|
|
|
|
static const byte sqwave[4][8] =
|
|
{
|
|
{ 0, 0,-1, 0, 0, 0, 0, 0 },
|
|
{ 0,-1,-1, 0, 0, 0, 0, 0 },
|
|
{ -1,-1,-1,-1, 0, 0, 0, 0 },
|
|
{ -1, 0, 0,-1,-1,-1,-1,-1 }
|
|
};
|
|
|
|
static const int freqtab[8] =
|
|
{
|
|
(1<<14)*2,
|
|
(1<<14),
|
|
(1<<14)/2,
|
|
(1<<14)/3,
|
|
(1<<14)/4,
|
|
(1<<14)/5,
|
|
(1<<14)/6,
|
|
(1<<14)/7
|
|
};
|
|
|
|
struct snd snd IBSS_ATTR;
|
|
int pcm_submit(void);
|
|
|
|
#define RATE (snd.rate)
|
|
#define WAVE (snd.wave) /* ram.hi+0x30 */
|
|
#define S1 (snd.ch[0])
|
|
#define S2 (snd.ch[1])
|
|
#define S3 (snd.ch[2])
|
|
#define S4 (snd.ch[3])
|
|
|
|
rcvar_t sound_exports[] =
|
|
{
|
|
RCV_END
|
|
};
|
|
|
|
|
|
static void s1_freq_d(int d)
|
|
{
|
|
if (RATE > (d<<4)) S1.freq = 0;
|
|
else S1.freq = (RATE << 17)/d;
|
|
}
|
|
|
|
static void s1_freq(void)
|
|
{
|
|
s1_freq_d(2048 - (((R_NR14&7)<<8) + R_NR13));
|
|
}
|
|
|
|
static void s2_freq(void)
|
|
{
|
|
int d = 2048 - (((R_NR24&7)<<8) + R_NR23);
|
|
if (RATE > (d<<4)) S2.freq = 0;
|
|
else S2.freq = (RATE << 17)/d;
|
|
}
|
|
|
|
static void s3_freq(void)
|
|
{
|
|
int d = 2048 - (((R_NR34&7)<<8) + R_NR33);
|
|
if (RATE > (d<<3)) S3.freq = 0;
|
|
else S3.freq = (RATE << 21)/d;
|
|
}
|
|
|
|
static void s4_freq(void)
|
|
{
|
|
S4.freq = (freqtab[R_NR43&7] >> (R_NR43 >> 4)) * RATE;
|
|
if (S4.freq >> 18) S4.freq = 1<<18;
|
|
}
|
|
|
|
void sound_dirty(void)
|
|
{
|
|
S1.swlen = ((R_NR10>>4) & 7) << 14;
|
|
S1.len = (64-(R_NR11&63)) << 13;
|
|
S1.envol = R_NR12 >> 4;
|
|
S1.endir = (R_NR12>>3) & 1;
|
|
S1.endir |= S1.endir - 1;
|
|
S1.enlen = (R_NR12 & 7) << 15;
|
|
s1_freq();
|
|
S2.len = (64-(R_NR21&63)) << 13;
|
|
S2.envol = R_NR22 >> 4;
|
|
S2.endir = (R_NR22>>3) & 1;
|
|
S2.endir |= S2.endir - 1;
|
|
S2.enlen = (R_NR22 & 7) << 15;
|
|
s2_freq();
|
|
S3.len = (256-R_NR31) << 20;
|
|
s3_freq();
|
|
S4.len = (64-(R_NR41&63)) << 13;
|
|
S4.envol = R_NR42 >> 4;
|
|
S4.endir = (R_NR42>>3) & 1;
|
|
S4.endir |= S4.endir - 1;
|
|
S4.enlen = (R_NR42 & 7) << 15;
|
|
s4_freq();
|
|
}
|
|
|
|
void sound_off(void)
|
|
{
|
|
memset(&S1, 0, sizeof S1);
|
|
memset(&S2, 0, sizeof S2);
|
|
memset(&S3, 0, sizeof S3);
|
|
memset(&S4, 0, sizeof S4);
|
|
R_NR10 = 0x80;
|
|
R_NR11 = 0xBF;
|
|
R_NR12 = 0xF3;
|
|
R_NR14 = 0xBF;
|
|
R_NR21 = 0x3F;
|
|
R_NR22 = 0x00;
|
|
R_NR24 = 0xBF;
|
|
R_NR30 = 0x7F;
|
|
R_NR31 = 0xFF;
|
|
R_NR32 = 0x9F;
|
|
R_NR33 = 0xBF;
|
|
R_NR41 = 0xFF;
|
|
R_NR42 = 0x00;
|
|
R_NR43 = 0x00;
|
|
R_NR44 = 0xBF;
|
|
R_NR50 = 0x77;
|
|
R_NR51 = 0xF3;
|
|
R_NR52 = 0xF1;
|
|
sound_dirty();
|
|
}
|
|
|
|
void sound_reset(void)
|
|
{
|
|
memset(&snd, 0, sizeof snd);
|
|
if (pcm.hz) snd.rate = (1<<21) / pcm.hz;
|
|
else snd.rate = 0;
|
|
memcpy(WAVE, hw.cgb ? cgbwave : dmgwave, 16);
|
|
memcpy(ram.hi+0x30, WAVE, 16);
|
|
sound_off();
|
|
}
|
|
|
|
void sound_mix(void)
|
|
{
|
|
|
|
if (!options.sound) return;
|
|
int s, l, r, f, n;
|
|
|
|
if (!RATE || cpu.snd < RATE) return;
|
|
|
|
for (; cpu.snd >= RATE; cpu.snd -= RATE)
|
|
{
|
|
l = r = 0;
|
|
|
|
if (S1.on)
|
|
{
|
|
s = sqwave[R_NR11>>6][(S1.pos>>18)&7] & S1.envol;
|
|
S1.pos += S1.freq;
|
|
if ((R_NR14 & 64) && ((S1.cnt += RATE) >= S1.len))
|
|
S1.on = 0;
|
|
if (S1.enlen && (S1.encnt += RATE) >= S1.enlen)
|
|
{
|
|
S1.encnt -= S1.enlen;
|
|
S1.envol += S1.endir;
|
|
if (S1.envol < 0) S1.envol = 0;
|
|
if (S1.envol > 15) S1.envol = 15;
|
|
}
|
|
if (S1.swlen && (S1.swcnt += RATE) >= S1.swlen)
|
|
{
|
|
S1.swcnt -= S1.swlen;
|
|
f = S1.swfreq;
|
|
n = (R_NR10 & 7);
|
|
if (R_NR10 & 8) f -= (f >> n);
|
|
else f += (f >> n);
|
|
if (f > 2047)
|
|
S1.on = 0;
|
|
else
|
|
{
|
|
S1.swfreq = f;
|
|
R_NR13 = f;
|
|
R_NR14 = (R_NR14 & 0xF8) | (f>>8);
|
|
s1_freq_d(2048 - f);
|
|
}
|
|
}
|
|
s <<= 2;
|
|
if (R_NR51 & 1) r += s;
|
|
if (R_NR51 & 16) l += s;
|
|
}
|
|
|
|
if (S2.on)
|
|
{
|
|
s = sqwave[R_NR21>>6][(S2.pos>>18)&7] & S2.envol;
|
|
S2.pos += S2.freq;
|
|
if ((R_NR24 & 64) && ((S2.cnt += RATE) >= S2.len))
|
|
S2.on = 0;
|
|
if (S2.enlen && (S2.encnt += RATE) >= S2.enlen)
|
|
{
|
|
S2.encnt -= S2.enlen;
|
|
S2.envol += S2.endir;
|
|
if (S2.envol < 0) S2.envol = 0;
|
|
if (S2.envol > 15) S2.envol = 15;
|
|
}
|
|
s <<= 2;
|
|
if (R_NR51 & 2) r += s;
|
|
if (R_NR51 & 32) l += s;
|
|
}
|
|
|
|
if (S3.on)
|
|
{
|
|
s = WAVE[(S3.pos>>22) & 15];
|
|
if (S3.pos & (1<<21)) s &= 15;
|
|
else s >>= 4;
|
|
s -= 8;
|
|
S3.pos += S3.freq;
|
|
if ((R_NR34 & 64) && ((S3.cnt += RATE) >= S3.len))
|
|
S3.on = 0;
|
|
if (R_NR32 & 96) s <<= (3 - ((R_NR32>>5)&3));
|
|
else s = 0;
|
|
if (R_NR51 & 4) r += s;
|
|
if (R_NR51 & 64) l += s;
|
|
}
|
|
|
|
if (S4.on)
|
|
{
|
|
if (R_NR43 & 8) s = 1 & (noise7[
|
|
(S4.pos>>20)&15] >> (7-((S4.pos>>17)&7)));
|
|
else s = 1 & (noise15[
|
|
(S4.pos>>20)&4095] >> (7-((S4.pos>>17)&7)));
|
|
s = (-s) & S4.envol;
|
|
S4.pos += S4.freq;
|
|
if ((R_NR44 & 64) && ((S4.cnt += RATE) >= S4.len))
|
|
S4.on = 0;
|
|
if (S4.enlen && (S4.encnt += RATE) >= S4.enlen)
|
|
{
|
|
S4.encnt -= S4.enlen;
|
|
S4.envol += S4.endir;
|
|
if (S4.envol < 0) S4.envol = 0;
|
|
if (S4.envol > 15) S4.envol = 15;
|
|
}
|
|
s += s << 1;
|
|
if (R_NR51 & 8) r += s;
|
|
if (R_NR51 & 128) l += s;
|
|
}
|
|
|
|
l *= (R_NR50 & 0x07);
|
|
r *= ((R_NR50 & 0x70)>>4);
|
|
l >>= 4;
|
|
r >>= 4;
|
|
|
|
if (l > 127) l = 127;
|
|
else if (l < -128) l = -128;
|
|
if (r > 127) r = 127;
|
|
else if (r < -128) r = -128;
|
|
|
|
if (pcm.buf)
|
|
{
|
|
if (pcm.pos >= pcm.len)
|
|
pcm_submit();
|
|
if (pcm.stereo)
|
|
{
|
|
pcm.buf[pcm.pos++] = l+128;
|
|
pcm.buf[pcm.pos++] = r+128;
|
|
}
|
|
else pcm.buf[pcm.pos++] = ((l+r)>>1)+128;
|
|
}
|
|
}
|
|
R_NR52 = (R_NR52&0xf0) | S1.on | (S2.on<<1) | (S3.on<<2) | (S4.on<<3);
|
|
}
|
|
|
|
|
|
|
|
byte sound_read(byte r)
|
|
{
|
|
if(!options.sound) return 0;
|
|
sound_mix();
|
|
/* printf("read %02X: %02X\n", r, REG(r)); */
|
|
return REG(r);
|
|
}
|
|
|
|
void s1_init(void)
|
|
{
|
|
S1.swcnt = 0;
|
|
S1.swfreq = ((R_NR14&7)<<8) + R_NR13;
|
|
S1.envol = R_NR12 >> 4;
|
|
S1.endir = (R_NR12>>3) & 1;
|
|
S1.endir |= S1.endir - 1;
|
|
S1.enlen = (R_NR12 & 7) << 15;
|
|
if (!S1.on) S1.pos = 0;
|
|
S1.on = 1;
|
|
S1.cnt = 0;
|
|
S1.encnt = 0;
|
|
}
|
|
|
|
void s2_init(void)
|
|
{
|
|
S2.envol = R_NR22 >> 4;
|
|
S2.endir = (R_NR22>>3) & 1;
|
|
S2.endir |= S2.endir - 1;
|
|
S2.enlen = (R_NR22 & 7) << 15;
|
|
if (!S2.on) S2.pos = 0;
|
|
S2.on = 1;
|
|
S2.cnt = 0;
|
|
S2.encnt = 0;
|
|
}
|
|
|
|
void s3_init(void)
|
|
{
|
|
int i;
|
|
if (!S3.on) S3.pos = 0;
|
|
S3.cnt = 0;
|
|
S3.on = R_NR30 >> 7;
|
|
if (S3.on) for (i = 0; i < 16; i++)
|
|
ram.hi[i+0x30] = 0x13 ^ ram.hi[i+0x31];
|
|
}
|
|
|
|
void s4_init(void)
|
|
{
|
|
S4.envol = R_NR42 >> 4;
|
|
S4.endir = (R_NR42>>3) & 1;
|
|
S4.endir |= S4.endir - 1;
|
|
S4.enlen = (R_NR42 & 7) << 15;
|
|
S4.on = 1;
|
|
S4.pos = 0;
|
|
S4.cnt = 0;
|
|
S4.encnt = 0;
|
|
}
|
|
|
|
|
|
void sound_write(byte r, byte b)
|
|
{
|
|
if(!options.sound) return;
|
|
#if 0
|
|
static void *timer;
|
|
if (!timer) timer = sys_timer();
|
|
printf("write %02X: %02X @ %d\n", r, b, sys_elapsed(timer));
|
|
#endif
|
|
|
|
if (!(R_NR52 & 128) && r != RI_NR52) return;
|
|
if ((r & 0xF0) == 0x30)
|
|
{
|
|
if (S3.on) sound_mix();
|
|
if (!S3.on)
|
|
WAVE[r-0x30] = ram.hi[r] = b;
|
|
return;
|
|
}
|
|
sound_mix();
|
|
switch (r)
|
|
{
|
|
case RI_NR10:
|
|
R_NR10 = b;
|
|
S1.swlen = ((R_NR10>>4) & 7) << 14;
|
|
S1.swfreq = ((R_NR14&7)<<8) + R_NR13;
|
|
break;
|
|
case RI_NR11:
|
|
R_NR11 = b;
|
|
S1.len = (64-(R_NR11&63)) << 13;
|
|
break;
|
|
case RI_NR12:
|
|
R_NR12 = b;
|
|
S1.envol = R_NR12 >> 4;
|
|
S1.endir = (R_NR12>>3) & 1;
|
|
S1.endir |= S1.endir - 1;
|
|
S1.enlen = (R_NR12 & 7) << 15;
|
|
break;
|
|
case RI_NR13:
|
|
R_NR13 = b;
|
|
s1_freq();
|
|
break;
|
|
case RI_NR14:
|
|
R_NR14 = b;
|
|
s1_freq();
|
|
if (b & 128) s1_init();
|
|
break;
|
|
case RI_NR21:
|
|
R_NR21 = b;
|
|
S2.len = (64-(R_NR21&63)) << 13;
|
|
break;
|
|
case RI_NR22:
|
|
R_NR22 = b;
|
|
S2.envol = R_NR22 >> 4;
|
|
S2.endir = (R_NR22>>3) & 1;
|
|
S2.endir |= S2.endir - 1;
|
|
S2.enlen = (R_NR22 & 7) << 15;
|
|
break;
|
|
case RI_NR23:
|
|
R_NR23 = b;
|
|
s2_freq();
|
|
break;
|
|
case RI_NR24:
|
|
R_NR24 = b;
|
|
s2_freq();
|
|
if (b & 128) s2_init();
|
|
break;
|
|
case RI_NR30:
|
|
R_NR30 = b;
|
|
if (!(b & 128)) S3.on = 0;
|
|
break;
|
|
case RI_NR31:
|
|
R_NR31 = b;
|
|
S3.len = (256-R_NR31) << 13;
|
|
break;
|
|
case RI_NR32:
|
|
R_NR32 = b;
|
|
break;
|
|
case RI_NR33:
|
|
R_NR33 = b;
|
|
s3_freq();
|
|
break;
|
|
case RI_NR34:
|
|
R_NR34 = b;
|
|
s3_freq();
|
|
if (b & 128) s3_init();
|
|
break;
|
|
case RI_NR41:
|
|
R_NR41 = b;
|
|
S4.len = (64-(R_NR41&63)) << 13;
|
|
break;
|
|
case RI_NR42:
|
|
R_NR42 = b;
|
|
S4.envol = R_NR42 >> 4;
|
|
S4.endir = (R_NR42>>3) & 1;
|
|
S4.endir |= S4.endir - 1;
|
|
S4.enlen = (R_NR42 & 7) << 15;
|
|
break;
|
|
case RI_NR43:
|
|
R_NR43 = b;
|
|
s4_freq();
|
|
break;
|
|
case RI_NR44:
|
|
R_NR44 = b;
|
|
if (b & 128) s4_init();
|
|
break;
|
|
case RI_NR50:
|
|
R_NR50 = b;
|
|
break;
|
|
case RI_NR51:
|
|
R_NR51 = b;
|
|
break;
|
|
case RI_NR52:
|
|
R_NR52 = b;
|
|
if (!(R_NR52 & 128))
|
|
sound_off();
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|