/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * The following code is rewrite of the C++ code provided in VisualBoyAdvance. * There are also portions of the original GNUboy code. * Copyright (C) 2001 the GNUboy development team * * VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. * Copyright (C) 1999-2003 Forgotten * Copyright (C) 2004 Forgotten and the VBA development team * * VisualBoyAdvance conversion from C++ to C by Karl Kurbjun July, 2007 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * */ #include "rockmacros.h" #include "defs.h" #include "pcm.h" #include "sound.h" #include "cpu-gb.h" #include "hw.h" #include "regs.h" static const byte soundWavePattern[4][32] = { {0x01,0x01,0x01,0x01, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff}, {0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff}, {0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff}, {0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff} }; int soundFreqRatio[8] ICONST_ATTR= { 1048576, // 0 524288, // 1 262144, // 2 174763, // 3 131072, // 4 104858, // 5 87381, // 6 74898 // 7 }; int soundShiftClock[16] ICONST_ATTR= { 2, // 0 4, // 1 8, // 2 16, // 3 32, // 4 64, // 5 128, // 6 256, // 7 512, // 8 1024, // 9 2048, // 10 4096, // 11 8192, // 12 16384, // 13 1, // 14 1 // 15 }; struct snd snd IBSS_ATTR; #define RATE (snd.rate) #define WAVE (ram.hi+0x30) #define S1 (snd.ch[0]) #define S2 (snd.ch[1]) #define S3 (snd.ch[2]) #define S4 (snd.ch[3]) #define SOUND_MAGIC 0x60000000 #define SOUND_MAGIC_2 0x30000000 #define NOISE_MAGIC 5 static void gbSoundChannel1(int *r, int *l) { int vol = S1.envol; int freq = 0; int value = 0; if(S1.on && (S1.len || !S1.cont)) { S1.pos += snd.quality*S1.skip; S1.pos &= 0x1fffffff; value = ((signed char)S1.wave[S1.pos>>24]) * vol; } if (snd.balance & 1) *r += value; if (snd.balance & 16) *l += value; if(S1.on) { if(S1.len) { S1.len-=snd.quality; if(S1.len <=0 && S1.cont) { R_NR52 &= 0xfe; S1.on = 0; } } if(S1.enlen) { S1.enlen-=snd.quality; if(S1.enlen<=0) { if(S1.endir) { if(S1.envol < 15) S1.envol++; } else { if(S1.envol) S1.envol--; } S1.enlen += S1.enlenreload; } } if(S1.swlen) { S1.swlen-=snd.quality; if(S1.swlen<=0) { freq = (((int)(R_NR14&7) << 8) | R_NR13); int updown = 1; if(S1.swdir) updown = -1; int newfreq = 0; if(S1.swsteps) { newfreq = freq + updown * freq / (1 << S1.swsteps); if(newfreq == freq) newfreq = 0; } else newfreq = freq; if(newfreq < 0) { S1.swlen += S1.swlenreload; } else if(newfreq > 2047) { S1.swlen = 0; S1.on = 0; R_NR52 &= 0xfe; } else { S1.swlen += S1.swlenreload; S1.skip = SOUND_MAGIC/(2048 - newfreq); R_NR13 = newfreq & 0xff; R_NR14 = (R_NR14 & 0xf8) |((newfreq >> 8) & 7); } } } } } static void gbSoundChannel2(int *r, int *l) { int vol = S2.envol; int value = 0; if(S2.on && (S2.len || !S2.cont)) { S2.pos += snd.quality*S2.skip; S2.pos &= 0x1fffffff; value = ((signed char)S2.wave[S2.pos>>24]) * vol; } if (snd.balance & 2) *r += value; if (snd.balance & 32) *l += value; if(S2.on) { if(S2.len) { S2.len-=snd.quality; if(S2.len <= 0 && S2.cont) { R_NR52 &= 0xfd; S2.on = 0; } } if(S2.enlen) { S2.enlen-=snd.quality; if(S2.enlen <= 0) { if(S2.endir) { if(S2.envol < 15) S2.envol++; } else { if(S2.envol) S2.envol--; } S2.enlen += S2.enlenreload; } } } } static void gbSoundChannel3(int *r, int *l) { int s; if (S3.on && (S3.len || !S3.cont)) { S3.pos += S3.skip*snd.quality; S3.pos &= 0x1fffffff; s=ram.hi[0x30 + (S3.pos>>25)]; if (S3.pos & 0x01000000) s &= 0x0f; else s >>= 4; s -= 8; switch(S3.outputlevel) { case 0: s=0; break; case 1: break; case 2: s=s>>1; break; case 3: s=s>>2; break; } if (snd.balance & 4) *r += s; if (snd.balance & 64) *l += s; } if(S3.on) { if(S3.len) { S3.len-=snd.quality; if(S3.len<=0 && S3.cont) { R_NR52 &= 0xFB; S3.on=0; } } } } static void gbSoundChannel4(int *r, int *l) { int vol = S4.envol; int value = 0; if(S4.clock <= 0x0c) { if(S4.on && (S4.len || !S4.cont)) { S4.pos += snd.quality*S4.skip; S4.shiftpos += snd.quality*S4.shiftskip; if(S4.nsteps) { while(S4.shiftpos > 0x1fffff) { S4.shiftright = (((S4.shiftright << 6) ^ (S4.shiftright << 5)) & 0x40) | (S4.shiftright >> 1); S4.shiftpos -= 0x200000; } } else { while(S4.shiftpos > 0x1fffff) { S4.shiftright = (((S4.shiftright << 14) ^ (S4.shiftright << 13)) & 0x4000) | (S4.shiftright >> 1); S4.shiftpos -= 0x200000; } } S4.pos &= 0x1fffff; S4.shiftpos &= 0x1fffff; value = ((S4.shiftright & 1)*2-1) * vol; } else { value = 0; } } if (snd.balance & 8) *r += value; if (snd.balance & 128) *l += value; if(S4.on) { if(S4.len) { S4.len-=snd.quality; if(S4.len <= 0 && S4.cont) { R_NR52 &= 0xfd; S4.on = 0; } } if(S4.enlen) { S4.enlen-=snd.quality; if(S4.enlen <= 0) { if(S4.endir) { if(S4.envol < 15) S4.envol++; } else { if(S4.envol) S4.envol--; } S4.enlen += S4.enlenreload; } } } } void sound_mix(void) { int l, r; if (!RATE || cpu.snd < RATE) return; for (; cpu.snd >= RATE; cpu.snd -= RATE) { l = r = 0; gbSoundChannel1(&r,&l); gbSoundChannel2(&r,&l); gbSoundChannel3(&r,&l); gbSoundChannel4(&r,&l); if(snd.gbDigitalSound) { l = snd.level1<<8; r = snd.level2<<8; } else { l *= snd.level1*60; r *= snd.level2*60; } if(l > 32767) l = 32767; if(l < -32768) l = -32768; if(r > 32767) r = 32767; if(r < -32768) r = -32768; if (pcm.buf) { if (pcm.pos >= pcm.len) pcm_submit(); if (pcm.stereo) { pcm.buf[pcm.pos++] = l; pcm.buf[pcm.pos++] = r; } else pcm.buf[pcm.pos++] = ((l+r)>>1); } } 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 sound_write(byte r, byte b) { int freq=0; ram.hi[r]=b; if(!options.sound) return; sound_mix(); switch (r) { case RI_NR10: S1.swlen = S1.swlenreload = 344 * ((b >> 4) & 7); S1.swsteps = b & 7; S1.swdir = b & 0x08; S1.swstep = 0; break; case RI_NR11: S1.len = 172 * (64 - (b & 0x3f)); S1.wave = soundWavePattern[b >> 6]; break; case RI_NR12: S1.envol = b >> 4; S1.endir = b & 0x08; S1.enlenreload = S1.enlen = 689 * (b & 7); break; case RI_NR13: freq = (((int)(R_NR14 & 7)) << 8) | b; S1.len = 172 * (64 - (R_NR11 & 0x3f)); freq = 2048 - freq; if(freq) { S1.skip = SOUND_MAGIC / freq; } else { S1.skip = 0; } break; case RI_NR14: freq = (((int)(b&7) << 8) | R_NR13); freq = 2048 - freq; S1.len = 172 * (64 - (R_NR11 & 0x3f)); S1.cont = b & 0x40; if(freq) { S1.skip = SOUND_MAGIC / freq; } else { S1.skip = 0; } if(b & 0x80) { R_NR52 |= 1; S1.envol = R_NR12 >> 4; S1.endir = R_NR12 & 0x08; S1.len = 172 * (64 - (R_NR11 & 0x3f)); S1.enlenreload = S1.enlen = 689 * (R_NR12 & 7); S1.swlen = S1.swlenreload = 344 * ((R_NR10 >> 4) & 7); S1.swsteps = R_NR10 & 7; S1.swdir = R_NR10 & 0x08; S1.swstep = 0; S1.pos = 0; S1.on = 1; } break; case RI_NR21: S2.wave = soundWavePattern[b >> 6]; S2.len = 172 * (64 - (b & 0x3f)); break; case RI_NR22: S2.envol = b >> 4; S2.endir = b & 0x08; S2.enlenreload = S2.enlen = 689 * (b & 7); break; case RI_NR23: freq = (((int)(R_NR24 & 7)) << 8) | b; S2.len = 172 * (64 - (R_NR21 & 0x3f)); freq = 2048 - freq; if(freq) { S2.skip = SOUND_MAGIC / freq; } else { S2.skip = 0; } break; case RI_NR24: freq = (((int)(b&7) << 8) | R_NR23); freq = 2048 - freq; S2.len = 172 * (64 - (R_NR21 & 0x3f)); S2.cont = b & 0x40; if(freq) { S2.skip = SOUND_MAGIC / freq; } else S2.skip = 0; if(b & 0x80) { R_NR52 |= 2; S2.envol = R_NR22 >> 4; S2.endir = R_NR22 & 0x08; S2.len = 172 * (64 - (R_NR21 & 0x3f)); S2.enlenreload = S2.enlen = 689 * (R_NR22 & 7); S2.pos = 0; S2.on = 1; } break; case RI_NR30: if (!(b & 0x80)){ R_NR52 &= 0xfb; S3.on = 0; } break; case RI_NR31: S3.len = (256-R_NR31) * 172; break; case RI_NR32: S3.outputlevel = (b >> 5) & 3; break; case RI_NR33: freq = 2048 - (((int)(R_NR34&7) << 8) | b); if(freq) S3.skip = SOUND_MAGIC_2 / freq; else S3.skip = 0; break; case RI_NR34: freq = 2048 - (((b&7)<<8) | R_NR33); if(freq) S3.skip = SOUND_MAGIC_2 / freq; else S3.skip = 0; S3.cont=b & 0x40; if((b & 0x80) && (R_NR30 & 0x80)) { R_NR52 |= 4; S3.len = 172 * (256 - R_NR31); S3.pos = 0; S3.on = 1; } break; case RI_NR41: S4.len = 172 * (64 - (b & 0x3f)); break; case RI_NR42: S4.envol = b >> 4; S4.endir = b & 0x08; S4.enlenreload = S4.enlen = 689 * (b & 7); break; case RI_NR43: freq = soundFreqRatio[b & 7]; S4.nsteps = b & 0x08; S4.skip = (freq << 8) / NOISE_MAGIC; S4.clock = b >> 4; freq = freq / soundShiftClock[S4.clock]; S4.shiftskip = (freq << 8) / NOISE_MAGIC; break; case RI_NR44: S4.cont = b & 0x40; if(b & 0x80) { R_NR52 |= 8; S4.envol = R_NR42 >> 4; S4.endir = R_NR42 & 0x08; S4.len = 172 * (64 - (R_NR41 & 0x3f)); S4.enlenreload = S4.enlen = 689 * (R_NR42 & 7); S4.on = 1; S4.pos = 0; S4.shiftpos = 0; freq = soundFreqRatio[R_NR43 & 7]; S4.shiftpos = (freq << 8) / NOISE_MAGIC; S4.nsteps = R_NR43 & 0x08; freq = freq / soundShiftClock[R_NR43 >> 4]; S4.shiftskip = (freq << 8) / NOISE_MAGIC; if(S4.nsteps) { S4.shiftright = 0x7fff; } else { S4.shiftright = 0x7f; } } break; case RI_NR50: snd.level1 = b & 7; snd.level2 = (b >> 4) & 7; break; case RI_NR51: snd.balance = b; break; case RI_NR52: if (!(b & 0x80)) { S1.on=0; S2.on=0; S3.on=0; S4.on=0; } break; } snd.gbDigitalSound = true; if(S1.on && S1.envol != 0) snd.gbDigitalSound = false; if(S2.on && S2.envol != 0) snd.gbDigitalSound = false; if(S3.on && S3.outputlevel != 0) snd.gbDigitalSound = false; if(S4.on && S4.envol != 0) snd.gbDigitalSound = false; } void sound_reset(void) { snd.level1 = 7; snd.level2 = 7; S1.on = S2.on = S3.on = S4.on = 0; S1.len = S2.len = S3.len = S4.len = 0; S1.skip = S2.skip = S3.skip = S4.skip = 0; S1.pos = S2.pos = S3.pos = S4.pos = 0; S1.cont = S2.cont = S3.cont = S4.cont = 0; S1.envol = S2.envol = S4.envol = 0; S1.enlen = S2.enlen = S4.enlen = 0; S1.endir = S2.endir = S4.endir = 0; S1.enlenreload = S2.enlenreload = S4.enlenreload = 0; S1.swlen = 0; S1.swlenreload = 0; S1.swsteps = 0; S1.swdir = 0; S1.swstep = 0; S1.wave = S2.wave = soundWavePattern[2]; S3.outputlevel = 0; S4.clock = 0; S4.shiftright = 0x7f; S4.nsteps = 0; sound_write(0x10, 0x80); sound_write(0x11, 0xbf); sound_write(0x12, 0xf3); sound_write(0x14, 0xbf); sound_write(0x16, 0x3f); sound_write(0x17, 0x00); sound_write(0x19, 0xbf); sound_write(0x1a, 0x7f); sound_write(0x1b, 0xff); sound_write(0x1c, 0xbf); sound_write(0x1e, 0xbf); sound_write(0x20, 0xff); sound_write(0x21, 0x00); sound_write(0x22, 0x00); sound_write(0x23, 0xbf); sound_write(0x24, 0x77); sound_write(0x25, 0xf3); sound_write(0x26, 0xf0); S1.on = 0; S2.on = 0; S3.on = 0; S4.on = 0; int addr = 0x30; while(addr < 0x40) { ram.hi[addr++] = 0x00; ram.hi[addr++] = 0xff; } if (pcm.hz) { snd.rate = (1<<21) / pcm.hz; snd.quality=44100 / pcm.hz; } else snd.rate = 0; } void sound_dirty(void) { sound_write(RI_NR10, R_NR10); sound_write(RI_NR11, R_NR11); sound_write(RI_NR12, R_NR12); sound_write(RI_NR13, R_NR13); sound_write(RI_NR14, R_NR14); sound_write(RI_NR21, R_NR21); sound_write(RI_NR22, R_NR22); sound_write(RI_NR23, R_NR23); sound_write(RI_NR24, R_NR24); sound_write(RI_NR30, R_NR30); sound_write(RI_NR31, R_NR31); sound_write(RI_NR32, R_NR32); sound_write(RI_NR33, R_NR33); sound_write(RI_NR34, R_NR34); sound_write(RI_NR42, R_NR42); sound_write(RI_NR43, R_NR43); sound_write(RI_NR44, R_NR44); sound_write(RI_NR50, R_NR50); sound_write(RI_NR51, R_NR51); sound_write(RI_NR52, R_NR52); }