diff options
Diffstat (limited to 'src/libxineadec/nosefart/nes_apu.c')
-rw-r--r-- | src/libxineadec/nosefart/nes_apu.c | 1256 |
1 files changed, 0 insertions, 1256 deletions
diff --git a/src/libxineadec/nosefart/nes_apu.c b/src/libxineadec/nosefart/nes_apu.c deleted file mode 100644 index 474e2ca1a..000000000 --- a/src/libxineadec/nosefart/nes_apu.c +++ /dev/null @@ -1,1256 +0,0 @@ -/* -** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com) -** -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of version 2 of the GNU Library General -** Public License as published by the Free Software Foundation. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -** Library General Public License for more details. To obtain a -** copy of the GNU Library General Public License, write to the Free -** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -** -** Any permitted reproduction of these routines, in whole or in part, -** must bear this legend. -** -** -** nes_apu.c -** -** NES APU emulation -** $Id: nes_apu.c,v 1.4 2005/05/07 09:11:39 valtri Exp $ -*/ - -#include <string.h> -#include "types.h" -#include "log.h" -#include "nes_apu.h" -#include "nes6502.h" - -#ifdef NSF_PLAYER -#include "nsf.h" -#else -#include "nes.h" -#include "nes_ppu.h" -#include "nes_mmc.h" -#include "nesinput.h" -#endif /* !NSF_PLAYER */ - - -#define APU_OVERSAMPLE -#define APU_VOLUME_DECAY(x) ((x) -= ((x) >> 7)) - - -/* pointer to active APU */ -static apu_t *apu; - -/* look up table madness */ -static int32 decay_lut[16]; -static int vbl_lut[32]; -static int trilength_lut[128]; - -/* noise lookups for both modes */ -#ifndef REALTIME_NOISE -static int8 noise_long_lut[APU_NOISE_32K]; -static int8 noise_short_lut[APU_NOISE_93]; -#endif /* !REALTIME_NOISE */ - -/* $$$ ben : last error */ -#define SET_APU_ERROR(APU,X) \ -if (APU) (APU)->errstr = "apu: " X; else - -#define APU_MIX_ENABLE(BIT) (apu->mix_enable&(1<<(BIT))) - -/* vblank length table used for rectangles, triangle, noise */ -static const uint8 vbl_length[32] = -{ - 5, 127, - 10, 1, - 19, 2, - 40, 3, - 80, 4, - 30, 5, - 7, 6, - 13, 7, - 6, 8, - 12, 9, - 24, 10, - 48, 11, - 96, 12, - 36, 13, - 8, 14, - 16, 15 -}; - -/* frequency limit of rectangle channels */ -static const int freq_limit[8] = -{ - 0x3FF, 0x555, 0x666, 0x71C, 0x787, 0x7C1, 0x7E0, 0x7F0 -}; - -/* noise frequency lookup table */ -static const int noise_freq[16] = -{ - 4, 8, 16, 32, 64, 96, 128, 160, - 202, 254, 380, 508, 762, 1016, 2034, 4068 -}; - -/* DMC transfer freqs */ -const int dmc_clocks[16] = -{ - 428, 380, 340, 320, 286, 254, 226, 214, - 190, 160, 142, 128, 106, 85, 72, 54 -}; - -/* ratios of pos/neg pulse for rectangle waves */ -static const int duty_lut[4] = { 2, 4, 8, 12 }; - - -void apu_setcontext(apu_t *src_apu) -{ - apu = src_apu; - /* $$$ ben reset eoor string here. */ - SET_APU_ERROR(apu,"no error"); -} - -/* -** Simple queue routines -*/ -#define APU_QEMPTY() (apu->q_head == apu->q_tail) - -static int apu_enqueue(apudata_t *d) -{ - ASSERT(apu); - apu->queue[apu->q_head] = *d; - - apu->q_head = (apu->q_head + 1) & APUQUEUE_MASK; - - if (APU_QEMPTY()) { - log_printf("apu: queue overflow\n"); - SET_APU_ERROR(apu,"queue overflow"); - return -1; - } - return 0; -} - -static apudata_t *apu_dequeue(void) -{ - int loc; - - ASSERT(apu); - - if (APU_QEMPTY()) { - log_printf("apu: queue empty\n"); - SET_APU_ERROR(apu,"queue empty"); - /* $$$ ben : should return 0 ??? */ - } - loc = apu->q_tail; - apu->q_tail = (apu->q_tail + 1) & APUQUEUE_MASK; - - return &apu->queue[loc]; -} - -int apu_setchan(int chan, boolean enabled) -{ - const unsigned int max = 6; - int old; - - ASSERT(apu); - if ((unsigned int)chan >= max) { - SET_APU_ERROR(apu,"channel out of range"); - return -1; - } - old = (apu->mix_enable>>chan) & 1; - if (enabled != (boolean)-1) { - apu->mix_enable = (apu->mix_enable & ~(1<<chan)) | ((!!enabled)<<chan); - } - return old; -} - -/* emulation of the 15-bit shift register the -** NES uses to generate pseudo-random series -** for the white noise channel -*/ -#ifdef REALTIME_NOISE -INLINE int8 shift_register15(uint8 xor_tap) -{ - static int sreg = 0x4000; - int bit0, tap, bit14; - - bit0 = sreg & 1; - tap = (sreg & xor_tap) ? 1 : 0; - bit14 = (bit0 ^ tap); - sreg >>= 1; - sreg |= (bit14 << 14); - return (bit0 ^ 1); -} -#else -static void shift_register15(int8 *buf, int count) -{ - static int sreg = 0x4000; - int bit0, bit1, bit6, bit14; - - if (count == APU_NOISE_93) - { - while (count--) - { - bit0 = sreg & 1; - bit6 = (sreg & 0x40) >> 6; - bit14 = (bit0 ^ bit6); - sreg >>= 1; - sreg |= (bit14 << 14); - *buf++ = bit0 ^ 1; - } - } - else /* 32K noise */ - { - while (count--) - { - bit0 = sreg & 1; - bit1 = (sreg & 2) >> 1; - bit14 = (bit0 ^ bit1); - sreg >>= 1; - sreg |= (bit14 << 14); - *buf++ = bit0 ^ 1; - } - } -} -#endif - -/* RECTANGLE WAVE -** ============== -** reg0: 0-3=volume, 4=envelope, 5=hold, 6-7=duty cycle -** reg1: 0-2=sweep shifts, 3=sweep inc/dec, 4-6=sweep length, 7=sweep on -** reg2: 8 bits of freq -** reg3: 0-2=high freq, 7-4=vbl length counter -*/ -#define APU_RECTANGLE_OUTPUT chan->output_vol -static int32 apu_rectangle(rectangle_t *chan) -{ - int32 output; - -#ifdef APU_OVERSAMPLE - int num_times; - int32 total; -#endif - - APU_VOLUME_DECAY(chan->output_vol); - - if (FALSE == chan->enabled || 0 == chan->vbl_length) - return APU_RECTANGLE_OUTPUT; - - /* vbl length counter */ - if (FALSE == chan->holdnote) - chan->vbl_length--; - - /* envelope decay at a rate of (env_delay + 1) / 240 secs */ - chan->env_phase -= 4; /* 240/60 */ - while (chan->env_phase < 0) - { - chan->env_phase += chan->env_delay; - - if (chan->holdnote) - chan->env_vol = (chan->env_vol + 1) & 0x0F; - else if (chan->env_vol < 0x0F) - chan->env_vol++; - } - - if ((FALSE == chan->sweep_inc && chan->freq > chan->freq_limit) - || chan->freq < APU_TO_FIXED(4)) - return APU_RECTANGLE_OUTPUT; - - /* frequency sweeping at a rate of (sweep_delay + 1) / 120 secs */ - if (chan->sweep_on && chan->sweep_shifts) - { - chan->sweep_phase -= 2; /* 120/60 */ - while (chan->sweep_phase < 0) - { - chan->sweep_phase += chan->sweep_delay; - if (chan->sweep_inc) /* ramp up */ - chan->freq -= chan->freq >> (chan->sweep_shifts); - else /* ramp down */ - chan->freq += chan->freq >> (chan->sweep_shifts); - } - } - - chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */ - if (chan->phaseacc >= 0) - return APU_RECTANGLE_OUTPUT; - -#ifdef APU_OVERSAMPLE - num_times = total = 0; - - if (chan->fixed_envelope) - output = chan->volume << 8; /* fixed volume */ - else - output = (chan->env_vol ^ 0x0F) << 8; -#endif - - while (chan->phaseacc < 0) - { - chan->phaseacc += chan->freq; - chan->adder = (chan->adder + 1) & 0x0F; - -#ifdef APU_OVERSAMPLE - if (chan->adder < chan->duty_flip) - total += output; - else - total -= output; - - num_times++; -#endif - } - -#ifdef APU_OVERSAMPLE - chan->output_vol = total / num_times; -#else - if (chan->fixed_envelope) - output = chan->volume << 8; /* fixed volume */ - else - output = (chan->env_vol ^ 0x0F) << 8; - - if (0 == chan->adder) - chan->output_vol = output; - else if (chan->adder == chan->duty_flip) - chan->output_vol = -output; -#endif - - return APU_RECTANGLE_OUTPUT; -} - -/* TRIANGLE WAVE -** ============= -** reg0: 7=holdnote, 6-0=linear length counter -** reg2: low 8 bits of frequency -** reg3: 7-3=length counter, 2-0=high 3 bits of frequency -*/ -#define APU_TRIANGLE_OUTPUT (chan->output_vol + (chan->output_vol >> 2)) -static int32 apu_triangle(triangle_t *chan) -{ - APU_VOLUME_DECAY(chan->output_vol); - - if (FALSE == chan->enabled || 0 == chan->vbl_length) - return APU_TRIANGLE_OUTPUT; - - if (chan->counter_started) - { - if (chan->linear_length > 0) - chan->linear_length--; - if (chan->vbl_length && FALSE == chan->holdnote) - chan->vbl_length--; - } - else if (FALSE == chan->holdnote && chan->write_latency) - { - if (--chan->write_latency == 0) - chan->counter_started = TRUE; - } -/* - if (chan->countmode == COUNTMODE_COUNT) - { - if (chan->linear_length > 0) - chan->linear_length--; - if (chan->vbl_length) - chan->vbl_length--; - } -*/ - if (0 == chan->linear_length || chan->freq < APU_TO_FIXED(4)) /* inaudible */ - return APU_TRIANGLE_OUTPUT; - - chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */ - while (chan->phaseacc < 0) - { - chan->phaseacc += chan->freq; - chan->adder = (chan->adder + 1) & 0x1F; - - if (chan->adder & 0x10) - chan->output_vol -= (2 << 8); - else - chan->output_vol += (2 << 8); - } - - return APU_TRIANGLE_OUTPUT; -} - - -/* WHITE NOISE CHANNEL -** =================== -** reg0: 0-3=volume, 4=envelope, 5=hold -** reg2: 7=small(93 byte) sample,3-0=freq lookup -** reg3: 7-4=vbl length counter -*/ -#define APU_NOISE_OUTPUT ((chan->output_vol + chan->output_vol + chan->output_vol) >> 2) - -static int32 apu_noise(noise_t *chan) -{ - int32 outvol; - -#if defined(APU_OVERSAMPLE) && defined(REALTIME_NOISE) -#else - int32 noise_bit; -#endif -#ifdef APU_OVERSAMPLE - int num_times; - int32 total; -#endif - - APU_VOLUME_DECAY(chan->output_vol); - - if (FALSE == chan->enabled || 0 == chan->vbl_length) - return APU_NOISE_OUTPUT; - - /* vbl length counter */ - if (FALSE == chan->holdnote) - chan->vbl_length--; - - /* envelope decay at a rate of (env_delay + 1) / 240 secs */ - chan->env_phase -= 4; /* 240/60 */ - while (chan->env_phase < 0) - { - chan->env_phase += chan->env_delay; - - if (chan->holdnote) - chan->env_vol = (chan->env_vol + 1) & 0x0F; - else if (chan->env_vol < 0x0F) - chan->env_vol++; - } - - chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */ - if (chan->phaseacc >= 0) - return APU_NOISE_OUTPUT; - -#ifdef APU_OVERSAMPLE - num_times = total = 0; - - if (chan->fixed_envelope) - outvol = chan->volume << 8; /* fixed volume */ - else - outvol = (chan->env_vol ^ 0x0F) << 8; -#endif - - while (chan->phaseacc < 0) - { - chan->phaseacc += chan->freq; - -#ifdef REALTIME_NOISE - -#ifdef APU_OVERSAMPLE - if (shift_register15(chan->xor_tap)) - total += outvol; - else - total -= outvol; - - num_times++; -#else - noise_bit = shift_register15(chan->xor_tap); -#endif - -#else - chan->cur_pos++; - - if (chan->short_sample) - { - if (APU_NOISE_93 == chan->cur_pos) - chan->cur_pos = 0; - } - else - { - if (APU_NOISE_32K == chan->cur_pos) - chan->cur_pos = 0; - } - -#ifdef APU_OVERSAMPLE - if (chan->short_sample) - noise_bit = noise_short_lut[chan->cur_pos]; - else - noise_bit = noise_long_lut[chan->cur_pos]; - - if (noise_bit) - total += outvol; - else - total -= outvol; - - num_times++; -#endif -#endif /* REALTIME_NOISE */ - } - -#ifdef APU_OVERSAMPLE - chan->output_vol = total / num_times; -#else - if (chan->fixed_envelope) - outvol = chan->volume << 8; /* fixed volume */ - else - outvol = (chan->env_vol ^ 0x0F) << 8; - -#ifndef REALTIME_NOISE - if (chan->short_sample) - noise_bit = noise_short_lut[chan->cur_pos]; - else - noise_bit = noise_long_lut[chan->cur_pos]; -#endif /* !REALTIME_NOISE */ - - if (noise_bit) - chan->output_vol = outvol; - else - chan->output_vol = -outvol; -#endif - - return APU_NOISE_OUTPUT; -} - - -INLINE void apu_dmcreload(dmc_t *chan) -{ - chan->address = chan->cached_addr; - chan->dma_length = chan->cached_dmalength; - chan->irq_occurred = FALSE; -} - -/* DELTA MODULATION CHANNEL -** ========================= -** reg0: 7=irq gen, 6=looping, 3-0=pointer to clock table -** reg1: output dc level, 6 bits unsigned -** reg2: 8 bits of 64-byte aligned address offset : $C000 + (value * 64) -** reg3: length, (value * 16) + 1 -*/ -#define APU_DMC_OUTPUT ((chan->output_vol + chan->output_vol + chan->output_vol) >> 2) -static int32 apu_dmc(dmc_t *chan) -{ - int delta_bit; - - APU_VOLUME_DECAY(chan->output_vol); - - /* only process when channel is alive */ - if (chan->dma_length) - { - chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */ - - while (chan->phaseacc < 0) - { - chan->phaseacc += chan->freq; - - delta_bit = (chan->dma_length & 7) ^ 7; - - if (7 == delta_bit) - { - chan->cur_byte = nes6502_getbyte(chan->address); - - /* steal a cycle from CPU*/ - nes6502_setdma(1); - - if (0xFFFF == chan->address) - chan->address = 0x8000; - else - chan->address++; - } - - if (--chan->dma_length == 0) - { - /* if loop bit set, we're cool to retrigger sample */ - if (chan->looping) - apu_dmcreload(chan); - else - { - /* check to see if we should generate an irq */ - if (chan->irq_gen) - { - chan->irq_occurred = TRUE; - nes6502_irq(); - } - - /* bodge for timestamp queue */ - chan->enabled = FALSE; - break; - } - } - - /* positive delta */ - if (chan->cur_byte & (1 << delta_bit)) - { - if (chan->regs[1] < 0x7D) - { - chan->regs[1] += 2; - chan->output_vol += (2 << 8); - } -/* - if (chan->regs[1] < 0x3F) - chan->regs[1]++; - - chan->output_vol &= ~(0x7E << 8); - chan->output_vol |= ((chan->regs[1] << 1) << 8); -*/ - } - /* negative delta */ - else - { - if (chan->regs[1] > 1) - { - chan->regs[1] -= 2; - chan->output_vol -= (2 << 8); - } - -/* - if (chan->regs[1] > 0) - chan->regs[1]--; - - chan->output_vol &= ~(0x7E << 8); - chan->output_vol |= ((chan->regs[1] << 1) << 8); -*/ - } - } - } - - return APU_DMC_OUTPUT; -} - - -static void apu_regwrite(uint32 address, uint8 value) -{ - int chan; - - ASSERT(apu); - switch (address) - { - /* rectangles */ - case APU_WRA0: - case APU_WRB0: - chan = (address & 4) ? 1 : 0; - apu->rectangle[chan].regs[0] = value; - - apu->rectangle[chan].volume = value & 0x0F; - apu->rectangle[chan].env_delay = decay_lut[value & 0x0F]; - apu->rectangle[chan].holdnote = (value & 0x20) ? TRUE : FALSE; - apu->rectangle[chan].fixed_envelope = (value & 0x10) ? TRUE : FALSE; - apu->rectangle[chan].duty_flip = duty_lut[value >> 6]; - break; - - case APU_WRA1: - case APU_WRB1: - chan = (address & 4) ? 1 : 0; - apu->rectangle[chan].regs[1] = value; - apu->rectangle[chan].sweep_on = (value & 0x80) ? TRUE : FALSE; - apu->rectangle[chan].sweep_shifts = value & 7; - apu->rectangle[chan].sweep_delay = decay_lut[(value >> 4) & 7]; - - apu->rectangle[chan].sweep_inc = (value & 0x08) ? TRUE : FALSE; - apu->rectangle[chan].freq_limit = APU_TO_FIXED(freq_limit[value & 7]); - break; - - case APU_WRA2: - case APU_WRB2: - chan = (address & 4) ? 1 : 0; - apu->rectangle[chan].regs[2] = value; -// if (apu->rectangle[chan].enabled) - apu->rectangle[chan].freq = APU_TO_FIXED((((apu->rectangle[chan].regs[3] & 7) << 8) + value) + 1); - break; - - case APU_WRA3: - case APU_WRB3: - chan = (address & 4) ? 1 : 0; - apu->rectangle[chan].regs[3] = value; - -// if (apu->rectangle[chan].enabled) - { - apu->rectangle[chan].vbl_length = vbl_lut[value >> 3]; - apu->rectangle[chan].env_vol = 0; - apu->rectangle[chan].freq = APU_TO_FIXED((((value & 7) << 8) + apu->rectangle[chan].regs[2]) + 1); - apu->rectangle[chan].adder = 0; - } - break; - - /* triangle */ - case APU_WRC0: -/* - if (0 == (apu->triangle.regs[0] & 0x80)) - apu->triangle.countmode = COUNTMODE_COUNT; - else - { - if (apu->triangle.countmode == COUNTMODE_LOAD && apu->triangle.vbl_length) - apu->triangle.linear_length = trilength_lut[value & 0x7F]; - - if (0 == (value & 0x80)) - apu->triangle.countmode = COUNTMODE_COUNT; - } -*/ - apu->triangle.regs[0] = value; - - apu->triangle.holdnote = (value & 0x80) ? TRUE : FALSE; - - -// if (apu->triangle.enabled) - { - if (FALSE == apu->triangle.counter_started && apu->triangle.vbl_length) - apu->triangle.linear_length = trilength_lut[value & 0x7F]; - } - - break; - - case APU_WRC2: - - apu->triangle.regs[1] = value; - -// if (apu->triangle.enabled) - apu->triangle.freq = APU_TO_FIXED((((apu->triangle.regs[2] & 7) << 8) + value) + 1); - break; - - case APU_WRC3: - - apu->triangle.regs[2] = value; - - /* this is somewhat of a hack. there appears to be some latency on - ** the Real Thing between when trireg0 is written to and when the - ** linear length counter actually begins its countdown. we want to - ** prevent the case where the program writes to the freq regs first, - ** then to reg 0, and the counter accidentally starts running because - ** of the sound queue's timestamp processing. - ** - ** set latency to a couple scanlines -- should be plenty of time for - ** the 6502 code to do a couple of table dereferences and load up the - ** other triregs - */ - - /* 06/13/00 MPC -- seems to work OK */ - apu->triangle.write_latency = (int) (2 * NES_SCANLINE_CYCLES / APU_FROM_FIXED(apu->cycle_rate)); -/* - apu->triangle.linear_length = trilength_lut[apu->triangle.regs[0] & 0x7F]; - if (0 == (apu->triangle.regs[0] & 0x80)) - apu->triangle.countmode = COUNTMODE_COUNT; - else - apu->triangle.countmode = COUNTMODE_LOAD; -*/ -// if (apu->triangle.enabled) - { - apu->triangle.freq = APU_TO_FIXED((((value & 7) << 8) + apu->triangle.regs[1]) + 1); - apu->triangle.vbl_length = vbl_lut[value >> 3]; - apu->triangle.counter_started = FALSE; - apu->triangle.linear_length = trilength_lut[apu->triangle.regs[0] & 0x7F]; - } - - break; - - /* noise */ - case APU_WRD0: - apu->noise.regs[0] = value; - apu->noise.env_delay = decay_lut[value & 0x0F]; - apu->noise.holdnote = (value & 0x20) ? TRUE : FALSE; - apu->noise.fixed_envelope = (value & 0x10) ? TRUE : FALSE; - apu->noise.volume = value & 0x0F; - break; - - case APU_WRD2: - apu->noise.regs[1] = value; - apu->noise.freq = APU_TO_FIXED(noise_freq[value & 0x0F]); - -#ifdef REALTIME_NOISE - apu->noise.xor_tap = (value & 0x80) ? 0x40: 0x02; -#else - /* detect transition from long->short sample */ - if ((value & 0x80) && FALSE == apu->noise.short_sample) - { - /* recalculate short noise buffer */ - shift_register15(noise_short_lut, APU_NOISE_93); - apu->noise.cur_pos = 0; - } - apu->noise.short_sample = (value & 0x80) ? TRUE : FALSE; -#endif - break; - - case APU_WRD3: - apu->noise.regs[2] = value; - -// if (apu->noise.enabled) - { - apu->noise.vbl_length = vbl_lut[value >> 3]; - apu->noise.env_vol = 0; /* reset envelope */ - } - break; - - /* DMC */ - case APU_WRE0: - apu->dmc.regs[0] = value; - - apu->dmc.freq = APU_TO_FIXED(dmc_clocks[value & 0x0F]); - apu->dmc.looping = (value & 0x40) ? TRUE : FALSE; - - if (value & 0x80) - apu->dmc.irq_gen = TRUE; - else - { - apu->dmc.irq_gen = FALSE; - apu->dmc.irq_occurred = FALSE; - } - break; - - case APU_WRE1: /* 7-bit DAC */ - /* add the _delta_ between written value and - ** current output level of the volume reg - */ - value &= 0x7F; /* bit 7 ignored */ - apu->dmc.output_vol += ((value - apu->dmc.regs[1]) << 8); - apu->dmc.regs[1] = value; -/* - apu->dmc.output_vol = (value & 0x7F) << 8; - apu->dmc.regs[1] = (value & 0x7E) >> 1; -*/ - break; - - case APU_WRE2: - apu->dmc.regs[2] = value; - apu->dmc.cached_addr = 0xC000 + (uint16) (value << 6); - break; - - case APU_WRE3: - apu->dmc.regs[3] = value; - apu->dmc.cached_dmalength = ((value << 4) + 1) << 3; - break; - - case APU_SMASK: - /* bodge for timestamp queue */ - apu->dmc.enabled = (value & 0x10) ? TRUE : FALSE; - - apu->enable_reg = value; - - for (chan = 0; chan < 2; chan++) - { - if (value & (1 << chan)) - apu->rectangle[chan].enabled = TRUE; - else - { - apu->rectangle[chan].enabled = FALSE; - apu->rectangle[chan].vbl_length = 0; - } - } - - if (value & 0x04) - apu->triangle.enabled = TRUE; - else - { - apu->triangle.enabled = FALSE; - apu->triangle.vbl_length = 0; - apu->triangle.linear_length = 0; - apu->triangle.counter_started = FALSE; - apu->triangle.write_latency = 0; - } - - if (value & 0x08) - apu->noise.enabled = TRUE; - else - { - apu->noise.enabled = FALSE; - apu->noise.vbl_length = 0; - } - - if (value & 0x10) - { - if (0 == apu->dmc.dma_length) - apu_dmcreload(&apu->dmc); - } - else - apu->dmc.dma_length = 0; - - apu->dmc.irq_occurred = FALSE; - break; - - /* unused, but they get hit in some mem-clear loops */ - case 0x4009: - case 0x400D: - break; - - default: - break; - } -} - -/* Read from $4000-$4017 */ -uint8 apu_read(uint32 address) -{ - uint8 value; - - ASSERT(apu); - - switch (address) - { - case APU_SMASK: - /* seems that bit 6 denotes vblank -- return 1 for now */ - value = 0x40; - - /* Return 1 in 0-5 bit pos if a channel is playing */ - if (apu->rectangle[0].enabled && apu->rectangle[0].vbl_length) - value |= 0x01; - if (apu->rectangle[1].enabled && apu->rectangle[1].vbl_length) - value |= 0x02; - if (apu->triangle.enabled && apu->triangle.vbl_length) - value |= 0x04; - if (apu->noise.enabled && apu->noise.vbl_length) - value |= 0x08; - - //if (apu->dmc.dma_length) - /* bodge for timestamp queue */ - if (apu->dmc.enabled) - value |= 0x10; - - if (apu->dmc.irq_occurred) - value |= 0x80; - - break; - -#ifndef NSF_PLAYER - case APU_JOY0: - value = input_get(INP_JOYPAD0); - break; - - case APU_JOY1: - value = input_get(INP_ZAPPER | INP_JOYPAD1 /*| INP_ARKANOID*/ /*| INP_POWERPAD*/); - break; -#endif /* !NSF_PLAYER */ - - default: - value = (address >> 8); /* heavy capacitance on data bus */ - break; - } - - return value; -} - - -void apu_write(uint32 address, uint8 value) -{ -#ifndef NSF_PLAYER - static uint8 last_write; -#endif /* !NSF_PLAYER */ - apudata_t d; - - switch (address) - { - case 0x4015: - /* bodge for timestamp queue */ - apu->dmc.enabled = (value & 0x10) ? TRUE : FALSE; - - case 0x4000: case 0x4001: case 0x4002: case 0x4003: - case 0x4004: case 0x4005: case 0x4006: case 0x4007: - case 0x4008: case 0x4009: case 0x400A: case 0x400B: - case 0x400C: case 0x400D: case 0x400E: case 0x400F: - case 0x4010: case 0x4011: case 0x4012: case 0x4013: - d.timestamp = nes6502_getcycles(FALSE); - d.address = address; - d.value = value; - apu_enqueue(&d); - break; - -#ifndef NSF_PLAYER - case APU_OAMDMA: - ppu_oamdma(address, value); - break; - - case APU_JOY0: - /* VS system VROM switching */ - mmc_vsvrom(value & 4); - - /* see if we need to strobe them joypads */ - value &= 1; - if ((0 == value) && last_write) - input_strobe(); - last_write = value; - break; - - case APU_JOY1: /* Some kind of IRQ control business */ - break; - -#endif /* !NSF_PLAYER */ - - default: - break; - } -} - -void apu_getpcmdata(void **data, int *num_samples, int *sample_bits) -{ - ASSERT(apu); - *data = apu->buffer; - *num_samples = apu->num_samples; - *sample_bits = apu->sample_bits; -} - - -void apu_process(void *buffer, int num_samples) -{ - apudata_t *d; - uint32 elapsed_cycles; - static int32 prev_sample = 0; - int32 next_sample, accum; - - ASSERT(apu); - - /* grab it, keep it local for speed */ - elapsed_cycles = (uint32) apu->elapsed_cycles; - - /* BLEH */ - apu->buffer = buffer; - - while (num_samples--) - { - while ((FALSE == APU_QEMPTY()) && (apu->queue[apu->q_tail].timestamp <= elapsed_cycles)) - { - d = apu_dequeue(); - apu_regwrite(d->address, d->value); - } - - elapsed_cycles += APU_FROM_FIXED(apu->cycle_rate); - - accum = 0; - if (APU_MIX_ENABLE(0)) accum += apu_rectangle(&apu->rectangle[0]); - if (APU_MIX_ENABLE(1)) accum += apu_rectangle(&apu->rectangle[1]); - if (APU_MIX_ENABLE(2)) accum += apu_triangle(&apu->triangle); - if (APU_MIX_ENABLE(3)) accum += apu_noise(&apu->noise); - if (APU_MIX_ENABLE(4)) accum += apu_dmc(&apu->dmc); - - if (apu->ext && APU_MIX_ENABLE(5)) accum += apu->ext->process(); - - /* do any filtering */ - if (APU_FILTER_NONE != apu->filter_type) - { - next_sample = accum; - - if (APU_FILTER_LOWPASS == apu->filter_type) - { - accum += prev_sample; - accum >>= 1; - } - else - accum = (accum + accum + accum + prev_sample) >> 2; - - prev_sample = next_sample; - } - - /* little extra kick for the kids */ - accum <<= 1; - - /* prevent clipping */ - if (accum > 0x7FFF) - accum = 0x7FFF; - else if (accum < -0x8000) - accum = -0x8000; - - /* signed 16-bit output, unsigned 8-bit */ - if (16 == apu->sample_bits) { - *(int16 *)(buffer) = (int16) accum; - buffer += sizeof(int16); - } - else { - *(uint8 *)(buffer) = (accum >> 8) ^ 0x80; - buffer += sizeof(uint8); - } - } - - /* resync cycle counter */ - apu->elapsed_cycles = nes6502_getcycles(FALSE); -} - -/* set the filter type */ -/* $$$ ben : - * Add a get feature (filter_type == -1) and returns old filter type - */ -int apu_setfilter(int filter_type) -{ - int old; - ASSERT(apu); - old = apu->filter_type; - if (filter_type != -1) { - apu->filter_type = filter_type; - } - return old; -} - -void apu_reset(void) -{ - uint32 address; - - ASSERT(apu); - - apu->elapsed_cycles = 0; - memset(&apu->queue, 0, APUQUEUE_SIZE * sizeof(apudata_t)); - apu->q_head = 0; - apu->q_tail = 0; - - /* use to avoid bugs =) */ - for (address = 0x4000; address <= 0x4013; address++) - apu_regwrite(address, 0); - -#ifdef NSF_PLAYER - apu_regwrite(0x400C, 0x10); /* silence noise channel on NSF start */ - apu_regwrite(0x4015, 0x0F); -#else - apu_regwrite(0x4015, 0); -#endif /* NSF_PLAYER */ - - if (apu->ext) - apu->ext->reset(); -} - -void apu_build_luts(int num_samples) -{ - int i; - - /* lut used for enveloping and frequency sweeps */ - for (i = 0; i < 16; i++) - decay_lut[i] = num_samples * (i + 1); - - /* used for note length, based on vblanks and size of audio buffer */ - for (i = 0; i < 32; i++) - vbl_lut[i] = vbl_length[i] * num_samples; - - /* triangle wave channel's linear length table */ - for (i = 0; i < 128; i++) - trilength_lut[i] = (i * num_samples) / 4; - -#ifndef REALTIME_NOISE - /* generate noise samples */ - shift_register15(noise_long_lut, APU_NOISE_32K); - shift_register15(noise_short_lut, APU_NOISE_93); -#endif /* !REALTIME_NOISE */ -} - -static void apu_setactive(apu_t *active) -{ - ASSERT(active); - apu = active; -} - -/* Initializes emulated sound hardware, creates waveforms/voices */ -apu_t *apu_create(int sample_rate, int refresh_rate, int sample_bits, boolean stereo) -{ - apu_t *temp_apu; -/* int channel; */ - - temp_apu = malloc(sizeof(apu_t)); - if (NULL == temp_apu) - return NULL; - /* $$$ ben : safety net, in case we forgot to init something */ - memset(temp_apu,0,sizeof(apu_t)); - - SET_APU_ERROR(temp_apu,"no error"); - temp_apu->sample_rate = sample_rate; - temp_apu->refresh_rate = refresh_rate; - temp_apu->sample_bits = sample_bits; - - temp_apu->num_samples = sample_rate / refresh_rate; - /* turn into fixed point! */ - temp_apu->cycle_rate = (int32) (APU_BASEFREQ * 65536.0 / (float) sample_rate); - - /* build various lookup tables for apu */ - apu_build_luts(temp_apu->num_samples); - - /* set the update routine */ - temp_apu->process = apu_process; - temp_apu->ext = NULL; - - apu_setactive(temp_apu); - apu_reset(); - - temp_apu->mix_enable = 0x3F; -/* for (channel = 0; channel < 6; channel++) */ -/* apu_setchan(channel, TRUE); */ - - apu_setfilter(APU_FILTER_LOWPASS); - - return temp_apu; -} - -apu_t *apu_getcontext(void) -{ - return apu; -} - -void apu_destroy(apu_t *src_apu) -{ - if (src_apu) - { - if (src_apu->ext) - src_apu->ext->shutdown(); - free(src_apu); - } -} - -int apu_setext(apu_t *src_apu, apuext_t *ext) -{ - ASSERT(src_apu); - - /* $$$ ben : seem cleaner like this */ - if (src_apu->ext) { - src_apu->ext->shutdown(); - } - - src_apu->ext = ext; - - /* initialize it */ - if (src_apu->ext) - src_apu->ext->init(); - - /* $$$ ben : May be one day extension int () will return error code */ - return 0; -} - -/* this exists for external mixing routines */ -int32 apu_getcyclerate(void) -{ - ASSERT(apu); - return apu->cycle_rate; -} - -/* -** $Log: nes_apu.c,v $ -** Revision 1.4 2005/05/07 09:11:39 valtri -** *BUGFIX* -** gcc4 patches from Dams Nadé (livna.org) and Keenan Pepper. -** -** Revision 1.3 2004/12/12 06:55:59 athp -** Code cleanups and elimination of some compiler warnings; patch courtesy of AL13N -** -** Revision 1.2 2003/08/25 21:51:43 f1rmb -** Reduce GCC verbosity (various prototype declaration fixes). ffmpeg, wine and fft*post are untouched (fft: for now). -** -** Revision 1.1 2003/01/08 07:04:35 tmmm -** initial import of Nosefart sources -** -** Revision 1.19 2000/07/04 04:53:26 matt -** minor changes, sound amplification -** -** Revision 1.18 2000/07/03 02:18:53 matt -** much better external module exporting -** -** Revision 1.17 2000/06/26 11:01:55 matt -** made triangle a tad quieter -** -** Revision 1.16 2000/06/26 05:10:33 matt -** fixed cycle rate generation accuracy -** -** Revision 1.15 2000/06/26 05:00:37 matt -** cleanups -** -** Revision 1.14 2000/06/23 11:06:24 matt -** more faithful mixing of channels -** -** Revision 1.13 2000/06/23 03:29:27 matt -** cleaned up external sound inteface -** -** Revision 1.12 2000/06/20 00:08:39 matt -** bugfix to rectangle wave -** -** Revision 1.11 2000/06/13 13:48:58 matt -** fixed triangle write latency for fixed point apu cycle rate -** -** Revision 1.10 2000/06/12 01:14:36 matt -** minor change to clipping extents -** -** Revision 1.9 2000/06/09 20:00:56 matt -** fixed noise hiccup in NSF player mode -** -** Revision 1.8 2000/06/09 16:49:02 matt -** removed all floating point from sound generation -** -** Revision 1.7 2000/06/09 15:12:28 matt -** initial revision -** -*/ |