Say hello to D-Pad Hero 2 repository
[dpadhero2.git] / sound / sequencer.asm
blob5b41729b06d365a09b4152cd7cb46ba0efacab31
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 sequencer processes song structures and forwards music commands to the
23 ; relevant sound channel handlers.
25 ; Some quick notes on the "song" format.
26 ; --------------------------------------
27 ; It is inspired by MOD-style formats.
28 ; There are five "tracks", one for each sound channel.
29 ; With each track is associated an order table. At its simplest,
30 ; the order table is a list of "pattern" indices. However, it can only
31 ; contain special commands, such as looping and pattern transpose.
32 ; A pattern consists of a number of rows. On the start of each row
33 ; (tick 0), one or more commands can be executed on that track, such as
34 ; "set instrument", "set effect", "set volume", "play note".
35 ; A simple compression is used to keep patterns small. Every 8th row,
36 ; the next pattern byte is a bitmask for the following 8 rows. If a
37 ; bit is 1, that row has commands to be processed. If it is 0, no
38 ; commands are processed for that row. So, for example, if there is a
39 ; run of 8 rows where no note is played, it will occupy 1 byte with the
40 ; value $00, while a run of 8 rows where a new note is triggered every
41 ; row will occupy 1 byte with the value $FF PLUS however many bytes taken
42 ; by the row commands (determined implicitly when they are processed).
44 ; See also: mixer.asm, effect.asm, envelope.asm, tonals.asm, dmc.asm
46 .include "track.h"
48 .dataseg zeropage
50 ; Pointer to order (temp).
51 order .ptr
53 ; Pointer to pattern (temp).
54 pattern .ptr
56 ; Pointer to pattern pointer table.
57 pattern_table .ptr
59 ; Pointer to instrument table.
60 instrument_table .ptr
62 .public instrument_table
63 .public tracks
65 .dataseg
67 ; Array of track states.
68 tracks .track_state[5]
70 .ifdef ORDER_SEEKING_SUPPORT
71 ; During normal playback, data processing enabled.
72 ; During seeking, it's disabled.
73 pattern_processing_enabled .db
74 .endif
76 .ifdef PATTERN_ROW_CALLBACK_SUPPORT
77 pattern_row_callback .ptr
78 .endif
80 .codeseg
82 .public sequencer_tick
83 .public sequencer_load
84 .public fetch_pattern_byte
85 .public set_track_speed
86 .public set_all_tracks_speed
87 .ifdef PATTERN_ROW_CALLBACK_SUPPORT
88 .public set_pattern_row_callback
89 .endif
90 .ifdef ORDER_SEEKING_SUPPORT
91 .public sequencer_seek_order_relative
92 .endif
94 .extrn bitmasktable:byte
96 .extrn process_dmc_pattern_byte:proc
97 .extrn process_tonal_pattern_byte:proc
99 .ifdef BULLET_TIME_SUPPORT
100 .extrn time_mode:byte
101 .endif
103 ; Gets the next byte from track's order data,
104 ; and increments the order position.
105 ; Params: X = offset of track structure
106 ; Returns: A = order byte
107 ; F = flags
108 ; Destroys: Y
109 .macro fetch_order_byte
110 ldy tracks.order.pos,x
111 inc tracks.order.pos,x
112 lda [order],y
113 .endm
115 ; Gets the next byte from track's pattern data,
116 ; and increments the pattern position.
117 ; Params: X = offset of track structure
118 ; Returns: A = pattern byte
119 ; F = flags
120 ; Destroys: Y
121 .proc fetch_pattern_byte
122 ldy tracks.pattern.pos,x
123 lda [pattern],y
124 inc tracks.pattern.pos,x
125 bne +
126 inc pattern.hi
127 inc tracks.pattern.ptr.hi,x
128 + rts
129 .endp
131 ; Updates sequencer tracks.
133 .proc sequencer_tick
134 lda order.lo
135 ora order.hi
136 bne really_tick
139 really_tick:
140 ldx #sizeof track_state * 4
141 ; do one track
142 track_tick:
143 ; if order position is $FF, track isn't used
144 lda tracks.order.pos,x
145 cmp #$FF
146 bne next_tick
147 jmp next_track
149 next_tick:
150 ; increment tick
151 inc tracks.tick,x
152 ; check if reached new row
153 .ifdef BULLET_TIME_SUPPORT
154 lda time_mode
155 beq +
156 asl tracks.speed,x
158 .endif
159 lda tracks.tick,x
160 cmp tracks.speed,x
161 .ifdef BULLET_TIME_SUPPORT
162 lda time_mode
163 beq +
165 lsr tracks.speed,x
168 .endif
169 bcs next_row
170 jmp next_track
172 next_row:
173 .ifdef PATTERN_ROW_CALLBACK_SUPPORT
174 cpx #sizeof track_state * 4
175 bne +
176 lda pattern_row_callback.hi
177 beq +
178 jsr call_pattern_row_callback
180 .endif
181 ; next row
182 lda #0
183 sta tracks.tick,x ; reset tick
184 inc tracks.pattern.row,x
185 ; check if reached end of pattern
186 lda tracks.pattern.row,x
187 cmp tracks.pattern.row_count,x
188 beq end_of_pattern
189 jmp no_new_pattern
191 end_of_pattern:
192 ; end of pattern
193 lda #0
194 sta tracks.pattern.row,x ; reset row
195 sta tracks.pattern.pos,x
196 ; check if pattern should be looped
197 dec tracks.pattern.loop_count,x
198 beq order_fetch_loop
200 ; play same pattern again
201 inc tracks.pattern.pos,x ; skip pattern row count byte
202 jmp no_new_pattern
204 order_fetch_loop:
205 ; fetch and process order data
206 fetch_order_byte
207 ; ### FIXME
208 cmp #$F0
209 bcs order_special
211 ; pattern number in lower 7 bits
212 inc tracks.pattern.loop_count,x ; = 1 (play pattern once)
214 bcc +
215 inc pattern_table.hi
216 + tay
217 lda [pattern_table],y
219 sta tracks.pattern.ptr.lo,x
220 sta pattern.lo
221 lda [pattern_table],y
222 sta tracks.pattern.ptr.hi,x
223 sta pattern.hi
224 bcc +
225 dec pattern_table.hi
226 ; fetch # of rows
227 + jsr fetch_pattern_byte
228 sta tracks.pattern.row_count,x
229 jmp maybe_fetch_row_status
231 ; process special order entry
232 order_special:
233 cmp #$F0
234 bcc set_pattern_loop_count
235 ; command
236 cmp #$FA
237 beq set_speed
238 cmp #$FB
239 beq set_order_loop
240 cmp #$FC
241 beq loop_order
242 cmp #$FD
243 beq set_transpose
244 cmp #$FE
245 beq set_order_pos
246 bcs stop_playing ; $FF = stop playing the track
248 set_speed:
249 fetch_order_byte
250 sta tracks.speed,x
251 jmp order_fetch_loop
253 set_order_loop:
254 fetch_order_byte
255 sta tracks.order.loop_count,x
256 lda tracks.order.pos,x
257 sta tracks.order.loop_pos,x
258 jmp order_fetch_loop
260 loop_order:
261 dec tracks.order.loop_count,x
262 beq order_fetch_loop
263 lda tracks.order.loop_pos,x
264 sta tracks.order.pos,x
265 jmp order_fetch_loop
267 set_transpose:
268 fetch_order_byte
269 sta tracks.pattern.transpose,x
270 jmp order_fetch_loop
272 set_order_pos:
273 fetch_order_byte
274 sta tracks.order.pos,x
275 jmp order_fetch_loop
277 stop_playing:
278 lda #$FF
279 sta tracks.order.pos,x
280 bne next_track
282 set_pattern_loop_count:
283 and #$7F
284 sta tracks.pattern.loop_count,x
285 jmp order_fetch_loop
287 no_new_pattern:
288 lda tracks.pattern.ptr.lo,x
289 sta pattern.lo
290 lda tracks.pattern.ptr.hi,x
291 sta pattern.hi
293 maybe_fetch_row_status:
294 .ifdef ORDER_SEEKING_SUPPORT
295 lda pattern_processing_enabled
296 beq next_track
297 .endif
298 lda tracks.pattern.row,x
299 and #7
300 bne no_row_status_fetch
302 ; fetch row status for upcoming 8 rows
304 jsr fetch_pattern_byte
305 sta tracks.pattern.row_status,x
308 no_row_status_fetch:
310 .ifdef ORDER_SEEKING_SUPPORT
311 lda pattern_processing_enabled
312 beq next_track
313 .endif
314 lda bitmasktable,y
315 and tracks.pattern.row_status,x
316 beq next_track
318 ; fetch and process pattern data
319 pattern_fetch_loop:
320 jsr fetch_pattern_byte
321 jsr process_pattern_byte
322 bcs pattern_fetch_loop
324 next_track:
327 sbc #sizeof track_state
329 bmi tracks_done
330 jmp track_tick
331 tracks_done:
333 .endp
335 .proc process_pattern_byte
336 cpx #4*sizeof track_state
337 beq +
338 jmp process_tonal_pattern_byte
339 + jmp process_dmc_pattern_byte
340 .endp
342 ; Loads the sequencer tracks/order tables/pattern table.
343 ; Params: A = Low address of song structure
344 ; Y = High address of song structure
346 .proc sequencer_load
347 sta order.lo
348 sty order.hi
349 ldy #0
350 ldx #0
351 ; init one track
352 - lda [order],y
354 sta tracks.order.pos,x
355 cmp #$FF
356 beq +
357 lda [order],y
359 sta tracks.speed,x
360 sta tracks.tick,x
361 .ifdef BULLET_TIME_SUPPORT
362 lda time_mode
363 beq ++
364 asl tracks.tick,x
366 .endif
367 dec tracks.tick,x
368 lda #1
369 sta tracks.pattern.row_count,x
370 sta tracks.pattern.loop_count,x
371 lda #0
372 sta tracks.pattern.row,x
373 + txa
375 adc #sizeof track_state
377 cpx #5*sizeof track_state
378 bne -
379 ; instrument table
380 lda [order],y
382 sta instrument_table.lo
383 lda [order],y
385 sta instrument_table.hi
386 ; pattern ptr table
387 lda [order],y
389 sta pattern_table.lo
390 lda [order],y
392 sta pattern_table.hi
393 ; advance order pointer to actual order
396 adc order.lo
397 sta order.lo
398 bcc +
399 inc order.hi
401 .ifdef ORDER_SEEKING_SUPPORT
402 lda #1
403 sta pattern_processing_enabled
404 .endif
406 .endp
408 .proc set_track_speed
409 sta tracks.speed,x
411 .endp
413 ; Sets speed of all tracks.
414 ; Only works if the speed command is used on channel 4 (DMC)!
415 ; A = new speed
416 .proc set_all_tracks_speed
417 sta tracks[0].speed
418 sta tracks[1].speed
419 sta tracks[2].speed
420 sta tracks[3].speed
421 sta tracks[4].speed
424 sbc #1
425 sta tracks[0].tick
426 sta tracks[1].tick
427 sta tracks[2].tick
428 sta tracks[3].tick
430 .endp
432 .ifdef PATTERN_ROW_CALLBACK_SUPPORT
433 .proc set_pattern_row_callback
434 sta pattern_row_callback.lo
435 sty pattern_row_callback.hi
437 .endp
439 .proc call_pattern_row_callback
440 jmp [pattern_row_callback]
441 .endp
442 .endif
444 .ifdef ORDER_SEEKING_SUPPORT
445 ; A = number of patterns to skip, starting from current order table position
446 .proc sequencer_seek_order_relative
447 -- pha
448 lda #0 : sta pattern_processing_enabled
449 jsr sequencer_tick
450 ldx #0
451 - lda tracks.order.pos,x
452 cmp #$FF
453 beq +
454 ; set current row to row_count - 1
455 lda tracks.pattern.row_count,x
456 sta tracks.pattern.row,x
457 dec tracks.pattern.row,x
458 ; set current tick to speed - 1
459 lda tracks.speed,x
460 sta tracks.tick,x
461 .ifdef BULLET_TIME_SUPPORT
462 lda time_mode
463 beq ++
464 asl tracks.tick,x
466 .endif
467 dec tracks.tick,x
468 + txa : clc : adc #sizeof track_state : tax
469 cpx #5*sizeof track_state
470 bne -
472 sec : sbc #1
473 bpl --
474 lda #1 : sta pattern_processing_enabled
476 .endp
477 .endif
479 .end