Difference between revisions of "Snippet:Playsong"

From veswiki
Jump to: navigation, search
 
Line 1: Line 1:
 
This code is taken from [[Cartridge:Pro_Football|cart #24]]. It plays a song from memory using the address stored in [[Data counter|DC0]]. It starts a loop through the data, taking two bytes at a time; the first is the duration of the note, and the second is the frequency. It outputs it through the [[port]]s, delays, and takes another two bytes. If it finds the duration byte is equal to 0, it exits, so a 0 byte marks the end of a song. If it finds the frequency byte is 255, it instead generates a pause for the specified duration. (note: using a frequency of 254 ''or'' 255 otherwise outputs the same note) The pausing functionality isn't in the original disassembly, but was added afterwards.
 
This code is taken from [[Cartridge:Pro_Football|cart #24]]. It plays a song from memory using the address stored in [[Data counter|DC0]]. It starts a loop through the data, taking two bytes at a time; the first is the duration of the note, and the second is the frequency. It outputs it through the [[port]]s, delays, and takes another two bytes. If it finds the duration byte is equal to 0, it exits, so a 0 byte marks the end of a song. If it finds the frequency byte is 255, it instead generates a pause for the specified duration. (note: using a frequency of 254 ''or'' 255 otherwise outputs the same note) The pausing functionality isn't in the original disassembly, but was added afterwards.
 +
 +
Second version has some edits and additions B00daW from his Sleizsa-package v.0.3.
  
 
<pre>
 
<pre>
Line 65: Line 67:
  
 
</pre>
 
</pre>
 +
 +
<pre>
 +
Sleizsa-version:
 +
 +
;---------------------------------------------------------------------------
 +
; Play Song Routine / Visuals
 +
;---------------------------------------------------------------------------
 +
; plays a song from a memory location stored in DC0
 +
; (taken from Pro Football)
 +
;
 +
; r0 = unused
 +
; r1 = noise value
 +
; r2 = octave set value
 +
; r3 = octave set value copy
 +
; r4 = delay/tempo
 +
; r5 = delay/tempo copy
 +
; r6 = frequency/pitch
 +
; r7 = pulse value
 +
; r8 = note/rest length
 +
;
 +
; **Leave this Alone!**
 +
 +
playSong:
 +
 +
.playSongDelay1:     
 +
inc ; increase A
 +
bnz .playSongDelay1 ; [Branch if not zero] back to .psdly1
 +
am ; memory addressed by DC0 is added to A, flags set - get duration
 +
lr 8, A ; store note/rest length
 +
bz .playSongEnd ; go to end if zero
 +
lm ; load memory into A (addressed by DC0) - get frequency
 +
lr 6, A ; store frequency
 +
 +
; check to see if we should delay
 +
inc ; increment A to see if 255 (rest) supplied
 +
bnc  .playSongLoop ; if it didn't wrap, play notes
 +
 +
.playSongPause:
 +
clr ; A = $00
 +
 +
.playSongPauseLoop:
 +
inc ; increment A as "rest counter"
 +
bnc .playSongPauseLoop ; loop until counter wraps
 +
ds 8 ; decrement duration of the pause
 +
bnz .playSongPause ; rest until note/rest length counter depleted
 +
 +
; play the next note
 +
br playSong ; loop back and fetch more data
 +
 +
.playNoise
 +
lr A, (IS)
 +
xs 6
 +
sl 1
 +
xs 1
 +
lr 6, A
 +
br .playSound
 +
 +
.playSongLoop
 +
lr A, 7 ; A = instrument value
 +
ci $F0 ; instrument = $F0 (noise)?
 +
bz .playNoise ; if so, play noise. if not, pulse.
 +
 +
.playSound
 +
outs 5 ; send A -> port 5 (to make sound)
 +
lr A, 6 ; load frequency
 +
 +
.playSongDelay2:
 +
inc ; increment A as "frequency speed"
 +
bnz .playSongDelay2 ; loop until frequency rate met
 +
outs 5 ; send A -> port 5 (to make sound)
 +
lr A, 6 ; exchange pitch values to A
 +
ds 2 ; decrement delay for lower octaves
 +
bnz .playSongLoop ; if so, loop.
 +
lr A, 3
 +
lr 2, A ; restore original "octave" value
 +
lr A, 6 ; exchange pitch values to A
 +
 +
.playSongDelay3:
 +
inc ; increase A
 +
bnz .playSongDelay3
 +
ds 4 ; decrement delay for "tempo"
 +
bnz .playSongLoop ; if so, loop.
 +
lr A, 5
 +
lr 4, A ; restore original "tempo" value
 +
ds 8 ; decrement note/rest delay
 +
bnz .playSongLoop
 +
br playSong ; start over - branch to beginning
 +
 +
.playSongEnd:
 +
pop ; return from the subroutine
 +
 +
</pre>
 +
  
 
== How does it sound? ==
 
== How does it sound? ==

Latest revision as of 11:59, 11 November 2013

This code is taken from cart #24. It plays a song from memory using the address stored in DC0. It starts a loop through the data, taking two bytes at a time; the first is the duration of the note, and the second is the frequency. It outputs it through the ports, delays, and takes another two bytes. If it finds the duration byte is equal to 0, it exits, so a 0 byte marks the end of a song. If it finds the frequency byte is 255, it instead generates a pause for the specified duration. (note: using a frequency of 254 or 255 otherwise outputs the same note) The pausing functionality isn't in the original disassembly, but was added afterwards.

Second version has some edits and additions B00daW from his Sleizsa-package v.0.3.

;-----------;
; Play Song ;
;-----------;

; plays a song from memory
; song address stored in DC0
; uses r3, r4, r5, r7

playSong:                       				; taken from Pro Football
	lis	3						; A = 3
	lr	4, A						; A -> r4, r4 = 3

.playSongDelay1:      
	inc							; increase A
	bnz	.playSongDelay1					; [Branch if not zero] back to .psdly1
	am							; Memory adressed by DC0 is added to A, flags set  - get duration data
	lr	7, A						; A -> r7
	bz	.playSongEnd					; Go to end if zero
	lm							; Load memory into A (adressed by DC0) - get frequency data
	lr	5, A						; A -> r5

	; check to see if we should pause
	inc
	bnc  .playSongLoop					; it didn't roll over, play a note instead

.playSongPause:
	li	$ff
	lr	6, A
.playSongPauseLoop:
	ds	6						; pause counter
	bnz	.playSongPauseLoop
	ds	7						; duration of the pause
	bnz	.playSongPause

	; play the next note
	br	playSong

.playSongLoop:
	li	$80						; A= $80 
	outs	5						; Send  A -> port 5
	lr	A, 5						; r5 -> A

.playSongDelay2:
	inc							; increase A
	bnz	.playSongDelay2						; [Branch if not zero] back to .psdly2
	outs	5						; A -> port 5
	lr	A, 5						; r5 -> A

.playSongDelay3:
	inc							; increase A
	bnz	.playSongDelay3					; [Branch if not zero] back to .psdly3
	ds	4						; decrease r4
	bnz	.playSongLoop					; [Branch if not zero] back to .psloop
	lis	3						; A = 3
	lr	4, A						; A -> r4
	ds	7						; decrease r7
	bnz	.playSongLoop						; [Branch if not zero] back to .psloop
	br	playSong					; start over - branch to beginning

.playSongEnd:
	pop							; return from the subroutine

Sleizsa-version:

;---------------------------------------------------------------------------
; Play Song Routine / Visuals
;---------------------------------------------------------------------------
; plays a song from a memory location stored in DC0
; (taken from Pro Football)
;
; r0 = unused
; r1 = noise value
; r2 = octave set value
; r3 = octave set value copy
; r4 = delay/tempo
; r5 = delay/tempo copy
; r6 = frequency/pitch
; r7 = pulse value
; r8 = note/rest length
;
;	**Leave this Alone!**

playSong:

.playSongDelay1:      
	inc					; increase A
	bnz	.playSongDelay1			; [Branch if not zero] back to .psdly1
	am					; memory addressed by DC0 is added to A, flags set - get duration
	lr	8, A				; store note/rest length
	bz	.playSongEnd			; go to end if zero
	lm					; load memory into A (addressed by DC0) - get frequency
	lr	6, A				; store frequency

	; check to see if we should delay
	inc					; increment A to see if 255 (rest) supplied
	bnc  .playSongLoop			; if it didn't wrap, play notes

.playSongPause:
	clr					; A = $00

.playSongPauseLoop:
	inc					; increment A as "rest counter"
	bnc	.playSongPauseLoop		; loop until counter wraps
	ds	8				; decrement duration of the pause
	bnz	.playSongPause			; rest until note/rest length counter depleted

	; play the next note
	br	playSong			; loop back and fetch more data	

.playNoise
	lr	A, (IS)
	xs	6
	sl	1
	xs	1
	lr	6, A
	br	.playSound

.playSongLoop
	lr	A, 7				; A = instrument value
	ci	$F0				; instrument = $F0 (noise)?
	bz	.playNoise			; if so, play noise. if not, pulse.

.playSound	
	outs	5				; send A -> port 5 (to make sound)	
	lr	A, 6				; load frequency

.playSongDelay2:
	inc					; increment A as "frequency speed"
	bnz	.playSongDelay2			; loop until frequency rate met
	outs	5				; send A -> port 5 (to make sound)
	lr	A, 6				; exchange pitch values to A
	ds	2				; decrement delay for lower octaves
	bnz	.playSongLoop			; if so, loop.
	lr	A, 3
	lr	2, A				; restore original "octave" value
	lr	A, 6				; exchange pitch values to A

.playSongDelay3:
	inc					; increase A
	bnz	.playSongDelay3
	ds	4				; decrement delay for "tempo"
	bnz	.playSongLoop			; if so, loop.	
	lr	A, 5				 
	lr	4, A				; restore original "tempo" value
	ds	8				; decrement note/rest delay
	bnz	.playSongLoop
	br	playSong			; start over - branch to beginning

.playSongEnd:
	pop					; return from the subroutine


How does it sound?

Second generation PAL and NTSC machines differ a little in clock frequency, the values doesn't match for both machines. Here's a table for PAL with the frequency contents of each value in this playSong routine, the step size increases the further up you go:

F8-Value	frequency	note
		(Hz)	
	1	197.63	G3+14
	2	194.17	-16
	3	194.88	-9
	4	195.62	-6
	5	196.36	+3
	6	197.31	+11
	7	198.06	+18
	8	198.82	+24
	9	199.58	+31
	10	200.34	+37
	11	201.32	+46
	12	202.09	G#3-47
	13	202.87	-40
	14	203.66	-33
	15	204.44	-27
	16	205.43	-18
	17	206.23	-11
	18	207.03	-5
	19	208.05	+3
	20	208.85	+9
	21	209.68	+16
	22	210.50	+23
	23	211.53	+32
	24	212.36	+38
	25	213.20	+45
	26	214.25	A3-45
	27	215.10	-38
	28	216.16	-30
	29	217.02	-23
	30	217.89	-16
	31	218.97	-8
	32	219.85	-1
	33	221.12	+8
	34	221.83	+14
	35	222.93	+22
	36	223.83	+29
	37	224.94	+38
	38	225.86	+45
	39	226.61	A#3-48
	40	227.76	-39
	41	228.94	-31
	42	229.91	-23
	43	231.03	-15
	44	232.06	-7
	45	233.09	+0
	46	234.41	+9
	47	235.39	+17
	48	236.46	+24
	49	237.69	+33
	50	238.60	+40
	51	239.72	+48
	52	241.06	B3-41
	53	242.09	-34
	54	243.17	-26
	55	244.55	-16
	56	245.88	-7
	57	247.09	+1
	58	248.06	+7
	59	249.40	+17
	60	250.74	+26
	61	252.01	+35
	62	253.41	+44
	63	254.58	C4-47
	64	255.97	-37
	65	257.28	-28
	66	258.61	-20
	67	260.11	-10
	68	260.80	-5
	69	262.62	+6
	70	263.99	+15
	71	265.51	+25
	72	266.85	+34
	73	268.34	+43
	74	269.98	C#4-45
	75	271.49	-35
	76	272.91	-26
	77	274.39	-17
	78	276.04	-7
	79	277.44	+1
	80	278.97	+11
	81	280.49	+20
	82	282.25	+31
	83	283.75	+40
	84	285.51	D4-48
	85	287.00	-39
	86	288.89	-28
	87	290.33	-19
	88	292.27	-8
	89	293.77	+0
	90	295.71	+12
	91	297.24	+20
	92	299.21	+32
	93	301.09	+43
	94	302.64	D#4-47
	95	304.67	-36
	96	306.47	-26
	97	308.54	-14
	98	310.46	-3
	99	312.04	+5
	100	314.46	+18
	101	316.09	+27
	102	317.90	+37
	103	320.07	+49
	104	322.15	E4-39
	105	324.20	-28
	106	326.63	-15
	107	328.88	-3
	108	331.21	+8
	109	332.87	+16
	110	335.47	+30
	111	337.60	+41
	112	339.61	F4-48
	113	342.17	-35
	114	344.80	-22
	115	346.89	-11
	116	349.52	+1
	117	352.10	+14
	118	354.48	+25
	119	357.00	+38
	120	359.81	F#4-48
	121	362.28	-36
	122	364.54	-25
	123	367.24	-12
	124	370.34	+1
	125	373.13	+14
	126	375.88	+27
	127	378.97	+41
	128	382.00	G4-44
	129	385.09	-30
	130	387.80	-18
	131	390.64	-5
	132	393.93	+8
	133	397.12	+22
	134	400.21	+35
	135	402.77	+46
	136	406.64	G#4-36
	137	410.17	-21
	138	412.97	-9
	139	416.96	+6
	140	420.36	+20
	141	424.09	+36
	142	427.67	A4-49
	143	431.23	-34
	144	435.11	-19
	145	438.92	-4
	146	443.29	+12
	147	447.12	+27
	148	449.97	+38
	149	455.10	A#4-41
	150	459.31	-25
	151	463.47	-10
	152	468.12	+7
	153	472.46	+23
	154	477.27	+40
	155	481.98	B4-42
	156	486.27	-26
	157	491.54	-8
	158	496.25	+8
	159	500.77	+23
	160	506.17	+42
	161	511.54	C5-39
	162	517.05	-20
	163	522.58	-2
	164	528.02	+15
	165	533.80	+34
	166	539.37	C#5-47
	167	544.95	-29
	168	550.92	-10
	169	556.89	+7
	170	564.15	+30
	171	570.45	+49
	172	577.08	D5-30
	173	583.86	-10
	174	591.05	+10
	175	597.40	+29
	176	604.65	D#5-49
	177	612.17	-28
	178	620.08	-6
	179	628.17	+16
	180	636.08	+38
	181	644.40	E5-39
	182	652.50	-17
	183	661.27	+5
	184	670.35	+28
	185	679.88	F5-46
	186	689.31	-22
	187	689.90	+1
	188	708.89	+25
	189	719.03	F#5-49
	190	729.74	-24
	191	740.57	+1
	192	751.86	+27
	193	763.37	G5-46
	194	774.81	-20
	195	787.47	+7
	196	799.71	+34
	197	813.22	G#5-36
	198	846.49	-8
	199	840.01	+19
	200	854.95	A5-49
	201	869.59	-20
	202	885.80	+11
	203	901.40	+41
	204	917.70	A#5-27
	205	935.11	+5
	206	953.14	+38
	207	972.19	B5-27
	208	990.96	+5
	209	1011.8	+41
	210	1033.1	C6-22
	211	1054.8	+13
	212	1077.7	C#6-49
	213	1101.8	-10
	214	1126.7	+27
	215	1152.5	D6-33
	216	1180.0	+7
	217	1208.8	+49
	218	1239.1	D#6-7
	219	1270.7	+36
	220	1303.8	E6-19
	221	1338.8	+26
	222	1375.8	F6-26
	223	1414.1	+21
	224	1455.2	F#6-29
	225	1500.3	+23
	226	1547.4	G6-22
	227	1597.1	+31
	228	1650.3	G#6-11
	229	1705.5	+45
	230	1767.4	A6+7
	231	1830.0	A#6-32
	232	1900.6	+33
	233	1976.0	B6+0
	234	2028.5	+45
	235	2146.9	C7+44
	236	2245.9	C#7+22
	237	2350.1	D7+0
	238	2467.3	D#7-15
	239	2597.1	E7-26
	240	2739.8	F7-33
	241	2899.9	F#7-35
	242	3080.9	G7-30
	243	3281.5	G#7-21
	244	3515.6	A7-2
	245	3781.4	A#7+24
	246	4096.3	C8-38
	247	4462.4	C#8+10
	248	4907.9	D#8-24
	249	5445.5	F8-44
	250	6107.0	G8-46
	251	6969.4	A8-17
	252	8100.4	B8+42
	253	9677.9	D#9-49
	254	12022.0	F#9+26



See Also

  • Music_60.h - Contains the correct frequency and duration for notes ranging from G3-G5 in the form of DASM macros, to make song writing easier.