| /*************************************************************************** |
| * __________ __ ___. |
| * 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); |
| 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); |
|