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 @@ | |||
1 | |||
2 | //C64 emulation (SID-playback related) | ||
3 | |||
4 | |||
5 | #include "../libcRSID.h" | ||
6 | |||
7 | #include "MEM.c" | ||
8 | #include "CPU.c" | ||
9 | #include "CIA.c" | ||
10 | #include "VIC.c" | ||
11 | #include "SID.c" | ||
12 | |||
13 | |||
14 | cRSID_C64instance* cRSID_createC64 (cRSID_C64instance* C64, unsigned short samplerate) { //init a basic PAL C64 instance | ||
15 | if(samplerate) C64->SampleRate = samplerate; | ||
16 | else C64->SampleRate = samplerate = 44100; | ||
17 | C64->SampleClockRatio = (985248<<4)/samplerate; //shifting (multiplication) enhances SampleClockRatio precision | ||
18 | C64->Attenuation = 26; | ||
19 | C64->CPU.C64 = C64; | ||
20 | cRSID_createSIDchip ( C64, &C64->SID[1], 8580, 0xD400 ); //default C64 setup with only 1 SID and 2 CIAs and 1 VIC | ||
21 | cRSID_createCIAchip ( C64, &C64->CIA[1], 0xDC00 ); | ||
22 | cRSID_createCIAchip ( C64, &C64->CIA[2], 0xDD00 ); | ||
23 | cRSID_createVICchip ( C64, &C64->VIC, 0xD000 ); | ||
24 | //if(C64->RealSIDmode) { | ||
25 | cRSID_setROMcontent ( C64 ); | ||
26 | //} | ||
27 | cRSID_initC64(C64); | ||
28 | return C64; | ||
29 | } | ||
30 | |||
31 | |||
32 | void cRSID_setC64 (cRSID_C64instance* C64) { //set hardware-parameters (Models, SIDs) for playback of loaded SID-tune | ||
33 | enum C64clocks { C64_PAL_CPUCLK=985248, C64_NTSC_CPUCLK=1022727 }; | ||
34 | enum C64scanlines { C64_PAL_SCANLINES = 312, C64_NTSC_SCANLINES = 263 }; | ||
35 | enum C64scanlineCycles { C64_PAL_SCANLINE_CYCLES = 63, C64_NTSC_SCANLINE_CYCLES = 65 }; | ||
36 | //enum C64framerates { PAL_FRAMERATE = 50, NTSC_FRAMERATE = 60 }; //Hz | ||
37 | |||
38 | static const unsigned int CPUspeeds[] = { C64_NTSC_CPUCLK, C64_PAL_CPUCLK }; | ||
39 | static const unsigned short ScanLines[] = { C64_NTSC_SCANLINES, C64_PAL_SCANLINES }; | ||
40 | static const unsigned char ScanLineCycles[] = { C64_NTSC_SCANLINE_CYCLES, C64_PAL_SCANLINE_CYCLES }; | ||
41 | //unsigned char FrameRates[] = { NTSC_FRAMERATE, PAL_FRAMERATE }; | ||
42 | |||
43 | static const short Attenuations[]={0,26,43,137}; //increase for 2SID (to 43) and 3SID (to 137) | ||
44 | short SIDmodel; | ||
45 | unsigned char SIDchipCount; | ||
46 | |||
47 | |||
48 | C64->VideoStandard = ( (C64->SIDheader->ModelFormatStandard & 0x0C) >> 2 ) != 2; | ||
49 | if (C64->SampleRate==0) C64->SampleRate = 44100; | ||
50 | C64->CPUfrequency = CPUspeeds[C64->VideoStandard]; | ||
51 | C64->SampleClockRatio = ( C64->CPUfrequency << 4 ) / C64->SampleRate; //shifting (multiplication) enhances SampleClockRatio precision | ||
52 | |||
53 | C64->VIC.RasterLines = ScanLines[C64->VideoStandard]; | ||
54 | C64->VIC.RasterRowCycles = ScanLineCycles[C64->VideoStandard]; | ||
55 | C64->FrameCycles = C64->VIC.RasterLines * C64->VIC.RasterRowCycles; ///C64->SampleRate / PAL_FRAMERATE; //1x speed tune with VIC Vertical-blank timing | ||
56 | |||
57 | C64->PrevRasterLine=-1; //so if $d012 is set once only don't disturb FrameCycleCnt | ||
58 | |||
59 | C64->SID[1].ChipModel = (C64->SIDheader->ModelFormatStandard&0x30) >= 0x20? 8580:6581; | ||
60 | |||
61 | SIDmodel = C64->SIDheader->ModelFormatStandard & 0xC0; | ||
62 | if (SIDmodel) SIDmodel = (SIDmodel >= 0x80) ? 8580:6581; else SIDmodel = C64->SID[1].ChipModel; | ||
63 | cRSID_createSIDchip ( C64, &C64->SID[2], SIDmodel, 0xD000 + C64->SIDheader->SID2baseAddress*16 ); | ||
64 | |||
65 | SIDmodel = C64->SIDheader->ModelFormatStandardH & 0x03; | ||
66 | if (SIDmodel) SIDmodel = (SIDmodel >= 0x02) ? 8580:6581; else SIDmodel = C64->SID[1].ChipModel; | ||
67 | cRSID_createSIDchip ( C64, &C64->SID[3], SIDmodel, 0xD000 + C64->SIDheader->SID3baseAddress*16 ); | ||
68 | |||
69 | SIDchipCount = 1 + (C64->SID[2].BaseAddress > 0) + (C64->SID[3].BaseAddress > 0); | ||
70 | C64->Attenuation = Attenuations[SIDchipCount]; | ||
71 | } | ||
72 | |||
73 | |||
74 | void cRSID_initC64 (cRSID_C64instance* C64) { //C64 Reset | ||
75 | cRSID_initSIDchip( &C64->SID[1] ); | ||
76 | cRSID_initCIAchip( &C64->CIA[1] ); cRSID_initCIAchip( &C64->CIA[2] ); | ||
77 | cRSID_initMem(C64); | ||
78 | cRSID_initCPU( &C64->CPU, (cRSID_readMemC64(C64,0xFFFD)<<8) + cRSID_readMemC64(C64,0xFFFC) ); | ||
79 | C64->IRQ = C64->NMI = 0; | ||
80 | } | ||
81 | |||
82 | |||
83 | int cRSID_emulateC64 (cRSID_C64instance *C64) { | ||
84 | static unsigned char InstructionCycles; | ||
85 | static int Output; | ||
86 | |||
87 | |||
88 | //Cycle-based part of emulations: | ||
89 | |||
90 | |||
91 | while (C64->SampleCycleCnt <= C64->SampleClockRatio) { | ||
92 | |||
93 | if (!C64->RealSIDmode) { | ||
94 | if (C64->FrameCycleCnt >= C64->FrameCycles) { | ||
95 | C64->FrameCycleCnt -= C64->FrameCycles; | ||
96 | if (C64->Finished) { //some tunes (e.g. Barbarian, A-Maze-Ing) doesn't always finish in 1 frame | ||
97 | cRSID_initCPU ( &C64->CPU, C64->PlayAddress ); //(PSID docs say bank-register should always be set for each call's region) | ||
98 | C64->Finished=0; //C64->SampleCycleCnt=0; //PSID workaround for some tunes (e.g. Galdrumway): | ||
99 | if (C64->TimerSource==0) C64->IObankRD[0xD019] = 0x81; //always simulate to player-calls that VIC-IRQ happened | ||
100 | else C64->IObankRD[0xDC0D] = 0x83; //always simulate to player-calls that CIA TIMERA/TIMERB-IRQ happened | ||
101 | }} | ||
102 | if (C64->Finished==0) { | ||
103 | if ( (InstructionCycles = cRSID_emulateCPU()) >= 0xFE ) { InstructionCycles=6; C64->Finished=1; } | ||
104 | } | ||
105 | else InstructionCycles=7; //idle between player-calls | ||
106 | C64->FrameCycleCnt += InstructionCycles; | ||
107 | C64->IObankRD[0xDC04] += InstructionCycles; //very simple CIA1 TimerA simulation for PSID (e.g. Delta-Mix_E-Load_loader) | ||
108 | } | ||
109 | |||
110 | else { //RealSID emulations: | ||
111 | if ( cRSID_handleCPUinterrupts(&C64->CPU) ) { C64->Finished=0; InstructionCycles=7; } | ||
112 | else if (C64->Finished==0) { | ||
113 | if ( (InstructionCycles = cRSID_emulateCPU()) >= 0xFE ) { | ||
114 | InstructionCycles=6; C64->Finished=1; | ||
115 | } | ||
116 | } | ||
117 | else InstructionCycles=7; //idle between IRQ-calls | ||
118 | C64->IRQ = C64->NMI = 0; //prepare for collecting IRQ sources | ||
119 | C64->IRQ |= cRSID_emulateCIA (&C64->CIA[1], InstructionCycles); | ||
120 | C64->NMI |= cRSID_emulateCIA (&C64->CIA[2], InstructionCycles); | ||
121 | C64->IRQ |= cRSID_emulateVIC (&C64->VIC, InstructionCycles); | ||
122 | } | ||
123 | |||
124 | C64->SampleCycleCnt += (InstructionCycles<<4); | ||
125 | |||
126 | cRSID_emulateADSRs (&C64->SID[1], InstructionCycles); | ||
127 | if ( C64->SID[2].BaseAddress != 0 ) cRSID_emulateADSRs (&C64->SID[2], InstructionCycles); | ||
128 | if ( C64->SID[3].BaseAddress != 0 ) cRSID_emulateADSRs (&C64->SID[3], InstructionCycles); | ||
129 | |||
130 | } | ||
131 | C64->SampleCycleCnt -= C64->SampleClockRatio; | ||
132 | |||
133 | |||
134 | //Samplerate-based part of emulations: | ||
135 | |||
136 | |||
137 | if (!C64->RealSIDmode) { //some PSID tunes use CIA TOD-clock (e.g. Kawasaki Synthesizer Demo) | ||
138 | --C64->TenthSecondCnt; | ||
139 | if (C64->TenthSecondCnt <= 0) { | ||
140 | C64->TenthSecondCnt = C64->SampleRate / 10; | ||
141 | ++(C64->IObankRD[0xDC08]); | ||
142 | if(C64->IObankRD[0xDC08]>=10) { | ||
143 | C64->IObankRD[0xDC08]=0; ++(C64->IObankRD[0xDC09]); | ||
144 | //if(C64->IObankRD[0xDC09]% | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | |||
149 | Output = cRSID_emulateWaves (&C64->SID[1]); | ||
150 | if ( C64->SID[2].BaseAddress != 0 ) Output += cRSID_emulateWaves (&C64->SID[2]); | ||
151 | if ( C64->SID[3].BaseAddress != 0 ) Output += cRSID_emulateWaves (&C64->SID[3]); | ||
152 | |||
153 | return Output; | ||
154 | } | ||
155 | |||
156 | |||
157 | static inline short cRSID_playPSIDdigi(cRSID_C64instance* C64) { | ||
158 | enum PSIDdigiSpecs { DIGI_VOLUME = 1200 }; //80 }; | ||
159 | static unsigned char PlaybackEnabled=0, NybbleCounter=0, RepeatCounter=0, Shifts; | ||
160 | static unsigned short SampleAddress, RatePeriod; | ||
161 | static short Output=0; | ||
162 | static int PeriodCounter; | ||
163 | |||
164 | if (C64->IObankWR[0xD41D]) { | ||
165 | PlaybackEnabled = (C64->IObankWR[0xD41D] >= 0xFE); | ||
166 | PeriodCounter = 0; NybbleCounter = 0; | ||
167 | SampleAddress = C64->IObankWR[0xD41E] + (C64->IObankWR[0xD41F]<<8); | ||
168 | RepeatCounter = C64->IObankWR[0xD43F]; | ||
169 | } | ||
170 | C64->IObankWR[0xD41D] = 0; | ||
171 | |||
172 | if (PlaybackEnabled) { | ||
173 | RatePeriod = C64->IObankWR[0xD45D] + (C64->IObankWR[0xD45E]<<8); | ||
174 | if (RatePeriod) PeriodCounter += C64->CPUfrequency / RatePeriod; | ||
175 | if ( PeriodCounter >= C64->SampleRate ) { | ||
176 | PeriodCounter -= C64->SampleRate; | ||
177 | |||
178 | if ( SampleAddress < C64->IObankWR[0xD43D] + (C64->IObankWR[0xD43E]<<8) ) { | ||
179 | if (NybbleCounter) { | ||
180 | Shifts = C64->IObankWR[0xD47D] ? 4:0; | ||
181 | ++SampleAddress; | ||
182 | } | ||
183 | else Shifts = C64->IObankWR[0xD47D] ? 0:4; | ||
184 | Output = ( ( (C64->RAMbank[SampleAddress]>>Shifts) & 0xF) - 8 ) * DIGI_VOLUME; //* (C64->IObankWR[0xD418]&0xF); | ||
185 | NybbleCounter^=1; | ||
186 | } | ||
187 | else if (RepeatCounter) { | ||
188 | SampleAddress = C64->IObankWR[0xD47F] + (C64->IObankWR[0xD47E]<<8); | ||
189 | RepeatCounter--; | ||
190 | } | ||
191 | |||
192 | } | ||
193 | } | ||
194 | |||
195 | return Output; | ||
196 | } | ||
197 | |||