📄 nes_apu.c
字号:
/* 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 >> 2))
#define APU_NOISE_OUTPUT ((chan->output_vol*13)>>4)
static int32 apu_noise(noise_t *chan)
{
int32 outvol;
#ifdef APU_YANO
static int32 noise_bit;
double total;
double sample_weight;
#else /* !APU_YANO */
#if defined(APU_OVERSAMPLE) && defined(REALTIME_NOISE)
#else /* !(APU_OVERSAMPLE && REALTIME_NOISE) */
int32 noise_bit;
#endif /* !(APU_OVERSAMPLE && REALTIME_NOISE) */
#ifdef APU_OVERSAMPLE
int num_times;
int32 total;
#endif /* APU_OVERSAMPLE */
APU_VOLUME_DECAY(chan->output_vol);
#endif /* !APU_YANO */
if (FALSE == chan->enabled || chan->vbl_length <= 0)
return APU_NOISE_OUTPUT;
/* vbl length counter */
if (FALSE == chan->holdnote)
chan->vbl_length -= apu_cnt_rate;
/* envelope decay at a rate of (env_delay + 1) / 240 secs */
#if 0
chan->env_phase -= 4 * apu_cnt_rate; /* 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++;
}
#else
{
int env_phase = chan->env_phase;
int env_delay = chan->env_delay;
int holdnote = chan->holdnote;
int env_vol = chan->env_vol;
env_phase -= 4 * apu_cnt_rate; /* 240/60 */
while (env_phase < 0)
{
env_phase += env_delay;
if (holdnote)
env_vol = (env_vol + 1) & 0x0F;
else if (env_vol < 0x0F)
env_vol++;
}
chan->env_phase = env_phase;
chan->env_delay = env_delay;
chan->holdnote = holdnote;
chan->env_vol = env_vol;
}
#endif
#ifdef APU_YANO
if (chan->fixed_envelope)
outvol = chan->volume << 8; /* fixed volume */
else
outvol = (chan->env_vol ^ 0x0F) << 8;
sample_weight = chan->phaseacc;
if ( sample_weight > apu->cycle_rate) {
sample_weight = apu->cycle_rate;
}
total = noise_bit ? sample_weight:-sample_weight;
chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */
while ( chan->phaseacc < 0) {
chan->phaseacc += chan->freq;
#ifdef REALTIME_NOISE
noise_bit = shift_register15(chan->xor_tap);
#else /* !REALTIME_NOISE */
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;
}
if (chan->short_sample)
noise_bit = noise_short_lut[chan->cur_pos];
else
noise_bit = noise_long_lut[chan->cur_pos];
#endif /* !REALTIME_NOISE */
sample_weight = chan->freq;
if (chan->phaseacc > 0) {
sample_weight -= chan->phaseacc;
}
total += noise_bit ? sample_weight:-sample_weight;
}
chan->output_vol = (int)floor( outvol*total/apu->cycle_rate + 0.5);
#else /* !APU_YANO */
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 /* APU_OVERSAMPLE */
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 /* !APU_OVERSAMPLE */
noise_bit = shift_register15(chan->xor_tap);
#endif /* !APU_OVERSAMPLE */
#else /* !REALTIME_NOISE */
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 /* APU_OVERSAMPLE */
#endif /* !REALTIME_NOISE */
}
#ifdef APU_OVERSAMPLE
chan->output_vol = total / num_times;
#else /* !APU_OVERSAMPLE */
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 /* !APU_OVERSAMPLE */
#endif /* !APU_YANO */
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 >> 3))
#define APU_DMC_OUTPUT ((chan->output_vol*13)>>4)
static int32 apu_dmc(dmc_t *chan)
{
#ifdef APU_YANO
double total;
double sample_weight;
#endif /* APU_YANO */
int delta_bit;
#ifndef APU_YANO
APU_VOLUME_DECAY(chan->output_vol);
#endif /* !APU_YANO */
/* only process when channel is alive */
if (chan->dma_length)
{
#ifdef APU_YANO
sample_weight = chan->phaseacc;
if ( sample_weight > apu->cycle_rate) {
sample_weight = apu->cycle_rate;
}
total = (chan->regs[1]<<8)*sample_weight;
#endif /* APU_YANO */
chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */
while (chan->phaseacc < 0)
{
chan->phaseacc += chan->freq;
if (0 == (chan->dma_length & 7))
{
chan->cur_byte = nes6502_getbyte(chan->address);
/* steal a cycle from CPU*/
nes6502_burn(1); // for New nes6502.c v1.23 by matt
// nes6502_setdma(1); // for nes6502.c v1.4 by matt
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;
#ifndef NSF_PLAYER
//DCR nes_irq();
#endif /* !NSF_PLAYER */
}
/* bodge for timestamp queue */
#ifdef APU_YANO
sample_weight = chan->freq - chan->phaseacc;
total += (chan->regs[1]<<8)*sample_weight;
while ( chan->phaseacc < 0) chan->phaseacc += chan->freq;
#endif /* !APU_YANO */
chan->enabled = FALSE;
break;
}
}
delta_bit = (chan->dma_length & 7) ^ 7;
/* positive delta */
if (chan->cur_byte & (1 << delta_bit))
{
if (chan->regs[1] < 0x7D)
{
chan->regs[1] += 2;
#ifndef APU_YANO
chan->output_vol += (2 << 8);
#endif /* !APU_YANO */
}
}
/* negative delta */
else
{
if (chan->regs[1] > 1)
{
chan->regs[1] -= 2;
#ifndef APU_YANO
chan->output_vol -= (2 << 8);
#endif /* !APU_YANO */
}
}
#ifdef APU_YANO
sample_weight = chan->freq;
if (chan->phaseacc > 0) {
sample_weight -= chan->phaseacc;
}
total += (chan->regs[1]<<8)*sample_weight;
#endif /* !APU_YANO */
}
#ifdef APU_YANO
chan->output_vol = (int)floor( total/apu->cycle_rate + 0.5);
#endif /* !APU_YANO */
}
#ifdef APU_YANO
else
{
chan->output_vol = chan->regs[1] << 8;
}
#endif /* !APU_YANO */
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:
#if 0
chan = (address & 4) ? 1 : 0;
apu->apus.rectangle[chan].regs[0] = value;
apu->apus.rectangle[chan].volume = value & 0x0F;
apu->apus.rectangle[chan].env_delay = decay_lut[value & 0x0F];
apu->apus.rectangle[chan].holdnote = (value & 0x20) ? TRUE : FALSE;
apu->apus.rectangle[chan].fixed_envelope = (value & 0x10) ? TRUE : FALSE;
apu->apus.rectangle[chan].duty_flip = duty_lut[value >> 6];
#else
{
rectangle_t * rect = (address & 4) ? &(apu->apus.rectangle[1]) : &(apu->apus.rectangle[0]);
rect->regs[0] = value;
rect->volume = value & 0x0F;
rect->env_delay = decay_lut[value & 0x0F];
rect->holdnote = (value & 0x20) ? TRUE : FALSE;
rect->fixed_envelope = (value & 0x10) ? TRUE : FALSE;
rect->duty_flip = duty_lut[value >> 6];
}
#endif
break;
case APU_WRA1:
case APU_WRB1:
#if 0
chan = (address & 4) ? 1 : 0;
apu->apus.rectangle[chan].regs[1] = value;
apu->apus.rectangle[chan].sweep_on = (value & 0x80) ? TRUE : FALSE;
apu->apus.rectangle[chan].sweep_shifts = value & 7;
apu->apus.rectangle[chan].sweep_delay = decay_lut[(value >> 4) & 7];
apu->apus.rectangle[chan].sweep_inc = (value & 0x08) ? TRUE : FALSE;
apu->apus.rectangle[chan].freq_limit = freq_limit[value & 7];
#else
{
rectangle_t * rect = (address & 4) ? &(apu->apus.rectangle[1]) : &(apu->apus.rectangle[0]);
rect->regs[1] = value;
rect->sweep_on = (value & 0x80) ? TRUE : FALSE;
rect->sweep_shifts = value & 7;
rect->freq_limit = freq_limit[value & 7];
rect->sweep_delay = decay_lut[(value >> 4) & 7];
rect->sweep_inc = (value & 0x08) ? TRUE : FALSE;
}
#endif
break;
case APU_WRA2:
case APU_WRB2:
#if 0
chan = (address & 4) ? 1 : 0;
apu->apus.rectangle[chan].regs[2] = value;
apu->apus.rectangle[chan].freq = (apu->apus.rectangle[chan].freq & ~0xFF) | value;
#else
{
rectangle_t * rect = (address & 4) ? &(apu->apus.rectangle[1]) : &(apu->apus.rectangle[0]);
rect->regs[2] = value;
rect->freq = (rect->freq & ~0xFF) | value;
}
#endif
break;
case APU_WRA3:
case APU_WRB3:
#if 0
chan = (address & 4) ? 1 : 0;
apu->apus.rectangle[chan].regs[3] = value;
apu->apus.rectangle[chan].vbl_length = vbl_lut[value >> 3];
apu->apus.rectangle[chan].env_vol = 0;
apu->apus.rectangle[chan].freq = ((value & 7) << 8) | (apu->apus.rectangle[chan].freq & 0xFF);
apu->apus.rectangle[chan].adder = 0;
if ( apu->enable_reg & (1<<chan))
apu->apus.rectangle[chan].enabled = TRUE;
#else
{
rectangle_t * rect;
int enable_mask;
if (address & 4) {
rect = &(apu->apus.rectangle[1]);
enable_mask = 2;
} else {
rect = &(apu->apus.rectangle[0]);
enable_mask = 1;
}
rect->regs[3] = value;
rect->vbl_length = vbl_lut[value >> 3];
rect->env_vol = 0;
rect->freq = ((value & 7) << 8) | (rect->freq & 0xFF);
rect->adder = 0;
if (apu->enable_reg & enable_mask)
rect->enabled = TRUE;
}
#endif
break;
/* triangle */
case APU_WRC0:
#if 0
apu->apus.triangle.regs[0] = value;
apu->apus.triangle.holdnote = (value & 0x80) ? TRUE : FALSE;
if (FALSE == apu->apus.triangle.counter_started && apu->apus.triangle.vbl_length > 0)
apu->apus.triangle.linear_length = trilength_lut[value & 0x7F];
#else
{
triangle_t * tri = &(apu->apus.triangle);
tri->regs[0] = value;
tri->holdnote = (value & 0x80) ? TRUE : FALSE;
if (FALSE == tri->counter_started && tri->vbl_length > 0)
tri->linear_length = trilength_lut[value & 0x7F];
}
#endif
break;
case APU_WRC2:
#if 0
apu->apus.triangle.regs[1] = value;
apu->apus.triangle.freq = APU_TO_FIXED((((apu->apus.triangle.regs[2] & 7) << 8) + value) + 1);
#else
{
triangle_t * tri = &(apu->apus.triangle);
tri->regs[1] = value;
tri->freq = APU_TO_FIXED((((tri->regs[2] & 7) << 8) + value) + 1);
}
#endif
break;
case APU_WRC3:
#if 0
apu->apus.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 hundred cycles -- 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->apus.triangle.write_latency = (int) (228 / APU_FROM_FIXED(apu->cycle_rate));
apu->apus.triangle.freq = APU_TO_FIXED((((value & 7) << 8) + apu->apus.triangle.regs[1]) + 1);
apu->apus.triangle.vbl_length = vbl_lut[value >> 3];
apu->apus.triangle.counter_started = FALSE;
apu->apus.triangle.linear_length = trilength_lut[apu->apus.triangle.regs[0] & 0x7F];
if ( apu->enable_reg & 0x04) apu->apus.triangle.enabled = TRUE;
#else
{
triangle_t * tri = &(apu->apus.triangle);
tri->regs[2] = value;
tri->write_latency = (int) (228 / APU_FROM_FIXED(apu->cycle_rate));
tri->freq = APU_TO_FIXED((((value & 7) << 8) + tri->regs[1]) + 1);
tri->vbl_length = vbl_lut[value >> 3];
tri->counter_started = FALSE;
tri->linear_length = trilength_lut[tri->regs[0] & 0x7F];
if (apu->enable_reg & 0x04)
tri->enabled = TRUE;
}
#endif
break;
/* noise */
case APU_WRD0:
#if 0
apu->apus.noise.regs[0] = value;
apu->apus.noise.env_delay = decay_lut[value & 0x0F];
apu->apus.noise.holdnote = (value & 0x20) ? TRUE : FALSE;
apu->apus.noise.fixed_envelope = (value & 0x10) ? TRUE : FALSE;
apu->apus.noise.volume = value & 0x0F;
#else
{
noise_t * noise = &(apu->apus.noise);
noise->regs[0] = value;
noise->volume = value & 0x0F;
noise->env_delay = decay_lut[value & 0x0F];
noise->holdnote = (value & 0x20) ? TRUE : FALSE;
noise->fixed_envelope = (value & 0x10) ? TRUE : FALSE;
}
#endif
break;
case APU_WRD2:
#if 0
apu->apus.noise.regs[1] = value;
apu->apus.noise.freq = APU_TO_FIXED(noise_freq[value & 0x0F]);
#ifdef REALTIME_NOISE
apu->apus.noise.xor_tap = (value & 0x80) ? 0x40: 0x02;
#else /* !REALTIME_NOISE */
/* detect transition from long->short sample */
if ((value & 0x80) && FALSE == apu->apus.noise.short_sample)
{
/* recalculate short noise buffer */
shift_register15(noise_short_lut, APU_NOISE_93);
apu->apus.noise.cur_pos = 0;
}
apu->apus.noise.short_sample = (value & 0x80) ? TRUE : FALSE;
#endif /* !REALTIME_NOISE */
#else /* if 0 */
{
noise_t * noise = &(apu->apus.noise);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -