diff options
Diffstat (limited to 'lib/rbcodec/codecs/sid.c')
-rw-r--r-- | lib/rbcodec/codecs/sid.c | 1340 |
1 files changed, 1340 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/sid.c b/lib/rbcodec/codecs/sid.c new file mode 100644 index 0000000000..9b19a20ba7 --- /dev/null +++ b/lib/rbcodec/codecs/sid.c | |||
@@ -0,0 +1,1340 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * SID Codec for rockbox based on the TinySID engine | ||
11 | * | ||
12 | * Written by Tammo Hinrichs (kb) and Rainer Sinsch in 1998-1999 | ||
13 | * Ported to rockbox on 14 April 2006 | ||
14 | * | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or | ||
17 | * modify it under the terms of the GNU General Public License | ||
18 | * as published by the Free Software Foundation; either version 2 | ||
19 | * of the License, or (at your option) any later version. | ||
20 | * | ||
21 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
22 | * KIND, either express or implied. | ||
23 | * | ||
24 | ****************************************************************************/ | ||
25 | |||
26 | /***************************** | ||
27 | * kb explicitly points out that this emulation sounds crappy, though | ||
28 | * we decided to put it open source so everyone can enjoy sidmusic | ||
29 | * on rockbox | ||
30 | * | ||
31 | *****************************/ | ||
32 | |||
33 | /********************* | ||
34 | * v1.1 | ||
35 | * Added 16-04-2006: Rainer Sinsch | ||
36 | * Removed all time critical floating point operations and | ||
37 | * replaced them with quick & dirty integer calculations | ||
38 | * | ||
39 | * Added 17-04-2006: Rainer Sinsch | ||
40 | * Improved quick & dirty integer calculations for the resonant filter | ||
41 | * Improved audio quality by 4 bits | ||
42 | * | ||
43 | * v1.2 | ||
44 | * Added 17-04-2006: Dave Chapman | ||
45 | * Improved file loading | ||
46 | * | ||
47 | * Added 17-04-2006: Rainer Sinsch | ||
48 | * Added sample routines | ||
49 | * Added cia timing routines | ||
50 | * Added fast forwarding capabilities | ||
51 | * Corrected bug in sid loading | ||
52 | * | ||
53 | * v1.2.1 | ||
54 | * Added 04-05-2006: Rainer Sinsch | ||
55 | * Implemented Marco Alanens suggestion for subsong selection: | ||
56 | * Select the subsong by seeking: Each second represents a subsong | ||
57 | * | ||
58 | **************************/ | ||
59 | |||
60 | #define USE_FILTER | ||
61 | |||
62 | #include "debug.h" | ||
63 | #include "codeclib.h" | ||
64 | #include <inttypes.h> | ||
65 | |||
66 | CODEC_HEADER | ||
67 | |||
68 | #define CHUNK_SIZE (1024*2) | ||
69 | #define SID_BUFFER_SIZE 0x10000 | ||
70 | #define SAMPLE_RATE 44100 | ||
71 | |||
72 | /* This codec supports SID Files: | ||
73 | * | ||
74 | */ | ||
75 | |||
76 | static int32_t samples[CHUNK_SIZE] IBSS_ATTR; /* The sample buffer */ | ||
77 | |||
78 | void sidPoke(int reg, unsigned char val) ICODE_ATTR; | ||
79 | |||
80 | #define FLAG_N 128 | ||
81 | #define FLAG_V 64 | ||
82 | #define FLAG_B 16 | ||
83 | #define FLAG_D 8 | ||
84 | #define FLAG_I 4 | ||
85 | #define FLAG_Z 2 | ||
86 | #define FLAG_C 1 | ||
87 | |||
88 | #define imp 0 | ||
89 | #define imm 1 | ||
90 | #define _abs 2 | ||
91 | #define absx 3 | ||
92 | #define absy 4 | ||
93 | #define zp 6 | ||
94 | #define zpx 7 | ||
95 | #define zpy 8 | ||
96 | #define ind 9 | ||
97 | #define indx 10 | ||
98 | #define indy 11 | ||
99 | #define acc 12 | ||
100 | #define rel 13 | ||
101 | |||
102 | enum { | ||
103 | adc, _and, asl, bcc, bcs, beq, bit, bmi, bne, bpl, _brk, bvc, bvs, clc, | ||
104 | cld, cli, clv, cmp, cpx, cpy, dec, dex, dey, eor, inc, inx, iny, jmp, | ||
105 | jsr, lda, ldx, ldy, lsr, _nop, ora, pha, php, pla, plp, rol, ror, rti, | ||
106 | rts, sbc, sec, sed, sei, sta, stx, sty, tax, tay, tsx, txa, txs, tya, | ||
107 | xxx | ||
108 | }; | ||
109 | |||
110 | /* SID register definition */ | ||
111 | struct s6581 { | ||
112 | struct sidvoice { | ||
113 | unsigned short freq; | ||
114 | unsigned short pulse; | ||
115 | unsigned char wave; | ||
116 | unsigned char ad; | ||
117 | unsigned char sr; | ||
118 | } v[3]; | ||
119 | unsigned char ffreqlo; | ||
120 | unsigned char ffreqhi; | ||
121 | unsigned char res_ftv; | ||
122 | unsigned char ftp_vol; | ||
123 | }; | ||
124 | |||
125 | /* internal oscillator def */ | ||
126 | struct sidosc { | ||
127 | unsigned long freq; | ||
128 | unsigned long pulse; | ||
129 | unsigned char wave; | ||
130 | unsigned char filter; | ||
131 | unsigned long attack; | ||
132 | unsigned long decay; | ||
133 | unsigned long sustain; | ||
134 | unsigned long release; | ||
135 | unsigned long counter; | ||
136 | signed long envval; | ||
137 | unsigned char envphase; | ||
138 | unsigned long noisepos; | ||
139 | unsigned long noiseval; | ||
140 | unsigned char noiseout; | ||
141 | }; | ||
142 | |||
143 | /* internal filter def */ | ||
144 | struct sidflt { | ||
145 | int freq; | ||
146 | unsigned char l_ena; | ||
147 | unsigned char b_ena; | ||
148 | unsigned char h_ena; | ||
149 | unsigned char v3ena; | ||
150 | int vol; | ||
151 | int rez; | ||
152 | int h; | ||
153 | int b; | ||
154 | int l; | ||
155 | }; | ||
156 | |||
157 | /* ------------------------ pseudo-constants (depending on mixing freq) */ | ||
158 | int mixing_frequency IDATA_ATTR; | ||
159 | unsigned long freqmul IDATA_ATTR; | ||
160 | int filtmul IDATA_ATTR; | ||
161 | #ifndef ROCKBOX | ||
162 | unsigned long attacks [16] IDATA_ATTR; | ||
163 | unsigned long releases[16] IDATA_ATTR; | ||
164 | #endif | ||
165 | |||
166 | /* ------------------------------------------------------------ globals */ | ||
167 | struct s6581 sid IDATA_ATTR; | ||
168 | struct sidosc osc[3] IDATA_ATTR; | ||
169 | struct sidflt filter IDATA_ATTR; | ||
170 | |||
171 | /* ------------------------------------------------------ C64 Emu Stuff */ | ||
172 | unsigned char bval IDATA_ATTR; | ||
173 | unsigned short wval IDATA_ATTR; | ||
174 | /* -------------------------------------------------- Register & memory */ | ||
175 | unsigned char a,x,y,s,p IDATA_ATTR; | ||
176 | unsigned short pc IDATA_ATTR; | ||
177 | |||
178 | unsigned char memory[65536]; | ||
179 | |||
180 | /* ----------------------------------------- Variables for sample stuff */ | ||
181 | static int sample_active IDATA_ATTR; | ||
182 | static int sample_position, sample_start, sample_end, sample_repeat_start IDATA_ATTR; | ||
183 | static int fracPos IDATA_ATTR; /* Fractal position of sample */ | ||
184 | static int sample_period IDATA_ATTR; | ||
185 | static int sample_repeats IDATA_ATTR; | ||
186 | static int sample_order IDATA_ATTR; | ||
187 | static int sample_nibble IDATA_ATTR; | ||
188 | |||
189 | static int internal_period, internal_order, internal_start, internal_end, | ||
190 | internal_add, internal_repeat_times, internal_repeat_start IDATA_ATTR; | ||
191 | |||
192 | /* ---------------------------------------------------------- constants */ | ||
193 | #ifndef ROCKBOX | ||
194 | static const float attackTimes[16] ICONST_ATTR = | ||
195 | { | ||
196 | 0.0022528606, 0.0080099577, 0.0157696042, 0.0237795619, | ||
197 | 0.0372963655, 0.0550684591, 0.0668330845, 0.0783473987, | ||
198 | 0.0981219818, 0.244554021, 0.489108042, 0.782472742, | ||
199 | 0.977715461, 2.93364701, 4.88907793, 7.82272493 | ||
200 | }; | ||
201 | static const float decayReleaseTimes[16] ICONST_ATTR = | ||
202 | { | ||
203 | 0.00891777693, 0.024594051, 0.0484185907, 0.0730116639, 0.114512475, | ||
204 | 0.169078356, 0.205199432, 0.240551975, 0.301266125, 0.750858245, | ||
205 | 1.50171551, 2.40243682, 3.00189298, 9.00721405, 15.010998, 24.0182111 | ||
206 | }; | ||
207 | #else | ||
208 | #define DIV(X) ((int)(0x1000000 / (X * SAMPLE_RATE))) | ||
209 | static const unsigned long attacks[16] ICONST_ATTR = | ||
210 | { | ||
211 | DIV(0.0022528606), DIV(0.0080099577), DIV(0.0157696042), DIV(0.0237795619), | ||
212 | DIV(0.0372963655), DIV(0.0550684591), DIV(0.0668330845), DIV(0.0783473987), | ||
213 | DIV(0.0981219818), DIV(0.2445540210), DIV(0.4891080420), DIV(0.7824727420), | ||
214 | DIV(0.9777154610), DIV(2.9336470100), DIV(4.8890779300), DIV(7.8227249300) | ||
215 | }; | ||
216 | static const unsigned long releases[16] ICONST_ATTR = | ||
217 | { | ||
218 | DIV(0.00891777693), DIV(0.0245940510), DIV(0.0484185907), DIV(0.0730116639), | ||
219 | DIV(0.11451247500), DIV(0.1690783560), DIV(0.2051994320), DIV(0.2405519750), | ||
220 | DIV(0.30126612500), DIV(0.7508582450), DIV(1.5017155100), DIV(2.4024368200), | ||
221 | DIV(3.00189298000), DIV(9.0072140500), DIV(15.010998000), DIV(24.018211100) | ||
222 | }; | ||
223 | #endif | ||
224 | static const int opcodes[256] ICONST_ATTR = { | ||
225 | _brk,ora,xxx,xxx,xxx,ora,asl,xxx,php,ora,asl,xxx,xxx,ora,asl,xxx, | ||
226 | bpl,ora,xxx,xxx,xxx,ora,asl,xxx,clc,ora,xxx,xxx,xxx,ora,asl,xxx, | ||
227 | jsr,_and,xxx,xxx,bit,_and,rol,xxx,plp,_and,rol,xxx,bit,_and,rol,xxx, | ||
228 | bmi,_and,xxx,xxx,xxx,_and,rol,xxx,sec,_and,xxx,xxx,xxx,_and,rol,xxx, | ||
229 | rti,eor,xxx,xxx,xxx,eor,lsr,xxx,pha,eor,lsr,xxx,jmp,eor,lsr,xxx, | ||
230 | bvc,eor,xxx,xxx,xxx,eor,lsr,xxx,cli,eor,xxx,xxx,xxx,eor,lsr,xxx, | ||
231 | rts,adc,xxx,xxx,xxx,adc,ror,xxx,pla,adc,ror,xxx,jmp,adc,ror,xxx, | ||
232 | bvs,adc,xxx,xxx,xxx,adc,ror,xxx,sei,adc,xxx,xxx,xxx,adc,ror,xxx, | ||
233 | xxx,sta,xxx,xxx,sty,sta,stx,xxx,dey,xxx,txa,xxx,sty,sta,stx,xxx, | ||
234 | bcc,sta,xxx,xxx,sty,sta,stx,xxx,tya,sta,txs,xxx,xxx,sta,xxx,xxx, | ||
235 | ldy,lda,ldx,xxx,ldy,lda,ldx,xxx,tay,lda,tax,xxx,ldy,lda,ldx,xxx, | ||
236 | bcs,lda,xxx,xxx,ldy,lda,ldx,xxx,clv,lda,tsx,xxx,ldy,lda,ldx,xxx, | ||
237 | cpy,cmp,xxx,xxx,cpy,cmp,dec,xxx,iny,cmp,dex,xxx,cpy,cmp,dec,xxx, | ||
238 | bne,cmp,xxx,xxx,xxx,cmp,dec,xxx,cld,cmp,xxx,xxx,xxx,cmp,dec,xxx, | ||
239 | cpx,sbc,xxx,xxx,cpx,sbc,inc,xxx,inx,sbc,_nop,xxx,cpx,sbc,inc,xxx, | ||
240 | beq,sbc,xxx,xxx,xxx,sbc,inc,xxx,sed,sbc,xxx,xxx,xxx,sbc,inc,xxx | ||
241 | }; | ||
242 | |||
243 | |||
244 | static const int modes[256] ICONST_ATTR = { | ||
245 | imp,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx, | ||
246 | rel,indy,xxx,xxx,xxx,zpx,zpx,xxx,imp,absy,xxx,xxx,xxx,absx,absx,xxx, | ||
247 | _abs,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx, | ||
248 | rel,indy,xxx,xxx,xxx,zpx,zpx,xxx,imp,absy,xxx,xxx,xxx,absx,absx,xxx, | ||
249 | imp,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx, | ||
250 | rel,indy,xxx,xxx,xxx,zpx,zpx,xxx,imp,absy,xxx,xxx,xxx,absx,absx,xxx, | ||
251 | imp,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,ind,_abs,_abs,xxx, | ||
252 | rel,indy,xxx,xxx,xxx,zpx,zpx,xxx,imp,absy,xxx,xxx,xxx,absx,absx,xxx, | ||
253 | imm,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx, | ||
254 | rel,indy,xxx,xxx,zpx,zpx,zpy,xxx,imp,absy,acc,xxx,xxx,absx,absx,xxx, | ||
255 | imm,indx,imm,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx, | ||
256 | rel,indy,xxx,xxx,zpx,zpx,zpy,xxx,imp,absy,acc,xxx,absx,absx,absy,xxx, | ||
257 | imm,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx, | ||
258 | rel,indy,xxx,xxx,zpx,zpx,zpx,xxx,imp,absy,acc,xxx,xxx,absx,absx,xxx, | ||
259 | imm,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,_abs,_abs,_abs,xxx, | ||
260 | rel,indy,xxx,xxx,zpx,zpx,zpx,xxx,imp,absy,acc,xxx,xxx,absx,absx,xxx | ||
261 | }; | ||
262 | |||
263 | /* Routines for quick & dirty float calculation */ | ||
264 | |||
265 | static inline int quickfloat_ConvertFromInt(int i) | ||
266 | { | ||
267 | return (i<<16); | ||
268 | } | ||
269 | #ifndef ROCKBOX | ||
270 | static inline int quickfloat_ConvertFromFloat(float f) | ||
271 | { | ||
272 | return (int)(f*(1<<16)); | ||
273 | } | ||
274 | #else | ||
275 | #define quickfloat_ConvertFromFloat(X) (int)(X*(1<<16)) | ||
276 | #endif | ||
277 | static inline int quickfloat_Multiply(int a, int b) | ||
278 | { | ||
279 | return (a>>8)*(b>>8); | ||
280 | } | ||
281 | static inline int quickfloat_ConvertToInt(int i) | ||
282 | { | ||
283 | return (i>>16); | ||
284 | } | ||
285 | |||
286 | /* Get the bit from an unsigned long at a specified position */ | ||
287 | static inline unsigned char get_bit(unsigned long val, unsigned char b) | ||
288 | { | ||
289 | return (unsigned char) ((val >> b) & 1); | ||
290 | } | ||
291 | |||
292 | |||
293 | static inline int GenerateDigi(int sIn) | ||
294 | { | ||
295 | static int sample = 0; | ||
296 | |||
297 | if (!sample_active) return(sIn); | ||
298 | |||
299 | if ((sample_position < sample_end) && (sample_position >= sample_start)) | ||
300 | { | ||
301 | sIn += sample; | ||
302 | |||
303 | fracPos += 985248/sample_period; | ||
304 | |||
305 | if (fracPos > mixing_frequency) | ||
306 | { | ||
307 | fracPos%=mixing_frequency; | ||
308 | |||
309 | // N�hstes Samples holen | ||
310 | if (sample_order == 0) { | ||
311 | sample_nibble++; // Nähstes Sample-Nibble | ||
312 | if (sample_nibble==2) { | ||
313 | sample_nibble = 0; | ||
314 | sample_position++; | ||
315 | } | ||
316 | } | ||
317 | else { | ||
318 | sample_nibble--; | ||
319 | if (sample_nibble < 0) { | ||
320 | sample_nibble=1; | ||
321 | sample_position++; | ||
322 | } | ||
323 | } | ||
324 | if (sample_repeats) | ||
325 | { | ||
326 | if (sample_position > sample_end) | ||
327 | { | ||
328 | sample_repeats--; | ||
329 | sample_position = sample_repeat_start; | ||
330 | } | ||
331 | else sample_active = 0; | ||
332 | } | ||
333 | |||
334 | sample = memory[sample_position&0xffff]; | ||
335 | if (sample_nibble==1) // Hi-Nibble holen? | ||
336 | sample = (sample & 0xf0)>>4; | ||
337 | else sample = sample & 0x0f; | ||
338 | |||
339 | sample -= 7; | ||
340 | sample <<= 10; | ||
341 | } | ||
342 | } | ||
343 | |||
344 | return (sIn); | ||
345 | } | ||
346 | |||
347 | /* ------------------------------------------------------------- synthesis | ||
348 | initialize SID and frequency dependant values */ | ||
349 | void synth_init(unsigned long mixfrq) ICODE_ATTR; | ||
350 | void synth_init(unsigned long mixfrq) | ||
351 | { | ||
352 | #ifndef ROCKBOX | ||
353 | int i; | ||
354 | #endif | ||
355 | mixing_frequency = mixfrq; | ||
356 | fracPos = 0; | ||
357 | freqmul = 15872000 / mixfrq; | ||
358 | filtmul = quickfloat_ConvertFromFloat(21.5332031f)/mixfrq; | ||
359 | #ifndef ROCKBOX | ||
360 | for (i=0;i<16;i++) { | ||
361 | attacks [i]=(int) (0x1000000 / (attackTimes[i]*mixfrq)); | ||
362 | releases[i]=(int) (0x1000000 / (decayReleaseTimes[i]*mixfrq)); | ||
363 | } | ||
364 | #endif | ||
365 | memset(&sid,0,sizeof(sid)); | ||
366 | memset(osc,0,sizeof(osc)); | ||
367 | memset(&filter,0,sizeof(filter)); | ||
368 | osc[0].noiseval = 0xffffff; | ||
369 | osc[1].noiseval = 0xffffff; | ||
370 | osc[2].noiseval = 0xffffff; | ||
371 | } | ||
372 | |||
373 | /* render a buffer of n samples with the actual register contents */ | ||
374 | void synth_render (int32_t *buffer, unsigned long len) ICODE_ATTR; | ||
375 | void synth_render (int32_t *buffer, unsigned long len) | ||
376 | { | ||
377 | unsigned long bp; | ||
378 | /* step 1: convert the not easily processable sid registers into some | ||
379 | more convenient and fast values (makes the thing much faster | ||
380 | if you process more than 1 sample value at once) */ | ||
381 | unsigned char v; | ||
382 | for (v=0;v<3;v++) { | ||
383 | osc[v].pulse = (sid.v[v].pulse & 0xfff) << 16; | ||
384 | osc[v].filter = get_bit(sid.res_ftv,v); | ||
385 | osc[v].attack = attacks[sid.v[v].ad >> 4]; | ||
386 | osc[v].decay = releases[sid.v[v].ad & 0xf]; | ||
387 | osc[v].sustain = sid.v[v].sr & 0xf0; | ||
388 | osc[v].release = releases[sid.v[v].sr & 0xf]; | ||
389 | osc[v].wave = sid.v[v].wave; | ||
390 | osc[v].freq = ((unsigned long)sid.v[v].freq)*freqmul; | ||
391 | } | ||
392 | |||
393 | #ifdef USE_FILTER | ||
394 | filter.freq = (16*sid.ffreqhi + (sid.ffreqlo&0x7)) * filtmul; | ||
395 | |||
396 | if (filter.freq>quickfloat_ConvertFromInt(1)) | ||
397 | filter.freq=quickfloat_ConvertFromInt(1); | ||
398 | /* the above line isnt correct at all - the problem is that the filter | ||
399 | works only up to rmxfreq/4 - this is sufficient for 44KHz but isnt | ||
400 | for 32KHz and lower - well, but sound quality is bad enough then to | ||
401 | neglect the fact that the filter doesnt come that high ;) */ | ||
402 | filter.l_ena = get_bit(sid.ftp_vol,4); | ||
403 | filter.b_ena = get_bit(sid.ftp_vol,5); | ||
404 | filter.h_ena = get_bit(sid.ftp_vol,6); | ||
405 | filter.v3ena = !get_bit(sid.ftp_vol,7); | ||
406 | filter.vol = (sid.ftp_vol & 0xf); | ||
407 | filter.rez = quickfloat_ConvertFromFloat(1.2f) - | ||
408 | quickfloat_ConvertFromFloat(0.04f)*(sid.res_ftv >> 4); | ||
409 | |||
410 | /* We precalculate part of the quick float operation, saves time in loop later */ | ||
411 | filter.rez>>=8; | ||
412 | #endif | ||
413 | |||
414 | |||
415 | /* now render the buffer */ | ||
416 | for (bp=0;bp<len;bp++) { | ||
417 | #ifdef USE_FILTER | ||
418 | int outo=0; | ||
419 | #endif | ||
420 | int outf=0; | ||
421 | /* step 2 : generate the two output signals (for filtered and non- | ||
422 | filtered) from the osc/eg sections */ | ||
423 | for (v=0;v<3;v++) { | ||
424 | /* update wave counter */ | ||
425 | osc[v].counter = (osc[v].counter+osc[v].freq) & 0xFFFFFFF; | ||
426 | /* reset counter / noise generator if reset get_bit set */ | ||
427 | if (osc[v].wave & 0x08) { | ||
428 | osc[v].counter = 0; | ||
429 | osc[v].noisepos = 0; | ||
430 | osc[v].noiseval = 0xffffff; | ||
431 | } | ||
432 | unsigned char refosc = v?v-1:2; /* reference oscillator for sync/ring */ | ||
433 | /* sync oscillator to refosc if sync bit set */ | ||
434 | if (osc[v].wave & 0x02) | ||
435 | if (osc[refosc].counter < osc[refosc].freq) | ||
436 | osc[v].counter = osc[refosc].counter * osc[v].freq / osc[refosc].freq; | ||
437 | /* generate waveforms with really simple algorithms */ | ||
438 | unsigned char triout = (unsigned char) (osc[v].counter>>19); | ||
439 | if (osc[v].counter>>27) | ||
440 | triout^=0xff; | ||
441 | unsigned char sawout = (unsigned char) (osc[v].counter >> 20); | ||
442 | unsigned char plsout = (unsigned char) ((osc[v].counter > osc[v].pulse)-1); | ||
443 | |||
444 | /* generate noise waveform exactly as the SID does. */ | ||
445 | if (osc[v].noisepos!=(osc[v].counter>>23)) | ||
446 | { | ||
447 | osc[v].noisepos = osc[v].counter >> 23; | ||
448 | osc[v].noiseval = (osc[v].noiseval << 1) | | ||
449 | (get_bit(osc[v].noiseval,22) ^ get_bit(osc[v].noiseval,17)); | ||
450 | osc[v].noiseout = (get_bit(osc[v].noiseval,22) << 7) | | ||
451 | (get_bit(osc[v].noiseval,20) << 6) | | ||
452 | (get_bit(osc[v].noiseval,16) << 5) | | ||
453 | (get_bit(osc[v].noiseval,13) << 4) | | ||
454 | (get_bit(osc[v].noiseval,11) << 3) | | ||
455 | (get_bit(osc[v].noiseval, 7) << 2) | | ||
456 | (get_bit(osc[v].noiseval, 4) << 1) | | ||
457 | (get_bit(osc[v].noiseval, 2) << 0); | ||
458 | } | ||
459 | unsigned char nseout = osc[v].noiseout; | ||
460 | |||
461 | /* modulate triangle wave if ringmod bit set */ | ||
462 | if (osc[v].wave & 0x04) | ||
463 | if (osc[refosc].counter < 0x8000000) | ||
464 | triout ^= 0xff; | ||
465 | |||
466 | /* now mix the oscillators with an AND operation as stated in | ||
467 | the SID's reference manual - even if this is completely wrong. | ||
468 | well, at least, the $30 and $70 waveform sounds correct and there's | ||
469 | no real solution to do $50 and $60, so who cares. */ | ||
470 | |||
471 | unsigned char outv=0xFF; | ||
472 | if (osc[v].wave & 0x10) outv &= triout; | ||
473 | if (osc[v].wave & 0x20) outv &= sawout; | ||
474 | if (osc[v].wave & 0x40) outv &= plsout; | ||
475 | if (osc[v].wave & 0x80) outv &= nseout; | ||
476 | |||
477 | /* so now process the volume according to the phase and adsr values */ | ||
478 | switch (osc[v].envphase) { | ||
479 | case 0 : { /* Phase 0 : Attack */ | ||
480 | osc[v].envval+=osc[v].attack; | ||
481 | if (osc[v].envval >= 0xFFFFFF) | ||
482 | { | ||
483 | osc[v].envval = 0xFFFFFF; | ||
484 | osc[v].envphase = 1; | ||
485 | } | ||
486 | break; | ||
487 | } | ||
488 | case 1 : { /* Phase 1 : Decay */ | ||
489 | osc[v].envval-=osc[v].decay; | ||
490 | if ((signed int) osc[v].envval <= (signed int) (osc[v].sustain<<16)) | ||
491 | { | ||
492 | osc[v].envval = osc[v].sustain<<16; | ||
493 | osc[v].envphase = 2; | ||
494 | } | ||
495 | break; | ||
496 | } | ||
497 | case 2 : { /* Phase 2 : Sustain */ | ||
498 | if ((signed int) osc[v].envval != (signed int) (osc[v].sustain<<16)) | ||
499 | { | ||
500 | osc[v].envphase = 1; | ||
501 | } | ||
502 | /* :) yes, thats exactly how the SID works. and maybe | ||
503 | a music routine out there supports this, so better | ||
504 | let it in, thanks :) */ | ||
505 | break; | ||
506 | } | ||
507 | case 3 : { /* Phase 3 : Release */ | ||
508 | osc[v].envval-=osc[v].release; | ||
509 | if (osc[v].envval < 0x40000) osc[v].envval= 0x40000; | ||
510 | |||
511 | /* the volume offset is because the SID does not | ||
512 | completely silence the voices when it should. most | ||
513 | emulators do so though and thats the main reason | ||
514 | why the sound of emulators is too, err... emulated :) */ | ||
515 | break; | ||
516 | } | ||
517 | } | ||
518 | |||
519 | #ifdef USE_FILTER | ||
520 | |||
521 | /* now route the voice output to either the non-filtered or the | ||
522 | filtered channel and dont forget to blank out osc3 if desired */ | ||
523 | |||
524 | if (v<2 || filter.v3ena) | ||
525 | { | ||
526 | if (osc[v].filter) | ||
527 | outf+=(((int)(outv-0x80))*osc[v].envval)>>22; | ||
528 | else | ||
529 | outo+=(((int)(outv-0x80))*osc[v].envval)>>22; | ||
530 | } | ||
531 | #endif | ||
532 | #ifndef USE_FILTER | ||
533 | /* Don't use filters, just mix all voices together */ | ||
534 | outf+=((signed short)(outv-0x80)) * (osc[v].envval>>4); | ||
535 | #endif | ||
536 | } | ||
537 | |||
538 | |||
539 | #ifdef USE_FILTER | ||
540 | /* step 3 | ||
541 | * so, now theres finally time to apply the multi-mode resonant filter | ||
542 | * to the signal. The easiest thing ist just modelling a real electronic | ||
543 | * filter circuit instead of fiddling around with complex IIRs or even | ||
544 | * FIRs ... | ||
545 | * it sounds as good as them or maybe better and needs only 3 MULs and | ||
546 | * 4 ADDs for EVERYTHING. SIDPlay uses this kind of filter, too, but | ||
547 | * Mage messed the whole thing completely up - as the rest of the | ||
548 | * emulator. | ||
549 | * This filter sounds a lot like the 8580, as the low-quality, dirty | ||
550 | * sound of the 6581 is uuh too hard to achieve :) */ | ||
551 | |||
552 | filter.h = quickfloat_ConvertFromInt(outf) - (filter.b>>8)*filter.rez - filter.l; | ||
553 | filter.b += quickfloat_Multiply(filter.freq, filter.h); | ||
554 | filter.l += quickfloat_Multiply(filter.freq, filter.b); | ||
555 | |||
556 | outf = 0; | ||
557 | |||
558 | if (filter.l_ena) outf+=quickfloat_ConvertToInt(filter.l); | ||
559 | if (filter.b_ena) outf+=quickfloat_ConvertToInt(filter.b); | ||
560 | if (filter.h_ena) outf+=quickfloat_ConvertToInt(filter.h); | ||
561 | |||
562 | int final_sample = (filter.vol*(outo+outf)); | ||
563 | *(buffer+bp)= GenerateDigi(final_sample)<<13; | ||
564 | #endif | ||
565 | #ifndef USE_FILTER | ||
566 | *(buffer+bp) = GenerateDigi(outf)<<3; | ||
567 | #endif | ||
568 | } | ||
569 | } | ||
570 | |||
571 | |||
572 | |||
573 | /* | ||
574 | * C64 Mem Routines | ||
575 | */ | ||
576 | static inline unsigned char getmem(unsigned short addr) | ||
577 | { | ||
578 | return memory[addr]; | ||
579 | } | ||
580 | |||
581 | static inline void setmem(unsigned short addr, unsigned char value) | ||
582 | { | ||
583 | if ((addr&0xfc00)==0xd400) | ||
584 | { | ||
585 | sidPoke(addr&0x1f,value); | ||
586 | /* New SID-Register */ | ||
587 | if (addr > 0xd418) | ||
588 | { | ||
589 | switch (addr) | ||
590 | { | ||
591 | case 0xd41f: /* Start-Hi */ | ||
592 | internal_start = (internal_start&0x00ff) | (value<<8); break; | ||
593 | case 0xd41e: /* Start-Lo */ | ||
594 | internal_start = (internal_start&0xff00) | (value); break; | ||
595 | case 0xd47f: /* Repeat-Hi */ | ||
596 | internal_repeat_start = (internal_repeat_start&0x00ff) | (value<<8); break; | ||
597 | case 0xd47e: /* Repeat-Lo */ | ||
598 | internal_repeat_start = (internal_repeat_start&0xff00) | (value); break; | ||
599 | case 0xd43e: /* End-Hi */ | ||
600 | internal_end = (internal_end&0x00ff) | (value<<8); break; | ||
601 | case 0xd43d: /* End-Lo */ | ||
602 | internal_end = (internal_end&0xff00) | (value); break; | ||
603 | case 0xd43f: /* Loop-Size */ | ||
604 | internal_repeat_times = value; break; | ||
605 | case 0xd45e: /* Period-Hi */ | ||
606 | internal_period = (internal_period&0x00ff) | (value<<8); break; | ||
607 | case 0xd45d: /* Period-Lo */ | ||
608 | internal_period = (internal_period&0xff00) | (value); break; | ||
609 | case 0xd47d: /* Sample Order */ | ||
610 | internal_order = value; break; | ||
611 | case 0xd45f: /* Sample Add */ | ||
612 | internal_add = value; break; | ||
613 | case 0xd41d: /* Start sampling */ | ||
614 | sample_repeats = internal_repeat_times; | ||
615 | sample_position = internal_start; | ||
616 | sample_start = internal_start; | ||
617 | sample_end = internal_end; | ||
618 | sample_repeat_start = internal_repeat_start; | ||
619 | sample_period = internal_period; | ||
620 | sample_order = internal_order; | ||
621 | switch (value) | ||
622 | { | ||
623 | case 0xfd: sample_active = 0; break; | ||
624 | case 0xfe: | ||
625 | case 0xff: sample_active = 1; break; | ||
626 | default: return; | ||
627 | } | ||
628 | break; | ||
629 | } | ||
630 | } | ||
631 | } | ||
632 | else memory[addr]=value; | ||
633 | } | ||
634 | |||
635 | /* | ||
636 | * Poke a value into the sid register | ||
637 | */ | ||
638 | void sidPoke(int reg, unsigned char val) ICODE_ATTR; | ||
639 | void sidPoke(int reg, unsigned char val) | ||
640 | { | ||
641 | int voice=0; | ||
642 | |||
643 | if ((reg >= 7) && (reg <=13)) {voice=1; reg-=7;} | ||
644 | else if ((reg >= 14) && (reg <=20)) {voice=2; reg-=14;} | ||
645 | |||
646 | switch (reg) { | ||
647 | case 0: { /* Set frequency: Low byte */ | ||
648 | sid.v[voice].freq = (sid.v[voice].freq&0xff00)+val; | ||
649 | break; | ||
650 | } | ||
651 | case 1: { /* Set frequency: High byte */ | ||
652 | sid.v[voice].freq = (sid.v[voice].freq&0xff)+(val<<8); | ||
653 | break; | ||
654 | } | ||
655 | case 2: { /* Set pulse width: Low byte */ | ||
656 | sid.v[voice].pulse = (sid.v[voice].pulse&0xff00)+val; | ||
657 | break; | ||
658 | } | ||
659 | case 3: { /* Set pulse width: High byte */ | ||
660 | sid.v[voice].pulse = (sid.v[voice].pulse&0xff)+(val<<8); | ||
661 | break; | ||
662 | } | ||
663 | case 4: { sid.v[voice].wave = val; | ||
664 | /* Directly look at GATE-Bit! | ||
665 | * a change may happen twice or more often during one cpujsr | ||
666 | * Put the Envelope Generator into attack or release phase if desired | ||
667 | */ | ||
668 | if ((val & 0x01) == 0) osc[voice].envphase=3; | ||
669 | else if (osc[voice].envphase==3) osc[voice].envphase=0; | ||
670 | break; | ||
671 | } | ||
672 | |||
673 | case 5: { sid.v[voice].ad = val; break;} | ||
674 | case 6: { sid.v[voice].sr = val; break;} | ||
675 | |||
676 | case 21: { sid.ffreqlo = val; break; } | ||
677 | case 22: { sid.ffreqhi = val; break; } | ||
678 | case 23: { sid.res_ftv = val; break; } | ||
679 | case 24: { sid.ftp_vol = val; break;} | ||
680 | } | ||
681 | return; | ||
682 | } | ||
683 | |||
684 | static inline unsigned char getaddr(int mode) | ||
685 | { | ||
686 | unsigned short ad,ad2; | ||
687 | switch(mode) | ||
688 | { | ||
689 | case imp: | ||
690 | return 0; | ||
691 | case imm: | ||
692 | return getmem(pc++); | ||
693 | case _abs: | ||
694 | ad=getmem(pc++); | ||
695 | ad|=256*getmem(pc++); | ||
696 | return getmem(ad); | ||
697 | case absx: | ||
698 | ad=getmem(pc++); | ||
699 | ad|=256*getmem(pc++); | ||
700 | ad2=ad+x; | ||
701 | return getmem(ad2); | ||
702 | case absy: | ||
703 | ad=getmem(pc++); | ||
704 | ad|=256*getmem(pc++); | ||
705 | ad2=ad+y; | ||
706 | return getmem(ad2); | ||
707 | case zp: | ||
708 | ad=getmem(pc++); | ||
709 | return getmem(ad); | ||
710 | case zpx: | ||
711 | ad=getmem(pc++); | ||
712 | ad+=x; | ||
713 | return getmem(ad&0xff); | ||
714 | case zpy: | ||
715 | ad=getmem(pc++); | ||
716 | ad+=y; | ||
717 | return getmem(ad&0xff); | ||
718 | case indx: | ||
719 | ad=getmem(pc++); | ||
720 | ad+=x; | ||
721 | ad2=getmem(ad&0xff); | ||
722 | ad++; | ||
723 | ad2|=getmem(ad&0xff)<<8; | ||
724 | return getmem(ad2); | ||
725 | case indy: | ||
726 | ad=getmem(pc++); | ||
727 | ad2=getmem(ad); | ||
728 | ad2|=getmem((ad+1)&0xff)<<8; | ||
729 | ad=ad2+y; | ||
730 | return getmem(ad); | ||
731 | case acc: | ||
732 | return a; | ||
733 | } | ||
734 | return 0; | ||
735 | } | ||
736 | |||
737 | static inline void setaddr(int mode, unsigned char val) | ||
738 | { | ||
739 | unsigned short ad,ad2; | ||
740 | switch(mode) | ||
741 | { | ||
742 | case _abs: | ||
743 | ad=getmem(pc-2); | ||
744 | ad|=256*getmem(pc-1); | ||
745 | setmem(ad,val); | ||
746 | return; | ||
747 | case absx: | ||
748 | ad=getmem(pc-2); | ||
749 | ad|=256*getmem(pc-1); | ||
750 | ad2=ad+x; | ||
751 | setmem(ad2,val); | ||
752 | return; | ||
753 | case zp: | ||
754 | ad=getmem(pc-1); | ||
755 | setmem(ad,val); | ||
756 | return; | ||
757 | case zpx: | ||
758 | ad=getmem(pc-1); | ||
759 | ad+=x; | ||
760 | setmem(ad&0xff,val); | ||
761 | return; | ||
762 | case acc: | ||
763 | a=val; | ||
764 | return; | ||
765 | } | ||
766 | } | ||
767 | |||
768 | |||
769 | static inline void putaddr(int mode, unsigned char val) | ||
770 | { | ||
771 | unsigned short ad,ad2; | ||
772 | switch(mode) | ||
773 | { | ||
774 | case _abs: | ||
775 | ad=getmem(pc++); | ||
776 | ad|=getmem(pc++)<<8; | ||
777 | setmem(ad,val); | ||
778 | return; | ||
779 | case absx: | ||
780 | ad=getmem(pc++); | ||
781 | ad|=getmem(pc++)<<8; | ||
782 | ad2=ad+x; | ||
783 | setmem(ad2,val); | ||
784 | return; | ||
785 | case absy: | ||
786 | ad=getmem(pc++); | ||
787 | ad|=getmem(pc++)<<8; | ||
788 | ad2=ad+y; | ||
789 | setmem(ad2,val); | ||
790 | return; | ||
791 | case zp: | ||
792 | ad=getmem(pc++); | ||
793 | setmem(ad,val); | ||
794 | return; | ||
795 | case zpx: | ||
796 | ad=getmem(pc++); | ||
797 | ad+=x; | ||
798 | setmem(ad&0xff,val); | ||
799 | return; | ||
800 | case zpy: | ||
801 | ad=getmem(pc++); | ||
802 | ad+=y; | ||
803 | setmem(ad&0xff,val); | ||
804 | return; | ||
805 | case indx: | ||
806 | ad=getmem(pc++); | ||
807 | ad+=x; | ||
808 | ad2=getmem(ad&0xff); | ||
809 | ad++; | ||
810 | ad2|=getmem(ad&0xff)<<8; | ||
811 | setmem(ad2,val); | ||
812 | return; | ||
813 | case indy: | ||
814 | ad=getmem(pc++); | ||
815 | ad2=getmem(ad); | ||
816 | ad2|=getmem((ad+1)&0xff)<<8; | ||
817 | ad=ad2+y; | ||
818 | setmem(ad,val); | ||
819 | return; | ||
820 | case acc: | ||
821 | a=val; | ||
822 | return; | ||
823 | } | ||
824 | } | ||
825 | |||
826 | |||
827 | static inline void setflags(int flag, int cond) | ||
828 | { | ||
829 | if (cond) p|=flag; | ||
830 | else p&=~flag; | ||
831 | } | ||
832 | |||
833 | |||
834 | static inline void push(unsigned char val) | ||
835 | { | ||
836 | setmem(0x100+s,val); | ||
837 | if (s) s--; | ||
838 | } | ||
839 | |||
840 | static inline unsigned char pop(void) | ||
841 | { | ||
842 | if (s<0xff) s++; | ||
843 | return getmem(0x100+s); | ||
844 | } | ||
845 | |||
846 | static inline void branch(int flag) | ||
847 | { | ||
848 | signed char dist; | ||
849 | dist=(signed char)getaddr(imm); | ||
850 | wval=pc+dist; | ||
851 | if (flag) pc=wval; | ||
852 | } | ||
853 | |||
854 | void cpuReset(void) ICODE_ATTR; | ||
855 | void cpuReset(void) | ||
856 | { | ||
857 | a=x=y=0; | ||
858 | p=0; | ||
859 | s=255; | ||
860 | pc=getaddr(0xfffc); | ||
861 | } | ||
862 | |||
863 | void cpuResetTo(unsigned short npc, unsigned char na) ICODE_ATTR; | ||
864 | void cpuResetTo(unsigned short npc, unsigned char na) | ||
865 | { | ||
866 | a=na; | ||
867 | x=0; | ||
868 | y=0; | ||
869 | p=0; | ||
870 | s=255; | ||
871 | pc=npc; | ||
872 | } | ||
873 | |||
874 | static inline void cpuParse(void) | ||
875 | { | ||
876 | unsigned char opc=getmem(pc++); | ||
877 | int cmd=opcodes[opc]; | ||
878 | int addr=modes[opc]; | ||
879 | int c; | ||
880 | switch (cmd) | ||
881 | { | ||
882 | case adc: | ||
883 | wval=(unsigned short)a+getaddr(addr)+((p&FLAG_C)?1:0); | ||
884 | setflags(FLAG_C, wval&0x100); | ||
885 | a=(unsigned char)wval; | ||
886 | setflags(FLAG_Z, !a); | ||
887 | setflags(FLAG_N, a&0x80); | ||
888 | setflags(FLAG_V, (!!(p&FLAG_C)) ^ (!!(p&FLAG_N))); | ||
889 | break; | ||
890 | case _and: | ||
891 | bval=getaddr(addr); | ||
892 | a&=bval; | ||
893 | setflags(FLAG_Z, !a); | ||
894 | setflags(FLAG_N, a&0x80); | ||
895 | break; | ||
896 | case asl: | ||
897 | wval=getaddr(addr); | ||
898 | wval<<=1; | ||
899 | setaddr(addr,(unsigned char)wval); | ||
900 | setflags(FLAG_Z,!wval); | ||
901 | setflags(FLAG_N,wval&0x80); | ||
902 | setflags(FLAG_C,wval&0x100); | ||
903 | break; | ||
904 | case bcc: | ||
905 | branch(!(p&FLAG_C)); | ||
906 | break; | ||
907 | case bcs: | ||
908 | branch(p&FLAG_C); | ||
909 | break; | ||
910 | case bne: | ||
911 | branch(!(p&FLAG_Z)); | ||
912 | break; | ||
913 | case beq: | ||
914 | branch(p&FLAG_Z); | ||
915 | break; | ||
916 | case bpl: | ||
917 | branch(!(p&FLAG_N)); | ||
918 | break; | ||
919 | case bmi: | ||
920 | branch(p&FLAG_N); | ||
921 | break; | ||
922 | case bvc: | ||
923 | branch(!(p&FLAG_V)); | ||
924 | break; | ||
925 | case bvs: | ||
926 | branch(p&FLAG_V); | ||
927 | break; | ||
928 | case bit: | ||
929 | bval=getaddr(addr); | ||
930 | setflags(FLAG_Z,!(a&bval)); | ||
931 | setflags(FLAG_N,bval&0x80); | ||
932 | setflags(FLAG_V,bval&0x40); | ||
933 | break; | ||
934 | case _brk: | ||
935 | pc=0; /* Just quit the emulation */ | ||
936 | break; | ||
937 | case clc: | ||
938 | setflags(FLAG_C,0); | ||
939 | break; | ||
940 | case cld: | ||
941 | setflags(FLAG_D,0); | ||
942 | break; | ||
943 | case cli: | ||
944 | setflags(FLAG_I,0); | ||
945 | break; | ||
946 | case clv: | ||
947 | setflags(FLAG_V,0); | ||
948 | break; | ||
949 | case cmp: | ||
950 | bval=getaddr(addr); | ||
951 | wval=(unsigned short)a-bval; | ||
952 | setflags(FLAG_Z,!wval); | ||
953 | setflags(FLAG_N,wval&0x80); | ||
954 | setflags(FLAG_C,a>=bval); | ||
955 | break; | ||
956 | case cpx: | ||
957 | bval=getaddr(addr); | ||
958 | wval=(unsigned short)x-bval; | ||
959 | setflags(FLAG_Z,!wval); | ||
960 | setflags(FLAG_N,wval&0x80); | ||
961 | setflags(FLAG_C,x>=bval); | ||
962 | break; | ||
963 | case cpy: | ||
964 | bval=getaddr(addr); | ||
965 | wval=(unsigned short)y-bval; | ||
966 | setflags(FLAG_Z,!wval); | ||
967 | setflags(FLAG_N,wval&0x80); | ||
968 | setflags(FLAG_C,y>=bval); | ||
969 | break; | ||
970 | case dec: | ||
971 | bval=getaddr(addr); | ||
972 | bval--; | ||
973 | setaddr(addr,bval); | ||
974 | setflags(FLAG_Z,!bval); | ||
975 | setflags(FLAG_N,bval&0x80); | ||
976 | break; | ||
977 | case dex: | ||
978 | x--; | ||
979 | setflags(FLAG_Z,!x); | ||
980 | setflags(FLAG_N,x&0x80); | ||
981 | break; | ||
982 | case dey: | ||
983 | y--; | ||
984 | setflags(FLAG_Z,!y); | ||
985 | setflags(FLAG_N,y&0x80); | ||
986 | break; | ||
987 | case eor: | ||
988 | bval=getaddr(addr); | ||
989 | a^=bval; | ||
990 | setflags(FLAG_Z,!a); | ||
991 | setflags(FLAG_N,a&0x80); | ||
992 | break; | ||
993 | case inc: | ||
994 | bval=getaddr(addr); | ||
995 | bval++; | ||
996 | setaddr(addr,bval); | ||
997 | setflags(FLAG_Z,!bval); | ||
998 | setflags(FLAG_N,bval&0x80); | ||
999 | break; | ||
1000 | case inx: | ||
1001 | x++; | ||
1002 | setflags(FLAG_Z,!x); | ||
1003 | setflags(FLAG_N,x&0x80); | ||
1004 | break; | ||
1005 | case iny: | ||
1006 | y++; | ||
1007 | setflags(FLAG_Z,!y); | ||
1008 | setflags(FLAG_N,y&0x80); | ||
1009 | break; | ||
1010 | case jmp: | ||
1011 | wval=getmem(pc++); | ||
1012 | wval|=256*getmem(pc++); | ||
1013 | switch (addr) | ||
1014 | { | ||
1015 | case _abs: | ||
1016 | pc=wval; | ||
1017 | break; | ||
1018 | case ind: | ||
1019 | pc=getmem(wval); | ||
1020 | pc|=256*getmem(wval+1); | ||
1021 | break; | ||
1022 | } | ||
1023 | break; | ||
1024 | case jsr: | ||
1025 | push((pc+1)>>8); | ||
1026 | push((pc+1)); | ||
1027 | wval=getmem(pc++); | ||
1028 | wval|=256*getmem(pc++); | ||
1029 | pc=wval; | ||
1030 | break; | ||
1031 | case lda: | ||
1032 | a=getaddr(addr); | ||
1033 | setflags(FLAG_Z,!a); | ||
1034 | setflags(FLAG_N,a&0x80); | ||
1035 | break; | ||
1036 | case ldx: | ||
1037 | x=getaddr(addr); | ||
1038 | setflags(FLAG_Z,!x); | ||
1039 | setflags(FLAG_N,x&0x80); | ||
1040 | break; | ||
1041 | case ldy: | ||
1042 | y=getaddr(addr); | ||
1043 | setflags(FLAG_Z,!y); | ||
1044 | setflags(FLAG_N,y&0x80); | ||
1045 | break; | ||
1046 | case lsr: | ||
1047 | bval=getaddr(addr); wval=(unsigned char)bval; | ||
1048 | wval>>=1; | ||
1049 | setaddr(addr,(unsigned char)wval); | ||
1050 | setflags(FLAG_Z,!wval); | ||
1051 | setflags(FLAG_N,wval&0x80); | ||
1052 | setflags(FLAG_C,bval&1); | ||
1053 | break; | ||
1054 | case _nop: | ||
1055 | break; | ||
1056 | case ora: | ||
1057 | bval=getaddr(addr); | ||
1058 | a|=bval; | ||
1059 | setflags(FLAG_Z,!a); | ||
1060 | setflags(FLAG_N,a&0x80); | ||
1061 | break; | ||
1062 | case pha: | ||
1063 | push(a); | ||
1064 | break; | ||
1065 | case php: | ||
1066 | push(p); | ||
1067 | break; | ||
1068 | case pla: | ||
1069 | a=pop(); | ||
1070 | setflags(FLAG_Z,!a); | ||
1071 | setflags(FLAG_N,a&0x80); | ||
1072 | break; | ||
1073 | case plp: | ||
1074 | p=pop(); | ||
1075 | break; | ||
1076 | case rol: | ||
1077 | bval=getaddr(addr); | ||
1078 | c=!!(p&FLAG_C); | ||
1079 | setflags(FLAG_C,bval&0x80); | ||
1080 | bval<<=1; | ||
1081 | bval|=c; | ||
1082 | setaddr(addr,bval); | ||
1083 | setflags(FLAG_N,bval&0x80); | ||
1084 | setflags(FLAG_Z,!bval); | ||
1085 | break; | ||
1086 | case ror: | ||
1087 | bval=getaddr(addr); | ||
1088 | c=!!(p&FLAG_C); | ||
1089 | setflags(FLAG_C,bval&1); | ||
1090 | bval>>=1; | ||
1091 | bval|=128*c; | ||
1092 | setaddr(addr,bval); | ||
1093 | setflags(FLAG_N,bval&0x80); | ||
1094 | setflags(FLAG_Z,!bval); | ||
1095 | break; | ||
1096 | case rti: | ||
1097 | /* Treat RTI like RTS */ | ||
1098 | case rts: | ||
1099 | wval=pop(); | ||
1100 | wval|=pop()<<8; | ||
1101 | pc=wval+1; | ||
1102 | break; | ||
1103 | case sbc: | ||
1104 | bval=getaddr(addr)^0xff; | ||
1105 | wval=(unsigned short)a+bval+((p&FLAG_C)?1:0); | ||
1106 | setflags(FLAG_C, wval&0x100); | ||
1107 | a=(unsigned char)wval; | ||
1108 | setflags(FLAG_Z, !a); | ||
1109 | setflags(FLAG_N, a>127); | ||
1110 | setflags(FLAG_V, (!!(p&FLAG_C)) ^ (!!(p&FLAG_N))); | ||
1111 | break; | ||
1112 | case sec: | ||
1113 | setflags(FLAG_C,1); | ||
1114 | break; | ||
1115 | case sed: | ||
1116 | setflags(FLAG_D,1); | ||
1117 | break; | ||
1118 | case sei: | ||
1119 | setflags(FLAG_I,1); | ||
1120 | break; | ||
1121 | case sta: | ||
1122 | putaddr(addr,a); | ||
1123 | break; | ||
1124 | case stx: | ||
1125 | putaddr(addr,x); | ||
1126 | break; | ||
1127 | case sty: | ||
1128 | putaddr(addr,y); | ||
1129 | break; | ||
1130 | case tax: | ||
1131 | x=a; | ||
1132 | setflags(FLAG_Z, !x); | ||
1133 | setflags(FLAG_N, x&0x80); | ||
1134 | break; | ||
1135 | case tay: | ||
1136 | y=a; | ||
1137 | setflags(FLAG_Z, !y); | ||
1138 | setflags(FLAG_N, y&0x80); | ||
1139 | break; | ||
1140 | case tsx: | ||
1141 | x=s; | ||
1142 | setflags(FLAG_Z, !x); | ||
1143 | setflags(FLAG_N, x&0x80); | ||
1144 | break; | ||
1145 | case txa: | ||
1146 | a=x; | ||
1147 | setflags(FLAG_Z, !a); | ||
1148 | setflags(FLAG_N, a&0x80); | ||
1149 | break; | ||
1150 | case txs: | ||
1151 | s=x; | ||
1152 | break; | ||
1153 | case tya: | ||
1154 | a=y; | ||
1155 | setflags(FLAG_Z, !a); | ||
1156 | setflags(FLAG_N, a&0x80); | ||
1157 | break; | ||
1158 | } | ||
1159 | } | ||
1160 | |||
1161 | void cpuJSR(unsigned short npc, unsigned char na) ICODE_ATTR; | ||
1162 | void cpuJSR(unsigned short npc, unsigned char na) | ||
1163 | { | ||
1164 | a=na; | ||
1165 | x=0; | ||
1166 | y=0; | ||
1167 | p=0; | ||
1168 | s=255; | ||
1169 | pc=npc; | ||
1170 | push(0); | ||
1171 | push(0); | ||
1172 | |||
1173 | while (pc > 1) | ||
1174 | cpuParse(); | ||
1175 | |||
1176 | } | ||
1177 | |||
1178 | void c64Init(int nSampleRate) ICODE_ATTR; | ||
1179 | void c64Init(int nSampleRate) | ||
1180 | { | ||
1181 | synth_init(nSampleRate); | ||
1182 | memset(memory, 0, sizeof(memory)); | ||
1183 | |||
1184 | cpuReset(); | ||
1185 | } | ||
1186 | |||
1187 | |||
1188 | |||
1189 | unsigned short LoadSIDFromMemory(void *pSidData, unsigned short *load_addr, | ||
1190 | unsigned short *init_addr, unsigned short *play_addr, unsigned char *subsongs, unsigned char *startsong, unsigned char *speed, unsigned short size) ICODE_ATTR; | ||
1191 | unsigned short LoadSIDFromMemory(void *pSidData, unsigned short *load_addr, | ||
1192 | unsigned short *init_addr, unsigned short *play_addr, unsigned char *subsongs, unsigned char *startsong, unsigned char *speed, unsigned short size) | ||
1193 | { | ||
1194 | unsigned char *pData; | ||
1195 | unsigned char data_file_offset; | ||
1196 | |||
1197 | pData = (unsigned char*)pSidData; | ||
1198 | data_file_offset = pData[7]; | ||
1199 | |||
1200 | *load_addr = pData[8]<<8; | ||
1201 | *load_addr|= pData[9]; | ||
1202 | |||
1203 | *init_addr = pData[10]<<8; | ||
1204 | *init_addr|= pData[11]; | ||
1205 | |||
1206 | *play_addr = pData[12]<<8; | ||
1207 | *play_addr|= pData[13]; | ||
1208 | |||
1209 | *subsongs = pData[0xf]-1; | ||
1210 | *startsong = pData[0x11]-1; | ||
1211 | |||
1212 | *load_addr = pData[data_file_offset]; | ||
1213 | *load_addr|= pData[data_file_offset+1]<<8; | ||
1214 | |||
1215 | *speed = pData[0x15]; | ||
1216 | |||
1217 | memset(memory, 0, sizeof(memory)); | ||
1218 | memcpy(&memory[*load_addr], &pData[data_file_offset+2], size-(data_file_offset+2)); | ||
1219 | |||
1220 | if (*play_addr == 0) | ||
1221 | { | ||
1222 | cpuJSR(*init_addr, 0); | ||
1223 | *play_addr = (memory[0x0315]<<8)+memory[0x0314]; | ||
1224 | } | ||
1225 | |||
1226 | return *load_addr; | ||
1227 | } | ||
1228 | |||
1229 | static int nSamplesRendered = 0; | ||
1230 | static int nSamplesPerCall = 882; /* This is PAL SID single speed (44100/50Hz) */ | ||
1231 | static int nSamplesToRender = 0; | ||
1232 | |||
1233 | /* this is the codec entry point */ | ||
1234 | enum codec_status codec_main(enum codec_entry_call_reason reason) | ||
1235 | { | ||
1236 | if (reason == CODEC_LOAD) { | ||
1237 | /* Make use of 44.1khz */ | ||
1238 | ci->configure(DSP_SWITCH_FREQUENCY, SAMPLE_RATE); | ||
1239 | /* Sample depth is 28 bit host endian */ | ||
1240 | ci->configure(DSP_SET_SAMPLE_DEPTH, 28); | ||
1241 | /* Mono output */ | ||
1242 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | ||
1243 | } | ||
1244 | |||
1245 | return CODEC_OK; | ||
1246 | } | ||
1247 | |||
1248 | /* this is called for each file to process */ | ||
1249 | enum codec_status codec_run(void) | ||
1250 | { | ||
1251 | size_t filesize; | ||
1252 | unsigned short load_addr, init_addr, play_addr; | ||
1253 | unsigned char subSongsMax, subSong, song_speed; | ||
1254 | unsigned char *sidfile = NULL; | ||
1255 | intptr_t param; | ||
1256 | |||
1257 | if (codec_init()) { | ||
1258 | return CODEC_ERROR; | ||
1259 | } | ||
1260 | |||
1261 | codec_set_replaygain(ci->id3); | ||
1262 | |||
1263 | /* Load SID file the read_filebuf callback will return the full requested | ||
1264 | * size if at all possible, so there is no need to loop */ | ||
1265 | ci->seek_buffer(0); | ||
1266 | sidfile = ci->request_buffer(&filesize, SID_BUFFER_SIZE); | ||
1267 | |||
1268 | if (filesize == 0) { | ||
1269 | return CODEC_ERROR; | ||
1270 | } | ||
1271 | |||
1272 | c64Init(SAMPLE_RATE); | ||
1273 | LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr, | ||
1274 | &subSongsMax, &subSong, &song_speed, (unsigned short)filesize); | ||
1275 | sidPoke(24, 15); /* Turn on full volume */ | ||
1276 | cpuJSR(init_addr, subSong); /* Start the song initialize */ | ||
1277 | |||
1278 | |||
1279 | /* Set the elapsed time to the current subsong (in seconds) */ | ||
1280 | ci->set_elapsed(subSong*1000); | ||
1281 | |||
1282 | /* The main decoder loop */ | ||
1283 | while (1) { | ||
1284 | enum codec_command_action action = ci->get_command(¶m); | ||
1285 | |||
1286 | if (action == CODEC_ACTION_HALT) | ||
1287 | break; | ||
1288 | |||
1289 | if (action == CODEC_ACTION_SEEK_TIME) { | ||
1290 | /* New time is ready in param */ | ||
1291 | |||
1292 | /* Start playing from scratch */ | ||
1293 | c64Init(SAMPLE_RATE); | ||
1294 | LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr, | ||
1295 | &subSongsMax, &subSong, &song_speed, (unsigned short)filesize); | ||
1296 | sidPoke(24, 15); /* Turn on full volume */ | ||
1297 | subSong = param / 1000; /* Now use the current seek time in seconds as subsong */ | ||
1298 | cpuJSR(init_addr, subSong); /* Start the song initialize */ | ||
1299 | nSamplesToRender = 0; /* Start the rendering from scratch */ | ||
1300 | |||
1301 | /* Set the elapsed time to the current subsong (in seconds) */ | ||
1302 | ci->set_elapsed(subSong*1000); | ||
1303 | ci->seek_complete(); | ||
1304 | } | ||
1305 | |||
1306 | nSamplesRendered = 0; | ||
1307 | while (nSamplesRendered < CHUNK_SIZE) | ||
1308 | { | ||
1309 | if (nSamplesToRender == 0) | ||
1310 | { | ||
1311 | cpuJSR(play_addr, 0); | ||
1312 | |||
1313 | /* Find out if cia timing is used and how many samples | ||
1314 | have to be calculated for each cpujsr */ | ||
1315 | int nRefreshCIA = (int)(20000*(memory[0xdc04]|(memory[0xdc05]<<8))/0x4c00); | ||
1316 | if ((nRefreshCIA==0) || (song_speed == 0)) | ||
1317 | nRefreshCIA = 20000; | ||
1318 | nSamplesPerCall = mixing_frequency*nRefreshCIA/1000000; | ||
1319 | |||
1320 | nSamplesToRender = nSamplesPerCall; | ||
1321 | } | ||
1322 | if (nSamplesRendered + nSamplesToRender > CHUNK_SIZE) | ||
1323 | { | ||
1324 | synth_render(samples+nSamplesRendered, CHUNK_SIZE-nSamplesRendered); | ||
1325 | nSamplesToRender -= CHUNK_SIZE-nSamplesRendered; | ||
1326 | nSamplesRendered = CHUNK_SIZE; | ||
1327 | } | ||
1328 | else | ||
1329 | { | ||
1330 | synth_render(samples+nSamplesRendered, nSamplesToRender); | ||
1331 | nSamplesRendered += nSamplesToRender; | ||
1332 | nSamplesToRender = 0; | ||
1333 | } | ||
1334 | } | ||
1335 | |||
1336 | ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE); | ||
1337 | } | ||
1338 | |||
1339 | return CODEC_OK; | ||
1340 | } | ||