| /*************************************************************************** |
| * __________ __ ___. |
| * 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); |
| } |