Say hello to D-Pad Hero 2 repository
[dpadhero2.git] / sound / mixer.asm
blobe19099fc81a9b330bcc93bb848f22ea628d2e453
2 ; Copyright (C) 2004, 2005 Kent Hansen.
4 ; This file is part of Neotoxin.
6 ; Neotoxin is free software; you can redistribute it and/or modify
7 ; it under the terms of the GNU General Public License as published by
8 ; the Free Software Foundation; either version 2 of the License, or
9 ; (at your option) any later version.
11 ; Neotoxin is distributed in the hope that it will be useful,
12 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ; GNU General Public License for more details.
16 ; You should have received a copy of the GNU General Public License
17 ; along with this program; if not, write to the Free Software
18 ; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 ; Description:
22 ; The mixer is responsible for outputting proper audio.
24 .include "mixer.h"
25 .include "track.h"
27 .dataseg
29 .public mixer .mixer_state
31 .codeseg
33 .if sizeof tonal_state != sizeof envelope_state
34 .error "tonal_state and envelope_state must have the same size"
35 .endif
36 .if sizeof tonal_state != sizeof track_state
37 .error "tonal_state and track_state must have the same size"
38 .endif
40 ;.define NO_SFX
41 ;.define NO_MUTABLE_CHANNELS
43 .public mixer_tick
44 .public mixer_reset
45 .ifndef NO_MUTABLE_CHANNELS
46 .public mixer_get_muted_channels
47 .public mixer_set_muted_channels
48 .endif
49 .public mixer_get_master_vol
50 .public mixer_set_master_vol
51 .public mixer_invalidate_period_save
53 .extrn envelope_tick:proc
54 .extrn effect_tick:proc
55 .extrn sfx_tick:proc
56 .extrn volume_table:byte
57 .extrn sound_status:byte
59 ; Executes one "tick" of the mixer.
60 ; This involves updating volume envelopes, music effects (vibrato, for example),
61 ; and so forth, and most importantly, writing proper values to the audio
62 ; hardware registers.
63 ; The mixer handles updating of sound effects, and selects the proper "audio
64 ; source" (sound effect or music) depending on whether a sound effect is playing
65 ; or not.
67 .proc mixer_tick
68 lda sound_status
69 and #$20 ; paused?
70 bne @@skip
72 ; update volume envelopes
73 ldx #3*sizeof envelope_state
74 - jsr envelope_tick
75 txa
76 sec
77 sbc #sizeof envelope_state
78 tax
79 bpl -
81 ; update tonal effects
82 ldx #3*sizeof tonal_state
83 - jsr effect_tick
84 txa
85 sec
86 sbc #sizeof tonal_state
87 tax
88 bpl -
90 ; update square duties
91 ; ### consider making this a plain effect instead
92 ldx #1*sizeof tonal_state
93 - lda mixer.tonals.square.counter,x
94 beq +
95 dec mixer.tonals.square.counter,x
96 bne +
97 lda mixer.tonals.square.duty_ctrl,x
98 asl
99 asl
100 and #$C0
101 sta mixer.tonals.square.duty,x ; set the new duty
102 + txa
104 sbc #sizeof tonal_state
106 bpl -
108 @@skip:
109 ; write to NES audio regs
111 ; channel 0
112 .ifndef NO_SFX
113 lda mixer.sfx[0].ptr.hi
114 beq +
116 ; sfx
117 ldx #0*sizeof sfx_state
118 jsr sfx_tick
119 jmp ++
121 ; music
123 .endif
124 .ifndef NO_MUTABLE_CHANNELS
125 lda sound_status
127 .endif
128 lda mixer.envelopes[0].master
129 .ifndef NO_MUTABLE_CHANNELS
130 bcc +
131 lda #0 ; the channel is muted
133 .endif
134 and #$F0
135 ora mixer.envelopes[0].vol.int
137 lda volume_table,y
138 ora mixer.master_vol
140 lda volume_table,y
141 ora mixer.tonals[0].square.duty
142 ora #$30
143 sta $4000
144 lda #0
145 sta $4001
146 lda mixer.tonals[0].period.lo
147 sta $4002
148 lda mixer.tonals[0].period.hi
149 cmp mixer.tonals[0].square.period_save
150 beq ++
151 sta $4003
152 sta mixer.tonals[0].square.period_save
154 ; channel 1
156 .ifndef NO_SFX
157 lda mixer.sfx[1].ptr.hi
158 beq +
160 ; sfx
161 ldx #1*sizeof sfx_state
162 jsr sfx_tick
163 jmp ++
165 ; music
167 .endif
168 .ifndef NO_MUTABLE_CHANNELS
169 lda sound_status
172 .endif
173 lda mixer.envelopes[1].master
174 .ifndef NO_MUTABLE_CHANNELS
175 bcc +
176 lda #0 ; the channel is muted
178 .endif
179 and #$F0
180 ora mixer.envelopes[1].vol.int
182 lda volume_table,y
183 ora mixer.master_vol
185 lda volume_table,y
186 ora mixer.tonals[1].square.duty
187 ora #$30
188 sta $4004
189 lda #0
190 sta $4005
191 lda mixer.tonals[1].period.lo
192 sta $4006
193 lda mixer.tonals[1].period.hi
194 cmp mixer.tonals[1].square.period_save
195 beq ++
196 sta $4007
197 sta mixer.tonals[1].square.period_save
199 ; channel 2
201 .ifndef NO_SFX
202 lda mixer.sfx[2].ptr.hi
203 beq +
205 ; sfx
206 ldx #2*sizeof sfx_state
207 jsr sfx_tick
208 jmp ++
210 ; music
212 .endif
213 .ifndef NO_MUTABLE_CHANNELS
214 lda sound_status
218 .endif
219 lda mixer.envelopes[2].master
220 .ifndef NO_MUTABLE_CHANNELS
221 bcc +
222 lda #0 ; the channel is muted
224 .endif
225 and #$F0
226 ora mixer.envelopes[2].vol.int
228 lda volume_table,y
229 ora mixer.master_vol
231 lda volume_table,y
232 beq +
233 ora #$FF
234 + sta $4008
235 lda mixer.tonals[2].period.lo
236 sta $400A
237 lda mixer.tonals[2].period.hi
238 sta $400B
240 ; channel 3
241 .ifndef NO_SFX
242 ++ lda mixer.sfx[3].ptr.hi
243 beq +
245 ; sfx
246 ldx #3*sizeof sfx_state
247 jsr sfx_tick
250 ; music
252 .endif
253 .ifndef NO_MUTABLE_CHANNELS
254 lda sound_status
259 .endif
260 lda mixer.envelopes[3].master
261 .ifndef NO_MUTABLE_CHANNELS
262 bcc +
263 lda #0 ; the channel is muted
265 .endif
266 and #$F0
267 ora mixer.envelopes[3].vol.int
269 lda volume_table,y
270 ora mixer.master_vol
272 lda volume_table,y
273 ora #$30
274 sta $400C
275 lda mixer.tonals[3].period.lo
277 lda mixer.tonals[3].period.hi
278 rol ; period / 128
279 ora mixer.tonals[3].square.duty_ctrl ; bit 7 = RNG mode
280 sta $400E
281 lda #$08
282 sta $400F
284 .endp
286 ; Resets the mixer.
287 ; It sets important fields of the mixer data structures so that things will
288 ; behave correctly on invocations to mixer_tick().
290 .proc mixer_reset
291 lda #$F0
292 sta mixer.master_vol
293 ; zap volume envelopes
294 ldx #3*sizeof envelope_state
295 - lda #0
296 sta mixer.envelopes.phase,x
297 sta mixer.envelopes.vol.int,x
298 lda #$F0
299 sta mixer.envelopes.master,x
302 sbc #sizeof envelope_state
304 bpl -
305 ; zap tonals
306 ldx #3*sizeof effect_state
307 - lda #0
308 sta mixer.tonals.effect.kind,x
309 sta mixer.tonals.period_index,x
310 sta mixer.tonals.period.lo,x
311 sta mixer.tonals.period.hi,x
314 sbc #sizeof effect_state
316 bpl -
317 .ifndef NO_SFX
318 ; zap sound effects
319 ldx #3*sizeof sfx_state
320 - lda #0
321 sta mixer.sfx.ptr.hi,x
324 sbc #sizeof sfx_state
326 bpl -
327 .endif
328 ; custom channel init
329 lda #$80
330 sta mixer.tonals[0].square.period_save
331 sta mixer.tonals[1].square.period_save
332 ; enable sound hardware channels
333 lda #$0F
334 sta $4015
336 .endp
338 .ifndef NO_MUTABLE_CHANNELS
339 .proc mixer_get_muted_channels
340 lda sound_status
341 and #$1F
343 .endp
345 .proc mixer_set_muted_channels
347 lda sound_status
348 and #$E0
349 sta sound_status
351 and #$1F
352 ora sound_status
353 sta sound_status
355 .endp
356 .endif
358 .proc mixer_get_master_vol
359 lda mixer.master_vol
361 .endp
363 .proc mixer_set_master_vol
364 sta mixer.master_vol
366 .endp
368 .proc mixer_invalidate_period_save
369 lda mixer.tonals[0].square.period_save
370 eor #$FF
371 sta mixer.tonals[0].square.period_save
372 lda mixer.tonals[1].square.period_save
373 eor #$FF
374 sta mixer.tonals[1].square.period_save
376 .endp
378 .end