Exemplo n.º 1
0
            public void RunOne()
            {
                if (data.IRQ_delay != 0x7FFF)
                {
                    if (data.IRQ_delay > 0)
                    {
                        --data.IRQ_delay.Value;
                    }
                    else
                    {
                        sequencer_irq = true; SyncIRQ(); data.IRQ_delay.Value = 0x7FFF;
                    }
                }
                if (data.frame_delay > 0)
                {
                    --data.frame_delay.Value;
                }
                else
                {
                    bool Do240 = true, Do120 = false;
                    data.frame_delay.Value += frame_period;
                    switch (data.frame.Value++)
                    {
                    case 0:
                        if (!(bool)data.IRQdisable && !(bool)data.FiveCycleDivider)
                        {
                            data.IRQ_delay.Value = frame_period * 4 + 2;
                        }
                        // passthru
                        goto case 2;

                    case 2:
                        Do120 = true;
                        break;

                    case 1:
                        data.frame_delay.Value -= 2;
                        break;

                    case 3:
                        data.frame.Value = 0;
                        if ((bool)data.FiveCycleDivider)
                        {
                            data.frame_delay.Value += frame_period - 6;
                        }
                        break;
                    }
                    // Some events are invoked at 96 Hz or 120 Hz rate. Others, 192 Hz or 240 Hz.
                    for (int c = 0; c < 4; ++c)
                    {
                        APUchannel ch = channels[c];
                        int        wl = ch.WaveLength;

                        // 96/120 Hz events:
                        if (Do120)
                        {
                            // Length tick (all channels except DMC, but different disable bit for triangle wave)
                            if ((bool)ch.length_counter &&
                                !(c == 2 ? (bool)ch.LinearCounterDisable : (bool)ch.LengthCounterDisable))
                            {
                                ch.length_counter.Value -= 1;
                            }

                            // Sweep tick (square waves only)
                            int ref_ch_sweep_delay = ch.sweep_delay;
                            if (c < 2 && count(ref ref_ch_sweep_delay, ch.SweepRate))
                            {
                                if (wl >= 8 && (bool)ch.SweepEnable && (bool)ch.SweepShift)
                                {
                                    int s = wl >> ch.SweepShift;
                                    wl += ((bool)ch.SweepDecrease ? ((c != 0) ? -s : ~s) : s);
                                    if (wl < 0x800)
                                    {
                                        ch.WaveLength.Value = (uint)wl;
                                    }
                                }
                            }

                            ch.sweep_delay.Value = ref_ch_sweep_delay;
                        }

                        // 240/192 Hz events:
                        if (Do240)
                        {
                            // Linear tick (triangle wave only) (all ticks)
                            if (c == 2)
                            {
                                ch.linear_counter.Value = (bool)ch.LinearCounterDisable
                                                                ? ch.LinearCounterInit
                                                                : (ch.linear_counter > 0 ? ch.linear_counter - 1 : 0);
                            }

                            // Envelope tick (square and noise channels) (all ticks)
                            int ref_ch_env_delay = ch.env_delay;
                            if (c != 2 && count(ref ref_ch_env_delay, ch.EnvDecayRate))
                            {
                                if (ch.envelope > 0 || (bool)ch.EnvDecayLoopEnable)
                                {
                                    ch.envelope.Value = (ch.envelope - 1) & 15;
                                }
                            }
                            ch.env_delay.Value = ref_ch_env_delay;
                        }
                    }
                }

                // Mix the audio: Get the momentary sample from each channel and mix them.
                // #define s(c) tick<c>()
                //v = [](float m,float n, float d) { return n!=0.f ? m/n : d; };
                Func <float, float, float, float> v = (float m, float n, float d) => (n != 0.0f ? m / n : d);

                short sample = (short)(30000 *
                                       (
                                           // Square 0 and 1
                                           v(95.88f, (100.0f + v(8128.0f, tick(0) + tick(1), -100.0f)), 0.0f)
                                           // Triangle, noise, DMC
                                           + v(159.79f, (100.0f + v(1.0f, tick(2) / 8227.0f + tick(3) / 12241.0f + tick(4) / 22638.0f, -1000.0f)), 0.0f)
                                           // GamePak audio (these volume values are bogus, but sound acceptable)
                                           + v(95.88f, (100.0f + v(32512.0f, /*GamePak::ExtAudio()*/ 0, -100.0f)), 0.0f)
                                           - 0.5f
                                       ));

                EmitSample(sample);

                ////this (and the similar line below) is a crude hack
                ////we should be generating logic to suppress the $4015 clear when the assert signal is set instead
                ////be sure to test "apu_test" if you mess with this
                //sequencer_irq |= sequencer_irq_assert;
            }
