diff options
Diffstat (limited to 'lib/rbcodec/codecs/cRSID/C64/C64.c')
-rw-r--r-- | lib/rbcodec/codecs/cRSID/C64/C64.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/cRSID/C64/C64.c b/lib/rbcodec/codecs/cRSID/C64/C64.c new file mode 100644 index 0000000000..81dca35542 --- /dev/null +++ b/lib/rbcodec/codecs/cRSID/C64/C64.c @@ -0,0 +1,197 @@ + +//C64 emulation (SID-playback related) + + +#include "../libcRSID.h" + +#include "MEM.c" +#include "CPU.c" +#include "CIA.c" +#include "VIC.c" +#include "SID.c" + + +cRSID_C64instance* cRSID_createC64 (cRSID_C64instance* C64, unsigned short samplerate) { //init a basic PAL C64 instance + if(samplerate) C64->SampleRate = samplerate; + else C64->SampleRate = samplerate = 44100; + C64->SampleClockRatio = (985248<<4)/samplerate; //shifting (multiplication) enhances SampleClockRatio precision + C64->Attenuation = 26; + C64->CPU.C64 = C64; + cRSID_createSIDchip ( C64, &C64->SID[1], 8580, 0xD400 ); //default C64 setup with only 1 SID and 2 CIAs and 1 VIC + cRSID_createCIAchip ( C64, &C64->CIA[1], 0xDC00 ); + cRSID_createCIAchip ( C64, &C64->CIA[2], 0xDD00 ); + cRSID_createVICchip ( C64, &C64->VIC, 0xD000 ); + //if(C64->RealSIDmode) { + cRSID_setROMcontent ( C64 ); + //} + cRSID_initC64(C64); + return C64; +} + + +void cRSID_setC64 (cRSID_C64instance* C64) { //set hardware-parameters (Models, SIDs) for playback of loaded SID-tune + enum C64clocks { C64_PAL_CPUCLK=985248, C64_NTSC_CPUCLK=1022727 }; + enum C64scanlines { C64_PAL_SCANLINES = 312, C64_NTSC_SCANLINES = 263 }; + enum C64scanlineCycles { C64_PAL_SCANLINE_CYCLES = 63, C64_NTSC_SCANLINE_CYCLES = 65 }; + //enum C64framerates { PAL_FRAMERATE = 50, NTSC_FRAMERATE = 60 }; //Hz + + static const unsigned int CPUspeeds[] = { C64_NTSC_CPUCLK, C64_PAL_CPUCLK }; + static const unsigned short ScanLines[] = { C64_NTSC_SCANLINES, C64_PAL_SCANLINES }; + static const unsigned char ScanLineCycles[] = { C64_NTSC_SCANLINE_CYCLES, C64_PAL_SCANLINE_CYCLES }; + //unsigned char FrameRates[] = { NTSC_FRAMERATE, PAL_FRAMERATE }; + + static const short Attenuations[]={0,26,43,137}; //increase for 2SID (to 43) and 3SID (to 137) + short SIDmodel; + unsigned char SIDchipCount; + + + C64->VideoStandard = ( (C64->SIDheader->ModelFormatStandard & 0x0C) >> 2 ) != 2; + if (C64->SampleRate==0) C64->SampleRate = 44100; + C64->CPUfrequency = CPUspeeds[C64->VideoStandard]; + C64->SampleClockRatio = ( C64->CPUfrequency << 4 ) / C64->SampleRate; //shifting (multiplication) enhances SampleClockRatio precision + + C64->VIC.RasterLines = ScanLines[C64->VideoStandard]; + C64->VIC.RasterRowCycles = ScanLineCycles[C64->VideoStandard]; + C64->FrameCycles = C64->VIC.RasterLines * C64->VIC.RasterRowCycles; ///C64->SampleRate / PAL_FRAMERATE; //1x speed tune with VIC Vertical-blank timing + + C64->PrevRasterLine=-1; //so if $d012 is set once only don't disturb FrameCycleCnt + + C64->SID[1].ChipModel = (C64->SIDheader->ModelFormatStandard&0x30) >= 0x20? 8580:6581; + + SIDmodel = C64->SIDheader->ModelFormatStandard & 0xC0; + if (SIDmodel) SIDmodel = (SIDmodel >= 0x80) ? 8580:6581; else SIDmodel = C64->SID[1].ChipModel; + cRSID_createSIDchip ( C64, &C64->SID[2], SIDmodel, 0xD000 + C64->SIDheader->SID2baseAddress*16 ); + + SIDmodel = C64->SIDheader->ModelFormatStandardH & 0x03; + if (SIDmodel) SIDmodel = (SIDmodel >= 0x02) ? 8580:6581; else SIDmodel = C64->SID[1].ChipModel; + cRSID_createSIDchip ( C64, &C64->SID[3], SIDmodel, 0xD000 + C64->SIDheader->SID3baseAddress*16 ); + + SIDchipCount = 1 + (C64->SID[2].BaseAddress > 0) + (C64->SID[3].BaseAddress > 0); + C64->Attenuation = Attenuations[SIDchipCount]; +} + + +void cRSID_initC64 (cRSID_C64instance* C64) { //C64 Reset + cRSID_initSIDchip( &C64->SID[1] ); + cRSID_initCIAchip( &C64->CIA[1] ); cRSID_initCIAchip( &C64->CIA[2] ); + cRSID_initMem(C64); + cRSID_initCPU( &C64->CPU, (cRSID_readMemC64(C64,0xFFFD)<<8) + cRSID_readMemC64(C64,0xFFFC) ); + C64->IRQ = C64->NMI = 0; +} + + +int cRSID_emulateC64 (cRSID_C64instance *C64) { + static unsigned char InstructionCycles; + static int Output; + + + //Cycle-based part of emulations: + + + while (C64->SampleCycleCnt <= C64->SampleClockRatio) { + + if (!C64->RealSIDmode) { + if (C64->FrameCycleCnt >= C64->FrameCycles) { + C64->FrameCycleCnt -= C64->FrameCycles; + if (C64->Finished) { //some tunes (e.g. Barbarian, A-Maze-Ing) doesn't always finish in 1 frame + cRSID_initCPU ( &C64->CPU, C64->PlayAddress ); //(PSID docs say bank-register should always be set for each call's region) + C64->Finished=0; //C64->SampleCycleCnt=0; //PSID workaround for some tunes (e.g. Galdrumway): + if (C64->TimerSource==0) C64->IObankRD[0xD019] = 0x81; //always simulate to player-calls that VIC-IRQ happened + else C64->IObankRD[0xDC0D] = 0x83; //always simulate to player-calls that CIA TIMERA/TIMERB-IRQ happened + }} + if (C64->Finished==0) { + if ( (InstructionCycles = cRSID_emulateCPU()) >= 0xFE ) { InstructionCycles=6; C64->Finished=1; } + } + else InstructionCycles=7; //idle between player-calls + C64->FrameCycleCnt += InstructionCycles; + C64->IObankRD[0xDC04] += InstructionCycles; //very simple CIA1 TimerA simulation for PSID (e.g. Delta-Mix_E-Load_loader) + } + + else { //RealSID emulations: + if ( cRSID_handleCPUinterrupts(&C64->CPU) ) { C64->Finished=0; InstructionCycles=7; } + else if (C64->Finished==0) { + if ( (InstructionCycles = cRSID_emulateCPU()) >= 0xFE ) { + InstructionCycles=6; C64->Finished=1; + } + } + else InstructionCycles=7; //idle between IRQ-calls + C64->IRQ = C64->NMI = 0; //prepare for collecting IRQ sources + C64->IRQ |= cRSID_emulateCIA (&C64->CIA[1], InstructionCycles); + C64->NMI |= cRSID_emulateCIA (&C64->CIA[2], InstructionCycles); + C64->IRQ |= cRSID_emulateVIC (&C64->VIC, InstructionCycles); + } + + C64->SampleCycleCnt += (InstructionCycles<<4); + + cRSID_emulateADSRs (&C64->SID[1], InstructionCycles); + if ( C64->SID[2].BaseAddress != 0 ) cRSID_emulateADSRs (&C64->SID[2], InstructionCycles); + if ( C64->SID[3].BaseAddress != 0 ) cRSID_emulateADSRs (&C64->SID[3], InstructionCycles); + + } + C64->SampleCycleCnt -= C64->SampleClockRatio; + + + //Samplerate-based part of emulations: + + + if (!C64->RealSIDmode) { //some PSID tunes use CIA TOD-clock (e.g. Kawasaki Synthesizer Demo) + --C64->TenthSecondCnt; + if (C64->TenthSecondCnt <= 0) { + C64->TenthSecondCnt = C64->SampleRate / 10; + ++(C64->IObankRD[0xDC08]); + if(C64->IObankRD[0xDC08]>=10) { + C64->IObankRD[0xDC08]=0; ++(C64->IObankRD[0xDC09]); + //if(C64->IObankRD[0xDC09]% + } + } + } + + Output = cRSID_emulateWaves (&C64->SID[1]); + if ( C64->SID[2].BaseAddress != 0 ) Output += cRSID_emulateWaves (&C64->SID[2]); + if ( C64->SID[3].BaseAddress != 0 ) Output += cRSID_emulateWaves (&C64->SID[3]); + + return Output; +} + + +static inline short cRSID_playPSIDdigi(cRSID_C64instance* C64) { + enum PSIDdigiSpecs { DIGI_VOLUME = 1200 }; //80 }; + static unsigned char PlaybackEnabled=0, NybbleCounter=0, RepeatCounter=0, Shifts; + static unsigned short SampleAddress, RatePeriod; + static short Output=0; + static int PeriodCounter; + + if (C64->IObankWR[0xD41D]) { + PlaybackEnabled = (C64->IObankWR[0xD41D] >= 0xFE); + PeriodCounter = 0; NybbleCounter = 0; + SampleAddress = C64->IObankWR[0xD41E] + (C64->IObankWR[0xD41F]<<8); + RepeatCounter = C64->IObankWR[0xD43F]; + } + C64->IObankWR[0xD41D] = 0; + + if (PlaybackEnabled) { + RatePeriod = C64->IObankWR[0xD45D] + (C64->IObankWR[0xD45E]<<8); + if (RatePeriod) PeriodCounter += C64->CPUfrequency / RatePeriod; + if ( PeriodCounter >= C64->SampleRate ) { + PeriodCounter -= C64->SampleRate; + + if ( SampleAddress < C64->IObankWR[0xD43D] + (C64->IObankWR[0xD43E]<<8) ) { + if (NybbleCounter) { + Shifts = C64->IObankWR[0xD47D] ? 4:0; + ++SampleAddress; + } + else Shifts = C64->IObankWR[0xD47D] ? 0:4; + Output = ( ( (C64->RAMbank[SampleAddress]>>Shifts) & 0xF) - 8 ) * DIGI_VOLUME; //* (C64->IObankWR[0xD418]&0xF); + NybbleCounter^=1; + } + else if (RepeatCounter) { + SampleAddress = C64->IObankWR[0xD47F] + (C64->IObankWR[0xD47E]<<8); + RepeatCounter--; + } + + } + } + + return Output; +} + |