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
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
50 ; Pointer to order (temp).
53 ; Pointer to pattern (temp).
56 ; Pointer to pattern pointer table.
59 ; Pointer to instrument table.
62 .
public instrument_table
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
76 .ifdef PATTERN_ROW_CALLBACK_SUPPORT
77 pattern_row_callback .
ptr
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
90 .ifdef ORDER_SEEKING_SUPPORT
91 .
public sequencer_seek_order_relative
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
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
109 .
macro fetch_order_byte
110 ldy tracks.order.pos
,x
111 inc tracks.order.pos
,x
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
121 .
proc fetch_pattern_byte
122 ldy tracks.pattern.pos
,x
124 inc tracks.pattern.pos
,x
127 inc tracks.pattern.
ptr.hi
,x
131 ; Updates sequencer tracks.
140 ldx #sizeof track_state
* 4
143 ; if order position is $FF, track isn't used
144 lda tracks.order.pos
,x
152 ; check if reached new row
153 .ifdef BULLET_TIME_SUPPORT
161 .ifdef BULLET_TIME_SUPPORT
173 .ifdef PATTERN_ROW_CALLBACK_SUPPORT
174 cpx #sizeof track_state
* 4
176 lda pattern_row_callback.hi
178 jsr call_pattern_row_callback
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
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
200 ; play same pattern again
201 inc tracks.pattern.pos
,x
; skip pattern row count byte
205 ; fetch and process order data
211 ; pattern number in lower 7 bits
212 inc tracks.pattern.loop_count
,x
; = 1 (play pattern once)
217 lda
[pattern_table
],y
219 sta tracks.pattern.
ptr.lo
,x
221 lda
[pattern_table
],y
222 sta tracks.pattern.
ptr.hi
,x
227 + jsr fetch_pattern_byte
228 sta tracks.pattern.row_count
,x
229 jmp maybe_fetch_row_status
231 ; process special order entry
234 bcc set_pattern_loop_count
246 bcs stop_playing
; $FF = stop playing the track
255 sta tracks.order.loop_count
,x
256 lda tracks.order.pos
,x
257 sta tracks.order.loop_pos
,x
261 dec tracks.order.loop_count
,x
263 lda tracks.order.loop_pos
,x
264 sta tracks.order.pos
,x
269 sta tracks.pattern.transpose
,x
274 sta tracks.order.pos
,x
279 sta tracks.order.pos
,x
282 set_pattern_loop_count:
284 sta tracks.pattern.loop_count
,x
288 lda tracks.pattern.
ptr.lo
,x
290 lda tracks.pattern.
ptr.hi
,x
293 maybe_fetch_row_status:
294 .ifdef ORDER_SEEKING_SUPPORT
295 lda pattern_processing_enabled
298 lda tracks.pattern.row
,x
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
310 .ifdef ORDER_SEEKING_SUPPORT
311 lda pattern_processing_enabled
315 and tracks.pattern.row_status
,x
318 ; fetch and process pattern data
320 jsr fetch_pattern_byte
321 jsr process_pattern_byte
322 bcs pattern_fetch_loop
327 sbc #sizeof track_state
335 .
proc process_pattern_byte
336 cpx #
4*sizeof track_state
338 jmp process_tonal_pattern_byte
339 + jmp process_dmc_pattern_byte
342 ; Loads the sequencer tracks/order tables/pattern table.
343 ; Params: A = Low address of song structure
344 ; Y = High address of song structure
354 sta tracks.order.pos
,x
361 .ifdef BULLET_TIME_SUPPORT
369 sta tracks.pattern.row_count
,x
370 sta tracks.pattern.loop_count
,x
372 sta tracks.pattern.row
,x
375 adc #sizeof track_state
377 cpx #
5*sizeof track_state
382 sta instrument_table.lo
385 sta instrument_table.hi
393 ; advance order pointer to actual order
401 .ifdef ORDER_SEEKING_SUPPORT
403 sta pattern_processing_enabled
408 .
proc set_track_speed
413 ; Sets speed of all tracks.
414 ; Only works if the speed command is used on channel 4 (DMC)!
416 .
proc set_all_tracks_speed
432 .ifdef PATTERN_ROW_CALLBACK_SUPPORT
433 .
proc set_pattern_row_callback
434 sta pattern_row_callback.lo
435 sty pattern_row_callback.hi
439 .
proc call_pattern_row_callback
440 jmp [pattern_row_callback
]
444 .ifdef ORDER_SEEKING_SUPPORT
445 ; A = number of patterns to skip, starting from current order table position
446 .
proc sequencer_seek_order_relative
448 lda #
0 : sta pattern_processing_enabled
451 - lda tracks.order.pos
,x
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
461 .ifdef BULLET_TIME_SUPPORT
468 + txa
: clc : adc #sizeof track_state
: tax
469 cpx #
5*sizeof track_state
474 lda #
1 : sta pattern_processing_enabled