Print

On the Commodore 64, talking about SID DIGI (sample) play routines, there are many forms. What they all have in common is that they need to use a Timer routine to play samples at a certain rate. The time in cycles that it takes for that Timer interrupt driven sample playback determines the maximum sample rate. 

On a PAL machine, one cycle equals 1.015 microsecond. 

This means, if you wish to have a sample play at 8000 hz (update 8000 times a second), you need to call the sample play routine every 1/8000 = 0.000125 seconds. Or 125 microseconds. This is 125/1.015 = ~123 cycles. Meaning the Timer will need to be set to cause the interrupt at $7b cycles. It also means that the sample playback routine should not spend more than 123 cycles to complete in total, otherwise the next update will start when the previous did not finish yet. Also, if any other stuff needs to be done, like scrollers, graphical stuff, or even a whole game running (for example using other interrupts) things can get tricky if the sample play routine is taking too long, leaving no cycles for other things. 

So let's take a look at some sample playback routines used in the Commodore 64 music scene. 

Sample driver from: BMX Kidz
Year: 1987
Author: Rob Hubbard
Samples: 4-bit $D418
Size: 55 bytes
This is the FM-YAM adaptation, but same cycle and byte number. 

 Here's Rob's 6502 code:

	org $b92d
sample_driver
	sta sa+1	; store Accu to pick up at rti 		(3,3) 
	inc $fc		; $fc + 1 to toggle low/high nibble	(5,8)
	lda $fc		; load the toggle			(3,11)
	and #$01	; and with bit 1 (set zero y/n)	        (2,12)
	php		; store processor status for later	(3,15)
samp	lda $a3e5	; load sample from address		(4,19)
	cmp #$1f	; was it $1f? end of sample		(2,21)
	bne lb947	; if it wasn't then do next sample	(3,24)
	plp		; it was. clean proc. status		(4,28-1)
	lda #$00	; stop timer A 				(2,29)
	sta $dd0e	; by setting dd0e to 0			(4,33)
	jmp en		; and leave the routine 		(3,36)
lb947	plp		; restore proc. status			(4,28)
	beq lown	; was zero set? then low nibble		(3,31)
hign	and #$f0	; no, high nibble. isolate that		(2,33-1)
	jmp sid		; jmp to set the sid/fm-yam		(3,36)
lown	asl		; shift left 4 times to make it		(2,33)
	asl		; the high nibble for fm-yam		(2,35)
	asl		;					(2,37)
	asl		;					(2,39)
	inc samp+1	; increase the sample address		(5,44)
	bne sid		; need to increase high byte?		(3,47)
	inc samp+2	; yes, do that				(5,52-1)
sid	sta $df50	; store at data register fm-yam		(4,55,51,40)
en	lda $dd0d	; acknowledge timer interrupt		(4,59,55,44)
sa	lda #$00	; retrieve Accu value			(2,61,57,46)
	rti		; back to location before interrupt	(6,67,63,52) 

So the maximum number of cycles is 67 when the low nibble needs to be played and the high byte of the sample address needs to be increased as well. Otherwise it takes 63 cycles for the low nibble play, and 52 cycles for the high nibble play. 
So this routine will take an average number of 57.5 cycles. But the 67 cycles is the limiting factor. In the hypothetical situation that you would be able to call this routine at that rate, this would then allow a sample rate of 14705 Hz. Of course, in the game intro screen, other things are happening that take cycles. The sample rate for the samples playing never reaches that high maximum rate. Suffice it to say that the maximum sample rate will be determined by the sample routine above, but also cycles needed to do other things!

Sample driver from: Combat School
Year: 1987
Author: Martin Galway
Samples: 4-bit $D418
Size: Varies per instrument 

; sample "instruments" of 256 bytes 

; .C:b77d  A2 00       LDX #$00		; reset x	2, 2
; .C:b77f  BD 9F B9    LDA $B99F,X	; get sample	5, 7
; .C:b782  4A          LSR A		; get high nib	2, 9
; .C:b783  4A          LSR A		;		2, 11
; .C:b784  4A          LSR A		; 		2, 13
; .C:b785  4A          LSR A		; 		2, 15
; .C:b786  20 9A B7    JSR $B79A	; play sample	6, 127
; .C:b789  BD 9F B9    LDA $B99F,X	; get sample	5, 133
; .C:b78c  EA          NOP		; time it 	2, 135
; .C:b78d  EA          NOP		;		2, 137
; .C:b78e  EA          NOP		;		2, 139
; .C:b78f  29 0F       AND #$0F		; get low nib	2, 141
; .C:b791  20 9A B7    JSR $B79A	; play sample	6, 256
; .C:b794  E8          INX		; x + 1		2, 258
; .C:b795  D0 E8       BNE $B77F	; 256 samples 	3, 66559
; .C:b797  4C 0B B7    JMP $B70B	;		3, 66562
; .C:b79a  4C 83 09    STA $D418	; play sample	4, 24 
; .C:b79d  A0 12       LDY #$12		; set delay 18	2, 26 
; .C:b79f  88          DEY		; y -1 		2, 28 
; .C:b7a0  10 FD       BPL $B79F	; if not -1 go	3, 115
; .C:b7a2  60          RTS		; back. 	6, 121   