Exemplo n.º 2
0
            void WriteC(int chno, int index, byte value)
            {
                APUchannel ch = channels[chno];

                switch (index)
                {
                case 0:
                    if ((bool)ch.LinearCounterDisable)
                    {
                        ch.linear_counter.Value = value & 0x7F;
                    }
                    ch.reg0.Value = value;
                    break;

                case 1:
                    ch.reg1.Value        = value;
                    ch.sweep_delay.Value = ch.SweepRate;
                    break;

                case 2:
                    ch.reg2.Value = value;
                    break;

                case 3:
                    ch.reg3.Value = value;
                    if (data.ChannelsEnabled[chno] != 0)
                    {
                        ch.length_counter.Value = LengthCounters[ch.LengthCounterInit];
                    }
                    ch.linear_counter.Value = ch.LinearCounterInit;
                    ch.env_delay.Value      = ch.EnvDecayRate;
                    ch.envelope.Value       = 15;
                    if (index < 8)
                    {
                        ch.phase.Value = 0;
                    }
                    break;

                case 0x12:
                    ch.reg0.Value    = value;
                    ch.address.Value = ((int)ch.reg0 | 0x300) << 6;
                    break;

                case 0x10:
                    ch.reg3.Value       = value;
                    ch.WaveLength.Value = (uint)(DMCperiods[value & 0x0F] - 1);
                    if (!(bool)ch.IRQenable)
                    {
                        dmc_irq = false; SyncIRQ();
                    }
                    break;

                case 0x13:                         // sample length
                    ch.reg1.Value = value;
                    if (ch.length_counter == 0)
                    {
                        ch.length_counter.Value = ch.PCMlength * 16 + 1;
                    }
                    break;

                case 0x11:                         // dac value
                    ch.linear_counter.Value = value & 0x7F;
                    break;

                case 0x15:
                    for (int c = 0; c < 5; ++c)
                    {
                        data.ChannelsEnabled[c] = (uint)((value >> c) & 1);                                 //noteworthy tweak
                    }
                    for (int c = 0; c < 5; ++c)
                    {
                        if (data.ChannelsEnabled[c] == 0)
                        {
                            channels[c].length_counter.Value = 0;
                        }
                        else if (c == 4 && channels[c].length_counter == 0)
                        {
                            APUchannel chh = channels[c];
                            chh.length_counter.Value = chh.PCMlength * 16 + 1;
                            chh.address.Value        = ((int)chh.reg0 | 0x300) << 6;
                            chh.phase.Value          = 0;
                        }
                    }
                    //CPU::reg.APU_DMC_IRQ = false;
                    dmc_irq = false; SyncIRQ();
                    break;

                case 0x17:
                    data.IRQdisable.Value       = (uint)(value & 0x40);
                    data.FiveCycleDivider.Value = (uint)(value & 0x80);
                    // apu_test 1-len_ctr: Writing $80 to $4017 should clock length immediately
                    //                 But Writing $00 to $4017 shouldn't clock length immediately
                    data.frame_delay.Value &= 1;
                    data.frame.Value        = 0;
                    data.IRQ_delay.Value    = 0x7FFF;
                    if ((bool)data.IRQdisable)
                    {
                        sequencer_irq = false; SyncIRQ();
                    }
                    if (!(bool)data.FiveCycleDivider)
                    {
                        data.frame.Value        = 1;
                        data.frame_delay.Value += frame_period;

                        if (!(bool)data.IRQdisable)
                        {
                            data.IRQ_delay.Value = data.frame_delay + frame_period * 3 + 1 - 3;
                            // ^ "- 3" makes apu_test "4-jitter" not complain
                            //   that "Frame irq is set too late"
                        }
                    }
                    break;
                }
            }
