blob: 72f697421468770eb4dff4986c8187c9709a9367 [file] [log] [blame]
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2006 Adam Gashlin (hcs)
* Copyright (C) 2004 Disch
*
* This program 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.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/*
* This is a perversion of Disch's excellent NotSoFatso.
*/
#include "codeclib.h"
#include "inttypes.h"
#include "system.h"
CODEC_HEADER
/* arm doesn't benefit from IRAM? */
#ifdef CPU_ARM
#undef ICODE_ATTR
#define ICODE_ATTR
#undef IDATA_ATTR
#define IDATA_ATTR
#else
#define ICODE_INSTEAD_OF_INLINE
#endif
/* Maximum number of bytes to process in one iteration */
#define WAV_CHUNK_SIZE (1024*2)
static int16_t samples[WAV_CHUNK_SIZE] IBSS_ATTR;
#define ZEROMEMORY(addr,size) memset(addr,0,size)
/* simple profiling with USEC_TIMER
#define NSF_PROFILE
*/
#ifdef NSF_PROFILE
#define CREATE_TIMER(name) uint32_t nsf_timer_##name##_start,\
nsf_timer_##name##_total
#define ENTER_TIMER(name) nsf_timer_##name##_start=USEC_TIMER
#define EXIT_TIMER(name) nsf_timer_##name##_total+=\
(USEC_TIMER-nsf_timer_##name##_start)
#define READ_TIMER(name) (nsf_timer_##name##_total)
#define RESET_TIMER(name) nsf_timer_##name##_total=0
#define PRINT_TIMER_PCT(bname,tname,nstr) ci->fdprintf(
logfd,"%10ld ",READ_TIMER(bname));\
ci->fdprintf(logfd,"(%3d%%) " nstr "\t",\
((uint64_t)READ_TIMER(bname))*100/READ_TIMER(tname))
CREATE_TIMER(total);
CREATE_TIMER(cpu);
CREATE_TIMER(apu);
CREATE_TIMER(squares);
CREATE_TIMER(tnd);
CREATE_TIMER(tnd_enter);
CREATE_TIMER(tnd_tri);
CREATE_TIMER(tnd_noise);
CREATE_TIMER(tnd_dmc);
CREATE_TIMER(fds);
CREATE_TIMER(frame);
CREATE_TIMER(mix);
void reset_profile_timers(void) {
RESET_TIMER(total);
RESET_TIMER(cpu);
RESET_TIMER(apu);
RESET_TIMER(squares);
RESET_TIMER(tnd);
RESET_TIMER(tnd_enter);
RESET_TIMER(tnd_tri);
RESET_TIMER(tnd_noise);
RESET_TIMER(tnd_dmc);
RESET_TIMER(fds);
RESET_TIMER(frame);
RESET_TIMER(mix);
}
int logfd=-1;
void print_timers(char * path, int track) {
logfd = ci->open("/nsflog.txt",O_WRONLY|O_CREAT|O_APPEND, 0666);
ci->fdprintf(logfd,"%s[%d]:\t",path,track);
ci->fdprintf(logfd,"%10ld total\t",READ_TIMER(total));
PRINT_TIMER_PCT(cpu,total,"CPU");
PRINT_TIMER_PCT(apu,total,"APU");
ci->fdprintf(logfd,"\n\t");
PRINT_TIMER_PCT(squares,apu,"squares");
PRINT_TIMER_PCT(frame,apu,"frame");
PRINT_TIMER_PCT(mix,apu,"mix");
PRINT_TIMER_PCT(fds,apu,"FDS");
PRINT_TIMER_PCT(tnd,apu,"tnd");
ci->fdprintf(logfd,"\n\t\t");
PRINT_TIMER_PCT(tnd_enter,tnd,"enter");
PRINT_TIMER_PCT(tnd_tri,tnd,"triangle");
PRINT_TIMER_PCT(tnd_noise,tnd,"noise");
PRINT_TIMER_PCT(tnd_dmc,tnd,"DMC");
ci->fdprintf(logfd,"\n");
ci->close(logfd);
logfd=-1;
}
#else
#define CREATE_TIMER(name)
#define ENTER_TIMER(name)
#define EXIT_TIMER(name)
#define READ_TIMER(name)
#define RESET_TIMER(name)
#define print_timers(path,track)
#define reset_profile_timers()
#endif
/* proper handling of multibyte values */
#ifdef ROCKBOX_LITTLE_ENDIAN
union TWIN
{
uint16_t W;
struct{ uint8_t l; uint8_t h; } B;
};
union QUAD
{
uint32_t D;
struct{ uint8_t l; uint8_t h; uint16_t w; } B;
};
#else
union TWIN
{
uint16_t W;
struct{ uint8_t h; uint8_t l; } B;
};
union QUAD
{
uint32_t D;
struct{uint16_t w; uint8_t h; uint8_t l; } B;
};
#endif
#define NTSC_FREQUENCY 1789772.727273f
#define PAL_FREQUENCY 1652097.692308f
#define NTSC_NMIRATE 60.098814f
#define PAL_NMIRATE 50.006982f
#define NES_FREQUENCY 21477270
#define NTSC_FRAME_COUNTER_FREQ (NTSC_FREQUENCY / (NES_FREQUENCY / 89490.0f))
#define PAL_FRAME_COUNTER_FREQ (PAL_FREQUENCY / (NES_FREQUENCY / 89490.0f))
/****************** tables */
static const int32_t ModulationTable[8] ICONST_ATTR = {0,1,2,4,0,-4,-2,-1};
const uint16_t DMC_FREQ_TABLE[2][0x10] = {
/* NTSC */
{0x1AC,0x17C,0x154,0x140,0x11E,0x0FE,0x0E2,0x0D6,0x0BE,0x0A0,0x08E,0x080,
0x06A,0x054,0x048,0x036},
/* PAL */
{0x18C,0x160,0x13A,0x128,0x108,0x0EA,0x0D0,0x0C6,0x0B0,0x094,0x082,0x076,
0x062,0x04E,0x042,0x032}
};
const uint8_t DUTY_CYCLE_TABLE[4] = {2,4,8,12};
const uint8_t LENGTH_COUNTER_TABLE[0x20] = {
0x0A,0xFE,0x14,0x02,0x28,0x04,0x50,0x06,0xA0,0x08,0x3C,0x0A,0x0E,0x0C,0x1A,
0x0E,0x0C,0x10,0x18,0x12,0x30,0x14,0x60,0x16,0xC0,0x18,0x48,0x1A,0x10,0x1C,
0x20,0x1E
};
const uint16_t NOISE_FREQ_TABLE[0x10] = {
0x004,0x008,0x010,0x020,0x040,0x060,0x080,0x0A0,0x0CA,0x0FE,0x17C,0x1FC,
0x2FA,0x3F8,0x7F2,0xFE4
};
/****************** NSF loading ******************/
/* file format structs (both are little endian) */
struct NESM_HEADER
{
uint32_t nHeader;
uint8_t nHeaderExtra;
uint8_t nVersion;
uint8_t nTrackCount;
uint8_t nInitialTrack;
uint16_t nLoadAddress;
uint16_t nInitAddress;
uint16_t nPlayAddress;
uint8_t szGameTitle[32];
uint8_t szArtist[32];
uint8_t szCopyright[32];
uint16_t nSpeedNTSC;
uint8_t nBankSwitch[8];
uint16_t nSpeedPAL;
uint8_t nNTSC_PAL;
uint8_t nExtraChip;
uint8_t nExpansion[4];
};
struct NSFE_INFOCHUNK
{
uint16_t nLoadAddress;
uint16_t nInitAddress;
uint16_t nPlayAddress;
uint8_t nIsPal;
uint8_t nExt;
uint8_t nTrackCount;
uint8_t nStartingTrack;
};
int32_t LoadFile(uint8_t *,size_t);
int32_t LoadFile_NESM(uint8_t *,size_t);
int32_t LoadFile_NSFE(uint8_t *,size_t);
/* NSF file info */
/* basic NSF info */
int32_t bIsExtended=0; /* 0 = NSF, 1 = NSFE */
uint8_t nIsPal=0; /* 0 = NTSC, 1 = PAL,
2,3 = mixed NTSC/PAL (interpretted as NTSC) */
int32_t nfileLoadAddress=0; /* The address to which the NSF code is
loaded */
int32_t nfileInitAddress=0; /* The address of the Init routine
(called at track change) */
int32_t nfilePlayAddress=0; /* The address of the Play routine
(called several times a second) */
uint8_t nChipExtensions=0; /* Bitwise representation of the external chips
used by this NSF. */
/* old NESM speed stuff (blarg) */
int32_t nNTSC_PlaySpeed=0;
int32_t nPAL_PlaySpeed=0;
/* track info */
/* The number of tracks in the NSF (1 = 1 track, 5 = 5 tracks, etc) */
int32_t nTrackCount=0;
/* The initial track (ZERO BASED: 0 = 1st track, 4 = 5th track, etc) */
int32_t nInitialTrack=0;
/* nsf data */
uint8_t* pDataBuffer=0; /* the buffer containing NSF code. */
int32_t nDataBufferSize=0; /* the size of the above buffer. */
/* playlist */
uint8_t nPlaylist[256]; /* Each entry is the zero based index of the
song to play */
int32_t nPlaylistSize=0; /* the number of tracks in the playlist */
/* track time / fade */
int32_t nTrackTime[256]; /* track times -1 if no track times specified */
int32_t nTrackFade[256]; /* track fade times -1 if none are specified */
/* string info */
uint8_t szGameTitle[0x101];
uint8_t szArtist[0x101];
uint8_t szCopyright[0x101];
uint8_t szRipper[0x101];
/* bankswitching info */
uint8_t nBankswitch[8]={0}; /* The initial bankswitching registers needed
* for some NSFs. If the NSF does not use
* bankswitching, these values will all be zero
*/
int32_t LoadFile(uint8_t * inbuffer, size_t size)
{
if(!inbuffer) return -1;
int32_t ret = -1;
if(!memcmp(inbuffer,"NESM",4)) ret = LoadFile_NESM(inbuffer,size);
if(!memcmp(inbuffer,"NSFE",4)) ret = LoadFile_NSFE(inbuffer,size);
/*
* Snake's revenge puts '00' for the initial track,
* which (after subtracting 1) makes it 256 or -1 (bad!)
* This prevents that crap
*/
if(nInitialTrack >= nTrackCount)
nInitialTrack = 0;
if(nInitialTrack < 0)
nInitialTrack = 0;
/* if there's no tracks... this is a crap NSF */
if(nTrackCount < 1)
{
return -1;
}
return ret;
}
int32_t LoadFile_NESM(uint8_t* inbuffer, size_t size)
{
uint8_t ignoreversion=1;
uint8_t needdata=1;
/* read the info */
struct NESM_HEADER hdr;
memcpy(&hdr,inbuffer,sizeof(hdr));
/* confirm the header */
if(memcmp("NESM",&(hdr.nHeader),4)) return -1;
if(hdr.nHeaderExtra != 0x1A) return -1;
/* stupid NSFs claim to be above version 1 >_> */
if((!ignoreversion) && (hdr.nVersion != 1)) return -1;
/*
* NESM is generally easier to work with (but limited!)
* just move the data over from NESM_HEADER over to our member data
*/
bIsExtended = 0;
nIsPal = hdr.nNTSC_PAL & 0x03;
nPAL_PlaySpeed = letoh16(hdr.nSpeedPAL);
nNTSC_PlaySpeed = letoh16(hdr.nSpeedNTSC);
nfileLoadAddress = letoh16(hdr.nLoadAddress);
nfileInitAddress = letoh16(hdr.nInitAddress);
nfilePlayAddress = letoh16(hdr.nPlayAddress);
nChipExtensions = hdr.nExtraChip;
nTrackCount = hdr.nTrackCount;
nInitialTrack = hdr.nInitialTrack - 1;
memcpy(nBankswitch,hdr.nBankSwitch,8);
memcpy(szGameTitle,hdr.szGameTitle,32);
memcpy(szArtist ,hdr.szArtist ,32);
memcpy(szCopyright,hdr.szCopyright,32);
/* read the NSF data */
if(needdata)
{
pDataBuffer=inbuffer+0x80;
nDataBufferSize=size-0x80;
}
/* if we got this far... it was a successful read */
return 0;
}
int32_t LoadFile_NSFE(uint8_t* inbuffer, size_t size)
{
/* the vars we'll be using */
uint32_t nChunkType;
int32_t nChunkSize;
int32_t nChunkUsed;
int32_t i;
uint8_t * nDataPos = 0;
uint8_t bInfoFound = 0;
uint8_t bEndFound = 0;
uint8_t bBankFound = 0;
nPlaylistSize=-1;
struct NSFE_INFOCHUNK info;
ZEROMEMORY(&info,sizeof(struct NSFE_INFOCHUNK));
ZEROMEMORY(nBankswitch,8);
info.nTrackCount = 1; /* default values */
if (size < 8) return -1; /* must have at least NSFE,NEND */
/* confirm the header! */
memcpy(&nChunkType,inbuffer,4);
inbuffer+=4;
if(memcmp(&nChunkType,"NSFE",4)) return -1;
for (i=0;i<256;i++) {
nTrackTime[i]=-1;
nTrackFade[i]=-1;
}
/* begin reading chunks */
while(!bEndFound)
{
memcpy(&nChunkSize,inbuffer,4);
nChunkSize=letoh32(nChunkSize);
inbuffer+=4;
memcpy(&nChunkType,inbuffer,4);
inbuffer+=4;
if(!memcmp(&nChunkType,"INFO",4)) {
/* only one info chunk permitted */
if(bInfoFound) return -1;
if(nChunkSize < 8) return -1; /* minimum size */
bInfoFound = 1;
nChunkUsed = MIN((int32_t)sizeof(struct NSFE_INFOCHUNK),
nChunkSize);
memcpy(&info,inbuffer,nChunkUsed);
inbuffer+=nChunkSize;
bIsExtended = 1;
nIsPal = info.nIsPal & 3;
nfileLoadAddress = letoh16(info.nLoadAddress);
nfileInitAddress = letoh16(info.nInitAddress);
nfilePlayAddress = letoh16(info.nPlayAddress);
nChipExtensions = info.nExt;
nTrackCount = info.nTrackCount;
nInitialTrack = info.nStartingTrack;
nPAL_PlaySpeed = (uint16_t)(1000000 / PAL_NMIRATE);
nNTSC_PlaySpeed = (uint16_t)(1000000 / NTSC_NMIRATE);
} else if (!memcmp(&nChunkType,"DATA",4)) {
if(!bInfoFound) return -1;
if(nDataPos) return -1;
if(nChunkSize < 1) return -1;
nDataBufferSize = nChunkSize;
nDataPos = inbuffer;
inbuffer+=nChunkSize;
} else if (!memcmp(&nChunkType,"NEND",4)) {
bEndFound = 1;
} else if (!memcmp(&nChunkType,"time",4)) {
if(!bInfoFound) return -1;
for (nChunkUsed=0; nChunkUsed < MIN(nChunkSize / 4,nTrackCount);
nChunkUsed++,inbuffer+=4) {
nTrackTime[nChunkUsed]=
((uint32_t)inbuffer[0])|
((uint32_t)inbuffer[1]<<8)|
((uint32_t)inbuffer[2]<<16)|
((uint32_t)inbuffer[3]<<24);
}
inbuffer+=nChunkSize-(nChunkUsed*4);
/* negative signals to use default time */
for(; nChunkUsed < nTrackCount; nChunkUsed++)
nTrackTime[nChunkUsed] = -1;
} else if (!memcmp(&nChunkType,"fade",4)) {
if(!bInfoFound) return -1;
for (nChunkUsed=0; nChunkUsed < MIN(nChunkSize / 4,nTrackCount);
nChunkUsed++,inbuffer+=4) {
nTrackFade[nChunkUsed]=
((uint32_t)inbuffer[0])|
((uint32_t)inbuffer[1]<<8)|
((uint32_t)inbuffer[2]<<16)|
((uint32_t)inbuffer[3]<<24);
}
inbuffer+=nChunkSize-(nChunkUsed*4);
/* negative signals to use default time */
for(; nChunkUsed < nTrackCount; nChunkUsed++)
nTrackFade[nChunkUsed] = -1;
} else if (!memcmp(&nChunkType,"BANK",4)) {
if(bBankFound) return -1;
bBankFound = 1;
nChunkUsed = MIN(8,nChunkSize);
memcpy(nBankswitch,inbuffer,nChunkUsed);
inbuffer+=nChunkSize;
} else if (!memcmp(&nChunkType,"plst",4)) {
nPlaylistSize = nChunkSize;
if(nPlaylistSize >= 1) {
memcpy(nPlaylist,inbuffer,nChunkSize);
inbuffer+=nChunkSize;
}
} else if (!memcmp(&nChunkType,"auth",4)) {
uint8_t* ptr;
ptr = inbuffer;
uint8_t* ar[4] = {szGameTitle,szArtist,szCopyright,szRipper};
int32_t i;
for(i = 0; (ptr-inbuffer)<nChunkSize && i < 4; i++)
{
nChunkUsed = strlen(ptr) + 1;
memcpy(ar[i],ptr,nChunkUsed);
ptr += nChunkUsed;
}
inbuffer+=nChunkSize;
} else if (!memcmp(&nChunkType,"tlbl",4)) {
/* we unfortunately can't use these anyway */
inbuffer+=nChunkSize;
} else { /* unknown chunk */
nChunkType = letoh32(nChunkType)>>24; /* check the first byte */
/* chunk is vital... don't continue */
if((nChunkType >= 'A') && (nChunkType <= 'Z'))
return -1;
/* otherwise, just skip it */
inbuffer+=nChunkSize;
} /* end if series */
} /* end while */
/*
* if we exited the while loop without a 'return', we must have hit an NEND
* chunk if this is the case, the file was layed out as it was expected.
* now.. make sure we found both an info chunk, AND a data chunk... since
* these are minimum requirements for a valid NSFE file
*/
if(!bInfoFound) return -1;
if(!nDataPos) return -1;
/* if both those chunks existed, this file is valid.
Load the data if it's needed */
pDataBuffer=nDataPos;
/* return success! */
return 0;
}
/****************** Audio Device Structures ******************/
struct FDSWave
{
/* Envelope Unit */
uint8_t bEnvelopeEnable;
uint8_t nEnvelopeSpeed;
/* Volume Envelope */
uint8_t nVolEnv_Mode;
uint8_t nVolEnv_Decay;
uint8_t nVolEnv_Gain;
int32_t nVolEnv_Timer;
int32_t nVolEnv_Count;
uint8_t nVolume;
uint8_t bVolEnv_On;
/* Sweep Envenlope */
uint8_t nSweep_Mode;
uint8_t nSweep_Decay;
int32_t nSweep_Timer;
int32_t nSweep_Count;
uint8_t nSweep_Gain;
uint8_t bSweepEnv_On;
/* Effector / LFO / Modulation Unit */
int32_t nSweepBias;
uint8_t bLFO_Enabled;
union TWIN nLFO_Freq;
/*float fLFO_Timer;*/
/*float fLFO_Count;*/
int32_t nLFO_Timer; /* -17.14*/
int32_t nLFO_Count; /* -17.14*/
uint8_t nLFO_Addr;
uint8_t nLFO_Table[0x40];
uint8_t bLFO_On;
/* Main Output */
uint8_t nMainVolume;
uint8_t bEnabled;
union TWIN nFreq;
/*float fFreqCount;*/
int32_t nFreqCount; /* -17.14 */
uint8_t nMainAddr;
uint8_t nWaveTable[0x40];
uint8_t bWaveWrite;
uint8_t bMain_On;
/* Output and Downsampling */
int32_t nMixL;
/* Pop Reducer */
uint8_t bPopReducer;
uint8_t nPopOutput;
int32_t nPopCount;
};
int16_t FDS_nOutputTable_L[4][0x21][0x40];
struct FME07Wave
{
/* Frequency Control */
union TWIN nFreqTimer;
int32_t nFreqCount;
/* Channel Disabling */
uint8_t bChannelEnabled;
/* Volume */
uint8_t nVolume;
/* Duty Cycle */
uint8_t nDutyCount;
/* Output and Downsampling */
int32_t nMixL;
};
int16_t FME07_nOutputTable_L[0x10] IDATA_ATTR;
struct N106Wave
{
/* All Channel Stuff */
uint8_t nActiveChannels;
uint8_t bAutoIncrement;
uint8_t nCurrentAddress;
uint8_t nRAM[0x100]; /* internal memory for registers/wave data */
int32_t nFrequencyLookupTable[8]; /* lookup tbl for freq conversions */
/*
* Individual channel stuff
*/
/* Wavelength / Frequency */
union QUAD nFreqReg[8];
int32_t nFreqTimer[8];
int32_t nFreqCount[8];
/* Wave data length / remaining */
uint8_t nWaveSize[8];
uint8_t nWaveRemaining[8];
/* Wave data position */
uint8_t nWavePosStart[8];
uint8_t nWavePos[8];
uint8_t nOutput[8];
/* Volume */
uint8_t nVolume[8];
/* Pop Reducer */
uint8_t nPreVolume[8];
uint8_t nPopCheck[8];
/* Mixing */
int32_t nMixL[8];
};
int16_t N106_nOutputTable_L[0x10][0x10];
struct VRC6PulseWave
{
/* Frequency Control */
union TWIN nFreqTimer;
int32_t nFreqCount;
/* Flags */
uint8_t bChannelEnabled;
uint8_t bDigitized;
/* Volume */
uint8_t nVolume;
/* Duty Cycle */
uint8_t nDutyCycle;
uint8_t nDutyCount;
/* Output and Downsampling */
int32_t nMixL;
};
int16_t VRC6Pulse_nOutputTable_L[0x10] IDATA_ATTR;
struct VRC6SawWave
{
/* Frequency Control */
union TWIN nFreqTimer;
int32_t nFreqCount;
/* Flags */
uint8_t bChannelEnabled;
/* Phase Accumulator */
uint8_t nAccumRate;
uint8_t nAccum;
uint8_t nAccumStep;
/* Output and Downsampling */
int32_t nMixL;
};
int16_t VRC6Saw_nOutputTable_L[0x20] IDATA_ATTR;
struct Wave_Squares
{
/* Programmable Timer */
union TWIN nFreqTimer[2];
int32_t nFreqCount[2];
/* Length Counter */
uint8_t nLengthCount[2];
uint8_t bLengthEnabled[2];
uint8_t bChannelEnabled[2];
/* Volume / Decay */
uint8_t nVolume[2];
uint8_t nDecayVolume[2];
uint8_t bDecayEnable[2];
uint8_t bDecayLoop[2];
uint8_t nDecayTimer[2];
uint8_t nDecayCount[2];
/* Sweep Unit */
uint8_t bSweepEnable[2];
uint8_t bSweepMode[2];
uint8_t bSweepForceSilence[2];
uint8_t nSweepTimer[2];
uint8_t nSweepCount[2];
uint8_t nSweepShift[2];
/* Duty Cycle */
uint8_t nDutyCount[2];
uint8_t nDutyCycle[2];
/* Output and Downsampling */
int32_t nMixL;
};
int16_t Squares_nOutputTable_L[0x10][0x10] IDATA_ATTR;
struct Wave_TND
{
/*
* Triangle
*/
/* Programmable Timer */
union TWIN nTriFreqTimer;
int32_t nTriFreqCount;
/* Length Counter */
uint8_t nTriLengthCount;
uint8_t bTriLengthEnabled;
uint8_t bTriChannelEnabled;
/* Linear Counter */
uint8_t nTriLinearCount;
uint8_t nTriLinearLoad;
uint8_t bTriLinearHalt;
uint8_t bTriLinearControl;
/* Tri-Step Generator / Output */
uint8_t nTriStep;
uint8_t nTriOutput;
/*
* Noise
*/
/* Programmable Timer */
uint16_t nNoiseFreqTimer;
int32_t nNoiseFreqCount;
/* Length Counter */
uint8_t nNoiseLengthCount;
uint8_t bNoiseLengthEnabled;
uint8_t bNoiseChannelEnabled;
/* Volume / Decay */
uint8_t nNoiseVolume;
uint8_t nNoiseDecayVolume;
uint8_t bNoiseDecayEnable;
uint8_t bNoiseDecayLoop;
uint8_t nNoiseDecayTimer;
uint8_t nNoiseDecayCount;
/* Random Number Generator */
uint16_t nNoiseRandomShift;
uint8_t bNoiseRandomMode; /* 1 = 32k, 6 = 93-bit */
uint8_t bNoiseRandomOut;
/*
* DMC
*/
/* Play Mode */
uint8_t bDMCLoop;
uint8_t bDMCIRQEnabled;
uint8_t bDMCIRQPending;
/* Address / DMA */
uint8_t nDMCDMABank_Load;
uint16_t nDMCDMAAddr_Load;
uint8_t nDMCDMABank;
uint16_t nDMCDMAAddr;
uint8_t* pDMCDMAPtr[8];
/* Length / Input */
uint16_t nDMCLength;
uint16_t nDMCBytesRemaining;
uint8_t nDMCDelta;
uint8_t nDMCDeltaBit;
uint8_t bDMCDeltaSilent;
uint8_t nDMCSampleBuffer;
uint8_t bDMCSampleBufferEmpty;
/* Frequency */
uint16_t nDMCFreqTimer;
int32_t nDMCFreqCount;
/* Output */
uint8_t bDMCActive;
uint8_t nDMCOutput;
int32_t nMixL;
};
/* channels */
struct Wave_Squares mWave_Squares IDATA_ATTR; /* Square channels 1 and 2 */
struct Wave_TND mWave_TND IDATA_ATTR; /* Triangle/Noise/DMC channels */
struct VRC6PulseWave mWave_VRC6Pulse[2] IDATA_ATTR;
struct VRC6SawWave mWave_VRC6Saw IDATA_ATTR;
struct N106Wave mWave_N106 IDATA_ATTR;
struct FDSWave mWave_FDS IDATA_ATTR;
struct FME07Wave mWave_FME07[3] IDATA_ATTR; /* FME-07's 3 pulse channels */
/****************** MMC5 ******************/
/* will include MMC5 sound channels some day,
currently only multiply is supported */
/****************** N106 (Disch loves this chip) ******************/
#ifdef ICODE_INSTEAD_OF_INLINE
void Wave_N106_DoTicks(const int32_t ticks) ICODE_ATTR;
void Wave_N106_DoTicks(const int32_t ticks)
#else
inline void Wave_N106_DoTicks(const int32_t ticks);
inline void Wave_N106_DoTicks(const int32_t ticks)
#endif
{
register int32_t i;
for(i = (7 - mWave_N106.nActiveChannels); i < 8; i++)
{
if(!mWave_N106.nFreqReg[i].D)
{
/* written frequency of zero will cause divide by zero error
makes me wonder if the formula was supposed to be Reg+1 */
mWave_N106.nVolume[i] = mWave_N106.nPreVolume[i];
continue;
}
{
mWave_N106.nMixL[i] =
N106_nOutputTable_L[mWave_N106.nVolume[i]]
[mWave_N106.nOutput[i]];
if(mWave_N106.nFreqTimer[i] < 0)
mWave_N106.nFreqTimer[i] =
(mWave_N106.nFrequencyLookupTable[mWave_N106.nActiveChannels] /
mWave_N106.nFreqReg[i].D);
if(mWave_N106.nFreqCount[i] > mWave_N106.nFreqTimer[i])
mWave_N106.nFreqCount[i] = mWave_N106.nFreqTimer[i];
mWave_N106.nFreqCount[i] -= ticks << 8;
while(mWave_N106.nFreqCount[i] <= 0)
{
mWave_N106.nFreqCount[i] += mWave_N106.nFreqTimer[i];
if(mWave_N106.nWaveRemaining[i])
{
mWave_N106.nWaveRemaining[i]--;
mWave_N106.nWavePos[i]++;
}
if(!mWave_N106.nWaveRemaining[i])
{
mWave_N106.nWaveRemaining[i] = mWave_N106.nWaveSize[i];
mWave_N106.nWavePos[i] = mWave_N106.nWavePosStart[i];
if(mWave_N106.nVolume[i] != mWave_N106.nPreVolume[i])
{
if(++mWave_N106.nPopCheck[i] >= 2)
{
mWave_N106.nPopCheck[i] = 0;
mWave_N106.nVolume[i] = mWave_N106.nPreVolume[i];
}
}
}
mWave_N106.nOutput[i] =
mWave_N106.nRAM[mWave_N106.nWavePos[i]];
if(!mWave_N106.nOutput[i])
{
mWave_N106.nPopCheck[i] = 0;
mWave_N106.nVolume[i] = mWave_N106.nPreVolume[i];
}
}
}
}
}
/****************** VRC6 ******************/
#ifdef ICODE_INSTEAD_OF_INLINE
void Wave_VRC6_DoTicks(const int32_t ticks) ICODE_ATTR;
void Wave_VRC6_DoTicks(const int32_t ticks)
#else
inline void Wave_VRC6_DoTicks(const int32_t ticks);
inline void Wave_VRC6_DoTicks(const int32_t ticks)
#endif
{
register int32_t i;
for(i = 0; i < 2; i++) {
if(mWave_VRC6Pulse[i].bChannelEnabled) {
mWave_VRC6Pulse[i].nFreqCount -= ticks;
if(mWave_VRC6Pulse[i].nDutyCount <=
mWave_VRC6Pulse[i].nDutyCycle)
{
mWave_VRC6Pulse[i].nMixL =
VRC6Pulse_nOutputTable_L[mWave_VRC6Pulse[i].nVolume];
}
else
mWave_VRC6Pulse[i].nMixL = 0;
while(mWave_VRC6Pulse[i].nFreqCount <= 0) {
mWave_VRC6Pulse[i].nFreqCount +=
mWave_VRC6Pulse[i].nFreqTimer.W + 1;
if(!mWave_VRC6Pulse[i].bDigitized)
mWave_VRC6Pulse[i].nDutyCount =
(mWave_VRC6Pulse[i].nDutyCount + 1) & 0x0F;
}
}
}
if(mWave_VRC6Saw.bChannelEnabled) {
mWave_VRC6Saw.nFreqCount -= ticks;
mWave_VRC6Saw.nMixL =
VRC6Saw_nOutputTable_L[mWave_VRC6Saw.nAccum >> 3];
while(mWave_VRC6Saw.nFreqCount <= 0) {
mWave_VRC6Saw.nFreqCount += mWave_VRC6Saw.nFreqTimer.W + 1;
mWave_VRC6Saw.nAccumStep++;
if(mWave_VRC6Saw.nAccumStep == 14)
{
mWave_VRC6Saw.nAccumStep = 0;
mWave_VRC6Saw.nAccum = 0;
}
else if(!(mWave_VRC6Saw.nAccumStep & 1))
mWave_VRC6Saw.nAccum += mWave_VRC6Saw.nAccumRate;
}
}
}
/****************** Square waves ******************/
/* decay */
#ifdef ICODE_INSTEAD_OF_INLINE
void Wave_Squares_ClockMajor(void) ICODE_ATTR;
void Wave_Squares_ClockMajor()
#else
inline void Wave_Squares_ClockMajor(void);
inline void Wave_Squares_ClockMajor()
#endif
{
if(mWave_Squares.nDecayCount[0])
mWave_Squares.nDecayCount[0]--;
else
{
mWave_Squares.nDecayCount[0] = mWave_Squares.nDecayTimer[0];
if(mWave_Squares.nDecayVolume[0])
mWave_Squares.nDecayVolume[0]--;
else
{
if(mWave_Squares.bDecayLoop[0])
mWave_Squares.nDecayVolume[0] = 0x0F;
}
if(mWave_Squares.bDecayEnable[0])
mWave_Squares.nVolume[0] = mWave_Squares.nDecayVolume[0];
}
if(mWave_Squares.nDecayCount[1])
mWave_Squares.nDecayCount[1]--;
else
{
mWave_Squares.nDecayCount[1] = mWave_Squares.nDecayTimer[1];
if(mWave_Squares.nDecayVolume[1])
mWave_Squares.nDecayVolume[1]--;
else
{
if(mWave_Squares.bDecayLoop[1])
mWave_Squares.nDecayVolume[1] = 0x0F;
}
if(mWave_Squares.bDecayEnable[1])
mWave_Squares.nVolume[1] = mWave_Squares.nDecayVolume[1];
}
}
#ifdef ICODE_INSTEAD_OF_INLINE
void Wave_Squares_CheckSweepForcedSilence(const int32_t i) ICODE_ATTR;
void Wave_Squares_CheckSweepForcedSilence(const int32_t i)
#else
inline void Wave_Squares_CheckSweepForcedSilence(const int32_t i);
inline void Wave_Squares_CheckSweepForcedSilence(const int32_t i)
#endif
{
if(mWave_Squares.nFreqTimer[i].W < 8) {
mWave_Squares.bSweepForceSilence[i] = 1; return;
}
if(!mWave_Squares.bSweepMode[i] &&
(( mWave_Squares.nFreqTimer[i].W +
(mWave_Squares.nFreqTimer[i].W >> mWave_Squares.nSweepShift[i]))
>= 0x0800)) { mWave_Squares.bSweepForceSilence[i] = 1; return; }
mWave_Squares.bSweepForceSilence[i] = 0;
}
/* sweep / length */
#ifdef ICODE_INSTEAD_OF_INLINE
void Wave_Squares_ClockMinor(void) ICODE_ATTR;
void Wave_Squares_ClockMinor()
#else
inline void Wave_Squares_ClockMinor(void);
inline void Wave_Squares_ClockMinor()
#endif
{
/* unrolled a little loop
static int i = 0;
for(i = 0; i < 2; i++)
{
*/
if(mWave_Squares.bLengthEnabled[0] && mWave_Squares.nLengthCount[0])
mWave_Squares.nLengthCount[0]--;
if(!mWave_Squares.bSweepEnable[0] || !mWave_Squares.nLengthCount[0] ||
mWave_Squares.bSweepForceSilence[0] || !mWave_Squares.nSweepShift[0])
goto other_square;
if(mWave_Squares.nSweepCount[0])
mWave_Squares.nSweepCount[0]--;
else
{
mWave_Squares.nSweepCount[0] = mWave_Squares.nSweepTimer[0];
if(mWave_Squares.bSweepMode[0]) mWave_Squares.nFreqTimer[0].W -=
(mWave_Squares.nFreqTimer[0].W >> mWave_Squares.nSweepShift[0])+1;
else mWave_Squares.nFreqTimer[0].W +=
(mWave_Squares.nFreqTimer[0].W >> mWave_Squares.nSweepShift[0]);
Wave_Squares_CheckSweepForcedSilence(0);
}
/* */
other_square:
if(mWave_Squares.bLengthEnabled[1] && mWave_Squares.nLengthCount[1])
mWave_Squares.nLengthCount[1]--;
if(!mWave_Squares.bSweepEnable[1] || !mWave_Squares.nLengthCount[1] ||
mWave_Squares.bSweepForceSilence[1] || !mWave_Squares.nSweepShift[1])
return;
if(mWave_Squares.nSweepCount[1])
mWave_Squares.nSweepCount[1]--;
else
{
mWave_Squares.nSweepCount[1] = mWave_Squares.nSweepTimer[1];
if(mWave_Squares.bSweepMode[1]) mWave_Squares.nFreqTimer[1].W -=
(mWave_Squares.nFreqTimer[1].W >> mWave_Squares.nSweepShift[1]);
else mWave_Squares.nFreqTimer[1].W +=
(mWave_Squares.nFreqTimer[1].W >> mWave_Squares.nSweepShift[1]);
Wave_Squares_CheckSweepForcedSilence(1);
}
}
/****************** Triangle/noise/DMC ******************/
/* decay (noise), linear (tri) */
#ifdef ICODE_INSTEAD_OF_INLINE
void Wave_TND_ClockMajor(void) ICODE_ATTR;
void Wave_TND_ClockMajor()
#else
inline void Wave_TND_ClockMajor(void);
inline void Wave_TND_ClockMajor()
#endif
{
/* noise's decay */
if(mWave_TND.nNoiseDecayCount)
mWave_TND.nNoiseDecayCount--;
else
{
mWave_TND.nNoiseDecayCount = mWave_TND.nNoiseDecayTimer;
if(mWave_TND.nNoiseDecayVolume)
mWave_TND.nNoiseDecayVolume--;
else
{
if(mWave_TND.bNoiseDecayLoop)
mWave_TND.nNoiseDecayVolume = 0x0F;
}
if(mWave_TND.bNoiseDecayEnable)
mWave_TND.nNoiseVolume = mWave_TND.nNoiseDecayVolume;
}
/* triangle's linear */
if(mWave_TND.bTriLinearHalt)
mWave_TND.nTriLinearCount = mWave_TND.nTriLinearLoad;
else if(mWave_TND.nTriLinearCount)
mWave_TND.nTriLinearCount--;
if(!mWave_TND.bTriLinearControl)
mWave_TND.bTriLinearHalt = 0;
}
/* length */
#ifdef ICODE_INSTEAD_OF_INLINE
void Wave_TND_ClockMinor(void) ICODE_ATTR;
void Wave_TND_ClockMinor()
#else
inline void Wave_TND_ClockMinor(void);
inline void Wave_TND_ClockMinor()
#endif
{
if(mWave_TND.bNoiseLengthEnabled && mWave_TND.nNoiseLengthCount)
mWave_TND.nNoiseLengthCount--;
if(mWave_TND.bTriLengthEnabled && mWave_TND.nTriLengthCount)
mWave_TND.nTriLengthCount--;
}
/*#undef this*/
/****************** NSF Core ******************/
/* start globals */
/*
* Memory
*/
/* RAM: 0x0000 - 0x07FF */
uint8_t pRAM[0x800] IDATA_ATTR;
/* SRAM: 0x6000 - 0x7FFF (non-FDS only) */
uint8_t pSRAM[0x2000];
/* ExRAM: 0x5C00 - 0x5FF5 (MMC5 only)
* Also holds NSF player code (at 0x5000 - 0x500F) */
uint8_t pExRAM[0x1000];
/* Full ROM buffer */
uint8_t* pROM_Full IDATA_ATTR;
uint16_t main_nOutputTable_L[0x8000];
uint8_t* pROM[10] IDATA_ATTR;/* ROM banks (point to areas in pROM_Full) */
/* 0x8000 - 0xFFFF */
/* also includes 0x6000 - 0x7FFF (FDS only) */
uint8_t* pStack; /* the stack (points to areas in pRAM) */
/* 0x0100 - 0x01FF */
int32_t nROMSize; /* size of this ROM file in bytes */
int32_t nROMBankCount; /* max number of 4k banks */
int32_t nROMMaxSize; /* size of allocated pROM_Full buffer */
/*
* Memory Proc Pointers
*/
typedef uint8_t ( *ReadProc)(uint16_t);
typedef void ( *WriteProc)(uint16_t,uint8_t);
ReadProc ReadMemory[0x10] IDATA_ATTR;
WriteProc WriteMemory[0x10] IDATA_ATTR;
/*
* 6502 Registers / Mode
*/
uint8_t regA IDATA_ATTR; /* Accumulator */
uint8_t regX IDATA_ATTR; /* X-Index */
uint8_t regY IDATA_ATTR; /* Y-Index */
uint8_t regP IDATA_ATTR; /* Processor Status */
uint8_t regSP IDATA_ATTR; /* Stack Pointer */
uint16_t regPC IDATA_ATTR; /* Program Counter */
uint8_t bPALMode IDATA_ATTR;/* 1 if in PAL emulation mode, 0 if in NTSC */
uint8_t bCPUJammed IDATA_ATTR; /* 0 = not jammed. 1 = really jammed.
* 2 = 'fake' jammed */
/* fake jam caused by the NSF code to signal
* the end of the play/init routine */
/* Multiplication Register, for MMC5 chip only (5205+5206) */
uint8_t nMultIn_Low;
uint8_t nMultIn_High;
/*
* NSF Preparation Information
*/
uint8_t nBankswitchInitValues[10]; /* banks to swap to on tune init */
uint16_t nPlayAddress; /* Play routine address */
uint16_t nInitAddress; /* Init routine address */
uint8_t nExternalSound; /* external sound chips */
uint8_t nCurTrack;
float fNSFPlaybackSpeed;
/*
* pAPU
*/
uint8_t nFrameCounter; /* Frame Sequence Counter */
uint8_t nFrameCounterMax; /* Frame Sequence Counter Size
(3 or 4 depending on $4017.7) */
uint8_t bFrameIRQEnabled; /* TRUE if frame IRQs are enabled */
uint8_t bFrameIRQPending; /* TRUE if the frame sequencer is holding down
an IRQ */
uint8_t nFME07_Address;
/*
* Timing and Counters
*/
/* fixed point -15.16 */
int32_t nTicksUntilNextFrame;
int32_t nTicksPerPlay;
int32_t nTicksUntilNextPlay;
int32_t nTicksPerSample;
int32_t nTicksUntilNextSample;
uint32_t nCPUCycle IDATA_ATTR;
uint32_t nAPUCycle IDATA_ATTR;
uint32_t nTotalPlays; /* number of times the play subroutine has been called
(for tracking output time) */
/*
* Silence Tracker
*/
int32_t nSilentSamples;
int32_t nSilentSampleMax;
int32_t nSilenceTrackMS;
uint8_t bNoSilenceIfTime;
uint8_t bTimeNotDefault;
/*
* Sound output options
*/
const int32_t nSampleRate=44100;
/*
* Volume/fading/filter tracking
*/
uint32_t nStartFade; /* play call to start fading out */
uint32_t nEndFade; /* play call to stop fading out (song is over) */
uint8_t bFade; /* are we fading? */
float fFadeVolume;
float fFadeChange;
/*
* Designated Output Buffer
*/
uint8_t* pOutput IDATA_ATTR;
const uint8_t bDMCPopReducer=1;
uint8_t nDMCPop_Prev IDATA_ATTR = 0;
uint8_t bDMCPop_Skip IDATA_ATTR = 0;
uint8_t bDMCPop_SamePlay IDATA_ATTR = 0;
const uint8_t nForce4017Write=0;
const uint8_t bN106PopReducer=0;
const uint8_t bIgnore4011Writes=0;
const uint8_t bIgnoreBRK=0;
const uint8_t bIgnoreIllegalOps=0;
const uint8_t bNoWaitForReturn=0;
const uint8_t bPALPreference=0;
const uint8_t bCleanAXY=0;
const uint8_t bResetDuty=0;
/*
* Sound Filter
*/
int64_t nFilterAccL IDATA_ATTR;
int64_t nHighPass IDATA_ATTR;
int32_t nHighPassBase IDATA_ATTR;
uint8_t bHighPassEnabled IDATA_ATTR;
/* end globals */
#define CLOCK_MAJOR() { Wave_Squares_ClockMajor(); Wave_TND_ClockMajor(); }
#define CLOCK_MINOR() { Wave_Squares_ClockMinor(); Wave_TND_ClockMinor(); }
#define EXTSOUND_VRC6 0x01
#define EXTSOUND_VRC7 0x02
#define EXTSOUND_FDS 0x04
#define EXTSOUND_MMC5 0x08
#define EXTSOUND_N106 0x10
#define EXTSOUND_FME07 0x20
#define SILENCE_THRESHOLD 3
/*
* prototypes
*/
uint32_t Emulate6502(uint32_t runto) ICODE_ATTR;
void EmulateAPU(uint8_t bBurnCPUCycles) ICODE_ATTR;
int NSFCore_Initialize(void); /* 1 = initialized ok,
0 = couldn't initialize (memory allocation error) */
/*
* Song Loading
*/
int LoadNSF(int32_t); /* grab data from an existing file
1 = loaded ok, 0 = error loading */
/*
* Track Control
*/
void SetTrack(uint8_t track); /* Change tracks */
/*
* Getting Samples
*/
/* fill a buffer with samples */
int32_t GetSamples(uint8_t* buffer, int32_t buffersize);
/*
* Playback options
*/
/* Set desired playback options (0 = bad options couldn't be set) */
int SetPlaybackOptions(int32_t samplerate);
/* Speed throttling (0 = uses NSF specified speed) */
void SetPlaybackSpeed(float playspersec);
float GetPlaybackSpeed(void);
float GetMasterVolume(void);
/*
* Seeking
*/
/* gets the number of 'play' routine calls executed */
float GetPlayCalls(void);
/* gets the output time (based on the given play rate,
if basedplayspersec is zero, current playback speed is used */
uint32_t GetWrittenTime(float basedplayspersec);
/* sets the number of 'plays' routines executed (for precise seeking) */
void SetPlayCalls(float plays);
/* sets the written time (approx. seeking) */
void SetWrittenTime(uint32_t ms,float basedplays);
/*
* Fading
*/
void StopFade(void); /* stops all fading (plays indefinitely) */
uint8_t SongCompleted(void); /* song has faded out (samples have stopped
being generated) */
/* parameters are play calls */
void SetFade(int32_t fadestart,int32_t fadestop,uint8_t bNotDefault);
void SetFadeTime(uint32_t fadestart,uint32_t fadestop,float basedplays,
uint8_t bNotDefault); /* parameters are in milliseconds */
/*
* Internal Functions
*/
void RebuildOutputTables(void);
void RecalculateFade(void); /* called when fade status is changed. */
void RecalcFilter(void);
void RecalcSilenceTracker(void);
void WriteMemory_VRC6(uint16_t a,uint8_t v) ICODE_ATTR;
void WriteMemory_MMC5(uint16_t a,uint8_t v) ICODE_ATTR;
void WriteMemory_N106(uint16_t a,uint8_t v) ICODE_ATTR;
void WriteMemory_FME07(uint16_t a,uint8_t v) ICODE_ATTR;
/*
* Memory Read/Write routines
*/
uint8_t ReadMemory_RAM(uint16_t a) ICODE_ATTR;
uint8_t ReadMemory_ExRAM(uint16_t a) ICODE_ATTR;
uint8_t ReadMemory_SRAM(uint16_t a) ICODE_ATTR;
uint8_t ReadMemory_pAPU(uint16_t a) ICODE_ATTR;
uint8_t ReadMemory_ROM(uint16_t a) ICODE_ATTR;
uint8_t ReadMemory_Default(uint16_t a) ICODE_ATTR;
uint8_t ReadMemory_N106(uint16_t a) ICODE_ATTR;
void WriteMemory_RAM(uint16_t a,uint8_t v) ICODE_ATTR;
void WriteMemory_ExRAM(uint16_t a,uint8_t v) ICODE_ATTR;
void WriteMemory_SRAM(uint16_t a,uint8_t v) ICODE_ATTR;
void WriteMemory_pAPU(uint16_t a,uint8_t v) ICODE_ATTR;
void WriteMemory_FDSRAM(uint16_t a,uint8_t v) ICODE_ATTR;
void WriteMemory_Default(uint16_t a,uint8_t v) ICODE_ATTR;
uint8_t ReadMemory_RAM(uint16_t a) { return pRAM[a & 0x07FF]; }
uint8_t ReadMemory_ExRAM(uint16_t a) { return pExRAM[a & 0x0FFF]; }
uint8_t ReadMemory_SRAM(uint16_t a) { return pSRAM[a & 0x1FFF]; }
uint8_t ReadMemory_ROM(uint16_t a)
{ return pROM[(a >> 12) - 6][a & 0x0FFF]; }
uint8_t ReadMemory_Default(uint16_t a) { return (a >> 8); }
void WriteMemory_RAM(uint16_t a,uint8_t v)
{ pRAM[a & 0x07FF] = v; }
void WriteMemory_ExRAM(uint16_t a,uint8_t v);
void WriteMemory_SRAM(uint16_t a,uint8_t v)
{ pSRAM[a & 0x1FFF] = v; }
void WriteMemory_FDSRAM(uint16_t a,uint8_t v)
{ pROM[(a >> 12) - 6][a & 0x0FFF] = v; }
void WriteMemory_Default(uint16_t a,uint8_t v) { (void)a; (void)v; }
/* Read Memory Procs */
uint8_t ReadMemory_pAPU(uint16_t a)
{
EmulateAPU(1);
if(a == 0x4015)
{
uint8_t ret = 0;
if(mWave_Squares.nLengthCount[0]) ret |= 0x01;
if(mWave_Squares.nLengthCount[1]) ret |= 0x02;
if(mWave_TND.nTriLengthCount) ret |= 0x04;
if(mWave_TND.nNoiseLengthCount) ret |= 0x08;
if(mWave_TND.nDMCBytesRemaining) ret |= 0x10;
if(bFrameIRQPending) ret |= 0x40;
if(mWave_TND.bDMCIRQPending) ret |= 0x80;
bFrameIRQPending = 0;
return ret;
}
if(!(nExternalSound & EXTSOUND_FDS)) return 0x40;
if(bPALMode) return 0x40;
if((a >= 0x4040) && (a <= 0x407F))
return mWave_FDS.nWaveTable[a & 0x3F] | 0x40;
if(a == 0x4090)
return (mWave_FDS.nVolEnv_Gain & 0x3F) | 0x40;
if(a == 0x4092)
return (mWave_FDS.nSweep_Gain & 0x3F) | 0x40;
return 0x40;
}
uint8_t ReadMemory_N106(uint16_t a)
{
if(a != 0x4800)
return ReadMemory_pAPU(a);
uint8_t ret = mWave_N106.nRAM[(mWave_N106.nCurrentAddress << 1)] |
(mWave_N106.nRAM[(mWave_N106.nCurrentAddress << 1) + 1] << 4);
if(mWave_N106.bAutoIncrement)
mWave_N106.nCurrentAddress = (mWave_N106.nCurrentAddress + 1) & 0x7F;
return ret;
}
/* Write Memory Procs */
void WriteMemory_ExRAM(uint16_t a,uint8_t v)
{
if(a < 0x5FF6) /* Invalid */
return;
a -= 0x5FF6;
/* Swap out banks */
EmulateAPU(1);
/* stop it from swapping to a bank that doesn't exist */
if(v >= nROMBankCount)
v = 0;
pROM[a] = pROM_Full + (v << 12);
/* Update the DMC's DMA pointer, as well */
if(a >= 2)
mWave_TND.pDMCDMAPtr[a - 2] = pROM[a];
}
void WriteMemory_pAPU(uint16_t a,uint8_t v)
{
EmulateAPU(1);
switch(a)
{
/* Square 1 */
case 0x4000:
mWave_Squares.nDutyCycle[0] = DUTY_CYCLE_TABLE[v >> 6];
mWave_Squares.bLengthEnabled[0] =
!(mWave_Squares.bDecayLoop[0] = (v & 0x20));
mWave_Squares.bDecayEnable[0] = !(v & 0x10);
mWave_Squares.nDecayTimer[0] = (v & 0x0F);
if(!mWave_Squares.bDecayEnable[0])
mWave_Squares.nVolume[0] = mWave_Squares.nDecayTimer[0];
break;
case 0x4001:
mWave_Squares.bSweepEnable[0] = (v & 0x80);
mWave_Squares.nSweepTimer[0] = (v & 0x70) >> 4;
mWave_Squares.bSweepMode[0] = v & 0x08;
mWave_Squares.nSweepShift[0] = v & 0x07;
Wave_Squares_CheckSweepForcedSilence(0);
break;
case 0x4002:
mWave_Squares.nFreqTimer[0].B.l = v;
Wave_Squares_CheckSweepForcedSilence(0);
break;
case 0x4003:
mWave_Squares.nFreqTimer[0].B.h = v & 0x07;
Wave_Squares_CheckSweepForcedSilence(0);
mWave_Squares.nDecayVolume[0] = 0x0F;
if(mWave_Squares.bChannelEnabled[0])
mWave_Squares.nLengthCount[0] = LENGTH_COUNTER_TABLE[v >> 3];
if(bResetDuty)
mWave_Squares.nDutyCount[0] = 0;
break;
/* Square 2 */
case 0x4004:
mWave_Squares.nDutyCycle[1] = DUTY_CYCLE_TABLE[v >> 6];
mWave_Squares.bLengthEnabled[1] =
!(mWave_Squares.bDecayLoop[1] = (v & 0x20));
mWave_Squares.bDecayEnable[1] = !(v & 0x10);
mWave_Squares.nDecayTimer[1] = (v & 0x0F);
if(!mWave_Squares.bDecayEnable[1])
mWave_Squares.nVolume[1] = mWave_Squares.nDecayTimer[1];
break;
case 0x4005:
mWave_Squares.bSweepEnable[1] = (v & 0x80);
mWave_Squares.nSweepTimer[1] = (v & 0x70) >> 4;
mWave_Squares.bSweepMode[1] = v & 0x08;
mWave_Squares.nSweepShift[1] = v & 0x07;
Wave_Squares_CheckSweepForcedSilence(1);
break;
case 0x4006:
mWave_Squares.nFreqTimer[1].B.l = v;
Wave_Squares_CheckSweepForcedSilence(1);
break;
case 0x4007:
mWave_Squares.nFreqTimer[1].B.h = v & 0x07;
Wave_Squares_CheckSweepForcedSilence(1);
mWave_Squares.nDecayVolume[1] = 0x0F;
if(mWave_Squares.bChannelEnabled[1])
mWave_Squares.nLengthCount[1] = LENGTH_COUNTER_TABLE[v >> 3];
if(bResetDuty)
mWave_Squares.nDutyCount[1] = 0;
break;
/* Triangle */
case 0x4008:
mWave_TND.nTriLinearLoad = v & 0x7F;
mWave_TND.bTriLinearControl = v & 0x80;
mWave_TND.bTriLengthEnabled = !(v & 0x80);
break;
case 0x400A:
mWave_TND.nTriFreqTimer.B.l = v;
break;
case 0x400B:
mWave_TND.nTriFreqTimer.B.h = v & 0x07;
mWave_TND.bTriLinearHalt = 1;
if(mWave_TND.bTriChannelEnabled)
mWave_TND.nTriLengthCount = LENGTH_COUNTER_TABLE[v >> 3];
break;
/* Noise */
case 0x400C:
mWave_TND.bNoiseLengthEnabled =
!(mWave_TND.bNoiseDecayLoop = (v & 0x20));
mWave_TND.bNoiseDecayEnable = !(v & 0x10);
mWave_TND.nNoiseDecayTimer = (v & 0x0F);
if(mWave_TND.bNoiseDecayEnable)
mWave_TND.nNoiseVolume = mWave_TND.nNoiseDecayVolume;
else
mWave_TND.nNoiseVolume = mWave_TND.nNoiseDecayTimer;
break;
case 0x400E:
mWave_TND.nNoiseFreqTimer = NOISE_FREQ_TABLE[v & 0x0F];
mWave_TND.bNoiseRandomMode = (v & 0x80) ? 6 : 1;
break;
case 0x400F:
if(mWave_TND.bNoiseChannelEnabled)
mWave_TND.nNoiseLengthCount = LENGTH_COUNTER_TABLE[v >> 3];
mWave_TND.nNoiseDecayVolume = 0x0F;
if(mWave_TND.bNoiseDecayEnable)
mWave_TND.nNoiseVolume = 0x0F;
break;
/* DMC */
case 0x4010:
mWave_TND.bDMCLoop = v & 0x40;
mWave_TND.bDMCIRQEnabled = v & 0x80;
/* IRQ can't be pending if disabled */
if(!mWave_TND.bDMCIRQEnabled)
mWave_TND.bDMCIRQPending = 0;
mWave_TND.nDMCFreqTimer = DMC_FREQ_TABLE[bPALMode][v & 0x0F];
break;
case 0x4011:
if(bIgnore4011Writes)
break;
v &= 0x7F;
if(bDMCPopReducer)
{
if(bDMCPop_SamePlay)
mWave_TND.nDMCOutput = v;
else
{
if(bDMCPop_Skip)
{
bDMCPop_Skip = 0;
break;
}
if(nDMCPop_Prev == v) break;
if(mWave_TND.nDMCOutput == v) break;
mWave_TND.nDMCOutput = nDMCPop_Prev;
nDMCPop_Prev = v;
bDMCPop_SamePlay = 1;
}
}
else
mWave_TND.nDMCOutput = v;
break;
case 0x4012:
mWave_TND.nDMCDMABank_Load = (v >> 6) | 0x04;
mWave_TND.nDMCDMAAddr_Load = (v << 6) & 0x0FFF;
break;
case 0x4013:
mWave_TND.nDMCLength = (v << 4) + 1;
break;
/* All / General Purpose */
case 0x4015:
mWave_TND.bDMCIRQPending = 0;
if(v & 0x01){ mWave_Squares.bChannelEnabled[0] = 1; }
else { mWave_Squares.bChannelEnabled[0] =
mWave_Squares.nLengthCount[0] = 0; }
if(v & 0x02){ mWave_Squares.bChannelEnabled[1] = 1; }
else { mWave_Squares.bChannelEnabled[1] =
mWave_Squares.nLengthCount[1] = 0; }
if(v & 0x04){ mWave_TND.bTriChannelEnabled = 1; }
else { mWave_TND.bTriChannelEnabled =
mWave_TND.nTriLengthCount = 0; }
if(v & 0x08){ mWave_TND.bNoiseChannelEnabled = 1; }
else { mWave_TND.bNoiseChannelEnabled =
mWave_TND.nNoiseLengthCount = 0; }
if(v & 0x10)
{
if(!mWave_TND.nDMCBytesRemaining)
{
bDMCPop_Skip = 1;
mWave_TND.nDMCDMAAddr = mWave_TND.nDMCDMAAddr_Load;
mWave_TND.nDMCDMABank = mWave_TND.nDMCDMABank_Load;
mWave_TND.nDMCBytesRemaining = mWave_TND.nDMCLength;
mWave_TND.bDMCActive = 1;
}
}
else
mWave_TND.nDMCBytesRemaining = 0;
break;
case 0x4017:
bFrameIRQEnabled = !(v & 0x40);
bFrameIRQPending = 0;
nFrameCounter = 0;
nFrameCounterMax = (v & 0x80) ? 4 : 3;
nTicksUntilNextFrame =
(bPALMode ? PAL_FRAME_COUNTER_FREQ : NTSC_FRAME_COUNTER_FREQ)
* 0x10000;
CLOCK_MAJOR();
if(v & 0x80) CLOCK_MINOR();
break;
}
if(!(nExternalSound & EXTSOUND_FDS)) return;
if(bPALMode) return;
/* FDS Sound registers */
if(a < 0x4040) return;
/* wave table */
if(a <= 0x407F)
{
if(mWave_FDS.bWaveWrite)
mWave_FDS.nWaveTable[a - 0x4040] = v;
}
else
{
switch(a)
{
case 0x4080:
mWave_FDS.nVolEnv_Mode = (v >> 6);
if(v & 0x80)
{
mWave_FDS.nVolEnv_Gain = v & 0x3F;
if(!mWave_FDS.nMainAddr)
{
if(mWave_FDS.nVolEnv_Gain < 0x20)
mWave_FDS.nVolume = mWave_FDS.nVolEnv_Gain;
else mWave_FDS.nVolume = 0x20;
}
}
mWave_FDS.nVolEnv_Decay = v & 0x3F;
mWave_FDS.nVolEnv_Timer =
((mWave_FDS.nVolEnv_Decay + 1) * mWave_FDS.nEnvelopeSpeed * 8);
mWave_FDS.bVolEnv_On = mWave_FDS.bEnvelopeEnable &&
mWave_FDS.nEnvelopeSpeed && !(v & 0x80);
break;
case 0x4082:
mWave_FDS.nFreq.B.l = v;
mWave_FDS.bMain_On = mWave_FDS.nFreq.W && mWave_FDS.bEnabled &&
!mWave_FDS.bWaveWrite;
break;
case 0x4083:
mWave_FDS.bEnabled = !(v & 0x80);
mWave_FDS.bEnvelopeEnable = !(v & 0x40);
if(v & 0x80)
{
if(mWave_FDS.nVolEnv_Gain < 0x20)
mWave_FDS.nVolume = mWave_FDS.nVolEnv_Gain;
else mWave_FDS.nVolume = 0x20;
}
mWave_FDS.nFreq.B.h = v & 0x0F;
mWave_FDS.bMain_On = mWave_FDS.nFreq.W && mWave_FDS.bEnabled &&
!mWave_FDS.bWaveWrite;
mWave_FDS.bVolEnv_On = mWave_FDS.bEnvelopeEnable &&
mWave_FDS.nEnvelopeSpeed && !(mWave_FDS.nVolEnv_Mode & 2);
mWave_FDS.bSweepEnv_On = mWave_FDS.bEnvelopeEnable &&
mWave_FDS.nEnvelopeSpeed && !(mWave_FDS.nSweep_Mode & 2);
break;
case 0x4084:
mWave_FDS.nSweep_Mode = v >> 6;
if(v & 0x80)
mWave_FDS.nSweep_Gain = v & 0x3F;
mWave_FDS.nSweep_Decay = v & 0x3F;
mWave_FDS.nSweep_Timer =
((mWave_FDS.nSweep_Decay + 1) * mWave_FDS.nEnvelopeSpeed * 8);
mWave_FDS.bSweepEnv_On =
mWave_FDS.bEnvelopeEnable && mWave_FDS.nEnvelopeSpeed &&
!(v & 0x80);
break;
case 0x4085:
if(v & 0x40) mWave_FDS.nSweepBias = (v & 0x3F) - 0x40;
else mWave_FDS.nSweepBias = v & 0x3F;
mWave_FDS.nLFO_Addr = 0;
break;
case 0x4086:
mWave_FDS.nLFO_Freq.B.l = v;
mWave_FDS.bLFO_On =
mWave_FDS.bLFO_Enabled && mWave_FDS.nLFO_Freq.W;
if(mWave_FDS.nLFO_Freq.W)
mWave_FDS.nLFO_Timer = (0x10000<<14) / mWave_FDS.nLFO_Freq.W;
break;
case 0x4087:
mWave_FDS.bLFO_Enabled = !(v & 0x80);
mWave_FDS.nLFO_Freq.B.h = v & 0x0F;
mWave_FDS.bLFO_On =
mWave_FDS.bLFO_Enabled && mWave_FDS.nLFO_Freq.W;
if(mWave_FDS.nLFO_Freq.W)
mWave_FDS.nLFO_Timer = (0x10000<<14) / mWave_FDS.nLFO_Freq.W;
break;
case 0x4088:
if(mWave_FDS.bLFO_Enabled) break;
register int32_t i;
for(i = 0; i < 62; i++)
mWave_FDS.nLFO_Table[i] = mWave_FDS.nLFO_Table[i + 2];
mWave_FDS.nLFO_Table[62] = mWave_FDS.nLFO_Table[63] = v & 7;
break;
case 0x4089:
mWave_FDS.nMainVolume = v & 3;
mWave_FDS.bWaveWrite = v & 0x80;
mWave_FDS.bMain_On = mWave_FDS.nFreq.W && mWave_FDS.bEnabled &&
!mWave_FDS.bWaveWrite;
break;
case 0x408A:
mWave_FDS.nEnvelopeSpeed = v;
mWave_FDS.bVolEnv_On =
mWave_FDS.bEnvelopeEnable &&
mWave_FDS.nEnvelopeSpeed && !(mWave_FDS.nVolEnv_Mode & 2);
mWave_FDS.bSweepEnv_On =
mWave_FDS.bEnvelopeEnable &&
mWave_FDS.nEnvelopeSpeed && !(mWave_FDS.nSweep_Mode & 2);
break;
}
}
}
void WriteMemory_VRC6(uint16_t a,uint8_t v)
{
EmulateAPU(1);
if((a < 0xA000) && (nExternalSound & EXTSOUND_VRC7)) return;
else if(nExternalSound & EXTSOUND_FDS)
WriteMemory_FDSRAM(a,v);
switch(a)
{
/* Pulse 1 */
case 0x9000:
mWave_VRC6Pulse[0].nVolume = v & 0x0F;
mWave_VRC6Pulse[0].nDutyCycle = (v >> 4) & 0x07;
mWave_VRC6Pulse[0].bDigitized = v & 0x80;
if(mWave_VRC6Pulse[0].bDigitized)
mWave_VRC6Pulse[0].nDutyCount = 0;
break;
case 0x9001:
mWave_VRC6Pulse[0].nFreqTimer.B.l = v;
break;
case 0x9002:
mWave_VRC6Pulse[0].nFreqTimer.B.h = v & 0x0F;
mWave_VRC6Pulse[0].bChannelEnabled = v & 0x80;
break;
/* Pulse 2 */
case 0xA000:
mWave_VRC6Pulse[1].nVolume = v & 0x0F;
mWave_VRC6Pulse[1].nDutyCycle = (v >> 4) & 0x07;
mWave_VRC6Pulse[1].bDigitized = v & 0x80;
if(mWave_VRC6Pulse[1].bDigitized)
mWave_VRC6Pulse[1].nDutyCount = 0;
break;
case 0xA001:
mWave_VRC6Pulse[1].nFreqTimer.B.l = v;
break;
case 0xA002:
mWave_VRC6Pulse[1].nFreqTimer.B.h = v & 0x0F;
mWave_VRC6Pulse[1].bChannelEnabled = v & 0x80;
break;
/* Sawtooth */
case 0xB000:
mWave_VRC6Saw.nAccumRate = (v & 0x3F);
break;
case 0xB001:
mWave_VRC6Saw.nFreqTimer.B.l = v;
break;
case 0xB002:
mWave_VRC6Saw.nFreqTimer.B.h = v & 0x0F;
mWave_VRC6Saw.bChannelEnabled = v & 0x80;
break;
}
}
void WriteMemory_MMC5(uint16_t a,uint8_t v)
{
if((a <= 0x5015) && !bPALMode)
{
/* no audio emulation */
return;
}
if(a == 0x5205)
{
nMultIn_Low = v;
goto multiply;
}
if(a == 0x5206)
{
nMultIn_High = v;
multiply:
a = nMultIn_Low * nMultIn_High;
pExRAM[0x205] = a & 0xFF;
pExRAM[0x206] = a >> 8;
return;
}
if(a < 0x5C00) return;
pExRAM[a & 0x0FFF] = v;
if(a >= 0x5FF6)
WriteMemory_ExRAM(a,v);
}
void WriteMemory_N106(uint16_t a,uint8_t v)
{
if(a < 0x4800)
{
WriteMemory_pAPU(a,v);
return;
}
if(a == 0xF800)
{
mWave_N106.nCurrentAddress = v & 0x7F;
mWave_N106.bAutoIncrement = (v & 0x80);
return;
}
if(a == 0x4800)
{
EmulateAPU(1);
mWave_N106.nRAM[mWave_N106.nCurrentAddress << 1] = v & 0x0F;
mWave_N106.nRAM[(mWave_N106.nCurrentAddress << 1) + 1] = v >> 4;
a = mWave_N106.nCurrentAddress;
if(mWave_N106.bAutoIncrement)
mWave_N106.nCurrentAddress =
(mWave_N106.nCurrentAddress + 1) & 0x7F;
#define N106REGWRITE(ch,r0,r1,r2,r3,r4) \
case r0: if(mWave_N106.nFreqReg[ch].B.l == v) break; \
mWave_N106.nFreqReg[ch].B.l = v; \
mWave_N106.nFreqTimer[ch] = -1; \
break; \
case r1: if(mWave_N106.nFreqReg[ch].B.h == v) break; \
mWave_N106.nFreqReg[ch].B.h = v; \
mWave_N106.nFreqTimer[ch] = -1; \
break; \
case r2: if(mWave_N106.nFreqReg[ch].B.w != (v & 3)){ \
mWave_N106.nFreqReg[ch].B.w = v & 0x03; \
mWave_N106.nFreqTimer[ch] = -1;} \
mWave_N106.nWaveSize[ch] = 0x20 - (v & 0x1C); \
break; \
case r3: mWave_N106.nWavePosStart[ch] = v; \
break; \
case r4: mWave_N106.nPreVolume[ch] = v & 0x0F; \
if(!bN106PopReducer) \
mWave_N106.nVolume[ch] = v & 0x0F
switch(a)
{
N106REGWRITE(0,0x40,0x42,0x44,0x46,0x47); break;
N106REGWRITE(1,0x48,0x4A,0x4C,0x4E,0x4F); break;
N106REGWRITE(2,0x50,0x52,0x54,0x56,0x57); break;
N106REGWRITE(3,0x58,0x5A,0x5C,0x5E,0x5F); break;
N106REGWRITE(4,0x60,0x62,0x64,0x66,0x67); break;
N106REGWRITE(5,0x68,0x6A,0x6C,0x6E,0x6F); break;
N106REGWRITE(6,0x70,0x72,0x74,0x76,0x77); break;
N106REGWRITE(7,0x78,0x7A,0x7C,0x7E,0x7F);
v = (v >> 4) & 7;
if(mWave_N106.nActiveChannels == v) break;
mWave_N106.nActiveChannels = v;
mWave_N106.nFreqTimer[0] = -1;
mWave_N106.nFreqTimer[1] = -1;
mWave_N106.nFreqTimer[2] = -1;
mWave_N106.nFreqTimer[3] = -1;
mWave_N106.nFreqTimer[4] = -1;
mWave_N106.nFreqTimer[5] = -1;
mWave_N106.nFreqTimer[6] = -1;
mWave_N106.nFreqTimer[7] = -1;
break;
}
#undef N106REGWRITE
}
}
void WriteMemory_FME07(uint16_t a,uint8_t v)
{
if((a < 0xD000) && (nExternalSound & EXTSOUND_FDS))
WriteMemory_FDSRAM(a,v);
if(a == 0xC000)
nFME07_Address = v;
if(a == 0xE000)
{
switch(nFME07_Address)
{
case 0x00: mWave_FME07[0].nFreqTimer.B.l = v; break;
case 0x01: mWave_FME07[0].nFreqTimer.B.h = v & 0x0F; break;
case 0x02: mWave_FME07[1].nFreqTimer.B.l = v; break;
case 0x03: mWave_FME07[1].nFreqTimer.B.h = v & 0x0F; break;
case 0x04: mWave_FME07[2].nFreqTimer.B.l = v; break;
case 0x05: mWave_FME07[2].nFreqTimer.B.h = v & 0x0F; break;
case 0x07:
mWave_FME07[0].bChannelEnabled = !(v & 0x01);
mWave_FME07[1].bChannelEnabled = !(v & 0x02);
mWave_FME07[2].bChannelEnabled = !(v & 0x03);
break;
case 0x08: mWave_FME07[0].nVolume = v & 0x0F; break;
case 0x09: mWave_FME07[1].nVolume = v & 0x0F; break;
case 0x0A: mWave_FME07[2].nVolume = v & 0x0F; break;
}
}
}
/*
* Emulate APU
*/
int32_t fulltick;
void EmulateAPU(uint8_t bBurnCPUCycles)
{
int32_t tick;
int64_t diff;
int32_t tnd_out;
int square_out1;
int square_out2;
ENTER_TIMER(apu);
fulltick += (signed)(nCPUCycle - nAPUCycle);
int32_t burned;
int32_t mixL;
if(bFade && nSilentSampleMax && (nSilentSamples >= nSilentSampleMax))
fulltick = 0;
while(fulltick>0)
{
tick = (nTicksUntilNextSample+0xffff)>>16;
fulltick -= tick;
/*
* Sample Generation
*/
ENTER_TIMER(squares);
/* Square generation */
mWave_Squares.nFreqCount[0] -= tick;
mWave_Squares.nFreqCount[1] -= tick;
if((mWave_Squares.nDutyCount[0] < mWave_Squares.nDutyCycle[0]) &&
mWave_Squares.nLengthCount[0] &&
!mWave_Squares.bSweepForceSilence[0])
square_out1 = mWave_Squares.nVolume[0];
else
square_out1 = 0;
if((mWave_Squares.nDutyCount[1] < mWave_Squares.nDutyCycle[1]) &&
mWave_Squares.nLengthCount[1] &&
!mWave_Squares.bSweepForceSilence[1])
square_out2 = mWave_Squares.nVolume[1];
else
square_out2 = 0;
mWave_Squares.nMixL = Squares_nOutputTable_L[square_out1][square_out2];
if(mWave_Squares.nFreqCount[0]<=0)
{
int cycles =
(-mWave_Squares.nFreqCount[0])/
(mWave_Squares.nFreqTimer[0].W + 1) + 1;
mWave_Squares.nFreqCount[0] =
(mWave_Squares.nFreqTimer[0].W + 1)-
(-mWave_Squares.nFreqCount[0])%
(mWave_Squares.nFreqTimer[0].W + 1);
mWave_Squares.nDutyCount[0] =
(mWave_Squares.nDutyCount[0]+cycles)%0x10;
}
if(mWave_Squares.nFreqCount[1]<=0)
{
int cycles =
(-mWave_Squares.nFreqCount[1])/
(mWave_Squares.nFreqTimer[1].W + 1) + 1;
mWave_Squares.nFreqCount[1] =
(mWave_Squares.nFreqTimer[1].W + 1)-
(-mWave_Squares.nFreqCount[1])%
(mWave_Squares.nFreqTimer[1].W + 1);
mWave_Squares.nDutyCount[1] = (mWave_Squares.nDutyCount[1]+cycles)%
0x10;
}
/* end of Square generation */
EXIT_TIMER(squares);
ENTER_TIMER(tnd);
ENTER_TIMER(tnd_enter);
burned=0;
/* TND generation */
if(mWave_TND.nNoiseFreqTimer) mWave_TND.nNoiseFreqCount -= tick;
if(mWave_TND.nTriFreqTimer.W > 8)
mWave_TND.nTriFreqCount -= tick;
tnd_out = mWave_TND.nTriOutput << 11;
if(mWave_TND.bNoiseRandomOut && mWave_TND.nNoiseLengthCount)
tnd_out |= mWave_TND.nNoiseVolume << 7;
tnd_out |= mWave_TND.nDMCOutput;
mWave_TND.nMixL = main_nOutputTable_L[tnd_out];
EXIT_TIMER(tnd_enter);
ENTER_TIMER(tnd_tri);
/* Tri */
if(mWave_TND.nTriFreqCount<=0)
{
if(mWave_TND.nTriLengthCount && mWave_TND.nTriLinearCount)
{
do mWave_TND.nTriStep++;
while ((mWave_TND.nTriFreqCount +=
mWave_TND.nTriFreqTimer.W + 1) <= 0);
mWave_TND.nTriStep &= 0x1F;
if(mWave_TND.nTriStep & 0x10)
mWave_TND.nTriOutput = mWave_TND.nTriStep ^ 0x1F;
else mWave_TND.nTriOutput = mWave_TND.nTriStep;
} else mWave_TND.nTriFreqCount=mWave_TND.nTriFreqTimer.W+1;
}
EXIT_TIMER(tnd_tri);
ENTER_TIMER(tnd_noise);
/* Noise */
if(mWave_TND.nNoiseFreqTimer &&
mWave_TND.nNoiseVolume && mWave_TND.nNoiseFreqCount<=0)
{
mWave_TND.nNoiseFreqCount = mWave_TND.nNoiseFreqTimer;
mWave_TND.nNoiseRandomShift <<= 1;
mWave_TND.bNoiseRandomOut = (((mWave_TND.nNoiseRandomShift <<
mWave_TND.bNoiseRandomMode) ^
mWave_TND.nNoiseRandomShift) & 0x8000 ) ? 1 : 0;
if(mWave_TND.bNoiseRandomOut)
mWave_TND.nNoiseRandomShift |= 0x01;
}
EXIT_TIMER(tnd_noise);
ENTER_TIMER(tnd_dmc);
/* DMC */
if(mWave_TND.bDMCActive)
{
mWave_TND.nDMCFreqCount -= tick;
while (mWave_TND.nDMCFreqCount <= 0) {
if (!mWave_TND.bDMCActive) {
mWave_TND.nDMCFreqCount = mWave_TND.nDMCFreqTimer;
break;
}
mWave_TND.nDMCFreqCount += mWave_TND.nDMCFreqTimer;
if(mWave_TND.bDMCSampleBufferEmpty &&
mWave_TND.nDMCBytesRemaining)
{
burned += 4; /* 4 cycle burn! */
mWave_TND.nDMCSampleBuffer =
mWave_TND.pDMCDMAPtr[mWave_TND.nDMCDMABank]
[mWave_TND.nDMCDMAAddr];
mWave_TND.nDMCDMAAddr++;
if(mWave_TND.nDMCDMAAddr & 0x1000)
{
mWave_TND.nDMCDMAAddr &= 0x0FFF;
mWave_TND.nDMCDMABank =
(mWave_TND.nDMCDMABank + 1) & 0x07;
}
mWave_TND.bDMCSampleBufferEmpty = 0;
mWave_TND.nDMCBytesRemaining--;
if(!mWave_TND.nDMCBytesRemaining)
{
if(mWave_TND.bDMCLoop)
{
mWave_TND.nDMCDMABank = mWave_TND.nDMCDMABank_Load;
mWave_TND.nDMCDMAAddr = mWave_TND.nDMCDMAAddr_Load;
mWave_TND.nDMCBytesRemaining =mWave_TND.nDMCLength;
}
else if(mWave_TND.bDMCIRQEnabled)
mWave_TND.bDMCIRQPending = 1;
}
}
if(!mWave_TND.nDMCDeltaBit)
{
mWave_TND.nDMCDeltaBit = 8;
mWave_TND.bDMCDeltaSilent =mWave_TND.bDMCSampleBufferEmpty;
mWave_TND.nDMCDelta = mWave_TND.nDMCSampleBuffer;
mWave_TND.bDMCSampleBufferEmpty = 1;
}
if(mWave_TND.nDMCDeltaBit) {
mWave_TND.nDMCDeltaBit--;
if(!mWave_TND.bDMCDeltaSilent)
{
if(mWave_TND.nDMCDelta & 0x01)
{
if(mWave_TND.nDMCOutput < 0x7E)
mWave_TND.nDMCOutput += 2;
}
else if(mWave_TND.nDMCOutput > 1)
mWave_TND.nDMCOutput -= 2;
}
mWave_TND.nDMCDelta >>= 1;
}
if(!mWave_TND.nDMCBytesRemaining &&
mWave_TND.bDMCSampleBufferEmpty &&
mWave_TND.bDMCDeltaSilent)
mWave_TND.bDMCActive = mWave_TND.nDMCDeltaBit = 0;
}
}
EXIT_TIMER(tnd_dmc);
/* end of TND generation */
EXIT_TIMER(tnd);
if(nExternalSound && !bPALMode)
{
if(nExternalSound & EXTSOUND_VRC6)
Wave_VRC6_DoTicks(tick);
if(nExternalSound & EXTSOUND_N106)
Wave_N106_DoTicks(tick);
if(nExternalSound & EXTSOUND_FME07)
{
if (mWave_FME07[0].bChannelEnabled &&
mWave_FME07[0].nFreqTimer.W) {
mWave_FME07[0].nFreqCount -= tick;
if(mWave_FME07[0].nDutyCount < 16)
{
mWave_FME07[0].nMixL =
FME07_nOutputTable_L[mWave_FME07[0].nVolume];
} else mWave_FME07[0].nMixL = 0;
while(mWave_FME07[0].nFreqCount <= 0) {
mWave_FME07[0].nFreqCount +=
mWave_FME07[0].nFreqTimer.W;
mWave_FME07[0].nDutyCount=
(mWave_FME07[0].nDutyCount+1)&0x1f;
}
}
if (mWave_FME07[1].bChannelEnabled &&
mWave_FME07[1].nFreqTimer.W) {
mWave_FME07[1].nFreqCount -= tick;
if(mWave_FME07[1].nDutyCount < 16)
{
mWave_FME07[1].nMixL =
FME07_nOutputTable_L[mWave_FME07[1].nVolume];
} else mWave_FME07[1].nMixL = 0;
while(mWave_FME07[1].nFreqCount <= 0) {
mWave_FME07[1].nFreqCount +=
mWave_FME07[1].nFreqTimer.W;
mWave_FME07[1].nDutyCount=
(mWave_FME07[1].nDutyCount+1)&0x1f;
}
}
if (mWave_FME07[2].bChannelEnabled &&
mWave_FME07[2].nFreqTimer.W) {
mWave_FME07[2].nFreqCount -= tick;
if(mWave_FME07[2].nDutyCount < 16)
{
mWave_FME07[2].nMixL =
FME07_nOutputTable_L[mWave_FME07[2].nVolume];
} else mWave_FME07[2].nMixL = 0;
while(mWave_FME07[2].nFreqCount <= 0) {
mWave_FME07[2].nFreqCount +=
mWave_FME07[2].nFreqTimer.W;
mWave_FME07[2].nDutyCount=
(mWave_FME07[2].nDutyCount+1)&0x1f;
}
}
} /* end FME07 */
ENTER_TIMER(fds);
if(nExternalSound & EXTSOUND_FDS) {
/* Volume Envelope Unit */
if(mWave_FDS.bVolEnv_On)
{
mWave_FDS.nVolEnv_Count -= tick;
while(mWave_FDS.nVolEnv_Count <= 0)
{
mWave_FDS.nVolEnv_Count += mWave_FDS.nVolEnv_Timer;
if(mWave_FDS.nVolEnv_Mode) {
if(mWave_FDS.nVolEnv_Gain < 0x20)
mWave_FDS.nVolEnv_Gain++;
}
else {
if(mWave_FDS.nVolEnv_Gain)
mWave_FDS.nVolEnv_Gain--;
}
}
}
/* Sweep Envelope Unit */
if(mWave_FDS.bSweepEnv_On)
{
mWave_FDS.nSweep_Count -= tick;
while(mWave_FDS.nSweep_Count <= 0)
{
mWave_FDS.nSweep_Count += mWave_FDS.nSweep_Timer;
if(mWave_FDS.nSweep_Mode) {
if(mWave_FDS.nSweep_Gain < 0x20)
mWave_FDS.nSweep_Gain++;
} else {
if(mWave_FDS.nSweep_Gain) mWave_FDS.nSweep_Gain--;
}
}
}
/* Effector / LFO */
int32_t subfreq = 0;
if(mWave_FDS.bLFO_On)
{
mWave_FDS.nLFO_Count -= tick<<14;
while(mWave_FDS.nLFO_Count <= 0)
{
mWave_FDS.nLFO_Count += mWave_FDS.nLFO_Timer;
if(mWave_FDS.nLFO_Table[mWave_FDS.nLFO_Addr] == 4)
mWave_FDS.nSweepBias = 0;
else
mWave_FDS.nSweepBias +=
ModulationTable[
mWave_FDS.nLFO_Table[mWave_FDS.nLFO_Addr]
];
mWave_FDS.nLFO_Addr = (mWave_FDS.nLFO_Addr + 1) & 0x3F;
}
while(mWave_FDS.nSweepBias > 63)
mWave_FDS.nSweepBias -= 128;
while(mWave_FDS.nSweepBias < -64)
mWave_FDS.nSweepBias += 128;
register int32_t temp =
mWave_FDS.nSweepBias * mWave_FDS.nSweep_Gain;
if(temp & 0x0F)
{
temp /= 16;
if(mWave_FDS.nSweepBias < 0) temp--;
else temp += 2;
}
else
temp /= 16;
if(temp > 193) temp -= 258;
if(temp < -64) temp += 256;
subfreq = mWave_FDS.nFreq.W * temp / 64;
}
/* Main Unit */
if(mWave_FDS.bMain_On)
{
mWave_FDS.nMixL =
FDS_nOutputTable_L[mWave_FDS.nMainVolume]
[mWave_FDS.nVolume]
[mWave_FDS.nWaveTable[mWave_FDS.nMainAddr] ];
if((subfreq + mWave_FDS.nFreq.W) > 0)
{
int32_t freq = (0x10000<<14) / (subfreq + mWave_FDS.nFreq.W);
mWave_FDS.nFreqCount -= tick<<14;
while(mWave_FDS.nFreqCount <= 0)
{
mWave_FDS.nFreqCount += freq;
mWave_FDS.nMainAddr =
(mWave_FDS.nMainAddr + 1) & 0x3F;
mWave_FDS.nPopOutput =
mWave_FDS.nWaveTable[mWave_FDS.nMainAddr];
if(!mWave_FDS.nMainAddr)
{
if(mWave_FDS.nVolEnv_Gain < 0x20)
mWave_FDS.nVolume = mWave_FDS.nVolEnv_Gain;
else mWave_FDS.nVolume = 0x20;
}
}
}
else
mWave_FDS.nFreqCount = mWave_FDS.nLFO_Count;
}
else if(mWave_FDS.bPopReducer && mWave_FDS.nPopOutput)
{
mWave_FDS.nMixL = FDS_nOutputTable_L[mWave_FDS.nMainVolume]
[mWave_FDS.nVolume]
[mWave_FDS.nPopOutput];
mWave_FDS.nPopCount -= tick;
while(mWave_FDS.nPopCount <= 0)
{
mWave_FDS.nPopCount += 500;
mWave_FDS.nPopOutput--;
if(!mWave_FDS.nPopOutput)
mWave_FDS.nMainAddr = 0;
}
} /* end FDS */
}
EXIT_TIMER(fds);
} /* end while fulltick */
if(bBurnCPUCycles)
{
nCPUCycle += burned;
fulltick += burned;
}
/* Frame Sequencer */
ENTER_TIMER(frame);
nTicksUntilNextFrame -= tick<<16;
while(nTicksUntilNextFrame <= 0)
{
nTicksUntilNextFrame +=
(bPALMode ? PAL_FRAME_COUNTER_FREQ : NTSC_FRAME_COUNTER_FREQ) *
0x10000;
nFrameCounter++;
if(nFrameCounter > nFrameCounterMax)
nFrameCounter = 0;
if(nFrameCounterMax == 4)
{
if(nFrameCounter < 4)
{
CLOCK_MAJOR();
if(!(nFrameCounter & 1))
CLOCK_MINOR();
}
}
else
{
CLOCK_MAJOR();
if(nFrameCounter & 1)
CLOCK_MINOR();
if((nFrameCounter == 3) && bFrameIRQEnabled)
bFrameIRQPending = 1;
}
}
EXIT_TIMER(frame);
ENTER_TIMER(mix);
nTicksUntilNextSample -= tick<<16;
if(nTicksUntilNextSample <= 0)
{
nTicksUntilNextSample += nTicksPerSample;
mixL = mWave_Squares.nMixL;
mixL += mWave_TND.nMixL;
if(nExternalSound && !bPALMode)
{
if(nExternalSound & EXTSOUND_VRC6)
{
mixL += (mWave_VRC6Pulse[0].nMixL);
mixL += (mWave_VRC6Pulse[1].nMixL);
mixL += (mWave_VRC6Saw.nMixL);
}
if(nExternalSound & EXTSOUND_N106) {
mixL += (mWave_N106.nMixL[0]);
mixL += (mWave_N106.nMixL[1]);
mixL += (mWave_N106.nMixL[2]);
mixL += (mWave_N106.nMixL[3]);
mixL += (mWave_N106.nMixL[4]);
mixL += (mWave_N106.nMixL[5]);
mixL += (mWave_N106.nMixL[6]);
mixL += (mWave_N106.nMixL[7]);
}
if(nExternalSound & EXTSOUND_FME07)
{
mixL += (mWave_FME07[0].nMixL);
mixL += (mWave_FME07[1].nMixL);
mixL += (mWave_FME07[2].nMixL);
}
if(nExternalSound & EXTSOUND_FDS)
mixL += mWave_FDS.nMixL;
}
/* Filter */
diff = ((int64_t)mixL << 25) - nFilterAccL;
nFilterAccL += (diff * nHighPass) >> 16;
mixL = (int32_t)(diff >> 23);
/* End Filter */
if(bFade && (fFadeVolume < 1))
mixL = (int32_t)(mixL * fFadeVolume);
if(mixL < -32768) mixL = -32768;
if(mixL > 32767) mixL = 32767;
*((uint16_t*)pOutput) = (uint16_t)mixL;
pOutput += 2;
}
}
EXIT_TIMER(mix);
nAPUCycle = nCPUCycle;
EXIT_TIMER(apu);
}
/*
* Initialize
*
* Initializes Memory
*/
int NSFCore_Initialize()
{
int32_t i;
/* clear globals */
/* why, yes, this was easier when they were in a struct */
/*
* Memory
*/
ZEROMEMORY(pRAM,0x800);
ZEROMEMORY(pSRAM,0x2000);
ZEROMEMORY(pExRAM,0x1000);
pROM_Full=0;
ZEROMEMORY(pROM,10);
pStack=0;
nROMSize=0;
nROMBankCount=0;
nROMMaxSize=0;
/*
* Memory Proc Pointers
*/
ZEROMEMORY(ReadMemory,sizeof(ReadProc)*0x10);
ZEROMEMORY(WriteMemory,sizeof(WriteProc)*0x10);
/*
* 6502 Registers / Mode
*/
regA=0;
regX=0;
regY=0;
regP=0;
regSP=0;
regPC=0;
bPALMode=0;
bCPUJammed=0;
nMultIn_Low=0;
nMultIn_High=0;
/*
* NSF Preparation Information
*/
ZEROMEMORY(nBankswitchInitValues,10);
nPlayAddress=0;
nInitAddress=0;
nExternalSound=0;
nCurTrack=0;
fNSFPlaybackSpeed=0;
/*
* pAPU
*/
nFrameCounter=0;
nFrameCounterMax=0;
bFrameIRQEnabled=0;
bFrameIRQPending=0;
/*
* Timing and Counters
*/
nTicksUntilNextFrame=0;
nTicksPerPlay=0;
nTicksUntilNextPlay=0;
nTicksPerSample=0;
nTicksUntilNextSample=0;
nCPUCycle=0;
nAPUCycle=0;
nTotalPlays=0;
/*
* Silence Tracker
*/
nSilentSamples=0;
nSilentSampleMax=0;
nSilenceTrackMS=0;
bNoSilenceIfTime=0;
bTimeNotDefault=0;
/*
* Volume/fading/filter tracking
*/
nStartFade=0;
nEndFade=0;
bFade=0;
fFadeVolume=0;
fFadeChange=0;
pOutput=0;
nDMCPop_Prev=0;
bDMCPop_Skip=0;
bDMCPop_SamePlay=0;
/*
* Sound Filter
*/
nFilterAccL=0;
nHighPass=0;
nHighPassBase=0;
bHighPassEnabled=0;
/* channels */
ZEROMEMORY(&mWave_Squares,sizeof(struct Wave_Squares));
ZEROMEMORY(&mWave_TND,sizeof(struct Wave_TND));
ZEROMEMORY(mWave_VRC6Pulse,sizeof(struct VRC6PulseWave)*2);
ZEROMEMORY(&mWave_VRC6Saw,sizeof(struct VRC6SawWave));
ZEROMEMORY(&mWave_N106,sizeof(struct N106Wave));
ZEROMEMORY(mWave_FME07,sizeof(struct FME07Wave)*3);
ZEROMEMORY(&mWave_FDS,sizeof(struct FDSWave));
/* end clear globals */
// Default filter bases
nHighPassBase = 150;
bHighPassEnabled = 1;
mWave_TND.nNoiseRandomShift = 1;
for(i = 0; i < 8; i++)
mWave_TND.pDMCDMAPtr[i] = pROM[i + 2];
SetPlaybackOptions(nSampleRate);
for(i = 0; i < 8; i++)
mWave_N106.nFrequencyLookupTable[i] =
((((i + 1) * 45 * 0x40000) / (float)NES_FREQUENCY) *
(float)NTSC_FREQUENCY) * 256.0;
ZEROMEMORY(pRAM,0x800);