egra: new "hotlabel" widget; removed built-in title from single-line editor
[iv.d.git] / tinysid / sidengine.d
blob643e376ee04fe242b64b406138783943d1b277d4
1 //////////////////////////////////////////////////////////////////////
2 // The c64-emulation and the synth-renderer was written by Tammo Hinrichs (kb)
3 //
4 // Combining of the emulation and the renderer as well as improvements and bugfixes
5 // by Rainer Sinsch (sinsch@stud.uni-frankfurt.de)
6 //
7 // D Translation by Ketmar // Invisible Vector
8 //
9 // GNU GPLv3
10 module sidengine /*is aliced*/;
11 private:
13 import iv.alice;
14 import iv.vfs;
16 version = SidEngineUseFilter;
18 enum FLAG_N = 128;
19 enum FLAG_V = 64;
20 enum FLAG_B = 16;
21 enum FLAG_D = 8;
22 enum FLAG_I = 4;
23 enum FLAG_Z = 2;
24 enum FLAG_C = 1;
26 int pfloat_ConvertFromInt (int i) pure nothrow @safe @nogc { pragma(inline, true); return i<<16; }
27 int pfloat_ConvertFromFloat(float f) pure nothrow @safe @nogc { pragma(inline, true); return cast(int)(f*(1<<16)); }
28 int pfloat_Multiply (int a, int b) pure nothrow @safe @nogc { pragma(inline, true); return (a>>8)*(b>>8); }
29 int pfloat_ConvertToInt (int i) pure nothrow @safe @nogc { pragma(inline, true); return i>>16; }
31 enum {
32 adc, and, asl, bcc, bcs, beq, bit, bmi, bne, bpl, brk, bvc, bvs, clc,
33 cld, cli, clv, cmp, cpx, cpy, dec, dex, dey, eor, inc, inx, iny, jmp,
34 jsr, lda, ldx, ldy, lsr, nop, ora, pha, php, pla, plp, rol, ror, rti,
35 rts, sbc, sec, sed, sei, sta, stx, sty, tax, tay, tsx, txa, txs, tya,
36 xxx,
40 // SID register definition
41 struct s6581 {
42 struct sidvoice {
43 ushort freq;
44 ushort pulse;
45 ubyte wave;
46 ubyte ad;
47 ubyte sr;
49 sidvoice[3] v;
50 ubyte ffreqlo;
51 ubyte ffreqhi;
52 ubyte res_ftv;
53 ubyte ftp_vol;
57 // internal oscillator def
58 struct sidosc {
59 uint freq;
60 uint pulse;
61 ubyte wave;
62 ubyte filter;
63 uint attack;
64 uint decay;
65 uint sustain;
66 uint release;
67 uint counter; // Variablen
68 int envval;
69 ubyte envphase;
70 uint noisepos;
71 uint noiseval;
72 ubyte noiseout;
76 // internal filter def
77 struct sidflt {
78 int freq;
79 ubyte l_ena;
80 ubyte b_ena;
81 ubyte h_ena;
82 ubyte v3ena;
83 int vol;
84 int rez;
85 int h;
86 int b;
87 int l;
91 // ------------------------------------------------------------- constants
92 static immutable float[16] attackTimes = [
93 0.0022528606, 0.0080099577, 0.0157696042, 0.0237795619,
94 0.0372963655, 0.0550684591, 0.0668330845, 0.0783473987,
95 0.0981219818, 0.244554021, 0.489108042, 0.782472742,
96 0.977715461, 2.93364701, 4.88907793, 7.82272493,
99 static immutable float[16] decayReleaseTimes = [
100 0.00891777693, 0.024594051, 0.0484185907, 0.0730116639,
101 0.114512475, 0.169078356, 0.205199432, 0.240551975,
102 0.301266125, 0.750858245, 1.50171551, 2.40243682,
103 3.00189298, 9.00721405, 15.010998, 24.0182111,
107 // --------------------------- pseudo-constants (depending on mixing freq)
108 int mixing_frequency;
109 int freqmul;
110 int filtmul;
111 int[16] attacks;
112 int[16] releases;
115 // --------------------------------------------------------------- globals
116 s6581 sid;
117 sidosc[3] osc;
118 sidflt filter;
121 // --------------------------------------------------------- some aux stuff
122 ubyte get_bit (uint val, ubyte b) pure nothrow @safe @nogc { pragma(inline, true); return ((val>>b)&1); }
125 // ------------------------------------------------------------- synthesis
126 ubyte[65536] memory; // the C64 memory
128 int sample_active;
129 int sample_position, sample_start, sample_end, sample_repeat_start;
130 int fracPos = 0; // fractional position of sample
131 int sample_period;
132 int sample_repeats;
133 int sample_order;
134 int sample_nibble;
137 int generateDigi (int sIn) {
138 static int last_sample = 0;
139 static int sample = 0;
141 if (!sample_active) return sIn;
143 if (sample_position < sample_end && sample_position >= sample_start) {
144 //Interpolation routine
145 //float a = (float)fracPos/(float)mixing_frequency;
146 //float b = 1-a;
147 //sIn += a*sample + b*last_sample;
148 sIn += sample;
149 fracPos += 985248/sample_period;
150 if (fracPos > mixing_frequency) {
151 fracPos %= mixing_frequency;
152 last_sample = sample;
153 // N..hstes Samples holen
154 if (sample_order == 0) {
155 ++sample_nibble; // N..hstes Sample-Nibble
156 if (sample_nibble == 2) {
157 sample_nibble = 0;
158 ++sample_position;
160 } else {
161 --sample_nibble;
162 if (sample_nibble < 0) {
163 sample_nibble = 1;
164 ++sample_position;
167 if (sample_repeats) {
168 if (sample_position > sample_end) {
169 --sample_repeats;
170 sample_position = sample_repeat_start;
171 } else {
172 sample_active = 0;
175 sample = memory.ptr[sample_position&0xffff];
176 if (sample_nibble == 1) {
177 // Hi-Nibble holen
178 sample = (sample&0xf0)>>4;
179 } else {
180 sample = sample&0x0f;
182 sample -= 7;
183 sample <<= 10;
187 // clipping
189 if (sIn < -32767) return -32767;
190 if (sIn > 32767) return 32767;
192 return sIn;
196 // initialize SID and frequency dependant values
197 public void synth_init (uint mixfrq) {
198 mixing_frequency = mixfrq;
199 freqmul = 15872000/mixfrq;
200 filtmul = pfloat_ConvertFromFloat(21.5332031f)/mixfrq;
201 for (int i = 0; i < 16; ++i) {
202 attacks[i] = cast(int)(0x1000000/(attackTimes[i]*mixfrq));
203 releases[i] = cast(int)(0x1000000/(decayReleaseTimes[i]*mixfrq));
205 import core.stdc.string : memset;
206 memset(&sid, 0, sid.sizeof);
207 memset(&osc[0], 0, osc.sizeof);
208 memset(&filter, 0, filter.sizeof);
209 osc[0].noiseval = 0xffffff;
210 osc[1].noiseval = 0xffffff;
211 osc[2].noiseval = 0xffffff;
214 // render a buffer of n samples with the actual register contents
215 float filterl1 = 0, filterb1 = 0, freq = 0.8, filterrez = 0.1;
218 public void synth_render (short* buffer, uint len) {
219 ubyte v, refosc, outv;
220 uint bp;
221 ubyte triout, sawout, plsout, nseout;
222 short final_sample;
223 // step 1: convert the not easily processable sid registers into some
224 // more convenient and fast values (makes the thing much faster
225 // if you process more than 1 sample value at once)
226 for (v = 0; v < 3; ++v) {
227 osc[v].pulse = (sid.v[v].pulse&0xfff)<<16;
228 osc[v].filter = get_bit(sid.res_ftv, v);
229 osc[v].attack = attacks[sid.v[v].ad>>4];
230 osc[v].decay = releases[sid.v[v].ad&0xf];
231 osc[v].sustain = sid.v[v].sr&0xf0;
232 osc[v].release = releases[sid.v[v].sr&0xf];
233 osc[v].wave = sid.v[v].wave;
234 osc[v].freq = (cast(uint)sid.v[v].freq)*freqmul;
237 version(SidEngineUseFilter) {
238 filter.freq = (4*sid.ffreqhi+(sid.ffreqlo&0x7))*filtmul;
239 if (filter.freq > pfloat_ConvertFromInt(1)) filter.freq = pfloat_ConvertFromInt(1);
240 // the above line isnt correct at all - the problem is that the filter
241 // works only up to rmxfreq/4 - this is sufficient for 44KHz but isnt
242 // for 32KHz and lower - well, but sound quality is bad enough then to
243 // neglect the fact that the filter doesnt come that high ;)
244 filter.l_ena = get_bit(sid.ftp_vol, 4);
245 filter.b_ena = get_bit(sid.ftp_vol, 5);
246 filter.h_ena = get_bit(sid.ftp_vol, 6);
247 filter.v3ena = !get_bit(sid.ftp_vol, 7);
248 filter.vol = (sid.ftp_vol&0xf);
249 //filter.rez = 1.0-0.04*(float)(sid.res_ftv >> 4);
250 filter.rez = pfloat_ConvertFromFloat(1.2f)-pfloat_ConvertFromFloat(0.04f)*(sid.res_ftv>>4);
251 // we precalculate part of the quick float operation, saves time in loop later
252 filter.rez >>= 8;
255 // now render the buffer
256 for (bp = 0; bp < len; ++bp) {
257 int outo = 0;
258 int outf = 0;
259 // step 2 : generate the two output signals (for filtered and non-filtered) from the osc/eg sections
260 for (v = 0; v < 3; ++v) {
261 // update wave counter
262 osc[v].counter = (osc[v].counter+osc[v].freq)&0xFFFFFFF;
263 // reset counter / noise generator if reset get_bit set
264 if (osc[v].wave&0x08) {
265 osc[v].counter = 0;
266 osc[v].noisepos = 0;
267 osc[v].noiseval = 0xffffff;
269 refosc = cast(ubyte)(v ? v-1 : 2); // reference oscillator for sync/ring
270 // sync oscillator to refosc if sync bit set
271 if (osc[v].wave&0x02) {
272 if (osc[refosc].counter < osc[refosc].freq) osc[v].counter = osc[refosc].counter*osc[v].freq/osc[refosc].freq;
274 // generate waveforms with really simple algorithms
275 triout = cast(ubyte)(osc[v].counter>>19);
276 if (osc[v].counter>>27) triout ^= 0xff;
277 sawout = cast(ubyte)(osc[v].counter>>20);
278 plsout = cast(ubyte)((osc[v].counter > osc[v].pulse)-1);
280 // generate noise waveform exactly as the SID does.
281 if (osc[v].noisepos != (osc[v].counter>>23)) {
282 osc[v].noisepos = osc[v].counter>>23;
283 osc[v].noiseval = (osc[v].noiseval<<1)|(get_bit(osc[v].noiseval, 22)^get_bit(osc[v].noiseval, 17));
284 osc[v].noiseout = cast(ubyte)(
285 (get_bit(osc[v].noiseval, 22)<<7)|
286 (get_bit(osc[v].noiseval, 20)<<6)|
287 (get_bit(osc[v].noiseval, 16)<<5)|
288 (get_bit(osc[v].noiseval, 13)<<4)|
289 (get_bit(osc[v].noiseval, 11)<<3)|
290 (get_bit(osc[v].noiseval, 7)<<2)|
291 (get_bit(osc[v].noiseval, 4)<<1)|
292 (get_bit(osc[v].noiseval, 2)<<0)
295 nseout = osc[v].noiseout;
297 // modulate triangle wave if ringmod bit set
298 if (osc[v].wave&0x04) {
299 if (osc[refosc].counter < 0x8000000) triout ^= 0xff;
302 // now mix the oscillators with an AND operation as stated in
303 // the SID's reference manual - even if this is completely wrong.
304 // well, at least, the $30 and $70 waveform sounds correct and there's
305 // no real solution to do $50 and $60, so who cares.
306 outv = 0xFF;
307 if (osc[v].wave&0x10) outv &= triout;
308 if (osc[v].wave&0x20) outv &= sawout;
309 if (osc[v].wave&0x40) outv &= plsout;
310 if (osc[v].wave&0x80) outv &= nseout;
312 // now process the envelopes. the first thing about this is testing
313 // the gate bit and put the EG into attack or release phase if desired
314 if (!(osc[v].wave&0x01)) osc[v].envphase = 3;
315 else if (osc[v].envphase == 3) osc[v].envphase = 0;
316 // so now process the volume according to the phase and adsr values
317 switch (osc[v].envphase) {
318 case 0 : // Phase 0 : Attack
319 osc[v].envval += osc[v].attack;
320 if (osc[v].envval >= 0xFFFFFF) {
321 osc[v].envval = 0xFFFFFF;
322 osc[v].envphase = 1;
324 break;
325 case 1 : // Phase 1 : Decay
326 osc[v].envval -= osc[v].decay;
327 if (cast(int)osc[v].envval <= cast(int)(osc[v].sustain<<16)) {
328 osc[v].envval = osc[v].sustain<<16;
329 osc[v].envphase = 2;
331 break;
332 case 2 : // Phase 2 : Sustain
333 if (cast(int)osc[v].envval != cast(int)(osc[v].sustain<<16)) osc[v].envphase = 1;
334 // :) yes, thats exactly how the SID works. and maybe
335 // a music routine out there supports this, so better
336 // let it in, thanks :)
337 break;
338 case 3 : // Phase 3 : Release
339 osc[v].envval -= osc[v].release;
340 if (osc[v].envval < 0x40000) osc[v].envval= 0x40000;
341 // the volume offset is because the SID does not
342 // completely silence the voices when it should. most
343 // emulators do so though and thats the main reason
344 // why the sound of emulators is too, err... emulated :)
345 break;
346 default: break;
348 // now route the voice output to either the non-filtered or the
349 // filtered channel and dont forget to blank out osc3 if desired
350 version(SidEngineUseFilter) {
351 //???
352 if (v < 2 || filter.v3ena) {
353 if (osc[v].filter)
354 outf += ((cast(int)(outv-0x80))*osc[v].envval)>>22; //outf+=((float)osc[v].envval*(float)outv-0x8000000)/0x30000000;
355 else
356 outo += ((cast(int)(outv-0x80))*osc[v].envval)>>22; //outo+=((float)osc[v].envval*(float)outv-0x8000000)/0x30000000;
358 } else {
359 outf += (cast(short)(outv-0x80))*(osc[v].envval>>8);
363 // step 3
364 // so, now theres finally time to apply the multi-mode resonant filter
365 // to the signal. The easiest thing ist just modelling a real electronic
366 // filter circuit instead of fiddling around with complex IIRs or even
367 // FIRs ...
368 // it sounds as good as them or maybe better and needs only 3 MULs and
369 // 4 ADDs for EVERYTHING. SIDPlay uses this kind of filter, too, but
370 // Mage messed the whole thing completely up - as the rest of the
371 // emulator.
372 // This filter sounds a lot like the 8580, as the low-quality, dirty
373 // sound of the 6581 is uuh too hard to achieve :)
374 version(SidEngineUseFilter) {
375 //filter.h = outf - filter.b*filter.rez - filter.l;
376 //filter.h = pfloat_ConvertFromInt(outf) - pfloat_Multiply(filter.b, filter.rez) - filter.l;
377 filter.h = pfloat_ConvertFromInt(outf)-(filter.b>>8)*filter.rez-filter.l;
378 //filter.b += filter.freq * filter.h;
379 filter.b += pfloat_Multiply(filter.freq, filter.h);
380 //filter.l += filter.freq * filter.b;
381 filter.l += pfloat_Multiply(filter.freq, filter.b);
382 outf = 0;
383 if (filter.l_ena) outf += pfloat_ConvertToInt(filter.l);
384 if (filter.b_ena) outf += pfloat_ConvertToInt(filter.b);
385 if (filter.h_ena) outf += pfloat_ConvertToInt(filter.h);
386 final_sample = cast(short)(filter.vol*(outo+outf));
387 } else {
388 final_sample = outf>>10;
390 *(buffer+bp) = cast(short)generateDigi(final_sample);
396 static const int ROMbasicStart=0xA000;
397 static const int ROMbasicEnd=0xBFFF;
398 static byte ROMbasic[ROMbasicEnd-ROMbasicStart+1];
400 static const int ROMkernalStart=0xE000;
401 static const int ROMkernalEnd=0xFFFF;
402 static byte ROMkernal[ROMkernalEnd-ROMkernalStart+1];
404 static const int ROMcharStart=0xD000;
405 static const int ROMcharEnd=0xDFFF;
406 static byte ROMchar[ROMcharEnd-ROMcharStart+1];
410 void sidPoke (int reg, ubyte val) {
411 int voice = 0;
413 if (reg >= 0 && reg <= 6) voice = 0;
414 if (reg >= 7 && reg <= 13) { voice = 1; reg -= 7; }
415 if (reg >= 14 && reg <= 20) { voice = 2; reg -= 14; }
417 switch (reg) {
418 case 0: // Frequenz niederwertiges byte Stimme 1
419 sid.v[voice].freq = (sid.v[voice].freq&0xff00)+val;
420 //printf("Voice%d: %d\n", voice, sid.v[voice].freq);
421 break;
422 case 1: // Frequenz h..erwertiges byte Stimme 1
423 sid.v[voice].freq = (sid.v[voice].freq&0xff)+(val<<8);
424 break;
425 case 2: // Pulsbreite niederwertiges byte Stimme 1
426 sid.v[voice].pulse = (sid.v[voice].pulse&0xff00)+val;
427 break;
428 case 3: // Pulsbreite h..erwertiges byte Stimme 1
429 sid.v[voice].pulse = (sid.v[voice].pulse&0xff)+(val<<8);
430 break;
431 case 4: sid.v[voice].wave = val; break;
433 case 5: sid.v[voice].ad = val; break;
434 case 6: sid.v[voice].sr = val; break;
436 case 21: sid.ffreqlo = val; break;
437 case 22: sid.ffreqhi = val; break;
438 case 23: sid.res_ftv = val; break;
439 case 24: sid.ftp_vol = val; break;
441 default: break;
446 void sidReset () {
450 ubyte getmem (uint addr) {
451 addr &= 0xffff;
452 if (addr == 0xdd0d) memory.ptr[addr] = 0;
453 return memory.ptr[addr];
457 int internal_period, internal_order, internal_start, internal_end;
458 int internal_add, internal_repeat_times, internal_repeat_start;
460 void setmem (uint addr, ubyte value) {
461 addr &= 0xffff;
462 memory[addr] = value;
464 if ((addr&0xfc00) == 0xd400) {
465 //addr&=0x1f;
466 sidPoke(addr&0x1f, value);
467 // Neue SID-Register
468 if (addr > 0xd418 && addr < 0xd500) {
469 // Start-Hi
470 if (addr == 0xd41f) internal_start = (internal_start&0x00ff)|(value<<8);
471 // Start-Lo
472 if (addr == 0xd41e) internal_start = (internal_start&0xff00)|(value);
473 // Repeat-Hi
474 if (addr == 0xd47f) internal_repeat_start = (internal_repeat_start&0x00ff)|(value<<8);
475 // Repeat-Lo
476 if (addr == 0xd47e) internal_repeat_start = (internal_repeat_start&0xff00)|(value);
477 // End-Hi
478 if (addr == 0xd43e) internal_end = (internal_end&0x00ff)|(value<<8);
479 // End-Lo
480 if (addr == 0xd43d) internal_end = (internal_end&0xff00)|(value);
481 // Loop-Size
482 if (addr == 0xd43f) internal_repeat_times = value;
483 // Period-Hi
484 if (addr == 0xd45e) internal_period = (internal_period&0x00ff)|(value<<8);
485 // Period-Lo
486 if (addr == 0xd45d) internal_period = (internal_period&0xff00)|(value);
487 // Sample Order
488 if (addr == 0xd47d) internal_order = value;
489 // Sample Add
490 if (addr == 0xd45f) internal_add = value;
491 // Start-Sampling
492 if (addr == 0xd41d) {
493 sample_repeats = internal_repeat_times;
494 sample_position = internal_start;
495 sample_start = internal_start;
496 sample_end = internal_end;
497 sample_repeat_start = internal_repeat_start;
498 sample_period = internal_period;
499 sample_order = internal_order;
500 switch (value) {
501 case 0xfd: sample_active = 0; break;
502 case 0xfe: case 0xff: sample_active = 1; break;
503 default: return;
510 //enum { imp, imm, abs, absx, absy, zp, zpx, zpy, ind, indx, indy, acc, rel};
511 enum imp = 0;
512 enum imm = 1;
513 enum abs = 2;
514 enum absx = 3;
515 enum absy = 4;
516 enum zp = 6;
517 enum zpx = 7;
518 enum zpy = 8;
519 enum ind = 9;
520 enum indx = 10;
521 enum indy = 11;
522 enum acc = 12;
523 enum rel = 13;
525 static immutable ubyte[256] opcodes = [
526 brk,ora,xxx,xxx,xxx,ora,asl,xxx,php,ora,asl,xxx,xxx,ora,asl,xxx,
527 bpl,ora,xxx,xxx,xxx,ora,asl,xxx,clc,ora,xxx,xxx,xxx,ora,asl,xxx,
528 jsr,and,xxx,xxx,bit,and,rol,xxx,plp,and,rol,xxx,bit,and,rol,xxx,
529 bmi,and,xxx,xxx,xxx,and,rol,xxx,sec,and,xxx,xxx,xxx,and,rol,xxx,
530 rti,eor,xxx,xxx,xxx,eor,lsr,xxx,pha,eor,lsr,xxx,jmp,eor,lsr,xxx,
531 bvc,eor,xxx,xxx,xxx,eor,lsr,xxx,cli,eor,xxx,xxx,xxx,eor,lsr,xxx,
532 rts,adc,xxx,xxx,xxx,adc,ror,xxx,pla,adc,ror,xxx,jmp,adc,ror,xxx,
533 bvs,adc,xxx,xxx,xxx,adc,ror,xxx,sei,adc,xxx,xxx,xxx,adc,ror,xxx,
534 xxx,sta,xxx,xxx,sty,sta,stx,xxx,dey,xxx,txa,xxx,sty,sta,stx,xxx,
535 bcc,sta,xxx,xxx,sty,sta,stx,xxx,tya,sta,txs,xxx,xxx,sta,xxx,xxx,
536 ldy,lda,ldx,xxx,ldy,lda,ldx,xxx,tay,lda,tax,xxx,ldy,lda,ldx,xxx,
537 bcs,lda,xxx,xxx,ldy,lda,ldx,xxx,clv,lda,tsx,xxx,ldy,lda,ldx,xxx,
538 cpy,cmp,xxx,xxx,cpy,cmp,dec,xxx,iny,cmp,dex,xxx,cpy,cmp,dec,xxx,
539 bne,cmp,xxx,xxx,xxx,cmp,dec,xxx,cld,cmp,xxx,xxx,xxx,cmp,dec,xxx,
540 cpx,sbc,xxx,xxx,cpx,sbc,inc,xxx,inx,sbc,nop,xxx,cpx,sbc,inc,xxx,
541 beq,sbc,xxx,xxx,xxx,sbc,inc,xxx,sed,sbc,xxx,xxx,xxx,sbc,inc,xxx
544 static immutable ubyte[256] modes = [
545 imp,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,abs,abs,abs,xxx,
546 rel,indy,xxx,xxx,xxx,zpx,zpx,xxx,imp,absy,xxx,xxx,xxx,absx,absx,xxx,
547 abs,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,abs,abs,abs,xxx,
548 rel,indy,xxx,xxx,xxx,zpx,zpx,xxx,imp,absy,xxx,xxx,xxx,absx,absx,xxx,
549 imp,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,abs,abs,abs,xxx,
550 rel,indy,xxx,xxx,xxx,zpx,zpx,xxx,imp,absy,xxx,xxx,xxx,absx,absx,xxx,
551 imp,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,ind,abs,abs,xxx,
552 rel,indy,xxx,xxx,xxx,zpx,zpx,xxx,imp,absy,xxx,xxx,xxx,absx,absx,xxx,
553 imm,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,abs,abs,abs,xxx,
554 rel,indy,xxx,xxx,zpx,zpx,zpy,xxx,imp,absy,acc,xxx,xxx,absx,absx,xxx,
555 imm,indx,imm,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,abs,abs,abs,xxx,
556 rel,indy,xxx,xxx,zpx,zpx,zpy,xxx,imp,absy,acc,xxx,absx,absx,absy,xxx,
557 imm,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,abs,abs,abs,xxx,
558 rel,indy,xxx,xxx,zpx,zpx,zpx,xxx,imp,absy,acc,xxx,xxx,absx,absx,xxx,
559 imm,indx,xxx,xxx,zp,zp,zp,xxx,imp,imm,acc,xxx,abs,abs,abs,xxx,
560 rel,indy,xxx,xxx,zpx,zpx,zpx,xxx,imp,absy,acc,xxx,xxx,absx,absx,xxx
564 // ----------------------------------------------- globale Faulheitsvariablen
565 int cycles;
566 ubyte bval;
567 ushort wval;
569 // ----------------------------------------------------------------- Register
570 ubyte a, x, y, s, p;
571 ushort pc;
574 // ----------------------------------------------------------- DER HARTE KERN
575 ubyte getaddr (ubyte mode) {
576 ushort ad, ad2;
577 switch (mode) {
578 case imp:
579 cycles += 2;
580 return 0;
581 case imm:
582 cycles += 2;
583 return getmem(pc++);
584 case abs:
585 cycles += 4;
586 ad = getmem(pc++);
587 ad |= getmem(pc++)<<8;
588 return getmem(ad);
589 case absx:
590 cycles += 4;
591 ad = getmem(pc++);
592 ad |= 256*getmem(pc++);
593 ad2 = cast(ushort)(ad+x);
594 if ((ad2&0xff00) != (ad&0xff00)) ++cycles;
595 return getmem(ad2);
596 case absy:
597 cycles += 4;
598 ad = getmem(pc++);
599 ad |= 256*getmem(pc++);
600 ad2 = cast(ushort)(ad+y);
601 if ((ad2&0xff00) != (ad&0xff00)) ++cycles;
602 return getmem(ad2);
603 case zp:
604 cycles += 3;
605 ad = getmem(pc++);
606 return getmem(ad);
607 case zpx:
608 cycles += 4;
609 ad = getmem(pc++);
610 ad += x;
611 return getmem(ad&0xff);
612 case zpy:
613 cycles += 4;
614 ad = getmem(pc++);
615 ad += y;
616 return getmem(ad&0xff);
617 case indx:
618 cycles += 6;
619 ad = getmem(pc++);
620 ad += x;
621 ad2 = getmem(ad&0xff);
622 ad++;
623 ad2 |= getmem(ad&0xff)<<8;
624 return getmem(ad2);
625 case indy:
626 cycles += 5;
627 ad = getmem(pc++);
628 ad2 = getmem(ad);
629 ad2 |= getmem((ad+1)&0xff)<<8;
630 ad = cast(ushort)(ad2+y);
631 if ((ad2&0xff00) != (ad&0xff00)) ++cycles;
632 return getmem(ad);
633 case acc:
634 cycles += 2;
635 return a;
636 default: break;
638 return 0;
642 void setaddr (ubyte mode, ubyte val) {
643 ushort ad, ad2;
644 switch (mode) {
645 case abs:
646 cycles += 2;
647 ad = getmem(pc-2);
648 ad |= 256*getmem(pc-1);
649 setmem(ad, val);
650 return;
651 case absx:
652 cycles += 3;
653 ad = getmem(pc-2);
654 ad |= 256*getmem(pc-1);
655 ad2 = cast(ushort)(ad+x);
656 if ((ad2&0xff00) != (ad&0xff00)) --cycles;
657 setmem(ad2, val);
658 return;
659 case zp:
660 cycles += 2;
661 ad = getmem(pc-1);
662 setmem(ad, val);
663 return;
664 case zpx:
665 cycles += 2;
666 ad = getmem(pc-1);
667 ad += x;
668 setmem(ad&0xff, val);
669 return;
670 case acc:
671 a = val;
672 return;
673 default: break;
678 void putaddr (ubyte mode, ubyte val) {
679 ushort ad, ad2;
680 switch (mode) {
681 case abs:
682 cycles += 4;
683 ad = getmem(pc++);
684 ad |= getmem(pc++)<<8;
685 setmem(ad, val);
686 return;
687 case absx:
688 cycles += 4;
689 ad = getmem(pc++);
690 ad |= getmem(pc++)<<8;
691 ad2 = cast(ushort)(ad+x);
692 setmem(ad2, val);
693 return;
694 case absy:
695 cycles += 4;
696 ad = getmem(pc++);
697 ad |= getmem(pc++)<<8;
698 ad2 = cast(ushort)(ad+y);
699 if ((ad2&0xff00) != (ad&0xff00)) ++cycles;
700 setmem(ad2, val);
701 return;
702 case zp:
703 cycles += 3;
704 ad = getmem(pc++);
705 setmem(ad, val);
706 return;
707 case zpx:
708 cycles += 4;
709 ad = getmem(pc++);
710 ad += x;
711 setmem(ad&0xff, val);
712 return;
713 case zpy:
714 cycles += 4;
715 ad = getmem(pc++);
716 ad += y;
717 setmem(ad&0xff, val);
718 return;
719 case indx:
720 cycles += 6;
721 ad = getmem(pc++);
722 ad += x;
723 ad2 = getmem(ad&0xff);
724 ++ad;
725 ad2 |= getmem(ad&0xff)<<8;
726 setmem(ad2, val);
727 return;
728 case indy:
729 cycles += 5;
730 ad = getmem(pc++);
731 ad2 = getmem(ad);
732 ad2 |= getmem((ad+1)&0xff)<<8;
733 ad = cast(ushort)(ad2+y);
734 setmem(ad, val);
735 return;
736 case acc:
737 cycles += 2;
738 a = val;
739 return;
740 default: break;
745 void setflags (int flag, int cond) nothrow @safe @nogc {
746 pragma(inline, true);
747 // cond?p|=flag:p&=~flag;
748 if (cond) p |= flag; else p &= ~flag;
752 void push (ubyte val) {
753 setmem(0x100+s, val);
754 if (s) --s;
758 ubyte pop () {
759 if (s < 0xff) ++s;
760 return getmem(0x100+s);
764 void branch (int flag) {
765 byte dist = cast(byte)getaddr(imm);
766 wval = cast(ushort)(pc+dist);
767 if (flag) {
768 cycles += ((pc&0x100) != (wval&0x100) ? 2 : 1);
769 pc = wval;
774 // ----------------------------------------------------- ffentliche Routinen
775 public void cpuReset () {
776 a = x = y = 0;
777 p = 0;
778 s = 255;
779 //pc = getaddr(0xfffc); //???BUG???
780 pc = cast(ushort)(getmem(0xfffc)|(getmem(0xfffd)<<8));
784 public void cpuResetTo (ushort npc) {
785 a = 0;
786 x = 0;
787 y = 0;
788 p = 0;
789 s = 255;
790 pc = npc;
794 public int cpuParse () {
795 cycles = 0;
796 ubyte opc = getmem(pc++);
797 ubyte cmd = opcodes[opc];
798 ubyte addr = modes[opc];
799 switch (cmd) {
800 case adc:
801 wval = cast(ushort)a+getaddr(addr)+(p&FLAG_C ? 1 : 0);
802 setflags(FLAG_C, wval&0x100);
803 a = cast(ubyte)wval;
804 setflags(FLAG_Z, !a);
805 setflags(FLAG_N, a&0x80);
806 setflags(FLAG_V, (!!(p&FLAG_C))^(!!(p&FLAG_N)));
807 break;
808 case and:
809 bval = getaddr(addr);
810 a &= bval;
811 setflags(FLAG_Z, !a);
812 setflags(FLAG_N, a&0x80);
813 break;
814 case asl:
815 wval = getaddr(addr);
816 wval <<= 1;
817 setaddr(addr, cast(ubyte)wval);
818 setflags(FLAG_Z, !wval);
819 setflags(FLAG_N, wval&0x80);
820 setflags(FLAG_C, wval&0x100);
821 break;
822 case bcc:
823 branch(!(p&FLAG_C));
824 break;
825 case bcs:
826 branch(p&FLAG_C);
827 break;
828 case bne:
829 branch(!(p&FLAG_Z));
830 break;
831 case beq:
832 branch(p&FLAG_Z);
833 break;
834 case bpl:
835 branch(!(p&FLAG_N));
836 break;
837 case bmi:
838 branch(p&FLAG_N);
839 break;
840 case bvc:
841 branch(!(p&FLAG_V));
842 break;
843 case bvs:
844 branch(p&FLAG_V);
845 break;
846 case bit:
847 bval = getaddr(addr);
848 setflags(FLAG_Z, !(a&bval));
849 setflags(FLAG_N, bval&0x80);
850 setflags(FLAG_V, bval&0x40);
851 break;
852 case brk:
853 push(pc&0xff);
854 push(pc>>8);
855 push(p);
856 setflags(FLAG_B, 1);
857 pc = getmem(0xfffe);
858 cycles += 7;
859 break;
860 case clc:
861 cycles += 2;
862 setflags(FLAG_C, 0);
863 break;
864 case cld:
865 cycles += 2;
866 setflags(FLAG_D, 0);
867 break;
868 case cli:
869 cycles += 2;
870 setflags(FLAG_I, 0);
871 break;
872 case clv:
873 cycles += 2;
874 setflags(FLAG_V, 0);
875 break;
876 case cmp:
877 bval = getaddr(addr);
878 wval = cast(ushort)(a-bval);
879 setflags(FLAG_Z, !wval);
880 setflags(FLAG_N, wval&0x80);
881 setflags(FLAG_C, a>=bval);
882 break;
883 case cpx:
884 bval = getaddr(addr);
885 wval = cast(ushort)(x-bval);
886 setflags(FLAG_Z, !wval);
887 setflags(FLAG_N, wval&0x80);
888 setflags(FLAG_C, a>=bval);
889 break;
890 case cpy:
891 bval = getaddr(addr);
892 wval = cast(ushort)(y-bval);
893 setflags(FLAG_Z, !wval);
894 setflags(FLAG_N, wval&0x80);
895 setflags(FLAG_C, a>=bval);
896 break;
897 case dec:
898 bval = getaddr(addr);
899 bval--;
900 setaddr(addr, bval);
901 setflags(FLAG_Z, !bval);
902 setflags(FLAG_N, bval&0x80);
903 break;
904 case dex:
905 cycles += 2;
906 x--;
907 setflags(FLAG_Z, !x);
908 setflags(FLAG_N, x&0x80);
909 break;
910 case dey:
911 cycles += 2;
912 y--;
913 setflags(FLAG_Z, !y);
914 setflags(FLAG_N, y&0x80);
915 break;
916 case eor:
917 bval = getaddr(addr);
918 a ^= bval;
919 setflags(FLAG_Z, !a);
920 setflags(FLAG_N, a&0x80);
921 break;
922 case inc:
923 bval = getaddr(addr);
924 bval++;
925 setaddr(addr, bval);
926 setflags(FLAG_Z, !bval);
927 setflags(FLAG_N, bval&0x80);
928 break;
929 case inx:
930 cycles += 2;
931 x++;
932 setflags(FLAG_Z, !x);
933 setflags(FLAG_N, x&0x80);
934 break;
935 case iny:
936 cycles += 2;
937 y++;
938 setflags(FLAG_Z, !y);
939 setflags(FLAG_N, y&0x80);
940 break;
941 case jmp:
942 cycles += 3;
943 wval = getmem(pc++);
944 wval |= 256*getmem(pc++);
945 switch (addr) {
946 case abs:
947 pc = wval;
948 break;
949 case ind:
950 pc = getmem(wval);
951 pc |= 256*getmem(wval+1);
952 cycles += 2;
953 break;
954 default: break;
956 break;
957 case jsr:
958 cycles += 6;
959 push(cast(ubyte)(pc+2));
960 push(cast(ubyte)((pc+2)>>8));
961 wval = getmem(pc++);
962 wval |= 256*getmem(pc++);
963 pc = wval;
964 break;
965 case lda:
966 a = getaddr(addr);
967 setflags(FLAG_Z, !a);
968 setflags(FLAG_N, a&0x80);
969 break;
970 case ldx:
971 x = getaddr(addr);
972 setflags(FLAG_Z, !x);
973 setflags(FLAG_N, x&0x80);
974 break;
975 case ldy:
976 y = getaddr(addr);
977 setflags(FLAG_Z, !y);
978 setflags(FLAG_N, y&0x80);
979 break;
980 case lsr:
981 //bval = wval = getaddr(addr);
982 bval = getaddr(addr);
983 wval = cast(ubyte)bval;
984 wval >>= 1;
985 setaddr(addr, cast(ubyte)wval);
986 setflags(FLAG_Z, !wval);
987 setflags(FLAG_N, wval&0x80);
988 setflags(FLAG_C, bval&1);
989 break;
990 case nop:
991 cycles += 2;
992 break;
993 case ora:
994 bval = getaddr(addr);
995 a |= bval;
996 setflags(FLAG_Z, !a);
997 setflags(FLAG_N, a&0x80);
998 break;
999 case pha:
1000 push(a);
1001 cycles += 3;
1002 break;
1003 case php:
1004 push(p);
1005 cycles += 3;
1006 break;
1007 case pla:
1008 a = pop();
1009 setflags(FLAG_Z, !a);
1010 setflags(FLAG_N, a&0x80);
1011 cycles += 4;
1012 break;
1013 case plp:
1014 p = pop();
1015 cycles += 4;
1016 break;
1017 case rol:
1018 bval = getaddr(addr);
1019 ubyte c = !!(p&FLAG_C);
1020 setflags(FLAG_C, bval&0x80);
1021 bval <<= 1;
1022 bval |= c;
1023 setaddr(addr, bval);
1024 setflags(FLAG_N, bval&0x80);
1025 setflags(FLAG_Z, !bval);
1026 break;
1027 case ror:
1028 bval = getaddr(addr);
1029 ubyte c = !!(p&FLAG_C);
1030 setflags(FLAG_C, bval&1);
1031 bval >>= 1;
1032 bval |= 128*c;
1033 setaddr(addr, bval);
1034 setflags(FLAG_N, bval&0x80);
1035 setflags(FLAG_Z, !bval);
1036 break;
1037 case rti:
1038 // NEU, rti wie rts, au..r das alle register wieder vom stack kommen
1039 //p = pop();
1040 p = pop();
1041 y = pop();
1042 x = pop();
1043 a = pop();
1044 // in_nmi = false;
1045 //write_console("NMI EXIT!");
1046 goto case;
1047 case rts:
1048 wval = cast(ushort)(cast(ushort)256*cast(ushort)pop());
1049 wval |= pop();
1050 pc = wval;
1051 cycles += 6;
1052 break;
1053 case sbc:
1054 bval = getaddr(addr)^0xff;
1055 wval = cast(ushort)a+bval+((p&FLAG_C)?1:0);
1056 setflags(FLAG_C, wval&0x100);
1057 a = cast(ubyte)wval;
1058 setflags(FLAG_Z, !a);
1059 setflags(FLAG_N, (a > 127 ? 1 : 0));
1060 setflags(FLAG_V, (!!(p&FLAG_C))^(!!(p&FLAG_N)));
1061 break;
1062 case sec:
1063 cycles += 2;
1064 setflags(FLAG_C, 1);
1065 break;
1066 case sed:
1067 cycles += 2;
1068 setflags(FLAG_D, 1);
1069 break;
1070 case sei:
1071 cycles += 2;
1072 setflags(FLAG_I, 1);
1073 break;
1074 case sta:
1075 putaddr(addr, a);
1076 break;
1077 case stx:
1078 putaddr(addr, x);
1079 break;
1080 case sty:
1081 putaddr(addr, y);
1082 break;
1083 case tax:
1084 cycles += 2;
1085 x = a;
1086 setflags(FLAG_Z, !x);
1087 setflags(FLAG_N, x&0x80);
1088 break;
1089 case tay:
1090 cycles += 2;
1091 y = a;
1092 setflags(FLAG_Z, !y);
1093 setflags(FLAG_N, y&0x80);
1094 break;
1095 case tsx:
1096 cycles += 2;
1097 x = s;
1098 setflags(FLAG_Z, !x);
1099 setflags(FLAG_N, x&0x80);
1100 break;
1101 case txa:
1102 cycles += 2;
1103 a = x;
1104 setflags(FLAG_Z, !a);
1105 setflags(FLAG_N, a&0x80);
1106 break;
1107 case txs:
1108 cycles += 2;
1109 s = x;
1110 break;
1111 case tya:
1112 cycles += 2;
1113 a = y;
1114 setflags(FLAG_Z, !a);
1115 setflags(FLAG_N, a&0x80);
1116 break;
1117 default: break;
1119 return cycles;
1123 public int cpuJSR (ushort npc, ubyte na) {
1124 a = na;
1125 x = 0;
1126 y = 0;
1127 p = 0;
1128 s = 255;
1129 pc = npc;
1130 push(0);
1131 push(0);
1132 int ccl = 0;
1133 while (pc) ccl += cpuParse();
1134 return ccl;
1138 public void c64Init (bool dbl=false) {
1139 //if (srate < 44100 || srate > 48000) assert(0, "invalid sampling rate");
1140 synth_init(44100/(dbl ? 2 : 1));
1141 memory[] = 0;
1142 cpuReset();
1146 void LoadSIDFromMemory (const(void)* pSidData, out ushort load_addr, out ushort init_addr, out ushort play_addr, out ubyte subsongs, out ubyte startsong, out ubyte speed, ushort size) {
1147 auto pData = cast(const(ubyte)*)pSidData;
1148 ubyte data_file_offset = pData[7];
1150 load_addr = pData[8]<<8;
1151 load_addr |= pData[9];
1153 init_addr = pData[10]<<8;
1154 init_addr |= pData[11];
1156 play_addr = pData[12]<<8;
1157 play_addr |= pData[13];
1159 subsongs = cast(ubyte)(pData[0xf]-1);
1160 startsong = cast(ubyte)(pData[0x11]-1);
1162 load_addr = pData[data_file_offset];
1163 load_addr |= pData[data_file_offset+1]<<8;
1165 speed = pData[0x15];
1167 memory[] = 0;
1168 //memcpy(&memory[*load_addr], &pData[data_file_offset+2], size-(data_file_offset+2));
1169 if (data_file_offset+2 < size) {
1170 uint sz = size-(data_file_offset+2);
1171 if (load_addr+sz > 65536) sz = 65536-load_addr;
1172 if (sz) memory[load_addr..load_addr+sz] = pData[data_file_offset+2..data_file_offset+2+sz];
1175 if (play_addr == 0) {
1176 cpuJSR(init_addr, 0);
1177 play_addr = (memory[0x0315]<<8)+memory[0x0314];
1182 public struct SidSong {
1183 ushort load_addr;
1184 ushort init_addr;
1185 ushort play_addr;
1186 ubyte sub_song_start;
1187 ubyte max_sub_songs;
1188 ubyte speed;
1189 const(char)[] name;
1190 const(char)[] author;
1191 const(char)[] copyright;
1192 char[32*3] nacbuf = 0;
1196 public ubyte c64SidGetSpeed (VFile fl) {
1197 fl.seek(0);
1199 char[4] sign;
1200 fl.rawReadExact(sign[]);
1201 if (sign != "PSID") throw new Exception("invalid file");
1203 fl.seek(0x15);
1204 return fl.readNum!ubyte;
1208 public void c64SidLoad (VFile fl, out SidSong song) {
1209 uint nacpos = 0;
1211 const(char)[] loadStr (uint ofs) {
1212 fl.seek(ofs);
1213 fl.rawReadExact(song.nacbuf[nacpos..nacpos+32]);
1214 const(char)[] res = song.nacbuf[nacpos..nacpos+32];
1215 nacpos += 32;
1216 foreach (immutable idx, char ch; res) if (ch == 0) return res[0..idx];
1217 return res;
1220 fl.seek(0);
1222 char[4] sign;
1223 fl.rawReadExact(sign[]);
1224 if (sign != "PSID") throw new Exception("invalid file");
1226 // Name holen
1227 song.name = loadStr(0x16);
1228 // Author holen
1229 song.author = loadStr(0x36);
1230 // Copyright holen
1231 song.copyright = loadStr(0x56);
1233 auto sidmem = new ubyte[](65536);
1234 scope(exit) delete sidmem;
1236 ushort load_addr;
1238 fl.seek(0);
1239 auto rd = fl.rawRead(sidmem[]);
1241 LoadSIDFromMemory(sidmem.ptr, song.load_addr, song.init_addr, song.play_addr, song.max_sub_songs, song.sub_song_start, song.speed, cast(ushort)rd.length);
1243 //return song.load_addr;