This sample takes around 67560 microseconds, 0.0676 seconds, when not interrupted. 

Galway's sample driver is rather crude. Samples are not timed using Timer A or B, but instead each sample has a specific routine that is called to generate the $D418 wave. The downside of such a technique is that everything else has to wait many cycles before the instrument is done playing. 

Above is an example of such an instrument. Each sample is played at a rate of 125 and 130 cycles, alternating (on a PAL machine that is fluctating around 8000 Hz). That rate is determined by the Y value in $b79d. The higher the number, the slower the playback rate. 

If samples needed to be longer (like the snare drum) the routine was copied right after the previous one, with one page further for the sample addresses. I also suspect that these samples were "handdrawn" and not actually sampled instruments. 

 

Sample driver from: Turbo Outrun
Year: 1987
Author: Maniacs of Noise
Samples: 4-bit $D418
Size: 69 bytes  

;original
; .C:00a0  D8          CLD		; make sure no decimal	2,2
; .C:00a1  85 0B       STA $0B		; store A for later	3,5
; .C:00a3  AD FE 80    LDA $80FE	; get sample		4,9
; .C:00a6  C9 1F       CMP #$1F		; is it 1f (end)?	2,11
; .C:00a8  D0 0C       BNE $00B6	; if not, play 		2/3,13/14
; .C:00aa  A9 00       LDA #$00		; stop timer A		2,15
; .C:00ac  8D 0E DD    STA $DD0E	;			4,19
; .C:00af  A9 38       LDA #$38		; set filter to enable	2,21
; .C:00b1  8D 18 D4    STA $D418	; low/band pass, vol 8	4,25
; .C:00b4  D0 2A       BNE $00E0	; jump out of here	2/3 28
; .C:00b6  24 0A       BIT $0A		; check nibble toggle	3,17,3
; .C:00b8  30 12       BMI $00CC	; if bit 7, do low	2/3,19,5
; .C:00ba  4A          LSR A		; high nibble sample	2,21,7
; .C:00bb  4A          LSR A		; get it into low 	2,23,9
; .C:00bc  4A          LSR A		; nibble		2,25,11
; .C:00bd  4A          LSR A		;			2,27,13
; .C:00be  EA          NOP		; timing cycles		2,29,15
; .C:00bf  EA          NOP		; or placeholders	2,31,17
; .C:00c0  18          CLC		; make sure to 		2,33,19
; .C:00c1  69 30       ADC #$30		; keep enabling filter	2,35,21
; .C:00c3  8D 18 D4    STA $D418	; and play sample       4,39,25
; .C:00c6  A9 80       LDA #$80		; set nibble toggle	2,41,27
; .C:00c8  85 0A       STA $0A		;			3,44,30
; .C:00ca  D0 14       BNE $00E0	; jump out of here	2/3,47,33
; .C:00cc  29 0F       AND #$0F		; low nibble, isolate	2,22
; .C:00ce  EA          NOP		; placeholders/timers	2,24
; .C:00cf  EA          NOP					2,26
; .C:00d0  18          CLC		; play sample with 	2,2,28
; .C:00d1  69 30       ADC #$30		; low and band filter	2,30
; .C:00d3  8D 18 D4    STA $D418	; on			4,34
; .C:00d6  A9 00       LDA #$00		; set nibble toggle	2,36
; .C:00d8  85 0A       STA $0A		;			3,39
; .C:00da  E6 A4       INC $A4		; calculate address	5,44
; .C:00dc  D0 02       BNE $00E0	; for next sample	2/3,46/47
; .C:00de  E6 A5       INC $A5		;			5,51
; .C:00e0  AD 0D DD    LDA $DD0D	; ack timer interrupt	4,55,51,51
; .C:00e3  A5 0B       LDA $0B		; retrieve A		3,58,54,54
; .C:00e5  40          RTI		; return from interrupt	6,64,60,60

60 cycles low nibble, 64 cycles high nibble max  

This routine is placed at zero page, for speed, which is good, and timed using Timer A. It relies on a toggle to select which 4-bit nibble to play and sets low pass and bas pass filter to on state with each sample played. NOPs are used to time the two nibble routines so each of them takes the same time (to ensure stable sample rate), 60 cycles each. When the sample address crosses a page, 4 cycles are added to the low nibble routine. The theoretical max sample rate / play back speed is then 16420 Hz on a PAL machine. Of course, this will never be attainable, since a lot of other stuff needs to be taken care of as well, either on screen, or at the least playing the SID part of the tune.