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