//////////////////////////////////////////////////////////////////////////////////////// // // Nestopia - NES/Famicom emulator written in C++ // // Copyright (C) 2003-2008 Martin Freij // // This file is part of Nestopia. // // Nestopia 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. // // Nestopia 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Nestopia; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //////////////////////////////////////////////////////////////////////////////////////// #ifndef NST_CPU_H #error Do not include NstApu.h directly! #endif #include "NstSoundRenderer.hpp" namespace Nes { namespace Core { namespace Sound { class Output; } namespace State { class Saver; class Loader; } class Cpu; class Apu { public: explicit Apu(Cpu&); void Reset(bool); void PowerOff(); void ClearBuffers(); void BeginFrame(Sound::Output*); void EndFrame(); void WriteFrameCtrl(uint); Cycle Clock(); void ClockDMA(uint=0); Result SetSampleRate(dword); Result SetSampleBits(uint); Result SetSpeed(uint); Result SetVolume(uint,uint); uint GetVolume(uint) const; uint GetCtrl(); void Mute(bool); void SetAutoTranspose(bool); void SetGenie(bool); void EnableStereo(bool); void SaveState(State::Saver&,dword) const; void LoadState(State::Loader&); class NST_NO_VTABLE Channel { Apu& apu; protected: explicit Channel(Apu&); ~Channel(); void Update() const; void Connect(bool); dword GetSampleRate() const; uint GetVolume(uint) const; void GetOscillatorClock(Cycle&,uint&) const; Cycle GetCpuClockBase() const; uint GetCpuClockDivider() const; Cycle GetCpuClock(uint=1) const; bool IsMuted() const; bool IsGenie() const; public: typedef Sound::Sample Sample; enum { APU_SQUARE1, APU_SQUARE2, APU_TRIANGLE, APU_NOISE, APU_DPCM, EXT_FDS, EXT_MMC5, EXT_VRC6, EXT_VRC7, EXT_N163, EXT_S5B }; enum { OUTPUT_MIN = -32767, OUTPUT_MAX = +32767, OUTPUT_MUL = 256, OUTPUT_DECAY = OUTPUT_MUL / 4 - 1, DEFAULT_VOLUME = 85 }; virtual void Reset() = 0; virtual Sample GetSample() = 0; virtual Cycle Clock(Cycle,Cycle,Cycle); virtual bool UpdateSettings() = 0; class LengthCounter { public: LengthCounter(); void Reset(); void LoadState(State::Loader&); void SaveState(State::Saver&,dword) const; private: uint enabled; uint count; static const byte lut[32]; public: uint Disable(bool disable) { enabled = disable - 1U; count &= enabled; return enabled; } void Write(uint data) { NST_ASSERT( (data >> 3) < sizeof(array(lut)) ); count = lut[data >> 3] & enabled; } void Write(uint data,bool frameCounterDelta) { NST_VERIFY_MSG( frameCounterDelta, "APU $40xx/framecounter conflict" ); if (frameCounterDelta || !count) Write( data ); } uint GetCount() const { return count; } bool Clock() { return count && !--count; } }; class Envelope { public: Envelope(); void Reset(); void SetOutputVolume(uint); void LoadState(State::Loader&); void SaveState(State::Saver&,dword) const; void Clock(); void Write(uint); private: void UpdateOutput(); dword output; uint outputVolume; byte regs[2]; byte count; bool reset; public: bool Looping() const { return regs[1] & 0x20U; } dword Volume() const { return output; } void ResetClock() { reset = true; } }; class DcBlocker { public: DcBlocker(); void Reset(); Sample Apply(Sample); void LoadState(State::Loader&); void SaveState(State::Saver&,dword) const; private: enum { POLE = 3 // ~0.9999 }; idword prev; idword next; idword acc; }; }; private: typedef void (NST_FASTCALL Apu::*Updater)(Cycle); inline void Update(Cycle); void Update(); void UpdateLatency(); bool UpdateDelta(); void Reset(bool,bool); void CalculateOscillatorClock(Cycle&,uint&) const; void Resync(dword); NST_NO_INLINE void ClearBuffers(bool); enum { MAX_CHANNELS = 11, STATUS_NO_FRAME_IRQ = 0x40, STATUS_SEQUENCE_5_STEP = 0x80, STATUS_FRAME_IRQ_ENABLE = 0, STATUS_BITS = STATUS_NO_FRAME_IRQ|STATUS_SEQUENCE_5_STEP, NLN_VOL = 192, NLN_SQ_F = 900, NLN_SQ_0 = 9552UL * Channel::OUTPUT_MUL * NLN_VOL * (NLN_SQ_F/100), NLN_SQ_1 = 8128UL * Channel::OUTPUT_MUL * NLN_SQ_F, NLN_SQ_2 = NLN_SQ_F * 100UL, NLN_TND_F = 500, NLN_TND_0 = 16367UL * Channel::OUTPUT_MUL * NLN_VOL * (NLN_TND_F/100), NLN_TND_1 = 24329UL * Channel::OUTPUT_MUL * NLN_TND_F, NLN_TND_2 = NLN_TND_F * 100UL }; NES_DECL_POKE( 4000 ); NES_DECL_POKE( 4001 ); NES_DECL_POKE( 4002 ); NES_DECL_POKE( 4003 ); NES_DECL_POKE( 4004 ); NES_DECL_POKE( 4005 ); NES_DECL_POKE( 4006 ); NES_DECL_POKE( 4007 ); NES_DECL_POKE( 4008 ); NES_DECL_POKE( 400A ); NES_DECL_POKE( 400B ); NES_DECL_POKE( 400C ); NES_DECL_POKE( 400E ); NES_DECL_POKE( 400F ); NES_DECL_POKE( 4010 ); NES_DECL_POKE( 4011 ); NES_DECL_POKE( 4012 ); NES_DECL_POKE( 4013 ); NES_DECL_POKE( 4015 ); NES_DECL_PEEK( 4015 ); NES_DECL_PEEK( 40xx ); NST_NO_INLINE Channel::Sample GetSample(); void NST_FASTCALL SyncOn (Cycle); void NST_FASTCALL SyncOnExt (Cycle); void NST_FASTCALL SyncOff (Cycle); NST_NO_INLINE void ClockFrameIRQ(Cycle); NST_NO_INLINE void ClockFrameCounter(); NST_NO_INLINE void ClockDmc(Cycle,uint=0); NST_NO_INLINE void ClockOscillators(bool); template void FlushSound(); void UpdateSettings(); void UpdateVolumes(); struct Cycles { Cycles(); void Update(dword,uint,const Cpu&); void Reset(bool,CpuModel); uint fixed; Cycle rate; Cycle rateCounter; Cycle frameCounter; Cycle extCounter; word frameDivider; word frameIrqRepeat; Cycle frameIrqClock; Cycle dmcClock; static const dword frameClocks[3][4]; static const dword oscillatorClocks[3][2][4]; }; class Synchronizer { uint sync; uint duty; dword streamed; dword rate; public: Synchronizer(); void Reset(uint,dword,const Cpu&); void Resync(uint,const Cpu&); NST_SINGLE_CALL dword Clock(dword,dword,const Cpu&); }; class Oscillator { enum { RESET_CYCLES = 2048 }; protected: Oscillator(); void Reset(); void UpdateSettings(dword,uint); ibool active; idword timer; Cycle rate; Cycle frequency; dword amp; uint fixed; public: inline void ClearAmp(); }; class Square : public Oscillator { public: void Reset(); void UpdateSettings(uint,dword,uint); void LoadState(State::Loader&); void SaveState(State::Saver&,dword) const; NST_SINGLE_CALL void WriteReg0(uint); NST_SINGLE_CALL void WriteReg1(uint); NST_SINGLE_CALL void WriteReg2(uint); NST_SINGLE_CALL void WriteReg3(uint,Cycle); NST_SINGLE_CALL void Disable(bool); dword GetSample(); NST_SINGLE_CALL void ClockEnvelope(); NST_SINGLE_CALL void ClockSweep(uint); inline uint GetLengthCounter() const; private: inline bool CanOutput() const; void UpdateFrequency(); enum { MIN_FRQ = 0x008, MAX_FRQ = 0x7FF, REG0_DUTY_SHIFT = 6, REG1_SWEEP_SHIFT = 0x07, REG1_SWEEP_DECREASE = 0x08, REG1_SWEEP_RATE = 0x70, REG1_SWEEP_RATE_SHIFT = 4, REG1_SWEEP_ENABLED = 0x80, REG3_WAVELENGTH_LOW = 0x00FF, REG3_WAVELENGTH_HIGH = 0x0700 }; uint step; uint duty; Channel::Envelope envelope; Channel::LengthCounter lengthCounter; bool validFrequency; bool sweepReload; byte sweepCount; byte sweepRate; uint sweepIncrease; word sweepShift; word waveLength; }; class Triangle : public Oscillator { public: Triangle(); void Reset(); void UpdateSettings(uint,dword,uint); void LoadState(State::Loader&); void SaveState(State::Saver&,dword) const; NST_SINGLE_CALL void WriteReg0(uint); NST_SINGLE_CALL void WriteReg2(uint); NST_SINGLE_CALL void WriteReg3(uint,Cycle); NST_SINGLE_CALL void Disable(bool); NST_SINGLE_CALL dword GetSample(); NST_SINGLE_CALL void ClockLinearCounter(); NST_SINGLE_CALL void ClockLengthCounter(); inline uint GetLengthCounter() const; private: inline bool CanOutput() const; enum { MIN_FRQ = 2 + 1, STEP_CHECK = 0x1F, REG0_LINEAR_COUNTER_LOAD = 0x7F, REG0_LINEAR_COUNTER_START = 0x80, REG2_WAVE_LENGTH_LOW = 0x00FF, REG3_WAVE_LENGTH_HIGH = 0x0700 }; enum Status { STATUS_COUNTING, STATUS_RELOAD }; uint step; uint outputVolume; Status status; word waveLength; byte linearCtrl; byte linearCounter; Channel::LengthCounter lengthCounter; }; class Noise : public Oscillator { public: void Reset(CpuModel); void UpdateSettings(uint,dword,uint); void LoadState(State::Loader&,CpuModel); void SaveState(State::Saver&,dword) const; NST_SINGLE_CALL void WriteReg0(uint); NST_SINGLE_CALL void WriteReg2(uint,CpuModel); NST_SINGLE_CALL void WriteReg3(uint,Cycle); NST_SINGLE_CALL void Disable(bool); NST_SINGLE_CALL dword GetSample(); NST_SINGLE_CALL void ClockEnvelope(); NST_SINGLE_CALL void ClockLengthCounter(); inline uint GetLengthCounter() const; private: inline bool CanOutput() const; uint GetFrequencyIndex() const; enum { REG2_FREQUENCY = 0x0F, REG2_93BIT_MODE = 0x80 }; uint bits; uint shifter; Channel::Envelope envelope; Channel::LengthCounter lengthCounter; static const word lut[3][16]; }; class Dmc { public: Dmc(); void Reset(CpuModel); void UpdateSettings(uint); void LoadState(State::Loader&,const Cpu&,CpuModel,Cycle&); void SaveState(State::Saver&,dword,const Cpu&,Cycle) const; NST_SINGLE_CALL bool WriteReg0(uint,CpuModel); NST_SINGLE_CALL void WriteReg1(uint); NST_SINGLE_CALL void WriteReg2(uint); NST_SINGLE_CALL void WriteReg3(uint); NST_SINGLE_CALL void Disable(bool,Cpu&); NST_SINGLE_CALL dword GetSample(); NST_SINGLE_CALL bool ClockDAC(); NST_SINGLE_CALL void Update(); NST_SINGLE_CALL void ClockDMA(Cpu&,Cycle&,uint=0); inline void ClearAmp(); inline uint GetLengthCounter() const; static Cycle GetResetFrequency(CpuModel); private: void DoDMA(Cpu&,Cycle,uint=0); enum { REG0_FREQUENCY = 0x0F, REG0_LOOP = 0x40, REG0_IRQ_ENABLE = 0x80, INP_STEP = 8 }; uint curSample; uint linSample; uint outputVolume; Cycle frequency; struct { uint ctrl; word lengthCounter; word address; } regs; struct { byte shifter; byte dac; byte buffer; bool active; } out; struct { word lengthCounter; word address; word buffered; word buffer; } dma; static const word lut[3][16]; }; struct Settings { Settings(); dword rate; uint bits; byte speed; bool muted; bool transpose; bool genie; bool stereo; bool audible; byte volumes[MAX_CHANNELS]; }; uint ctrl; Updater updater; Cpu& cpu; Cycles cycles; Synchronizer synchronizer; Square square[2]; Triangle triangle; Noise noise; Dmc dmc; Channel* extChannel; Channel::DcBlocker dcBlocker; Sound::Output* stream; Sound::Buffer buffer; Settings settings; public: dword GetSampleRate() const { return settings.rate; } uint GetSampleBits() const { return settings.bits; } uint GetSpeed() const { return settings.speed; } bool IsAutoTransposing() const { return settings.transpose; } bool IsGenie() const { return settings.genie; } bool InStereo() const { return settings.stereo; } bool IsMuted() const { return settings.muted; } bool IsAudible() const { return settings.audible && !settings.muted; } }; } }