Exemplo n.º 3
0
            int tick(int c)
            {
                APUchannel ch = channels[c];
                int        wl = ch.WaveLength;

                if (c != 4)
                {
                    ++wl;
                }
                if (c < 2)
                {
                    wl *= 2;
                }

                if (c == 3)
                {
                    wl = NoisePeriods[ch.NoiseFreq];
                }

                //if(c != 4) wl = wl * (IO::UISpeed);
                // ^ Match to the UI speed (but don't for DPCM, because it would skew the timings)

                int volume = (bool)ch.length_counter ? (bool)ch.EnvDecayDisable ? (int)ch.FixedVolume : ch.envelope : 0;
                // Sample may change at wavelen intervals.
                int ref_S = ch.level;
                int ref_ch_wave_counter = ch.wave_counter;

                if (!(data.ChannelsEnabled[c] != 0) ||
                    !count(ref ref_ch_wave_counter, wl))
                {
                    ch.wave_counter.Value = ref_ch_wave_counter;
                    return(ref_S);
                }
                ch.wave_counter.Value = ref_ch_wave_counter;
                switch (c)
                {
                case 0:
                default:
                case 1:                         // Square wave. With four different 8-step binary waveforms (32 bits of data total).
                    ch.phase.Value++;
                    if (wl < 8)
                    {
                        return(ref_S);
                    }
                    if ((bool)ch.SweepEnable && !(bool)ch.SweepDecrease)
                    {
                        if (wl + (wl >> ch.SweepShift) >= 0x800)
                        {
                            return(ref_S);
                        }
                    }
                    return(ref_S = ch.level.Value = (0xF33C0C04u & (1u << (ch.phase % 8 + ch.DutyCycle * 8))) != 0 ? volume : 0);

                case 2:                         // Triangle wave
                    if ((bool)ch.length_counter && (bool)ch.linear_counter && wl >= 3)
                    {
                        ++ch.phase.Value;
                    }
                    return(ref_S = ch.level.Value = (ch.phase & 15) ^ (((ch.phase & 16) != 0) ? 15 : 0));

                case 3:                         // Noise: Linear feedback shift register
                    if (!(bool)ch.hold)
                    {
                        ch.hold.Value = 1;
                    }
                    ch.hold.Value = (ch.hold >> 1)
                                    | (((ch.hold ^ (ch.hold >> ((bool)ch.NoiseType ? 6 : 1))) & 1) << 14);
                    return(ref_S = ch.level.Value = ((ch.hold & 1) != 0) ? 0 : volume);

                case 4:                         // Delta modulation channel (DMC)
                                                // hold           = 8 bit value
                                                // phase          = number of bits buffered
                                                // length_counter =
                    if (wl == 0)
                    {
                        return(ref_S);
                    }
                    if (ch.phase == 0)                                        // Nothing in sample buffer?
                    {
                        if (!(bool)ch.length_counter && (bool)ch.LoopEnabled) // Loop?
                        {
                            ch.length_counter.Value = ch.PCMlength * 16 + 1;
                            ch.address.Value        = (int)((ch.reg0 | 0x300) << 6);
                        }
                        if (ch.length_counter > 0)                                 // Load next 8 bits if available
                        {
                            //==========================TODO============== DMC COST=====================
                            //for(unsigned t = data.DMC_CycleCost; t > 1; --t)
                            //      CPU::RB(u16(ch.address) | 0x8000); // timing
                            ch.hold.Value  = nes.ReadMemory((ushort)((ch.address.Value++) | 0x8000));                                    // Fetch byte
                            ch.phase.Value = 8;
                            --ch.length_counter.Value;
                        }
                        else                                 // Otherwise, disable channel or issue IRQ
                        {
                            if ((bool)ch.IRQenable)
                            {
                                //CPU::reg.APU_DMC_IRQ = true;
                                dmc_irq = true;
                                SyncIRQ();
                            }

                            data.ChannelsEnabled[4] = 0;
                        }
                    }
                    if (ch.phase != 0)                             // Update the signal if sample buffer nonempty
                    {
                        int v = ch.linear_counter;
                        if (((ch.hold << --ch.phase.Value) & 0x80) != 0)
                        {
                            v += 2;
                        }
                        else
                        {
                            v -= 2;
                        }
                        if (v >= 0 && v <= 0x7F)
                        {
                            ch.linear_counter.Value = v;
                        }
                    }
                    return(ref_S = ch.level = ch.linear_counter);
                }
            }