-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathSwinSID88_reconstructed.asm
1457 lines (1342 loc) · 47.8 KB
/
SwinSID88_reconstructed.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;############################################################################
; SWINSID
;
; This file is a reconstruction of the SwinSID Nano source code. It is based
; on the last "Lazy Jones fix" firmware
;
; Original SwinSID firmware was developed by Swinkels
; Lazy Jones fix developed by Máté "CodeKiller" Sebök
;
; Source code reconstruction by Daniël Mantione
;
; Set your editor tab size to 4.
;
;############################################################################
#include <avr/io.h>
#include "avr_mcu_section_asm.h"
; Generate .mmcu section for SimAVR
AVR_MCU 32000000 atmega88
AVR_MCU_VOLTAGES 5000 5000 5000
.section .text
; Convenient symbols for port numbers when using in/out
p_PINB = PINB - __SFR_OFFSET
p_DDRB = DDRB - __SFR_OFFSET
p_PORTB = PORTB - __SFR_OFFSET
p_PINC = PINC - __SFR_OFFSET
p_DDRC = DDRC - __SFR_OFFSET
p_PORTC = PORTC - __SFR_OFFSET
p_PIND = PIND - __SFR_OFFSET
p_DDRD = DDRD - __SFR_OFFSET
p_PORTD = PORTD - __SFR_OFFSET
p_EIMSK = EIMSK - __SFR_OFFSET
p_TCCR0A = TCCR0A - __SFR_OFFSET
p_TCCR0B = TCCR0B - __SFR_OFFSET
p_OCR0A = OCR0A - __SFR_OFFSET
p_SPL = SPL - __SFR_OFFSET
p_SPH = SPH - __SFR_OFFSET
p_SREG = SREG - __SFR_OFFSET
;
; bit numbers:
;
b0 = 0x00
b1 = 0x01
b2 = 0x02
b3 = 0x03
b4 = 0x04
b5 = 0x05
b6 = 0x06
b7 = 0x07
;---------------------------------------
;
; GLOBAL REGISTERS
;
; R11 : Used by CS irq handler to temporary store data bus
; R12 : Used by CS irq handler as temp register
; R26/XL : Used by CS irq handler to create pointer to SID register being written.
; R27/XH : Fixed to SRAM area where SID registers are stored.
; R28/YL : CS irq handler stores R26 into this if D0=1. Purpose unknown.
.org 0x000,0xff
irq_reset:
rjmp reset
; Explicitely set the current address to make the assembler complain if above code
; is modified and increases in length.
.org 0x002,0xff
irq_chipselect:
; CPU generates INT0 interrupt and starts execution here when the
; SID chipselect line drops low
;
; The SID registers are stored in SRAM. The high byte, XH or r27 is global and
; never modified. The following code writes the address bus to XL or r26, making
; X a pointer to the right register in SRAM and then stores the data bus D0..D7
; to that memory location.
;
; AVR finishes current instruction on irq then needs 3 cycles before execution.
; In practice this means 4-6 cycles latency. Assume CS happens on rise of PHI2 + 80ns
; and we are running at 32MHz, so a cycle takes 31.25 ns. This means that execution starts
; here on rise PHI2 + 205-268ns. The number at the start of the comment below is the lower
; bound (rounded to whole ns) when the instruction starts add 62.5ns to get the upper bound.
; The second number is the number of clock cycles the instruction will take.
#ifdef LAZY_JONES_FIX
in r26,p_PINC ; 205 1 Read port C (PC0..PC4 = A0..A4, PC5 = D2, PC6 = reset)
in r11,p_PIND ; 236 1 Read port D (PD0..PD7 = D0..D7, except PD2 which is CS)
sbic p_PINB,b5 ; 268 1/2 If RW=0 (thus a write to sid) skip the RETI.
reti ; 299 1 Ignore reads from the SID.
in r12,p_SREG ; 330 1 Save AVR flags register SREG
bst r26,b5 ; 361 1 Load D2 into T flag
bld r11,b2 ; 393 1 Store T into bit 2, so r11 contains full D0..D7
andi r26,0x1f ; 424 1 Keep bits 0..4, so r26 contains A0..A4.
st X,r11 ; 455 2 Store read value from databus in correct SID register
out p_SREG,r12 ; 518 1 Restore AVR flags register
sbrs r11,b0 ; 549 1/2 Skip the mov if D0=1
mov r28,r26 ; 580 1 Copy A0..A4 to r28/yl. This is for the Lazy Jones fix
; Further checks performed outside IRQ handler.
reti ; 611 1
#else
#ifdef SWINKELS_20141027
in r26,p_PINC ; 205 1 Read port C (PC0..PC4 = A0..A4, PC5 = D2, PC6 = reset)
in r11,p_PIND ; 236 1 Read port D (PD0..PD7 = D0..D7, except PD2 which is CS)
in r12,p_SREG ; 268 1 Save AVR flags register SREG
bst r26,b5 ; 299 1 Load D2 into T flag
bld r11,b2 ; 330 1 Store T into bit 2, so r11 contains full D0..D7
andi r26,0x1f ; 361 1 Keep bits 0..4, so r26 contains A0..A4.
st X,r11 ; 393 2 Store read value from databus in correct SID register
subi r26,0xe0 ; 455 1 point to table of modified registers
sbrs r11,b0 ; 486 1/2 If bit 0 set (gate bit), skip next st (Lazy Jones Fix)
st X,r26 ; 518 1 mark register modified
out p_SREG,r12 ; 549 1 Restore AVR flags register
reti ; 580 1
#else
in r26,p_PINC ; 205 1 Read port C (PC0..PC4 = A0..A4, PC5 = D2, PC6 = reset)
in r11,p_PIND ; 236 1 Read port D (PD0..PD7 = D0..D7, except PD2 which is CS)
sbrc r11,b2; ; 268 1/2 Skip next jmp if CS low (redundant, irq means it is low)
rjmp no_cs ; 299 1 Get out of here if CS high
in r12,p_SREG ; 330 1 Save AVR flags register SREG
bst r26,b5 ; 361 1 Load D2 into T flag
bld r11,b2 ; 393 1 Store T into bit 2, so r11 contains full D0..D7
andi r26,0x1f ; 424 1 Keep bits 0..4, so r26 contains A0..A4.
st X,r11 ; 455 2 Store read value from databus in correct SID register
out p_SREG,r12 ; 518 1 Restore AVR flags register
no_cs:
reti ; 549 1
#endif
#endif
; Note that in a computer with a 6502 styled bus, you are expected to read the bus when PHI2
; falls. Due to interrupt latency this is next to impossible in a microcontroller, so the
; triggering on PHI2 and depending on the correct latency is a necessary alternatice.
; The MOS 6510 datasheet specifies the moment the data bus is stable in the value T_MDS.
; It specifies a typical 150ns and max 200ns. This means that the above code is compliant
; with 6510 timing.
; Explicitely set the current address to make the assembler complain if above code
; is modified and increases in length.
.org 0x01c,0x00
irq_timer0_compa:
; The timer 0 compare match A interrupt for every sample. It writes the computed
; sample in sample_l/sample_h to the PWN registers of timer 1, which means the audio
; output pins PB1/PB2 are updated.
;
; NOTE: This interrupt service routine is problematic: Because it disables interrupts
; while it runs, if a chipselect occurs while it is running, the SwinSID will handle the
; chipselect interrupt way too late and will read garbage from the bus.
sei
rjmp nexti1 ; Looks like a NOP?
nexti1:
push r23
lds r23,sample_h ; Get high byte of sample
sts OCR1AL,r23 ; Write to output compare register A
lds r23,sample_l ; Get low byte of sample
sts OCR1BL,r23 ; Write to output compare register B
ldi r23,0xff
sts sample_written,r23 ; Tell main loop to generate new sample
pop r23
reti
;****************************************************************************
; Macro for 8-bit signed saturated addition
; This macro adds the number b to a
;****************************************************************************
.macro satadds a b
add \a ,\b
brvc no_overflow\@
brpl underflow\@
; Overflow
ldi \a ,127
rjmp no_overflow\@
underflow\@:
ldi \a ,-128
no_overflow\@:
.endm
.macro satadds_crazy a b
add \a ,\b
mov r23,\a ; ??? Why just why move to r23 and back later?
brvc no_overflow\@
brpl underflow\@
; Overflow
ldi r23,127
rjmp no_overflow\@
underflow\@:
ldi r23,-128
no_overflow\@:
mov \a , r23
.endm
;****************************************************************************
; Macro for 16-bit signed saturated addition
; This macro adds the number bh:bl to ah:al
;
; The implementation of this is broken, because in case of overflow, the low
; byte is left unmodified. It might be on purpose, because the 6581 SID
; filter is known to behave weird in case of saturation, but I seriously
; doubt it because the SID filter is an analog circuit and does not
; behave like an ignored low byte. Most likely it is an unintentional bug.
;****************************************************************************
.macro satadds16 ah al bh bl
add \al ,\bl
adc \ah ,\bh
brvc no_overflow\@
brpl underflow\@
ldi \ah ,127
rjmp no_overflow\@
underflow\@:
ldi \ah ,-128
no_overflow\@:
.endm
; Crazy version with same functionality that uses r23 as temp register
.macro satadds16_crazy ah al bh bl
add \al ,\bl
adc \ah ,\bh
mov r23,\ah ; ??? Why just why move to r23 and back later?
brvc no_overflow\@
brpl underflow\@
ldi r23 ,127
rjmp no_overflow\@
underflow\@:
ldi r23 ,-128
no_overflow\@:
mov \ah ,r23
.endm
;****************************************************************************
; Macro for 8-bit signed saturated subtractionm
; This macro subtracts the number b from a
;****************************************************************************
.macro satsubs a b
sub \a ,\b
brvc no_overflow\@
brpl underflow\@
; Overflow
ldi \a ,127
rjmp no_overflow\@
underflow\@:
ldi \a ,-128
no_overflow\@:
.endm
;****************************************************************************
; Macro for 16-bit signed saturated subtraction
; This macro subtracts the number bh:bl from ah:al
;
; The implementation of this is broken, because in case of overflow, the low
; byte is left unmodified. It might be on purpose, because the 6581 SID
; filter is known to behave weird in case of saturation, but I seriously
; doubt it because the SID filter is an analog circuit and does not
; behave like an ignored low byte. Most likely it is an unintentional bug.
;****************************************************************************
.macro satsubs16 ah al bh bl
sub \al , \bl
sbc \ah , \bh
mov r23,\ah ; ??? Why just why move to r23 and back later?
brvc no_overflow\@
brpl underflow\@
ldi r23,127
rjmp no_overflow\@
underflow\@:
ldi r23,-128
no_overflow\@:
mov \ah ,r23 ; Move back to r5.
.endm
;****************************************************************************
; The following macro generates sample data for a single voice.
; The two parameters are the voice number and its buddy (for ring modulation
; and hard sync). The macro accesses variables through textual expansion.
; After execution of the macro, rhe sample value is returned in r1:r0
;
; Example: genvoice 3 2
; ... generates a sample for voice 3 its buddy is voice 2.
.macro gen_voice v b
;************************************************************************
; Oscillator
;************************************************************************
lds r23,ctrl\v
sbrs r23,1 ; If voice 3 syncronized with voice 2 (bit 1), skip the rjmp
rjmp osc_nosync\v
tst r20 ; Need to synchronize?
breq osc_nosync\v
; Synchronize!
clr r23
sts accu\v\()m,r23
sts accu\v\()h,r23
osc_nosync\v :
; Compute accu3:=accu3+24*freq
; r20 will become non-zero when accu3 overflows
;
; Freq is added 24 times, because SID runs at 1MHz, while our sample rate
; is 1/24 MHz (41116 Hz), so we have to add 24 times to replicate the SID
; oscillator behaviour.
;
; First step: r22:r21 = freqh3 * 12 + hi8(freql3 * 12)
; r0 = lo8(freql3 * 12)
lds r14,freqh\v
lds r13,freql\v
clr r19
ldi r23,12
mul r14,r23
mov r22,r1
mov r21,r0
mul r13,r23
add r21,r1
adc r22,r19
; Second step: Add r22:r21:r0 twice to accumulator
lds r19,accu\v\()l
lds r13,accu\v\()m
lds r14,accu\v\()h
clr r20
add r19,r0
adc r13,r21
adc r14,r22
rol r20
mov r23,r14 ; ??? Save high byte before first add
add r19,r0
adc r13,r21
adc r14,r22
rol r20
sts accu\v\()l,r19
sts accu\v\()m,r13
sts accu\v\()h,r14
mov r13,r23 ; ??? Saved high byte before first add
;************************************************************************
; ADSR
;************************************************************************
;
lds r18,envelope_val\v
#ifndef SWINKELS_20141027
lds r15,ctrl\v
#endif
lds r21,ad\v
lds r22,sr\v
lds r25,previous_ctrl\v
#ifdef SWINKELS_20141027
lds r15,ctrl\v
#endif
sts previous_ctrl\v ,r15
mov r23,r25
andi r23,0xf0 ; Was there a waveform enabled in previous sample?
brne wave_on\v ; Yes then skip.
mov r23,r15 ; ??? why temp move to r23?
andi r23,0x0f ; If no, then waveform bits in current sample don't matter
mov r15,r23 ; move back to r15
wave_on\v :
eor r25,r15 ; Compare previous ctrl with current ctrl
bst r25,b0 ; Has the gate bit been changed?
#ifdef LAZY_JONES_FIX
brts gate_changed\v ; Yes, then skip
; Gate bit not changed
cpi r28,(7*(\v - 1) + 4) ; Was ctrl\v the last register written?
brne gate_unchanged\v ; No, then skip gate handling
clr r28 ; Clear r28/yl
bst r15,b0 ; Was the gate turned on?
#endif
#ifdef SWINKELS_20141027
brts gate_changed\v ; Yes, then skip
; Gate bit not changed
lds r23,ctrl\v + 0x20
tst r23
breq gate_unchanged\v
clr r23
sts ctrl\v + 0x20,r23
bst r15,b0
#endif
brtc gate_unchanged\v ; No, then skip gate handling
gate_changed\v :
; Gate bit changed
clr r23
sts rampcounter\v ,r23
sts ramppos\v\()l,r23
sts ramppos\v\()h,r23
bst r15,b0 ; Was gate bit switched on?
brts gate_enabled\v ; Yes, then skip
; Gate bit was disabled
sts envelope_updown\v ,r23 ; Envelope is ramping down
rjmp setup_release\v
gate_enabled\v :
ldi r19,1
sts envelope_updown\v ,r19 ; Envelope is ramping up
rjmp setup_attack\v
setup_release\v :
mov r30,r22 ; Sustain/release
andi r30,0x0f ; Get release duration in r30/zl
ldi r31,hi8(decrel_rates) ; High byte of decay/release table in r31/zh
lsl r30 ; Compute offset
lpm r0,Z+ ; Get low byte of duration
lpm r1,Z ; Get high byte of duration
sts ramplength\v\()l,r0
sts ramplength\v\()h,r1
rjmp decrel_nextcounter\v
setup_decay\v :
clr r23
sts rampcounter\v ,r23
sts ramppos\v\()l,r23
sts ramppos\v\()h,r23
mov r30,r21 ; Attack/decay
andi r30,0x0f ; Get decay duration in r30/zl
ldi r31,hi8(decrel_rates) ; High byte of decay/release table in r31/zh
lsl r30 ; A rate entry is 2 bytes
lpm r0,Z+ ; Get low byte of duration
lpm r1,Z ; Get high byte of duration
sts ramplength\v\()l,r0
sts ramplength\v\()h,r1
rjmp decrel_nextcounter\v
setup_attack\v :
mov r30,r21 ; Attack decay
andi r30,0xf0 ; Mask attach duration
swap r30 ; Attack duration in r30/zl
lsl r30 ; A rate entry is 2 bytes
ldi r31,hi8(decrel_rates)
lpm r0,Z+
lpm r1,Z
sts ramplength\v\()l,r0
sts ramplength\v\()h,r1
rjmp store_progress_end_adsr\v
gate_unchanged\v :
; Gate bit unchanged
;
; Increase ramppos by 1
lds r17,ramppos\v\()l
lds r19,ramppos\v\()h
ldi r23,1
add r17,r23
clr r23
adc r19,r23
; Did we reach end of ramp?
lds r8,ramplength\v\()l
lds r9,ramplength\v\()h
cp r19,r9 ; Compare high byte
breq end_check_l\v ; If equal compare low byte as well
sts ramppos\v\()l,r17
sts ramppos\v\()h,r19
rjmp end_adsr\v ; Not end of ramp
end_check_l\v :
cp r17,r8 ; Compare low byte
breq end_reached\v ; If equal then end of ramp
sts ramppos\v\()l,r17
sts ramppos\v\()h,r19
rjmp end_adsr\v ; Not end of ramp
end_reached\v :
; End of ramp reached
clr r23
sts ramppos\v\()l,r23
sts ramppos\v\()h,r23
lds r19,envelope_updown\v
tst r19 ; Is envelop ramping up?
brne env_rampup\v ; Yes then jump
; If end of ramp and ramping down, we have reached end of release phase.
; We have reached ramplength, increase the ramp counter
lds r17,rampcounter\v
inc r17
lds r23,rampcounterend\v
cp r17,r23 ; Max repeats reached?
breq rdown_ctrend_reached\v ; Yes then branch
sts rampcounter\v ,r17 ; store increased value
rjmp decrel_nextcounter\v
env_rampup\v :
mov r30,r21 ; Attack decay
andi r30,0xf0 ; Mask attack duration
swap r30 ; Attack duration in r30/zl
ldi r31,hi8(decrel_rates)
lsl r30
lpm r0,Z+
lpm r1,Z
sts ramplength\v\()l,r0
sts ramplength\v\()h,r1
ldi r23,255
cp r18,r23 ; End of attack phase reached?
breq endofattack\v
inc r18 ; Envelope level up
rjmp store_progress_end_adsr\v
endofattack\v :
; End of attack phase
clr r19
sts envelope_updown\v ,r19
rjmp setup_decay\v
stepup_release_leveldown\v :
mov r30,r22 ; Sustain/release
andi r30,0x0f ; Mask release duration
ldi r31,hi8(decrel_rates) ; High byte of decay/release table in r31/zh
lsl r30 ; A rate entry is 2 bytes
lpm r0,Z+ ; Get low byte of duration
lpm r1,Z ; Get high byte of duration
sts ramplength\v\()l,r0
sts ramplength\v\()h,r1
rjmp leveldown\v
rdown_ctrend_reached\v :
; We are ramping down and have repeated the ramp sufficient times
clr r23
sts rampcounter\v ,r23
tst r18
breq decrel_nextcounter\v
bst r15,b0 ; Check gate to distinguish between decay and release
brtc stepup_release_leveldown\v ; If release then jump
; We are in decay
mov r23,r22 ; Sustain/release
andi r23,0xf0 ; Max sustain level
mov r0,r23
swap r0 ; Sustain level in r0
or r23,r0 ; Both high & low nibble contain sustain level
cp r18,r23 ; Sustain level recached?
brcs decrel_nextcounter\v ; Don't decrease any further if sustain level reached
leveldown\v :
dec r18 ; Envelope level down
decrel_nextcounter\v :
mov r30,r18
ldi r31,hi8(exptable)
lpm r0,Z
sts rampcounterend\v ,r0
store_progress_end_adsr\v :
sts envelope_val\v ,r18 ; Store adr_progress after modification
end_adsr\v :
;************************************************************************
; Waveform
;************************************************************************
sbrs r15,b7 ; Is noise enabled?
rjmp no_noisewave\v
; Noise waveform is enabled
; seed_c3:=(seed_c3+freqh3) shr 1 + freqh3;
; r23 becomes nonzero if any of the two adds did overflow
; What is likely going on in the following code is that every sample, a
; randomization interation happens on C3. When there is an overflow,
; some bigger randomization iteration happens on seed_a3 and seend_b3
; and the waveform value is updated.
lds r0,seed_c\v
lds r1,freqh\v
clr r23
add r0,r1
rol r23
lsr r1
add r0,r1
rol r23
sts seed_c\v ,r0
breq noise_no_overflow\v ; Branch if no overflow
; Some bit manipulation that looks like a random iteration over
; a random see;
lds r13,seed_a\v
lds r14,seed_b\v
mov r21,r14
mov r22,r14
lsl r21
eor r21,r22
bst r21,b7
bld r13,b0
lsl r13
rol r14
sts seed_a\v ,r13
sts seed_b\v ,r14
; More bit manipulation
clr r23
bst r14,b7
bld r23,b7
bst r14,b5
bld r23,b6
bst r14,b1
bld r23,b5
subi r23,0x80 ; Store new waveform value?
sts waveform_val\v ,r23
rjmp waveval_ready\v
noise_no_overflow\v :
; Load current waveform value
lds r23,waveform_val\v
rjmp waveval_ready\v
test_bit_set\v :
ldi r23,0x00
sts accu\v\()h,r23
sts accu\v\()m,r23
sts accu\v\()l,r23
rjmp build_wavetable_ptrh\v
no_noisewave\v :
sbrc r15,b3 ; If test bit cleared, skip next rjmp
rjmp test_bit_set\v ; If test bit set, reset oscillator (repeats each sample)
build_wavetable_ptrh\v :
ldi r31,0x10 ; base address of wavetable data
rjmp build_wavetable_ptrl\v
#ifndef LAZY_JONES_FIX
; Dead code, purpose unknown
mov r24,r16 ; R16 is only used below, no idea
sbrc r16, 5
add r13, r13
sbrc r16, 6
com r13
lds r23,poty + \v - 1 ; Very weird, these are the read-only registers in the SID
add r13, r23
andi r16,0x0f ; 15
add r16,r31
mov r23,r15
swap r23
andi r23,0x0f ; 15
add r31,r23
rjmp wave_ptr_ready\v
#endif
no_waveform_selected\v :
lds r23,freqh\v
sts waveform_val\v ,r23
subi r23,0x80
rjmp waveval_ready\v
build_wavetable_ptrl\v :
mov r23,r15
swap r23
andi r23,0x0f ; waveform mask into low nibble
breq no_waveform_selected\v
add r31,r23 ; high byte now points to correct wave table
mov r16,r31 ; Save r31
wave_ptr_ready\v:
bst r23,b2 ; Check if pulse is selected
brtc no_pulse_selected\v
; Get the pulse width. Least significant 4 bits are thrown away.
lds r19,pwl\v
andi r19,0xf0 ; Mask middle 4 bits of pulse width
lds r25,pwh\v
andi r25,0x0f ; Mask high 4 bits of pulse widt
add r25,r19 ; Add together
swap r25 ; Swap low & high nibble to get correct pulse width
mov r19,r25
mov r30,r14 ; R14 still contains accu3h (waveform progress value)
; that was loaded quite a bit up from here.
lpm r22,Z ; Get waveform data
cp r30,r19 ; Pulse width threshold exceeded?
brcc pwm_higha\v
clr r22 ; 0 when beyond pulse width threshold
pwm_higha\v :
mov r31,r16 ; ??? r31 is restored, but it wasn't modified!
mov r30,r13 ; Even weirder, this is the saved value from after the first
; add to the accumulator far above.
lpm r21,Z ; Get waveform data again
cp r30,r19 ; Pulse width threshold exceeded?
brcc pwm_highb\v
clr r21
pwm_highb\v :
rjmp waveval_loaded\v
no_pulse_selected\v :
mov r30,r14 ; R14 still contains accu3h (waveform progress value)
; that was loaded quite a bit up from here.
lpm r22,Z ; Get waveform data
mov r31,r16 ; ??? r31 is restored, but it wasn't modified!
mov r30,r13 ; ??? this is the saved value from after the first
; add to the accumulator far above.
lpm r21,Z
rjmp waveval_loaded\v
#ifndef LAZY_JONES_FIX
; Dead code, purpose unknown
subi r21, 128
subi r22, 128
satadds r21,r22
mov r23,r21
rjmp waveval_ready\v
#endif
ringmodulation\v :
; Ring modulate voice with buddy voice
bst r15,b4 ; Triangle enabled?
brtc waveval_postring\v ; If no then ring modulation is ignored
lds r1,accu\b\()h
sbrc r1,b7
com r23
rjmp waveval_postring\v
waveval_loaded\v :
lds r23,waveform_val\v ; save last waveform value
sts waveform_val\v ,r22 ; store new waveform value
; The following code is a mathematically broken way to average, it does:
;
; r23:=(r23+r22) div 2 + r21) div 2 - 128
;
; ... but mathematically this isn't the same as:
;
; r23:=(r23+r22+r21) div 4 - 128 (and you would rather like to divide by 3)
add r23,r22
ror r23
add r23,r21
ror r23
subi r23,0x80
waveval_ready\v :
sbrc r15,b2 ; ring modulation enabled?
rjmp ringmodulation\v ; this rjmp is skipped if not enabled
waveval_postring\v :
mulsu r23,r18 ; Multiply with envelope value (still in r18)
; Divide by 4:
asr r1
ror r0
asr r1
ror r0
clr r7 ; ??? r7 is not used
.endm
;****************************************************************************
; Main mixing loop
;
; This loop is continuously executed to generate samples for all voices
;****************************************************************************
mixing_loop:
; Output of voices that need to be filtered will be accumulated in r2/r3
clr r2
clr r3
; Output of voices that doesn't need to be filtered will be accumulated in r4/r5
clr r4
clr r5
;************************************************************************
; Voice 3
;************************************************************************
gen_voice 3 2
lds r23,reson
sbrs r23,b2 ; Is voice 3 filtered?
rjmp not_filtered3 ; This jump is skipped if filtered
add r2,r0 ; Add to voice data to be filtered
adc r3,r1 ; Add to voice data to be filtered
clr r0
clr r1
not_filtered3:
lds r23,vol_fil
sbrc r23,b7
rjmp voice_muted3
add r4,r0 ; Add to voice data not to be filtered
adc r5,r1 ; Add to voice data not to be filtered
voice_muted3:
;************************************************************************
; Voice 2
;************************************************************************
gen_voice 1 3
lds r23,reson
sbrs r23,b0 ; Is voice 1 filtered?
rjmp not_filtered1 ; This jump is skipped if filtered
add r2,r0 ; Add to voice data to be filtered
adc r3,r1 ; Add to voice data to be filtered
clr r0
clr r1
not_filtered1:
add r4,r0 ; Add to voice data not to be filtered
adc r5,r1 ; Add to voice data not to be filtered
;************************************************************************
; Voice 2
;************************************************************************
gen_voice 2 1
lds r23,reson
sbrs r23,b1 ; Is voice 2 filtered?
rjmp not_filtered2 ; This jump is skipped if filtered
add r2,r0 ; Add to voice data to be filtered
adc r3,r1 ; Add to voice data to be filtered
clr r0
clr r1
not_filtered2:
add r4,r0 ; Add to voice data not to be filtered
adc r5,r1 ; Add to voice data not to be filtered
;************************************************************************
; Filter
;************************************************************************
; The filter computes:
; - the high frequency component in r9
; - the band frequency component in r22:r21
; - the low frequency component in r18:r17
;
; These are then added to the output depending on which components are
; enabled in the vol_fil register
; Calculate high frequency component:
mov r18,r3
lds r19,filter_acc_low_h
satsubs r18,r19 ; Macro for signed saturated subtraction
lds r23,reson
ori r23,0x0f ; Bit 0..3 set, filter resonance in bit 4..7
mov r24,r23
ldi r25,150 ; Multiply by 150
mul r23,r25
mov r23,r1 ; Get high byte
com r23 ; Subtract from 255 (inverts)
lds r19,filter_acc_band_h
mulsu r19,r23
satsubs r18,r1 ; Macro for signed saturated subtraction
mov r9,r18
; The SwinSID only looks at the high byte of the cutoff frequency and
; therefore is inaccurate
lds r23,filterh
sbis p_PINB,b0 ; 6581 mode?
rjmp do_6581_filter
;8580 filter
;cutoff:=cutoff+(cutoff-(cutoff*cutoff) div 256)
mul r23,r23 ; Square
mov r0,r23
sub r0,r1 ; Subtract high byte of square from original value
add r23,r0 ; Add to original value
brcs overflow3
do_6581_filter:
tst r23 ; filterh zero?
brne filterh_nonzero
inc r23 ; make it non-zero
rjmp filterh_nonzero
overflow3:
ldi r23,255 ; If overflow then saturate
filterh_nonzero:
mulsu r18,r23
lds r21,filter_acc_band_l
lds r22,filter_acc_band_h
satadds16 r22 r21,r1 r0 ; Macro for saturated addition r5:r4 := r22:r21 + r1:r0
sts filter_acc_band_l,r21
sts filter_acc_band_h,r22
mulsu r22,r23
lds r17,filter_acc_low_l
lds r18,filter_acc_low_h
satadds16 r18 r17,r1 r0 ; Macro for saturated addition r5:r4 := r18:r17 + r1:r0
sts filter_acc_low_l,r17
sts filter_acc_low_h,r18
lds r2,vol_fil ; Low pass enabled?
sbrs r2,b4 ; Skip next jump if yes
rjmp no_low_pass ; Jump if low pass not enabled
; Add low frequency component
satsubs16 r5 r4,r18 r17 ; Macro for saturated substraction r5:r4 := r5:r4 - r18:r17
no_low_pass:
sbrs r2,b5 ; Band pass enabled?
rjmp no_band_pass ; Jump if low pass not enabled
; Add band frequency component
satadds16_crazy r5 r4,r22 r21 ; Macro for saturated addition r5:r4 := r5:r4 + r22:r21
no_band_pass:
sbrs r2,b6 ; High pass enabled?
rjmp no_high_pass ; Jump if low pass not enabled
; Add high frequency component
satadds_crazy r5,r9 ; Macro for signed saturated addition
no_high_pass:
;************************************************************************
; Master volume
;************************************************************************
lds r23,vol_fil
andi r23,0x0f ; Mask master volume
lds r25,previous_volume
lds r19,volume_change_progress ; Actual linear volume
cp r23,r25 ; Did the volume change?
breq volume_unchanged
; Volume has changed. When the volume changed, a bias is added to the output
; sample for 255 cycles, in order to allow digi playback.
mov r25,r23
sts previous_volume,r25
ldi r19,255 ; If the volume changes, for 255 samples, there is a bias
volume_unchanged:
tst r19
breq volume_change_complete
; Add the bias.
subi r19,0x01
sts volume_change_progress,r19
subi r25,8 ; Make it signed Range becomes -8 to 7
lsl r25 ; Multiply by 8 to get the sample at desired volume
lsl r25
lsl r25
; Add to the current sample
satadds r25,r5 ; Macro for signed saturated addition
mov r5,r25
rjmp sample_not_written
zero_sample:
clr r5
clr r4
rjmp sample_not_written
volume_change_complete:
tst r25
breq zero_sample
sample_not_written:
; Wait until the last sample has been written to PWM
lds r23,sample_written
tst r23 ; Zero?
breq sample_not_written ; If zero then loop
ldi r23,0x80
eor r5,r23
sts sample_h,r5
sts sample_l,r4
clr r23
sts sample_written,r23 ; Reset sample written flag
rjmp mixing_loop
;*****************************************************************************
; Startup code
;*****************************************************************************
reset:
cli
; Set stack pointer to top of SRAM
ldi r23,lo8(RAMEND)
out p_SPL,r23
ldi r23,hi8(RAMEND)
out p_SPH,r23
; Set the high byte of the output compare registers to 0, because timer1
; runs in 8-bit mode (counts only to 255).
; (the low bytes will be used to output samples)
ldi r23,0
sts OCR1AH,r23
sts OCR1BH,r23
; Set timer 1 in fast PWM 8-bit mode, enable PWM on PB1/PB2 and set the
; clock divider to 1 (so timer1 counts at 32MHz).
ldi r23,0xa1
sts TCCR1A,r23
ldi r23,0x09
sts TCCR1B,r23
; Set port C and D to input
ldi r23,0x00
out p_DDRC,r23
out p_DDRD,r23
; Enable built-in pull-up resistors on port C&D
ldi r23,0xff
out p_PORTC,r23
out p_PORTD,r23
; Enable built-in pull-up resistors on PB0 (6581/8580 selection)
ldi r23,0x01
out p_PORTB,r23
; Set PB1/PB2 (PWM audio output) to output
ldi r23,0x06
out p_DDRB,r23
; Initialize timer 0 (timer interrupt)
; The interrupt is triggered 32000000 / 8 / 96 = 41667 times per second
; Set "clear on compare match" mode, disable PWM
; Timer clock source = CLKIO/8
ldi r23,0x02
out p_TCCR0A,r23
ldi r23,0x02
out p_TCCR0B,r23
; OCR0A = 95 ; Reset timer on counter value 95
ldi r23,95
out p_OCR0A,r23
; Enable timer 0 output compare match A interrupt
ldi r23,0x02
sts TIMSK0,r23
; Set voice 1 to 1137.3Hz (between C#6 and D6?)
; 19081 * 1000000 / 16777216 = 1137.3
ldi r17,lo8(19081)
ldi r18,hi8(19081)
sts freql1,r17
sts freqh1,r18
; INT0 (chip select) triggers interrupt on falling edge
ldi r23,0x02
sts EICRA,r23
; Enable external interrupt INT0 (chip select)
ldi r23,0x01
out p_EIMSK,r23
; Set r27/xh (global register) to point to SID registers in SRAM
ldi r27,hi8(freql1)
; Play a triangle wave on voice 1
ldi r23,0x11
sts ctrl1,r23
ldi r23,0x10
sts previous_ctrl1,r23 ; So gate on is detected
; Attack 2ms, decay 1.5s, sustain 0, release 6ms
ldi r23,0x0a
sts ad1,r23
ldi r23,0x00
sts sr1,r23
; Initialize envelope values at zero volume
clr r23
sts envelope_val1,r23