#include "main.h" #include "nes_apu.h" #include "nes_main.h" ////////////////////////////////////////////////////////////////////////////////// //本程序移植自网友ye781205的NES模拟器工程 //ALIENTEK STM32F407开发板 //NES APU 驱动代码 //正点原子@ALIENTEK //技术论坛:www.openedv.com //创建日期:2014/7/1 //版本:V1.0 ////////////////////////////////////////////////////////////////////////////////// /* look up table madness */ #define APU_OVERSAMPLE #define APU_VOLUME_DECAY(x) ((x) -= ((x) >> 7)) //需要用到的汇编的代码及参数 u8 K6502_Read( u16 wAddr ); //6502.s extern u32 clocks; //6502.s //noise lookups for both modes */ //噪音查找两种模式 */ #ifndef REALTIME_NOISE //1 static char noise_long_lut[APU_NOISE_32K];//APU_NOISE_32K 0x7FFF static char noise_short_lut[APU_NOISE_93];//APU_NOISE_93 93 #endif /* !REALTIME_NOISE */ /* vblank长度表用于矩形、三角形、噪音 */ static const u8 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 }; /* 矩形通道的频率限制 */ static const int freq_limit[8] = { 0x3FF, 0x555, 0x666, 0x71C, 0x787, 0x7C1, 0x7E0, 0x7F0 }; /* 噪声频率查找表 */ 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 }; /* pos /底片的比率为矩形波脉冲 */ static const int duty_lut[4] = { 2, 4, 8, 12 }; /*** Simple queue routines简单队列例程*/ #define APU_QEMPTY() (apu->q_head == apu->q_tail) void apu_enqueue(apudata_t *d) { apu->queue[apu->q_head] = *d; apu->q_head = (apu->q_head + 1) & APUQUEUE_MASK; } apudata_t *apu_dequeue(void) { int loc; loc = apu->q_tail; apu->q_tail = (apu->q_tail + 1) & APUQUEUE_MASK; return &apu->queue[loc]; } void apu_setchan(int chan, u8 enabled) { apu->mix_enable[chan] = enabled; } /* emulation of the 15-bit shift register the ** NES uses to generate pseudo-random series ** for the white noise channel 模拟的15位移位寄存器   NES用来生成伪随机序列   白噪声信道 */ #ifdef REALTIME_NOISE //1 char shift_register15(u8 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 /* !REALTIME_NOISE */ void shift_register15(char *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 /* !REALTIME_NOISE */ /* 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 int apu_rectangle(rectangle_t *chan) { int output; #ifdef APU_OVERSAMPLE //1 int num_times; int total; #endif /* APU_OVERSAMPLE */ 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 */ #if 0 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++; } #endif { 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; /* 240/60 */ while (env_phase < 0) { if(env_delay==0)break;//*******我加的*****************1943这个游戏是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; } /* TODO: using a table of max frequencies is not technically ** clean, but it is fast and (or should be) accurate */ if (chan->freq < 8 || (FALSE == chan->sweep_inc && chan->freq > chan->freq_limit)) 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 */ { if (TRUE == chan->sweep_complement) chan->freq += ~(chan->freq >> chan->sweep_shifts); else 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 //1 num_times = total = 0; if (chan->fixed_envelope) output = chan->volume << 8; /* fixed volume */ else output = (chan->env_vol ^ 0x0F) << 8; #endif /* APU_OVERSAMPLE */ while (chan->phaseacc < 0) { chan->phaseacc += APU_TO_FIXED(chan->freq + 1); chan->adder = (chan->adder + 1) & 0x0F; #ifdef APU_OVERSAMPLE //1 if (chan->adder < chan->duty_flip) total += output; else total -= output; num_times++; #endif /* APU_OVERSAMPLE */ } #ifdef APU_OVERSAMPLE //1 chan->output_vol = total / num_times; #else /* !APU_OVERSAMPLE */ 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 /* !APU_OVERSAMPLE */ 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)) int 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 (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) int apu_noise(noise_t *chan) { int outvol; #if defined(APU_OVERSAMPLE) && defined(REALTIME_NOISE) #else /* !(APU_OVERSAMPLE && REALTIME_NOISE) */ int noise_bit; #endif /* !(APU_OVERSAMPLE && REALTIME_NOISE) */ #ifdef APU_OVERSAMPLE int num_times; int total; #endif /* APU_OVERSAMPLE */ 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 */ #if 0 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++; } #endif { 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; /* 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; } 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) { if(chan->freq==0)break;//*******我加的*****************这个游戏是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 */ return APU_NOISE_OUTPUT; } 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) int 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 =K6502_Read(chan->address);//chan->cur_byte = nes6502_getbyte(chan->address);********************* /* steal a cycle from CPU偷从CPU周期*/ clocks++; // nes6502_burn(1);//要CPU时钟数加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; } /* 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); } } /* negative delta */ else { if (chan->regs[1] > 1) { chan->regs[1] -= 2; chan->output_vol -= (2 << 8); } } } } return APU_DMC_OUTPUT; } void apu_regwrite(u32 address, u8 value) { int chan = (address & 4) ? 1 : 0; rectangle_t * rect = chan ? &(apu->rectangle[1]) : &(apu->rectangle[0]); 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 = apu->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]; */ rect->regs[0] = value; rect->volume = value & 0x0F; rect->env_delay = apu->decay_lut[value & 0x0F]; rect->holdnote = (value & 0x20) ? TRUE : FALSE; rect->fixed_envelope = (value & 0x10) ? TRUE : FALSE; rect->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 = apu->decay_lut[(value >> 4) & 7]; apu->rectangle[chan].sweep_inc = (value & 0x08) ? TRUE : FALSE; apu->rectangle[chan].freq_limit = freq_limit[value & 7]; */ rect->regs[1] = value; rect->sweep_on = (value & 0x80) ? TRUE : FALSE; rect->sweep_shifts = value & 7; rect->sweep_delay = apu->decay_lut[(value >> 4) & 7]; rect->sweep_inc = (value & 0x08) ? TRUE : FALSE; rect->freq_limit = 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->rectangle[chan].freq & ~0xFF) | value; */ rect->regs[2] = value; rect->freq = (apu->rectangle[chan].freq & ~0xFF) | value; break; case APU_WRA3: case APU_WRB3: /* chan = (address & 4) ? 1 : 0; apu->rectangle[chan].regs[3] = value; apu->rectangle[chan].vbl_length = apu->vbl_lut[value >> 3]; apu->rectangle[chan].env_vol = 0; apu->rectangle[chan].freq = ((value & 7) << 8) | (apu->rectangle[chan].freq & 0xFF); apu->rectangle[chan].adder = 0; */ rect->regs[3] = value; rect->vbl_length = apu->vbl_lut[value >> 3]; rect->env_vol = 0; rect->freq = ((value & 7) << 8) | (rect->freq & 0xFF); rect->adder = 0; break; /* triangle */ case APU_WRC0: /* apu->triangle.regs[0] = value; apu->triangle.holdnote = (value & 0x80) ? TRUE : FALSE; if (FALSE == apu->triangle.counter_started && apu->triangle.vbl_length) apu->triangle.linear_length = apu->trilength_lut[value & 0x7F]; */ { triangle_t * tri = &(apu->triangle); tri->regs[0] = value; tri->holdnote = (value & 0x80) ? TRUE : FALSE; if (FALSE == tri->counter_started && tri->vbl_length) tri->linear_length = apu->trilength_lut[value & 0x7F]; } break; case APU_WRC2: /* apu->triangle.regs[1] = value; apu->triangle.freq = APU_TO_FIXED((((apu->triangle.regs[2] & 7) << 8) + value) + 1); */ { triangle_t * tri = &(apu->triangle); tri->regs[1] = value; tri->freq = APU_TO_FIXED((((tri->regs[2] & 7) << 8) + value) + 1); } break; case APU_WRC3: #if 0 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 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->triangle.write_latency = (int) (228 / APU_FROM_FIXED(apu->cycle_rate)); apu->triangle.freq = APU_TO_FIXED((((value & 7) << 8) + apu->triangle.regs[1]) + 1); apu->triangle.vbl_length = apu->vbl_lut[value >> 3]; apu->triangle.counter_started = FALSE; apu->triangle.linear_length = apu->trilength_lut[apu->triangle.regs[0] & 0x7F]; #endif { triangle_t * tri = &(apu->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 = apu->vbl_lut[value >> 3]; tri->counter_started = FALSE; tri->linear_length = apu->trilength_lut[tri->regs[0] & 0x7F]; } break; /* noise */ case APU_WRD0: /* apu->noise.regs[0] = value; apu->noise.env_delay = apu->decay_lut[value & 0x0F]; apu->noise.holdnote = (value & 0x20) ? TRUE : FALSE; apu->noise.fixed_envelope = (value & 0x10) ? TRUE : FALSE; apu->noise.volume = value & 0x0F; */ { noise_t * noise = &(apu->noise); noise->regs[0] = value; noise->env_delay = apu->decay_lut[value & 0x0F]; noise->holdnote = (value & 0x20) ? TRUE : FALSE; noise->fixed_envelope = (value & 0x10) ? TRUE : FALSE; noise->volume = value & 0x0F; } break; case APU_WRD2: #if 0 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 /* !REALTIME_NOISE */ /* 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 /* !REALTIME_NOISE */ #endif { noise_t * noise = &(apu->noise); noise->regs[1] = value; noise->freq = APU_TO_FIXED(noise_freq[value & 0x0F]); #ifdef REALTIME_NOISE noise->xor_tap = (value & 0x80) ? 0x40: 0x02; #else /* !REALTIME_NOISE */ /* detect transition from long->short sample */ if ((value & 0x80) && FALSE == noise->short_sample) { /* recalculate short noise buffer */ shift_register15(noise_short_lut, APU_NOISE_93); noise->cur_pos = 0; } noise->short_sample = (value & 0x80) ? TRUE : FALSE; #endif /* !REALTIME_NOISE */ } break; case APU_WRD3: #if 0 apu->noise.regs[2] = value; apu->noise.vbl_length = apu->vbl_lut[value >> 3]; apu->noise.env_vol = 0; /* reset envelope */ #endif { noise_t * noise = &(apu->noise); noise->regs[2] = value; noise->vbl_length = apu->vbl_lut[value >> 3]; 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; } */ { dmc_t * dmc = &(apu->dmc); dmc->regs[0] = value; dmc->freq = APU_TO_FIXED(dmc_clocks[value & 0x0F]); dmc->looping = (value & 0x40) ? TRUE : FALSE; if (value & 0x80) dmc->irq_gen = TRUE; else { dmc->irq_gen = FALSE; dmc->irq_occurred = FALSE; } } break; case APU_WRE1: /* 7-bit DAC */ #if 0 /* 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; #endif { dmc_t * dmc = &(apu->dmc); value &= 0x7F; /* bit 7 ignored */ dmc->output_vol += ((value - apu->dmc.regs[1]) << 8); dmc->regs[1] = value; } break; case APU_WRE2: /* apu->dmc.regs[2] = value; apu->dmc.cached_addr = 0xC000 + (u16) (value << 6); */ { dmc_t * dmc = &(apu->dmc); dmc->regs[2] = value; dmc->cached_addr = 0xC000 + (u16) (value << 6); } break; case APU_WRE3: /* apu->dmc.regs[3] = value; apu->dmc.cached_dmalength = ((value << 4) + 1) << 3; */ { dmc_t * dmc = &(apu->dmc); dmc->regs[3] = value; dmc->cached_dmalength = ((value << 4) + 1) << 3; } break; case APU_SMASK: #if 0 /* 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; #endif { dmc_t * dmc = &(apu->dmc); triangle_t * tri = &(apu->triangle); noise_t * noise = &(apu->noise); 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) tri->enabled = TRUE; else { tri->enabled = FALSE; tri->vbl_length = 0; tri->linear_length = 0; tri->counter_started = FALSE; tri->write_latency = 0; } if (value & 0x08) noise->enabled = TRUE; else { noise->enabled = FALSE; noise->vbl_length = 0; } if (value & 0x10) { if (0 == dmc->dma_length) apu_dmcreload(&apu->dmc); } else dmc->dma_length = 0; dmc->irq_occurred = FALSE; } break; /* unused, but they get hit in some mem-clear loops */ case 0x4009: case 0x400D: break; default: //DCR if(apu->ext) { apu_memwrite* mw = apu->ext->mem_write; while(mw->max_range != -1)//整数转换导致改变的迹象 { if((mw->min_range <= address) && ((mw->max_range >= address))) { mw->write_func(address, value); break; } mw++; } } break; } } /* Read from $4000-$4017 */ u8 Apu_Read4015(u32 address)//*********************************************************************** { u8 value=0; // if(!(frame_irq_enabled & 0xC0)) { /* 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; /* bodge for timestamp queue */ if (apu->dmc.enabled) value |= 0x10; if (apu->dmc.irq_occurred) value |= 0x80; return value | 0x40; } // return value; } void Apu_Write4015(u8 value,u32 address ) { apudata_t d; apu->dmc.enabled = (value & 0x10) ? TRUE : FALSE; d.timestamp = clocks; //d.timestamp = nes6502_getcycles(0);********************* d.address = address|0x4000; d.value = value; apu_enqueue(&d); } void Apu_Write(u8 value,u32 address ) { apudata_t d; d.timestamp = clocks; //d.timestamp = nes6502_getcycles(0);********************* d.address = address|0x4000; d.value = value; apu_enqueue(&d); } void apu_getpcmdata(void **data, int *num_samples, int *sample_bits) { *data = apu->buffer; *num_samples = apu->num_samples; *sample_bits = apu->sample_bits; } // _local_sample_size = 8 // apu_process(buf, buf_len/(_local_sample_size/8)); void apu_process(u16 *buffer, int num_samples) { apudata_t *d; u32 elapsed_cycles; static int prev_sample = 0; int next_sample, accum; /* grab it, keep it local for speed */ elapsed_cycles = (u32) apu->elapsed_cycles; if (NULL == buffer) { /* just go through the motions... */ 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); } } else { // Rick u8 * mix_enable = apu->mix_enable; /* 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 (mix_enable[0]) accum += apu_rectangle(&apu->rectangle[0]); if (mix_enable[1]) accum += apu_rectangle(&apu->rectangle[1]); if (mix_enable[2]) accum += apu_triangle(&apu->triangle); if (mix_enable[3]) accum += apu_noise(&apu->noise); if (mix_enable[4]) accum += apu_dmc(&apu->dmc); if (apu->ext && 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; *buffer++=(u16)accum;//音频数据存入缓冲 } } /* resync cycle counter 重新同步循环计数器*/ apu->elapsed_cycles =clocks; // apu->elapsed_cycles = nes6502_getcycles(0);//***************************************************************** } void apu_reset(void) { u32 address; apu->elapsed_cycles = 0; mymemset(&apu->queue, 0, APUQUEUE_SIZE * sizeof(apudata_t)); apu->q_head = apu->q_tail = 0; /* use to avoid bugs =) */ for (address = 0x00; address <= 0x13; address++)apu_regwrite(address, 0); apu_regwrite(0x15, 0x00); 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++) apu->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++) apu->vbl_lut[i] = vbl_length[i] * num_samples; /* triangle wave channel's linear length table */ for (i = 0; i < 128; i++) apu->trilength_lut[i] = (int) (0.25 * (i * num_samples)); #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 */ } void apu_setparams(int sample_rate, int refresh_rate, int frag_size, int sample_bits) { apu->sample_rate = sample_rate; apu->refresh_rate = refresh_rate; apu->sample_bits = sample_bits; apu->num_samples = sample_rate / refresh_rate; frag_size = frag_size; /* quell warnings 平息警告*/ /* turn into fixed point! */ apu->cycle_rate = (int) (APU_BASEFREQ * 65536.0 / (float) sample_rate); /* build various lookup tables for apu */ apu_build_luts(apu->num_samples); if (apu->ext) apu->ext->paramschanged(); } /*硬件初始化模拟声音,创建波形/声音 */ // _local_sample_rate = 11025; _local_sample_size = 8; // apu_create(_local_sample_rate, 60, 0, _local_sample_size); //void apu_create(int sample_rate, int refresh_rate, int frag_size, int sample_bits) void apu_init(void) { int channel; mymemset(apu, 0, sizeof(apu_t)); /* set the stupid flag to tell difference between two rectangles */ apu->rectangle[0].sweep_complement = TRUE; apu->rectangle[1].sweep_complement = FALSE; apu->ext = NULL; //apu_setparams(sample_rate, refresh_rate, frag_size, sample_bits); apu_setparams(APU_SAMPLE_RATE,60,0,16);//APU_SAMPLE_RATE为22050Hz //采样率 apu_reset(); for(channel=0;channel<6;channel++)apu_setchan(channel,TRUE); apu->filter_type=APU_FILTER_LOWPASS; //设置筛选器类型 } //apu声音输出 void apu_soundoutput(void) { u16 i; apu_process(wave_buffers,APU_PCMBUF_SIZE); for(i=0;i<30;i++)if(wave_buffers[i]!=wave_buffers[i+1])break;//判断前30个数据,是不是都相等? if(i==30&&wave_buffers[i])//都相等,且不等于0 { for(i=0;i