1 //////////////////////////////////////////////////////////////////////
2 // The c64-emulation and the synth-renderer was written by Tammo Hinrichs (kb)
4 // Combining of the emulation and the renderer as well as improvements and bugfixes
5 // by Rainer Sinsch (sinsch@stud.uni-frankfurt.de)
7 // D Translation by Ketmar // Invisible Vector
10 module sidengine
/*is aliced*/;
16 version = SidEngineUseFilter
;
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; }
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
,
40 // SID register definition
57 // internal oscillator def
67 uint counter
; // Variablen
76 // internal filter def
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
;
115 // --------------------------------------------------------------- globals
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
129 int sample_position
, sample_start
, sample_end
, sample_repeat_start
;
130 int fracPos
= 0; // fractional position of sample
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;
147 //sIn += a*sample + b*last_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) {
162 if (sample_nibble
< 0) {
167 if (sample_repeats
) {
168 if (sample_position
> sample_end
) {
170 sample_position
= sample_repeat_start
;
175 sample
= memory
.ptr
[sample_position
&0xffff];
176 if (sample_nibble
== 1) {
178 sample
= (sample
&0xf0)>>4;
180 sample
= sample
&0x0f;
189 if (sIn < -32767) return -32767;
190 if (sIn > 32767) return 32767;
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
;
221 ubyte triout
, sawout
, plsout
, nseout
;
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
255 // now render the buffer
256 for (bp
= 0; bp
< len
; ++bp
) {
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) {
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.
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;
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;
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 :)
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 :)
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
) {
352 if (v
< 2 || filter
.v3ena
) {
354 outf
+= ((cast(int)(outv
-0x80))*osc
[v
].envval
)>>22; //outf+=((float)osc[v].envval*(float)outv-0x8000000)/0x30000000;
356 outo
+= ((cast(int)(outv
-0x80))*osc
[v
].envval
)>>22; //outo+=((float)osc[v].envval*(float)outv-0x8000000)/0x30000000;
359 outf
+= (cast(short)(outv
-0x80))*(osc
[v
].envval
>>8);
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
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
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
);
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
));
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
) {
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; }
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);
422 case 1: // Frequenz h..erwertiges byte Stimme 1
423 sid
.v
[voice
].freq
= (sid
.v
[voice
].freq
&0xff)+(val
<<8);
425 case 2: // Pulsbreite niederwertiges byte Stimme 1
426 sid
.v
[voice
].pulse
= (sid
.v
[voice
].pulse
&0xff00)+val
;
428 case 3: // Pulsbreite h..erwertiges byte Stimme 1
429 sid
.v
[voice
].pulse
= (sid
.v
[voice
].pulse
&0xff)+(val
<<8);
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;
450 ubyte getmem (uint addr
) {
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
) {
462 memory
[addr
] = value
;
464 if ((addr
&0xfc00) == 0xd400) {
466 sidPoke(addr
&0x1f, value
);
468 if (addr
> 0xd418 && addr
< 0xd500) {
470 if (addr
== 0xd41f) internal_start
= (internal_start
&0x00ff)|
(value
<<8);
472 if (addr
== 0xd41e) internal_start
= (internal_start
&0xff00)|
(value
);
474 if (addr
== 0xd47f) internal_repeat_start
= (internal_repeat_start
&0x00ff)|
(value
<<8);
476 if (addr
== 0xd47e) internal_repeat_start
= (internal_repeat_start
&0xff00)|
(value
);
478 if (addr
== 0xd43e) internal_end
= (internal_end
&0x00ff)|
(value
<<8);
480 if (addr
== 0xd43d) internal_end
= (internal_end
&0xff00)|
(value
);
482 if (addr
== 0xd43f) internal_repeat_times
= value
;
484 if (addr
== 0xd45e) internal_period
= (internal_period
&0x00ff)|
(value
<<8);
486 if (addr
== 0xd45d) internal_period
= (internal_period
&0xff00)|
(value
);
488 if (addr
== 0xd47d) internal_order
= value
;
490 if (addr
== 0xd45f) internal_add
= value
;
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
;
501 case 0xfd: sample_active
= 0; break;
502 case 0xfe: case 0xff: sample_active
= 1; break;
510 //enum { imp, imm, abs, absx, absy, zp, zpx, zpy, ind, indx, indy, acc, rel};
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
569 // ----------------------------------------------------------------- Register
574 // ----------------------------------------------------------- DER HARTE KERN
575 ubyte getaddr (ubyte mode
) {
587 ad |
= getmem(pc
++)<<8;
592 ad |
= 256*getmem(pc
++);
593 ad2
= cast(ushort)(ad
+x
);
594 if ((ad2
&0xff00) != (ad
&0xff00)) ++cycles
;
599 ad |
= 256*getmem(pc
++);
600 ad2
= cast(ushort)(ad
+y
);
601 if ((ad2
&0xff00) != (ad
&0xff00)) ++cycles
;
611 return getmem(ad
&0xff);
616 return getmem(ad
&0xff);
621 ad2
= getmem(ad
&0xff);
623 ad2 |
= getmem(ad
&0xff)<<8;
629 ad2 |
= getmem((ad
+1)&0xff)<<8;
630 ad
= cast(ushort)(ad2
+y
);
631 if ((ad2
&0xff00) != (ad
&0xff00)) ++cycles
;
642 void setaddr (ubyte mode
, ubyte val
) {
648 ad |
= 256*getmem(pc
-1);
654 ad |
= 256*getmem(pc
-1);
655 ad2
= cast(ushort)(ad
+x
);
656 if ((ad2
&0xff00) != (ad
&0xff00)) --cycles
;
668 setmem(ad
&0xff, val
);
678 void putaddr (ubyte mode
, ubyte val
) {
684 ad |
= getmem(pc
++)<<8;
690 ad |
= getmem(pc
++)<<8;
691 ad2
= cast(ushort)(ad
+x
);
697 ad |
= getmem(pc
++)<<8;
698 ad2
= cast(ushort)(ad
+y
);
699 if ((ad2
&0xff00) != (ad
&0xff00)) ++cycles
;
711 setmem(ad
&0xff, val
);
717 setmem(ad
&0xff, val
);
723 ad2
= getmem(ad
&0xff);
725 ad2 |
= getmem(ad
&0xff)<<8;
732 ad2 |
= getmem((ad
+1)&0xff)<<8;
733 ad
= cast(ushort)(ad2
+y
);
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
);
760 return getmem(0x100+s
);
764 void branch (int flag
) {
765 byte dist
= cast(byte)getaddr(imm
);
766 wval
= cast(ushort)(pc
+dist
);
768 cycles
+= ((pc
&0x100) != (wval
&0x100) ?
2 : 1);
774 // ----------------------------------------------------- ffentliche Routinen
775 public void cpuReset () {
779 //pc = getaddr(0xfffc); //???BUG???
780 pc
= cast(ushort)(getmem(0xfffc)|
(getmem(0xfffd)<<8));
784 public void cpuResetTo (ushort npc
) {
794 public int cpuParse () {
796 ubyte opc
= getmem(pc
++);
797 ubyte cmd
= opcodes
[opc
];
798 ubyte addr
= modes
[opc
];
801 wval
= cast(ushort)a
+getaddr(addr
)+(p
&FLAG_C ?
1 : 0);
802 setflags(FLAG_C
, wval
&0x100);
804 setflags(FLAG_Z
, !a
);
805 setflags(FLAG_N
, a
&0x80);
806 setflags(FLAG_V
, (!!(p
&FLAG_C
))^
(!!(p
&FLAG_N
)));
809 bval
= getaddr(addr
);
811 setflags(FLAG_Z
, !a
);
812 setflags(FLAG_N
, a
&0x80);
815 wval
= getaddr(addr
);
817 setaddr(addr
, cast(ubyte)wval
);
818 setflags(FLAG_Z
, !wval
);
819 setflags(FLAG_N
, wval
&0x80);
820 setflags(FLAG_C
, wval
&0x100);
847 bval
= getaddr(addr
);
848 setflags(FLAG_Z
, !(a
&bval
));
849 setflags(FLAG_N
, bval
&0x80);
850 setflags(FLAG_V
, bval
&0x40);
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
);
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
);
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
);
898 bval
= getaddr(addr
);
901 setflags(FLAG_Z
, !bval
);
902 setflags(FLAG_N
, bval
&0x80);
907 setflags(FLAG_Z
, !x
);
908 setflags(FLAG_N
, x
&0x80);
913 setflags(FLAG_Z
, !y
);
914 setflags(FLAG_N
, y
&0x80);
917 bval
= getaddr(addr
);
919 setflags(FLAG_Z
, !a
);
920 setflags(FLAG_N
, a
&0x80);
923 bval
= getaddr(addr
);
926 setflags(FLAG_Z
, !bval
);
927 setflags(FLAG_N
, bval
&0x80);
932 setflags(FLAG_Z
, !x
);
933 setflags(FLAG_N
, x
&0x80);
938 setflags(FLAG_Z
, !y
);
939 setflags(FLAG_N
, y
&0x80);
944 wval |
= 256*getmem(pc
++);
951 pc |
= 256*getmem(wval
+1);
959 push(cast(ubyte)(pc
+2));
960 push(cast(ubyte)((pc
+2)>>8));
962 wval |
= 256*getmem(pc
++);
967 setflags(FLAG_Z
, !a
);
968 setflags(FLAG_N
, a
&0x80);
972 setflags(FLAG_Z
, !x
);
973 setflags(FLAG_N
, x
&0x80);
977 setflags(FLAG_Z
, !y
);
978 setflags(FLAG_N
, y
&0x80);
981 //bval = wval = getaddr(addr);
982 bval
= getaddr(addr
);
983 wval
= cast(ubyte)bval
;
985 setaddr(addr
, cast(ubyte)wval
);
986 setflags(FLAG_Z
, !wval
);
987 setflags(FLAG_N
, wval
&0x80);
988 setflags(FLAG_C
, bval
&1);
994 bval
= getaddr(addr
);
996 setflags(FLAG_Z
, !a
);
997 setflags(FLAG_N
, a
&0x80);
1009 setflags(FLAG_Z
, !a
);
1010 setflags(FLAG_N
, a
&0x80);
1018 bval
= getaddr(addr
);
1019 ubyte c
= !!(p
&FLAG_C
);
1020 setflags(FLAG_C
, bval
&0x80);
1023 setaddr(addr
, bval
);
1024 setflags(FLAG_N
, bval
&0x80);
1025 setflags(FLAG_Z
, !bval
);
1028 bval
= getaddr(addr
);
1029 ubyte c
= !!(p
&FLAG_C
);
1030 setflags(FLAG_C
, bval
&1);
1033 setaddr(addr
, bval
);
1034 setflags(FLAG_N
, bval
&0x80);
1035 setflags(FLAG_Z
, !bval
);
1038 // NEU, rti wie rts, au..r das alle register wieder vom stack kommen
1045 //write_console("NMI EXIT!");
1048 wval
= cast(ushort)(cast(ushort)256*cast(ushort)pop());
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
)));
1064 setflags(FLAG_C
, 1);
1068 setflags(FLAG_D
, 1);
1072 setflags(FLAG_I
, 1);
1086 setflags(FLAG_Z
, !x
);
1087 setflags(FLAG_N
, x
&0x80);
1092 setflags(FLAG_Z
, !y
);
1093 setflags(FLAG_N
, y
&0x80);
1098 setflags(FLAG_Z
, !x
);
1099 setflags(FLAG_N
, x
&0x80);
1104 setflags(FLAG_Z
, !a
);
1105 setflags(FLAG_N
, a
&0x80);
1114 setflags(FLAG_Z
, !a
);
1115 setflags(FLAG_N
, a
&0x80);
1123 public int cpuJSR (ushort npc
, ubyte na
) {
1133 while (pc
) ccl
+= cpuParse();
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));
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];
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
{
1186 ubyte sub_song_start
;
1187 ubyte max_sub_songs
;
1190 const(char)[] author
;
1191 const(char)[] copyright
;
1192 char[32*3] nacbuf
= 0;
1196 public ubyte c64SidGetSpeed (VFile fl
) {
1200 fl
.rawReadExact(sign
[]);
1201 if (sign
!= "PSID") throw new Exception("invalid file");
1204 return fl
.readNum
!ubyte;
1208 public void c64SidLoad (VFile fl
, out SidSong song
) {
1211 const(char)[] loadStr (uint ofs
) {
1213 fl
.rawReadExact(song
.nacbuf
[nacpos
..nacpos
+32]);
1214 const(char)[] res
= song
.nacbuf
[nacpos
..nacpos
+32];
1216 foreach (immutable idx
, char ch
; res
) if (ch
== 0) return res
[0..idx
];
1223 fl
.rawReadExact(sign
[]);
1224 if (sign
!= "PSID") throw new Exception("invalid file");
1227 song
.name
= loadStr(0x16);
1229 song
.author
= loadStr(0x36);
1231 song
.copyright
= loadStr(0x56);
1233 auto sidmem
= new ubyte[](65536);
1234 scope(exit
) delete sidmem
;
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;