├── ADVM_old.zip
├── audiotest.vms
├── soundtest_dot_s_screen.png
├── sfr.i
├── README.md
├── audiotest.s
└── ADVM.s
/ADVM_old.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jvsTSX/ADVM/HEAD/ADVM_old.zip
--------------------------------------------------------------------------------
/audiotest.vms:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jvsTSX/ADVM/HEAD/audiotest.vms
--------------------------------------------------------------------------------
/soundtest_dot_s_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jvsTSX/ADVM/HEAD/soundtest_dot_s_screen.png
--------------------------------------------------------------------------------
/sfr.i:
--------------------------------------------------------------------------------
1 |
2 | *
3 | * Special Function Register addresses
4 | *
5 |
6 | ACC EQU $100
7 | PSW EQU $101
8 | B EQU $102
9 | C EQU $103
10 | TRL EQU $104
11 | TRH EQU $105
12 | SP EQU $106
13 | PCON EQU $107
14 | IE EQU $108
15 | IP EQU $109
16 | EXT EQU $10D
17 | OCR EQU $10E
18 | T0CON EQU $110
19 | T0PRR EQU $111
20 | T0L EQU $112
21 | T0LR EQU $113
22 | T0H EQU $114
23 | T0HR EQU $115
24 | T1CNT EQU $118
25 | T1LC EQU $11A
26 | T1L EQU $11B
27 | T1LR EQU $11B
28 | T1HC EQU $11C
29 | T1H EQU $11D
30 | T1HR EQU $11D
31 | MCR EQU $120
32 | STAD EQU $122
33 | CNR EQU $123
34 | TDR EQU $124
35 | XBNK EQU $125
36 | VCCR EQU $127
37 | SCON0 EQU $130
38 | SBUF0 EQU $131
39 | SBR EQU $132
40 | SCON1 EQU $134
41 | SBUF1 EQU $135
42 | P1 EQU $144
43 | P1DDR EQU $145
44 | P1FCR EQU $146
45 | P3 EQU $14C
46 | P3DDR EQU $14D
47 | P3INT EQU $14E
48 | P7 EQU $15C
49 | I01CR EQU $15D
50 | I23CR EQU $15E
51 | ISL EQU $15F
52 | VSEL EQU $163
53 | VRMAD1 EQU $164
54 | VRMAD2 EQU $165
55 | VTRBF EQU $166
56 | VLREG EQU $167
57 | BTCR EQU $17F
58 | XRAM EQU $180
59 |
60 | *
61 | * PSW bits
62 | *
63 |
64 | CY EQU 7
65 | AC EQU 6
66 | IRBK1 EQU 4
67 | IRBK0 EQU 3
68 | OV EQU 2
69 | RAMBK0 EQU 1
70 | P EQU 0
71 |
72 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ADVM
2 | Audio Driver for (Dreamcast) VMU
3 |
4 |
5 | Port and cut-down version of ADPM for the Dreamcast VMU, written entirely in LC86000 assembly language.
6 | Use Waterbear in order to assemble the source (https://github.com/wtetzner/waterbear),
7 | Example song also comes in binary form, use Elysian EVMU to run the binary if you want to take a quick look.
8 |
9 | Notice SFR.I is not made by me, it's originally at https://github.com/jahan-addison/snake
10 |
11 | ## How to assemble the source?
12 |
13 | Clone this repo (or just download zip why not) and run waterbear on the folder, you need at least soundtest.s, ADVM.s and sfr.i to be in the same folder if you wish to just drag and drop these into the waterbear folder (not the best option but if you only wanna quick assemble this it should work fine)
14 |
15 | then run `waterbear assemble audiotest.s -o audiotest.vms`
16 |
17 | ## Current version: 1.1
18 | - cleaned up some unnecessary instructions
19 | - added the Groove table (see ADVM.s)
20 | - changed the soundtest's bitmap to look more similar to the logo in ADVM.s
21 | - changed how the commands are fetched, it don't really matter to the song format, just reduces stack usage
22 | - fixed the music part stopping when an SFX was running instead of just muting (how did i not notice this)
23 |
--------------------------------------------------------------------------------
/audiotest.s:
--------------------------------------------------------------------------------
1 | .org 0 ; entry point
2 | jmpf Start
3 | .org $03 ; External int. (INTO) - I01CR
4 | reti
5 | .org $0B ; External int. (INT1) - I01CR
6 | reti
7 | .org $13 ; External int. (INT2) and Timer 0 low - I23CR and T0CNT
8 | reti
9 | .org $1B ; External int. (INT3) and base timer - I23CR and BTCR
10 | reti
11 | .org $23 ; Timer 0 high - T0CNT
12 | reti
13 | .org $2B ; Timer 1 Low and High - T1CNT
14 | reti
15 | .org $33 ; Serial IO 1 - SCON0
16 | reti
17 | .org $3B ; Serial IO 2 - SCON1
18 | reti
19 | .org $43 ; MAPLE? - MAPLE?????? (160h/161h) (MAPLE?????????)
20 | reti
21 | .org $4B ; Port 3 interrupt - P3INT
22 | reti
23 |
24 |
25 | .org $1F0 ; exit app mode
26 | GoodBye:
27 | not1 EXT, 0
28 | jmpf GoodBye
29 |
30 |
31 |
32 | .org $200
33 | .byte "audio drive test" ; ................... 16-byte Title
34 | .byte "https://github.com/jvsTSX/ADVM " ; ... 32-byte Description
35 | .org $240 ; >>> ICON HEADER
36 | .org $260 ; >>> PALETTE TABLE
37 | .org $280 ; >>> ICON DATA
38 |
39 | ; /////////////////////////////////////////////////////////////
40 | ; /// GAME CODE ///
41 | ; /////////////////////////////////////////////////////////////
42 |
43 | .include "sfr.i"
44 |
45 | ; ////// START
46 | Start:
47 |
48 | ; not1 PSW, 1
49 | mov #0, IE ; disable ints to configure them first
50 | mov #%10000000, VCCR
51 | mov #%00001001, MCR
52 | mov #%10000001, OCR
53 | mov #0, P3INT ; i don't want joypad ints
54 |
55 | ; setup T0
56 | mov #%01000001, T0CON ; Low running, int enabled
57 | mov #$A0, T0PRR
58 | mov #1, T0L
59 | mov #$BF, T0LR
60 |
61 | ; i don't know what these do but they enable T1 to output audio to P1
62 | mov #$80, P1FCR
63 | clr1 P1, 7
64 | mov #$80, P1DDR
65 | mov #%11010000, T1CNT ; except this one
66 |
67 | mov #SONGDATA_0, TRH
69 | callf ADVM_SETUP
70 | mov #$80, IE
71 |
72 | mov #TESTSFXLIST, ADVMSFX_ListLocalHigh
74 |
75 | mov #$FF, LastKeys
76 |
77 | ; copy the example picture
78 | mov #ADVMexampleScreen, TRH
80 | xor ACC
81 | st XBNK
82 | st C
83 | mov #6, B
84 | mov #$80, 2
85 | .CopyLoop
86 | ld C
87 | ldc
88 | st @r2
89 | inc 2
90 | inc C
91 | ld C
92 | ldc
93 | st @r2
94 | inc 2
95 | inc C
96 |
97 | dec B
98 | ld B
99 | bnz .CopyLoop
100 |
101 | ld 2
102 | add #4
103 | st 2
104 | mov #6, B
105 |
106 | bn PSW, 7, .CopyLoop
107 | bp XBNK, 0, .ExitCopyLoop
108 | inc XBNK
109 | set1 2, 7
110 | br .CopyLoop
111 | .ExitCopyLoop:
112 |
113 |
114 |
115 | ; ////// MAIN
116 | Main:
117 | LastKeys = $2F
118 |
119 | ; SFX dakjthlkdjgtadgjkfsabkjgasbfg
120 | ld P3 ; get keys from port 3
121 | st B
122 | ld LastKeys
123 | be B, .SkipInputs
124 |
125 |
126 | bp ACC, 0, .NoKeyUp
127 | mov #0, ADVMRAM_SFXoverlay
128 | .NoKeyUp:
129 |
130 | bp ACC, 1, .NoKeyDown
131 | mov #1, ADVMRAM_SFXoverlay
132 | .NoKeyDown:
133 |
134 | bp ACC, 2, .NoKeyLeft
135 | mov #2, ADVMRAM_SFXoverlay
136 | .NoKeyLeft:
137 |
138 | bp ACC, 3, .NoKeyRight
139 | mov #3, ADVMRAM_SFXoverlay
140 | .NoKeyRight:
141 |
142 | bp ACC, 6, .ModeNotPressed
143 | jmpf goodbye
144 | .ModeNotPressed
145 |
146 | ld B
147 | st LastKeys
148 | .SkipInputs:
149 |
150 |
151 | callf ADVM_RUN
152 | callf ADVMSFX_RUN
153 |
154 |
155 | mov #1, PCON
156 | br Main
157 |
158 | .include "ADVM1_1.s"
159 |
160 |
161 |
162 | SONGDATA_0:
163 | ; index list header
164 | .word TimeLineBuzzer
165 | .word PhrasesIndex
166 | .word GmacroIndex
167 | .word GrooveTable
168 | ; .byte 4 ; your song's tick speed
169 | .byte 0 ; groove table start pos
170 |
171 | GrooveTable:
172 | .byte $04
173 | .byte $FF, $FF
174 | .byte 02
175 | .byte $FF, $FF
176 |
177 | TimeLineBuzzer:
178 | ; phrase no | transpose
179 | ; .byte 03
180 | .byte 00, 0
181 | .byte 01, 0
182 | .byte 00, 0
183 | .byte 02, 0
184 | .byte 03
185 |
186 | PhrasesIndex:
187 | .word SONGDATA0_Phrase0
188 | .word SONGDATA0_Phrase1
189 | .word SONGDATA0_Phrase2
190 | .word SONGDATA0_Phrase3
191 |
192 | SONGDATA0_Phrase0:
193 | .byte 02, $E3, $0C, $FF, %10100000, 0
194 |
195 |
196 | .byte 00, $0B
197 | .byte 02, $17, %10100000, 0
198 |
199 |
200 | .byte 00, $0B
201 | .byte %01000001, %10100000, 0
202 |
203 | .byte 01, $21
204 |
205 | .byte 01, $20, %10100000, 0
206 |
207 | .byte 00, $21
208 | .byte 00, $0B
209 | .byte $21
210 |
211 |
212 | SONGDATA0_Phrase1:
213 | .byte 00, $20, %10100000, 0
214 | .byte 00, $21
215 | .byte 01, $20
216 |
217 | .byte 01, $20, %10100000, 0
218 |
219 | .byte 00, $1C
220 | .byte 00, $17
221 | .byte %01000000, %10100000, 0
222 | .byte 00, $0B
223 | .byte 01, $14
224 |
225 | .byte 00, $0B, %10100000, 0
226 | .byte 01, $17
227 |
228 | .byte 00, $0B
229 | .byte 01, $1F, %10100000, 0
230 |
231 | .byte 00, $13
232 | .byte 00, $21
233 | .byte %01000000, %10100000, 0
234 | .byte 00, $13
235 | .byte 01, $1E
236 |
237 | .byte 00, $13, %10100000, 0
238 | .byte 01, $1C
239 |
240 | .byte 00, $13
241 | .byte 01, $1A, %10100000, 0
242 |
243 | .byte 01, $13
244 |
245 | .byte 01, $21, %10100000, 0
246 |
247 | .byte 00, $15
248 | .byte 00, $23
249 | .byte %01000000, %10100000, 0
250 | .byte 00, $15
251 | .byte 01, $20
252 |
253 | .byte 00, $15, %10100000, 0
254 | .byte 01, $1C
255 |
256 | .byte 00, $15
257 | .byte 01, $17, %10100000, 0
258 |
259 | .byte 01, $09
260 |
261 | .byte $21
262 |
263 |
264 | SONGDATA0_Phrase2:
265 | .byte 00, $20, %10100000, 0
266 | .byte 00, $21
267 | .byte 00, $20
268 | .byte 00, $0B
269 | .byte 01, $20, %10100000, 0
270 |
271 | .byte 00, $23
272 | .byte 00, $28
273 | .byte %01000000, %10100000, 0
274 | .byte 01, $0B
275 |
276 | .byte 00, $2A
277 | .byte 01, $28, %10100000, 0
278 |
279 | .byte 01, $23
280 |
281 | .byte 01, $13, %10100000, 0
282 |
283 | .byte 01, $1F
284 |
285 | .byte 01, $1F, %10100000, 0
286 |
287 | .byte 01, $13
288 |
289 | .byte 01, $1E, %10100000, 0
290 |
291 | .byte 00, $13
292 | .byte 00, $1C
293 | .byte %01000000, %10100000, 0
294 | .byte 00, $13
295 | .byte 01, $1A
296 |
297 | .byte 01, $1A, %10100000, 0
298 |
299 | .byte 00, $15
300 | .byte 00, $1C
301 | .byte %01000000, %10100000, 0
302 | .byte 00, $15
303 | .byte 01, $19
304 |
305 | .byte 01, $15, %10100000, 0
306 |
307 | .byte 01, $1A
308 |
309 | .byte 01, $19, %10100000, 0
310 |
311 | .byte 01, $15
312 |
313 | .byte $21
314 |
315 |
316 |
317 | SONGDATA0_Phrase3:
318 | ; .byte 00, $E3, $0C, $FF
319 | ; .byte 00, $F3, $08, $FF
320 | .byte $20, $00
321 |
322 |
323 |
324 | GmacroIndex:
325 | .word SONGDATA0_Gmacro0
326 |
327 | SONGDATA0_Gmacro0:
328 | .byte %11010000, $A0
329 | .byte %11011110, $90
330 | .byte %11011110, $00
331 | .byte %01000000
332 |
333 |
334 |
335 |
336 | ; SFX TESTS
337 | TESTSFXLIST:
338 | .word .SFX0
339 | .word .SFX1
340 | .word .SFX2
341 | .word .SFX3
342 |
343 | .SFX0:
344 | .byte %11010000, $FE
345 | .byte %11010000, $FD
346 | .byte %11010000, $FC
347 | .byte %11010000, $FB
348 | .byte %11010000, $FA
349 | .byte 0
350 |
351 | .SFX1:
352 | .byte %11010000, $E9
353 | .byte %10010010
354 | .byte %10110100, 2
355 | .byte %10011000
356 | .byte 0
357 |
358 | .SFX2:
359 | .byte %11101000, $FB, 2
360 | .byte %11101000, $FC, 2
361 | .byte %11101000, $FD, 2
362 | .byte %11101000, $FA, 2
363 | .byte 0
364 |
365 | .SFX3:
366 | .byte %11010100, $EC
367 | .byte %11010100, $F0
368 | .byte %11010100, $F8
369 | .byte %11010100, $EC
370 | .byte %11010100, $F0
371 | .byte %11010100, $F8
372 | .byte %11010100, $EC
373 | .byte %11010100, $F0
374 | .byte %11010100, $F8
375 | .byte 0
376 |
377 |
378 |
379 | ADVMexampleScreen:
380 | .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000
381 | .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000
382 | .byte %00000000, %00000011, %00110000, %00000001, %11011101, %11011100
383 | .byte %00000000, %00101001, %00010000, %00000000, %01010100, %01001100
384 | .byte %00000000, %00101001, %00010000, %00000001, %10010101, %10000100
385 | .byte %00000000, %00010001, %01010000, %00000001, %11011101, %11011100
386 | .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000
387 | .byte %00000001, %11111111, %11111111, %11110011, %11111100, %11111100
388 | .byte %00000001, %00000100, %00000011, %00010010, %00100100, %10000100
389 | .byte %00000010, %00000100, %00000001, %00010100, %01000011, %00000100
390 | .byte %00000010, %01000100, %01110001, %00010100, %01000011, %00000100
391 | .byte %00000100, %01000100, %01010000, %10011000, %10000000, %00000100
392 | .byte %00000100, %11000100, %01001000, %10011000, %10000000, %00000100
393 | .byte %00001000, %11000100, %01001000, %10010001, %10001000, %01000100
394 | .byte %00001001, %11000100, %01001000, %10010001, %10001000, %01000100
395 | .byte %00010000, %00000100, %01001000, %10000010, %10001100, %11000100
396 | .byte %00010000, %00000100, %01010000, %10000010, %10001100, %11000100
397 | .byte %00100011, %11000100, %01110001, %00000100, %10001011, %01000100
398 | .byte %00100010, %01000100, %00000001, %00000100, %10001011, %01000100
399 | .byte %01000100, %01000100, %00000011, %00001000, %10001000, %01000100
400 | .byte %01111100, %01111111, %11111111, %11111000, %11111000, %01111100
401 | .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000
402 | .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000
403 | .byte %11111111, %11111111, %11111111, %11111111, %11111111, %11111111
404 | .byte %10011111, %11111111, %11111111, %11111111, %01111001, %00010101
405 | .byte %10101111, %11111111, %11111110, %01111101, %01110111, %01110101
406 | .byte %10101101, %10011001, %10011110, %01111011, %01110111, %01110101
407 | .byte %10001011, %01010111, %01111000, %00011000, %01111011, %00111011
408 | .byte %10111011, %00011011, %10111000, %00011111, %01111101, %01110101
409 | .byte %10111011, %01111101, %11011110, %01111111, %01111101, %01110101
410 | .byte %10111011, %10010011, %00111110, %01111111, %01110011, %01110101
411 | .byte %11111111, %11111111, %11111111, %11111111, %11111111, %11111111
412 |
413 |
414 | ; .byte %00000000, %00000001, %00000000, %00000000, %00000000, %00000000
415 | ; .byte %00000000, %00000000, %00000000, %00100000, %00000000, %00000000
416 | ; .byte %00000000, %00000001, %01001001, %10100000, %00000000, %00000000
417 | ; .byte %00000000, %00000001, %01001011, %00000000, %00000000, %00000000
418 | ; .byte %00000000, %00000101, %01010000, %10000000, %00000000, %00000000
419 | ; .byte %00000000, %00000110, %00100011, %10000000, %00000000, %00000000
420 | ; .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000
421 | ; .byte %00000000, %00011111, %11111111, %11100011, %11110111, %11000000
422 | ; .byte %00000000, %00010001, %00000011, %00100010, %01010100, %01000000
423 | ; .byte %00000000, %00100001, %00000001, %00100100, %10001000, %01000000
424 | ; .byte %00000000, %00100001, %00111001, %00100100, %10001000, %01000000
425 | ; .byte %00000000, %01000001, %00101000, %10101001, %00000000, %01000000
426 | ; .byte %00000000, %01001001, %00100100, %10101001, %00000000, %01000000
427 | ; .byte %00000000, %10001001, %00100100, %10110011, %00100010, %01000000
428 | ; .byte %00000000, %10000001, %00100100, %10110011, %00100010, %01000000
429 | ; .byte %00000001, %00000001, %00100100, %10000101, %00110110, %01000000
430 | ; .byte %00000001, %00111001, %00101000, %10000101, %00110110, %01000000
431 | ; .byte %00000010, %01001001, %00111001, %00001001, %00101010, %01000000
432 | ; .byte %00000010, %01001001, %00000001, %00001001, %00101010, %01000000
433 | ; .byte %00000100, %10001001, %00000011, %00010001, %00100010, %01000000
434 | ; .byte %00000111, %10001111, %11111111, %11110001, %11100011, %11000000
435 | ; .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000
436 | ; .byte %00000100, %10010001, %11000000, %00011101, %11011101, %11000000
437 | ; .byte %00000100, %10010001, %01000000, %00000101, %01000100, %11000000
438 | ; .byte %00000101, %00010001, %01000000, %00001001, %01001000, %01000000
439 | ; .byte %00000010, %00010101, %11000000, %00011101, %11011101, %11000000
440 | ; .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000
441 | ; .byte %11111111, %11111111, %11111111, %11111111, %11111111, %11111111
442 | ; .byte %11000110, %10001100, %11001111, %01111010, %11110010, %00101011
443 | ; .byte %11000101, %10011101, %11011110, %00111000, %11110110, %01110111
444 | ; .byte %11011101, %10001001, %10011111, %01111110, %11100110, %11101011
445 | ; .byte %11111111, %11111111, %11111111, %11111111, %11111111, %11111111
446 |
447 |
448 |
449 | .cnop 0, $200
--------------------------------------------------------------------------------
/ADVM.s:
--------------------------------------------------------------------------------
1 | ; ///////////////////////////////////////////////////////////////
2 | ; ____________ __ ______ ____ __++++++__
3 | ; / | __ \| | / / \ / | | @ |
4 | ; / /| | | \ \ | / / \/ | | ------ |
5 | ; / /_| | | | | |/ /| |\ /| | | | | |
6 | ; / ___ | | | | / | | \ / | | | | | |
7 | ; / / | | |__/ / / | | \/ | | | ------ |
8 | ; /__/ |__|_______/|___/ |__| |__| | _+_ o o |
9 | ; Audio Driver for (dreamcast) VMu | + O O |
10 | ; jvsTSX / 2023 --______--
11 | ; ///////////////////////////////////////////////////////////////
12 | ;
13 | ; special thanks to Tildearrow for the duty formula
14 | ; and the dreamcast community for incentivating this project
15 | ;
16 | ; port of ADPM for dreamcast VMU - cut down to fit for 133KHz apps
17 | ; features:
18 | ;
19 | ; version 1.0
20 | ; - timeline and phrase format like trackers
21 | ; - length encoded notes for better space usage
22 | ; - General purpose Macro (Gmacro)
23 | ; - delayed "off-grid" note support
24 | ; - F-0 to C#4 frequency range
25 | ; - fixed notes on Gmacro for drums
26 | ; - 5-bit pulse widths ranging from super thin to square
27 | ; - Sound effects sub-driver included (you can optionally replace it)
28 | ;
29 | ; version 1.1
30 | ; - groove table
31 | ;
32 | ; performance:
33 | ; - RAM usage : 34 bytes music, 6 bytes SFX
34 | ; - CPU usage : ~568 cycles worst ever case*
35 | ; - Flash usage (driver) : 1185 bytes
36 | ; - Flash usage (song) : 2-4 bytes per note, most other commands are 2 bytes
37 | ;
38 | ; *worst ever = 7 commands on Block B at once, everything enabled on Block A (and firing) with Gmacro running into a valid loop
39 |
40 |
41 |
42 | ; /////////////////////////////////////////////////////////////
43 | ; /// SETUP CODE ///
44 | ; /////////////////////////////////////////////////////////////
45 | ADVM_SETUP:
46 | xor ACC
47 | st ADVMRAM_OffsetNote
48 | st ADVMRAM_Flags
49 | ldc
50 | st ADVMRAM_TmLineLocalLow
51 | st ADVMRAM_TmLinePosLow
52 | mov #1, ACC
53 | st ADVMRAM_TickCount
54 | st ADVMRAM_WaitCount
55 | ldc
56 | st ADVMRAM_TmLineLocalHigh
57 | st ADVMRAM_TmLinePosHigh
58 | mov #2, ACC
59 | ldc
60 | st ADVMRAM_PhraseLocalLow
61 | mov #3, ACC
62 | ldc
63 | st ADVMRAM_PhraseLocalHigh
64 | mov #4, ACC
65 | ldc
66 | st ADVMRAM_GmacroLocalLow
67 | mov #5, ACC
68 | ldc
69 | st ADVMRAM_GmacroLocalHigh
70 | mov #6, ACC
71 | ldc
72 | st ADVMRAM_GrooveTableLocalLow
73 | mov #7, ACC
74 | ldc
75 | st ADVMRAM_GrooveTableLocalHigh
76 | mov #8, ACC
77 | ldc
78 | st ADVMRAM_GroovePos
79 |
80 | mov #$FF, ADVMRAM_SFXoverlay
81 | mov #$FF, ADVMRAM_FixedFreq
82 |
83 | ; reference and get new transpose
84 | ld ADVMRAM_TmLinePosLow
85 | st TRL
86 | ld ADVMRAM_TmLinePosHigh
87 | st TRH
88 | mov #1, ACC
89 | ldc
90 | st ADVMRAM_TransposePend
91 |
92 | ; add 2 to timeline pos
93 | ld ADVMRAM_TmLinePosLow
94 | add #2
95 | st ADVMRAM_TmLinePosLow
96 | xor ACC
97 | addc ADVMRAM_TmLinePosHigh
98 | st ADVMRAM_TmLinePosHigh
99 |
100 | ; get new phrase index
101 | xor ACC
102 | ldc
103 | st B
104 |
105 | ; reference index local
106 | ld ADVMRAM_PhraseLocalLow
107 | st TRL
108 | ld ADVMRAM_PhraseLocalHigh
109 | st TRH
110 |
111 | ; add the index local with offset*2 value, then reference it and store into phrase pos
112 | ld B
113 | add ACC ; *2 the offset
114 | bn PSW, 7, .NoCarry
115 | inc TRH ; in case the 8th bit becomes 9th in the *2 process
116 | .NoCarry:
117 | st B
118 | ldc
119 | st ADVMRAM_PhrasePosLow
120 | ld B
121 | inc ACC
122 | ldc
123 | st ADVMRAM_PhrasePosHigh
124 | ret
125 |
126 |
127 |
128 | ; /////////////////////////////////////////////////////////////
129 | ; /// ADVM SFX SUB-Engine ///
130 | ; /////////////////////////////////////////////////////////////
131 |
132 | ADVMSFX_RUN:
133 | ld ADVMRAM_SFXoverlay
134 | bne #$FF, .CheckRun
135 | .NoSFX:
136 | ret
137 | .CheckRun:
138 | be #$FE, .SFXisRunning
139 |
140 | ; fall condition: SFX to be setup
141 | st B
142 | ld ADVMSFX_ListLocalLow
143 | st TRL
144 | ld ADVMSFX_ListLocalHigh
145 | st TRH
146 |
147 | ld B
148 | add ACC
149 | bn PSW, 7, .NoCarry
150 | inc TRH
151 | .NoCarry:
152 | st B
153 | ldc
154 | st ADVMSFX_PosLow
155 | ld B
156 | inc ACC
157 | ldc
158 | st ADVMSFX_PosHigh
159 | mov #1, ADVMSFX_Wait ; avoid locking the song out for 255 ticks
160 | mov #$FE, ADVMRAM_SFXoverlay ; signal it's running
161 |
162 | .SFXisRunning:
163 | dec ADVMSFX_Wait ; check wait
164 | ld ADVMSFX_Wait
165 | bnz .NoSFX
166 | ld ADVMSFX_PosLow ; reference position
167 | st TRL
168 | ld ADVMSFX_PosHigh
169 | st TRH
170 |
171 | ; first parameter: header and duty
172 | xor ACC
173 | ldc
174 | bn ACC, 7, .DisableSFX
175 | ; continue SFX
176 | st B
177 | xor ACC
178 | st C
179 | bn B, 6, .DutyOnly
180 | ; otherwise get Pitch field
181 | inc C
182 | ld C
183 | ldc
184 | st T1LR
185 | .DutyOnly:
186 | ld B
187 | and #%00011111
188 | be #$1E, .NoDuty
189 | st ADVMSFX_Duty
190 | .NoDuty:
191 |
192 | xor ACC ; check for wait field
193 | bn B, 5, .WaitIsOne
194 | inc C
195 | ld C
196 | ldc
197 | .WaitIsOne:
198 | inc ACC
199 | st ADVMSFX_Wait
200 |
201 | inc C ; step SFX position
202 | ld C
203 | add TRL
204 | bn PSW, 7, .NoCarry2
205 | inc TRH
206 | .NoCarry2:
207 | st ADVMSFX_PosLow
208 | ld TRH
209 | st ADVMSFX_PosHigh
210 |
211 | ld ADVMSFX_Duty ; calculate duty
212 | be #$1F, .Mute
213 | st B
214 | ld T1LR
215 | xor #$FF
216 | st C
217 | xor ACC
218 | mul
219 | mov #32, B
220 | div
221 | ld C
222 | xor #$FF
223 | st T1LC
224 | ret
225 |
226 | .DisableSFX:
227 | mov #$FF, ADVMRAM_SFXoverlay
228 | ret
229 |
230 | .Mute:
231 | mov #$FF, T1LC
232 | mov #$FF, T1LR
233 | ret
234 |
235 |
236 |
237 |
238 |
239 | ; hello
240 | ; there
241 | ; this
242 | ; is
243 | ; pro
244 | ; crastination
245 | ;
246 | ; |\
247 | ; | \
248 | ; ___ | |\ ___
249 | ; - - / | \ - -
250 | ; / \ _-= |__ / \
251 | ; | _ \ _ / / \ _ / _ |
252 | ; | / \ | _/ \_ | / \ |
253 | ; | | | |/ _-- --_ \| | | |
254 | ; | | | / / | | \ \ | | |
255 | ; \ \_| | / =_ \ / _= \ | |_/ /
256 | ; \ | | || \ | | / || | | /
257 | ; ---| | || v| \___/ |v || | |---
258 | ; ---_____/ | \ --| "=" |-- / | \____---
259 | ; \_ \ --- _ --- / _/
260 | ; -__ \_ / \ _/ __-
261 | ; --_____ ---_________--- _____--
262 | ; --_______/\ /\______--
263 | ; \ \ /
264 | ; ||__--
265 | ;
266 | ; and also a space between code to not let my dumb ass get lost in the source
267 | ;
268 | ; ... if you're looking for SFX subdriver docs, it's moved to the DOCUMENTATION section
269 |
270 |
271 |
272 |
273 |
274 | ; /////////////////////////////////////////////////////////////
275 | ; /// DRIVER BLOCK A ///
276 | ; /////////////////////////////////////////////////////////////
277 | ADVM_RUN:
278 |
279 | ; ////////////////// Kill count ///////
280 | bn ADVMRAM_Flags, 0, .SkipKill
281 | dec ADVMRAM_KillCount
282 | ld ADVMRAM_KillCount
283 | bnz .SkipKill
284 | clr1 ADVMRAM_Flags, 0 ; disable KillCount
285 | clr1 ADVMRAM_Flags, 2 ; disable Gmacro
286 | mov #$FF, ADVMRAM_FixedFreq
287 | mov #$1F, ADVMRAM_CurrDuty ; mute channel
288 | .SkipKill: ; 12 cycles
289 |
290 |
291 | ; ////////////////// Delay count //////
292 | bn ADVMRAM_Flags, 1, .SkipDelay
293 | dec ADVMRAM_DelayCount
294 | ld ADVMRAM_DelayCount
295 | bnz .SkipDelay
296 | clr1 ADVMRAM_Flags, 1 ; disable DelayCount
297 |
298 | ld ADVMRAM_PendingNote ; grab note
299 | and #%00111111
300 | st ADVMRAM_CurrentNote
301 | bp ADVMRAM_PendingDuty, 5, .SkipDelay ; legato check
302 |
303 | ; apply duty
304 | bn ADVMRAM_PendingNote, 6, .UseLastDuty
305 | ld ADVMRAM_PendingDuty
306 | and #%00001111
307 | inc ACC
308 | st ADVMRAM_LastDuty
309 | br .SkipDuty
310 | .UseLastDuty:
311 | ld ADVMRAM_LastDuty
312 | .SkipDuty:
313 | st ADVMRAM_CurrDuty
314 |
315 | ; apply Gmacro
316 | bp ADVMRAM_PendingNote, 7, .NewGmacro
317 | br .UseLastGmacro
318 | .NewGmacro:
319 | ld ADVMRAM_PendingGmacro
320 | st ADVMRAM_LastGmacro
321 | .UseLastGmacro:
322 | ; GmacroPos = word[ byte[PhrasePos]*2 + word[GmacroLocal] ]
323 | ld ADVMRAM_GmacroLocalLow
324 | st TRL
325 | ld ADVMRAM_GmacroLocalHigh
326 | st TRH
327 |
328 | ld ADVMRAM_LastGmacro ; check Gmacro number, FF = off
329 | clr1 ADVMRAM_Flags, 2
330 | be #$FF, .SkipDelay
331 | set1 ADVMRAM_Flags, 2
332 |
333 | add ACC
334 | bn PSW, 7, .NoCarry
335 | inc TRH
336 | .NoCarry:
337 | st B
338 | ldc
339 | st ADVMRAM_GmacroPosLow
340 | ld B
341 | inc ACC
342 | ldc
343 | st ADVMRAM_GmacroPosHigh
344 | .SkipDelay: ; 47 cycles
345 |
346 |
347 |
348 | ; /////////////////////////////////////////////////////////////
349 | ; /// GMACRO HANDLER ///
350 | ; /////////////////////////////////////////////////////////////
351 | ADVM_Gmacro:
352 | bn ADVMRAM_Flags, 2, .GmacroDisabled
353 |
354 | dec ADVMRAM_GmacroWait
355 | ld ADVMRAM_GmacroWait
356 | bnz .SkipGmacro
357 |
358 | ld ADVMRAM_GmacroPosLow
359 | st TRL
360 | ld ADVMRAM_GmacroPosHigh
361 | st TRH
362 |
363 | ; first byte: action or end
364 | xor ACC
365 | st C
366 | ldc
367 | .Process:
368 | bp ACC, 7, .Action
369 |
370 | ; otherwise end/loop
371 | bp ACC, 6, .Stop
372 | inc ACC ; previous ACC = 0
373 | st ADVMRAM_GmacroWait ; reload wait so the macro don't softlock
374 | ldc ; grab offset
375 | st B ; important: the subtraction order DOES matter, TRL - B = good, B - TRL = bad
376 | ld TRL
377 | sub B
378 | bn PSW, 7, .NoBorrow ; ^ really dumb dev note
379 | dec TRH ; imagine not knowing basic maths
380 | .NoBorrow:
381 | st TRL
382 | xor ACC
383 | ldc
384 | bnz .Process
385 | .Stop:
386 | clr1 ADVMRAM_Flags, 2 ; disable Gmacro
387 | .GmacroDisabled:
388 | br .SkipGmacro
389 |
390 | .Action:
391 | ; pitch and duty
392 | st B
393 | bn B, 6, .RelModeDutyCheck
394 | inc C
395 | ld C
396 | ldc
397 | bpc ACC, 7, .FixedMode
398 |
399 | mov #$FF, ADVMRAM_FixedFreq
400 | bn ACC, 6, .NoEx
401 | set1 ACC, 7
402 | .NoEx:
403 | st ADVMRAM_OffsetNote
404 | ld B
405 | .RelModeDutyCheck:
406 | and #%00011111
407 | be #$1E, .NoFreq
408 | st ADVMRAM_CurrDuty
409 | br .NoFreq
410 |
411 | .FixedMode:
412 | st ADVMRAM_FixedFreq
413 | ld B
414 | and #%00011111
415 | be #$1E, .NoFreq
416 | st ADVMRAM_DutyOverlay
417 | .NoFreq:
418 |
419 | ; wait field
420 | xor ACC
421 | bn B, 5, .WaitIsOne
422 | inc C
423 | ld C
424 | ldc
425 | .WaitIsOne:
426 | inc ACC
427 | st ADVMRAM_GmacroWait
428 | inc C
429 | ld C
430 | add TRL
431 | bn PSW, 7, .NoCarry
432 | inc TRH
433 | .NoCarry:
434 | st ADVMRAM_GmacroPosLow
435 | ld TRH
436 | st ADVMRAM_GmacroPosHigh
437 | .SkipGmacro: ; 76 cycles worst
438 |
439 |
440 |
441 | ; /////////////////////////////////////////////////////////////
442 | ; /// PITCH PIPE ///
443 | ; /////////////////////////////////////////////////////////////
444 | ADVM_PitchPipe:
445 | ; check SFX status
446 | ld ADVMRAM_SFXoverlay
447 | be #$FE, ADVM_TickCounter
448 |
449 | mov #ADVM_NoteLut, TRH
451 | ld ADVMRAM_FixedFreq
452 | be #$FF, .RunPipe
453 | ; otherwise run a fixed note
454 | ldc
455 | st T1LR
456 | xor #$FF
457 | st C
458 | ld ADVMRAM_DutyOverlay
459 | br .DutyCalc
460 |
461 | .Mute:
462 | mov #$FF, T1LR
463 | mov #$FF, T1LC
464 | br ADVM_TickCounter
465 |
466 | .RunPipe:
467 | ; get note index
468 | ld ADVMRAM_CurrentNote
469 | add ADVMRAM_TransposeCurr
470 | add ADVMRAM_OffsetNote
471 | ldc
472 | st T1LR
473 |
474 | ; duty calculation procedure:
475 | ; Compare = byte( word( byte(pitch) * bit5(duty) ) / 32 )
476 | xor #$FF ; invert to make calculation easier
477 | st C
478 | ld ADVMRAM_CurrDuty
479 | .DutyCalc:
480 | be #$1F, .Mute
481 |
482 | st B
483 | xor ACC
484 | mul
485 | mov #32, B
486 | div
487 | ld C
488 | xor #$FF ; flip it back and store at T1 Compare data
489 | st T1LC
490 | ; 42 cycles full run
491 |
492 |
493 |
494 | ; /////////////////////////////////////////////////////////////
495 | ; /// DRIVER BLOCK B ///
496 | ; /////////////////////////////////////////////////////////////
497 | ADVM_TickCounter:
498 | dec ADVMRAM_TickCount
499 | ld ADVMRAM_TickCount
500 | bnz ADVM_EXIT
501 | ; groove step
502 | ld ADVMRAM_GrooveTableLocalLow
503 | st TRL
504 | ld ADVMRAM_GrooveTableLocalHigh
505 | st TRH
506 | ld ADVMRAM_GroovePos
507 | ldc
508 | be #$FF, .endgroove
509 | br .end
510 | .endgroove: ; groove reset
511 | ld ADVMRAM_GroovePos
512 | inc ACC ; get next byte
513 | ldc
514 | add ADVMRAM_GroovePos
515 | st ADVMRAM_GroovePos
516 | ldc
517 | .end:
518 | inc ADVMRAM_GroovePos
519 | st ADVMRAM_TickCount
520 |
521 | dec ADVMRAM_WaitCount ; check rest count
522 | ld ADVMRAM_WaitCount
523 | bnz ADVM_EXIT
524 |
525 | clr1 ADVMRAM_Flags, 7 ; clear continuity flag
526 | ; 11 cycles
527 |
528 | ; reference current phrase position
529 | ADVM_NextCMDfull:
530 | ld ADVMRAM_PhrasePosLow
531 | st TRL
532 | ld ADVMRAM_PhrasePosHigh
533 | ADVM_NextCMD:
534 | st TRH
535 | xor ACC
536 | ldc
537 | st C ; store it for later, low 5 bits might be useful
538 | mov #1, ACC
539 | ldc
540 |
541 | bp C, 7, .LowerHalf
542 | bp C, 6, .TopLowQuarter
543 | bp C, 5, .CmdIsEnd ; 00-
544 | jmpf ADVMCMD_PlayNote
545 | .CmdIsEnd:
546 | jmpf ADVMCMD_EndEvent
547 |
548 | .TopLowQuarter: ; 01-
549 | bp C, 5, .CmdIsKill
550 | jmpf ADVMCMD_Wait
551 | .CmdIsKill:
552 | jmpf ADVMCMD_KillNote
553 |
554 | .LowerHalf:
555 | bp C, 6, .LowerLowQuarter
556 | bp C, 5, .CommandIsGmacro
557 | jmpf ADVMCMD_DelayNote ; 10-
558 | .CommandIsGmacro:
559 | jmpf ADVMCMD_RunGmacro
560 |
561 | .LowerLowQuarter: ; 11-
562 | bp C, 5, .CmdIsSpeed
563 | jmpf ADVMCMD_SetDuty
564 | .CmdIsSpeed:
565 | jmpf ADVMCMD_SetSpeed
566 |
567 | ; 31 cycles any case ( full, first run )
568 | ; 20 cycles any case ( from end event command )
569 | ; 25 cycles any case ( when returning from CMD )
570 |
571 | ; PlayNote 000 2-4 bytes
572 | ; EndEvent 001 1-2 bytes
573 | ; Wait 010 1-2 bytes
574 | ; KillNote 011 2 bytes
575 | ; DelayNote 100 3-4 bytes
576 | ; RunGmacro 101 2 bytes
577 | ; SetDuty 110 1 byte
578 | ; SetSpeed 111 2 bytes
579 |
580 |
581 | ADVM_EXIT:
582 | ret
583 |
584 | ADVM_CMDreturn: ; return the byte skip amount in ACC
585 | ; offset phrase position by last command's size to fetch next command
586 | add ADVMRAM_PhrasePosLow
587 | st ADVMRAM_PhrasePosLow
588 | st TRL
589 | xor ACC
590 | addc ADVMRAM_PhrasePosHigh
591 | st ADVMRAM_PhrasePosHigh
592 | br ADVM_NextCMD
593 |
594 |
595 | ; /////////////////////////////////////////////////////////////
596 | ; /// LIBRARY SPACE ///
597 | ; /////////////////////////////////////////////////////////////
598 | ADVM_NoteLut:
599 | .byte $06 ; F-0 00
600 | .byte $14 ; F#0 01
601 | .byte $21 ; G-0 02
602 | .byte $2E ; G#0 03
603 | .byte $39 ; A-0 04
604 | .byte $45 ; A#0 05
605 | .byte $4F ; B-0 06
606 |
607 | .byte $59 ; C-1 07
608 | .byte $62 ; C#1 08
609 | .byte $6B ; D-1 09
610 | .byte $73 ; D#1 0A
611 | .byte $7C ; E-1 0B
612 | .byte $83 ; F-1 0C
613 | .byte $8A ; F#1 0D
614 | .byte $90 ; G-1 0E
615 | .byte $97 ; G#1 0F
616 | .byte $9D ; A-1 10
617 | .byte $A2 ; A#1 11
618 | .byte $A8 ; B-1 12
619 |
620 | .byte $AC ; C-2 13
621 | .byte $B1 ; C#2 14
622 | .byte $B6 ; D-2 15
623 | .byte $BA ; D#2 16
624 | .byte $BE ; E-2 17
625 | .byte $C1 ; F-2 18
626 | .byte $C5 ; F#2 19
627 | .byte $C8 ; G-2 1A
628 | .byte $CB ; G#2 1B
629 | .byte $CE ; A-2 1C
630 | .byte $D1 ; A#2 1D
631 | .byte $D4 ; B-2 1E
632 |
633 | .byte $D6 ; C-3 1F
634 | .byte $D9 ; C#3 20
635 | .byte $DB ; D-3 21
636 | .byte $DD ; D#3 22
637 | .byte $DF ; E-3 23
638 | .byte $E1 ; F-3 24
639 | .byte $E2 ; F#3 25
640 | .byte $E4 ; G-3 26
641 | .byte $E6 ; G#3 27
642 | .byte $E7 ; A-3 28
643 | .byte $E8 ; A#3 29
644 | .byte $EA ; B-3 2A
645 |
646 | .byte $EB ; C-4 2B
647 | .byte $EC ; C#4 2C
648 |
649 |
650 | ; /////////////////////////////////////////////////////////////
651 | ; /// COMMAND'S CODE ///
652 | ; /////////////////////////////////////////////////////////////
653 |
654 | ADVMCMD_PlayNote: ; ///////////////////////////////////// PLAY NOTE ///
655 | not1 ADVMRAM_Flags, 7
656 | bn ADVMRAM_Flags, 7, .ContinuityZero
657 |
658 | st B ; note index
659 | and #%00111111
660 | st ADVMRAM_CurrentNote
661 | ld ADVMRAM_TransposePend ; update transpose
662 | st ADVMRAM_TransposeCurr
663 |
664 | ; get wait count
665 | ld C
666 | and #%00001111
667 | inc ACC
668 | st ADVMRAM_WaitCount
669 |
670 | bp C, 4, .NoteIsLegato ; if the note is a legato note, it will ignore the other two params
671 | mov #1, C
672 | ld ADVMRAM_PhrasePosLow
673 | st TRL
674 | ld ADVMRAM_PhrasePosHigh
675 | st TRH
676 |
677 | ; grab duty parameter
678 | bn B, 6, .ReuseDuty
679 | inc C
680 | ld C
681 | ldc
682 | and #%00011111
683 | st ADVMRAM_LastDuty
684 | br .SkipDuty
685 | .ReuseDuty:
686 | ld ADVMRAM_LastDuty
687 | .SkipDuty:
688 | st ADVMRAM_CurrDuty
689 |
690 | ; grab Gmacro parameter
691 | mov #1, ADVMRAM_GmacroWait
692 | bp B, 7, .NewGmacro
693 | br .UseLastGmacro
694 |
695 | .NewGmacro:
696 | inc C
697 | ld C
698 | ldc
699 | st ADVMRAM_LastGmacro
700 |
701 | .UseLastGmacro:
702 | ; GmacroPos = word[ byte[PhrasePos]*2 + word[GmacroLocal] ]
703 | ld ADVMRAM_GmacroLocalLow
704 | st TRL
705 | ld ADVMRAM_GmacroLocalHigh
706 | st TRH
707 |
708 | ld ADVMRAM_LastGmacro ; check Gmacro number, FF = off
709 | clr1 ADVMRAM_Flags, 2
710 | be #$FF, .SkipGmacro
711 | set1 ADVMRAM_Flags, 2
712 |
713 | add ACC
714 | bn PSW, 7, .NoCarry
715 | inc TRH
716 | .NoCarry:
717 | st B
718 | ldc
719 | st ADVMRAM_GmacroPosLow
720 | ld B
721 | inc ACC
722 | ldc
723 | st ADVMRAM_GmacroPosHigh
724 | .SkipGmacro
725 |
726 | inc C
727 | ld C
728 | jmpf ADVM_CMDreturn ; 62 cycles worst
729 |
730 | .ContinuityZero:
731 | ret
732 |
733 | .NoteIsLegato:
734 | mov #2, ACC
735 | jmpf ADVM_CMDreturn
736 |
737 |
738 |
739 | ADVMCMD_EndEvent: ; ///////////////////////////////////// END EVENT ///
740 | ; END SONG PROCEDURE: TimeLinePos = offset*2 + [TimeLineLocal] ; then do below
741 | ; END PHRASE PROCEDURE: NewTranspose = byte[TimeLinePos + 1]
742 | ; TimeLinePos + 2
743 | ; PhrasePos = word[ byte[TimeLinePos + 0]*2 + word[PhraseLocal] ]
744 |
745 | bp C, 0, ADVM_CommandIsEndPhrase
746 |
747 | ; ; if it comes down here it's END SONG
748 | add ACC ; *2 the offset value
749 | mov #0, B ; use B to store the *2 carry if it happens (offset greater than 127)
750 | bn PSW, 7, ADVM_EndSongNoCarry
751 | inc B ; increment high if a carry occurs at *2
752 | ADVM_EndSongNoCarry:
753 | add ADVMRAM_TmLineLocalLow
754 | st ADVMRAM_TmLinePosLow
755 | ld B
756 | addc ADVMRAM_TmLineLocalHigh
757 | st ADVMRAM_TmLinePosHigh
758 |
759 |
760 |
761 | ADVM_CommandIsEndPhrase: ; ////////////////////////////// END PHRASE ///
762 | ; reference and get new transpose
763 | ld ADVMRAM_TmLinePosLow
764 | st TRL
765 | ld ADVMRAM_TmLinePosHigh
766 | st TRH
767 | mov #1, ACC
768 | ldc
769 | st ADVMRAM_TransposePend
770 |
771 | ; add 2 to timeline pos
772 | ld ADVMRAM_TmLinePosLow
773 | add #2
774 | st ADVMRAM_TmLinePosLow
775 | xor ACC
776 | addc ADVMRAM_TmLinePosHigh
777 | st ADVMRAM_TmLinePosHigh
778 |
779 | ; get new phrase index
780 | xor ACC
781 | ldc
782 | st B
783 |
784 | ; reference index local
785 | ld ADVMRAM_PhraseLocalLow
786 | st TRL
787 | ld ADVMRAM_PhraseLocalHigh
788 | st TRH
789 |
790 | ; add the index local with offset*2 value, then reference it and store into phrase pos
791 | ld B
792 | add ACC ; *2 the offset
793 | bn PSW, 7, ADVM_EndEvntNoCarry
794 | inc TRH ; in case the 8th bit becomes 9th in the *2 process
795 | ADVM_EndEvntNoCarry:
796 | st B
797 | ldc
798 | st ADVMRAM_PhrasePosLow
799 | ld B
800 | inc ACC
801 | ldc
802 | st ADVMRAM_PhrasePosHigh
803 | jmpf ADVM_NextCMDfull ; end phrase = 41 cycles, end song = 52 cycles
804 |
805 |
806 |
807 | ADVMCMD_Wait: ; ///////////////////////////////////////// WAIT STEPS ///
808 | not1 ADVMRAM_Flags, 7
809 | bn ADVMRAM_Flags, 7, .ContinuityZero
810 |
811 | st B
812 | ld C
813 | be #%01011111, .TwoBytes
814 |
815 | ; otherwise 1 byte
816 | and #%00011111
817 | inc ACC
818 | st ADVMRAM_WaitCount
819 |
820 | mov #1, ACC
821 | jmpf ADVM_CMDreturn ; 14 cycles
822 |
823 | .TwoBytes:
824 | ld B
825 | inc ACC
826 | st ADVMRAM_WaitCount
827 |
828 | mov #2, ACC
829 | jmpf ADVM_CMDreturn ; 14 cycles
830 |
831 | .ContinuityZero:
832 | ret
833 |
834 |
835 |
836 | ADVMCMD_KillNote: ; ///////////////////////////////////// KILL NOTE ///
837 | inc ACC
838 | st ADVMRAM_KillCount
839 | set1 ADVMRAM_Flags, 0
840 | mov #2, ACC
841 | jmpf ADVM_CMDreturn ; 7 cycles
842 |
843 |
844 |
845 | ADVMCMD_DelayNote: ; //////////////////////////////////// DELAYED NOTE ///
846 | st ADVMRAM_PendingNote ; store note index
847 | ld C
848 | st ADVMRAM_PendingDuty ; store duty
849 |
850 | ld ADVMRAM_PhrasePosLow ; setup TR to grab wait
851 | st TRL
852 | ld ADVMRAM_PhrasePosHigh
853 | st TRH
854 |
855 | mov #2, ACC ; grab wait
856 | ldc
857 | inc ACC
858 | st ADVMRAM_DelayCount
859 |
860 | mov #3, C
861 | bn ADVMRAM_PendingNote, 7, .NoGmacro ; grab Gmacro or else 3-byte command
862 | ld C
863 | inc C
864 | ldc
865 | st ADVMRAM_PendingGmacro
866 |
867 | .NoGmacro:
868 | set1 ADVMRAM_Flags, 1
869 | ld C
870 | jmpf ADVM_CMDreturn ; 24 cycles (worst)
871 |
872 |
873 |
874 | ADVMCMD_RunGmacro: ; //////////////////////////////////// RUN GMACRO ///
875 | mov #1, ADVMRAM_GmacroWait
876 | st B
877 |
878 | ld ADVMRAM_GmacroLocalLow
879 | st TRL
880 | ld ADVMRAM_GmacroLocalHigh
881 | st TRH
882 |
883 | ld B
884 | clr1 ADVMRAM_Flags, 2
885 | be #$FF, .SkipGmacro
886 | set1 ADVMRAM_Flags, 2
887 |
888 | add ACC
889 | bn PSW, 7, .NoCarry
890 | inc TRH
891 | .NoCarry:
892 | st B
893 | ldc
894 | st ADVMRAM_GmacroPosLow
895 | ld B
896 | inc ACC
897 | ldc
898 | st ADVMRAM_GmacroPosHigh
899 | .SkipGmacro
900 |
901 | mov #2, ACC
902 | jmpf ADVM_CMDreturn ; 29 cycles worst
903 |
904 |
905 |
906 | ADVMCMD_SetDuty: ; ////////////////////////////////////// SET DUTY ///
907 | ld C
908 | and #%00011111
909 | st ADVMRAM_CurrDuty
910 | mov #1, ACC
911 | jmpf ADVM_CMDreturn ; 7 cycles
912 |
913 |
914 |
915 | ADVMCMD_SetSpeed: ; ///////////////////////////////////// SET SPEED ///
916 | bp C, 2, .SpeedIsTempo
917 | st ADVMRAM_GroovePos
918 |
919 | ld ADVMRAM_GrooveTableLocalLow
920 | st TRL
921 | ld ADVMRAM_GrooveTableLocalHigh
922 | st TRH
923 | ld ADVMRAM_GroovePos
924 | ldc
925 | be #$FF, .endgroove
926 | br .end
927 |
928 | .endgroove:
929 | ld ADVMRAM_GroovePos
930 | inc ACC ; get next byte
931 | ldc
932 | add ADVMRAM_GroovePos
933 | st ADVMRAM_GroovePos
934 | ldc
935 | .end:
936 | inc ADVMRAM_GroovePos
937 | st ADVMRAM_TickCount
938 |
939 | mov #2, ACC
940 | jmpf ADVM_CMDreturn ; 25 cycles
941 |
942 | .SpeedIsTempo:
943 | bp C, 1, .Timer1
944 | bp C, 0, .T0High
945 | st T0LR
946 | mov #2, ACC
947 | jmpf ADVM_CMDreturn ; 11 cycles
948 |
949 | .T0High:
950 | st T0HR
951 | mov #2, ACC
952 | jmpf ADVM_CMDreturn ; 11 cycles
953 |
954 | .Timer1:
955 | st T1HR
956 | mov #2, ACC
957 | jmpf ADVM_CMDreturn ; 9 cycles
958 |
959 |
960 | ; /////////////////////////////////////////////////////////////
961 | ; /// RAM DEFINITIONS ///
962 | ; /////////////////////////////////////////////////////////////
963 |
964 | ; ADVM_MUSIC RAM USAGE: 34 bytes
965 |
966 | ; Block A
967 | ADVMRAM_KillCount = $04
968 | ADVMRAM_DelayCount = $05
969 | ADVMRAM_PendingNote = $06
970 | ADVMRAM_PendingGmacro = $07
971 | ADVMRAM_PendingDuty = $08
972 | ADVMRAM_GmacroPosLow = $09
973 | ADVMRAM_GmacroPosHigh = $0A
974 | ADVMRAM_GmacroWait = $0B
975 | ADVMRAM_CurrentNote = $0C
976 | ADVMRAM_OffsetNote = $0D
977 | ADVMRAM_TransposePend = $0E
978 | ADVMRAM_TransposeCurr = $0F
979 | ADVMRAM_FixedFreq = $10
980 | ADVMRAM_DutyOverlay = $11
981 | ADVMRAM_SFXoverlay = $12
982 | ADVMRAM_TickCount = $13
983 | ADVMRAM_GroovePos = $14
984 |
985 | ; Block B
986 | ADVMRAM_WaitCount = $15
987 | ADVMRAM_Flags = $16
988 | ADVMRAM_PhrasePosLow = $17
989 | ADVMRAM_PhrasePosHigh = $18
990 | ADVMRAM_CurrDuty = $19
991 | ADVMRAM_LastDuty = $1A
992 | ADVMRAM_LastGmacro = $1B
993 | ADVMRAM_TmLinePosLow = $1C
994 | ADVMRAM_TmLinePosHigh = $1D
995 |
996 | ; Position Block
997 | ADVMRAM_TmLineLocalLow = $1E
998 | ADVMRAM_TmLineLocalHigh = $1F
999 | ADVMRAM_PhraseLocalLow = $20
1000 | ADVMRAM_PhraseLocalHigh = $21
1001 | ADVMRAM_GmacroLocalLow = $22
1002 | ADVMRAM_GmacroLocalHigh = $23
1003 | ADVMRAM_GrooveTableLocalLow = $24
1004 | ADVMRAM_GrooveTableLocalHigh = $25
1005 |
1006 | ; SFX SUB-Engine block
1007 | ; ADVM_SFX RAM USAGE: 6 bytes
1008 | ADVMSFX_ListLocalLow = $26
1009 | ADVMSFX_ListLocalHigh = $27
1010 | ADVMSFX_Wait = $28
1011 | ADVMSFX_PosLow = $29
1012 | ADVMSFX_PosHigh = $2A
1013 | ADVMSFX_Duty = $2B
1014 |
1015 | ; TOTAL: 40 bytes or a little over 1/8 of the user arythmetic RAM
1016 |
1017 |
1018 |
1019 |
1020 |
1021 | ; /////////////////////////////////////////////////////////////
1022 | ; /// DOCUMENTATION ///
1023 | ; /////////////////////////////////////////////////////////////
1024 | ;
1025 | ; ////////////////////// COMMANDS' FORMAT ///
1026 | ;
1027 | ; 0: PLAY NOTE - 000LWWWW GDNNNNNN ---DDDDD GGGGGGGG
1028 | ; L: Legato bit, will basically just change the note and ignore the last parameter fields and their request bits
1029 | ; 0001WWWW --NNNNNN
1030 | ;
1031 | ; W: Wait paremeter, literal WaitCount value+1 because 0 would mean waiting 255 steps (oops)
1032 | ;
1033 | ; G: request new Gmacro bit - will request the GGGGGGGG field
1034 | ; bit on 0000WWWW 1DNNNNNN ... GGGGGGGG
1035 | ; bit off 0000WWWW 0DNNNNNN ...
1036 | ;
1037 | ; D: request new Duty value - requrests new 5-bit duty value at the ---DDDDD field
1038 | ; bit on 0000WWWW G1NNNNNN ---DDDDD ...
1039 | ; bit off 0000WWWW G0NNNNNN ...
1040 | ;
1041 | ; N: Note table index - grabs a note value in the Note table, you can see it in the //LIBRARY// section
1042 | ;
1043 | ; if the Legato is 0 and N or D are also zero, this command will re-use the last specified duty and Gmacro
1044 | ; stored at Block B -> LastDuty and LastGmacro
1045 | ;
1046 | ;
1047 | ;
1048 | ; 1: END EVENT - 001----1 or 001-----0 SSSSSSSS
1049 | ; if the 0th bit of this command is 1, it ends the phrase to grab the next entry in the time line data
1050 | ; or else, if this bit is 0, it will reset the time line position added with the SSSSSSSS field
1051 | ;
1052 | ; S: Song Start offset
1053 | ;
1054 | ;
1055 | ;
1056 | ; 2: WAIT STEPS - 010WWWWW or 001111111 WWWWWWWW
1057 | ; sets a wait value+1 to WaitCount variable
1058 | ; if the wait value is below 1F, the command is a single-byte command, otherwise it's two-byte
1059 | ;
1060 | ;
1061 | ;
1062 | ; 3: KILL NOTE - 011----- KKKKKKKK
1063 | ; a two-byte command that sets the note kill counter at Block A (+1 like wait steps)
1064 | ; one tick of this command is the base refresh rate you define before the tick counter
1065 | ;
1066 | ;
1067 | ;
1068 | ; 4: DELAYED NOTE - 100LDDDD GDNNNNNN WWWWWWWW GGGGGGGG
1069 | ; similar to the PLAY NOTE command but it will wait in Block A ticks (the base refresh before the tick counter)
1070 | ; however, Wait and Duty are swapped to preserve one byte
1071 | ; as a side effect of Duty being reduced to 4-bits, you can only define duties ranging from 1/32 to 16/32
1072 | ;
1073 | ; the parameter fields' meaning are the same as PLAY NOTE, see command 0 for details
1074 | ;
1075 | ;
1076 | ;
1077 | ; 5: RUN GMACRO - 101----- GGGGGGGG
1078 | ; a two-byte command that specifies a new Gmacro index, used for mid-note Gmacro changes like drums
1079 | ; if a non-legato PLAY NOTE happens afterwards, this value is discarded
1080 | ;
1081 | ;
1082 | ;
1083 | ; 6: SET PWM - 110DDDDD
1084 | ; an always single-byte command that specifies a new duty for mid-note changes
1085 | ; D is the 5-bit duty value for the duty formula, it's saved under CurrentDuty variable
1086 | ; if a non-legato PLAY NOTE happens afterwards, this value is discarded
1087 | ;
1088 | ;
1089 | ;
1090 | ; 7: SET SPEED - 111----- TTTTTTTT
1091 | ; a new value for the Tick counter (+1), used for changing the song's Block B run speed
1092 | ; NOTE FOR SELF: possibly make a mode where it reloads one of the T0 halves for tempo control?
1093 | ;
1094 | ;
1095 | ;
1096 | ; ////////////////////// GMACRO FORMAT ///
1097 | ;
1098 | ; APWDDDDD MFFFFFFF WWWWWWWW
1099 | ; |||||||| |||||||| ||||||||
1100 | ; |||||||| |||||||| ++++++++---- Wait parameter
1101 | ; |||||||| |+++++++------------- note LUT index
1102 | ; |||||||| +-------------------- pitch Mode
1103 | ; |||+++++---------------------- Duty number
1104 | ; ||+--------------------------- request Wait field
1105 | ; |+---------------------------- request Freq field
1106 | ; +----------------------------- Action bit
1107 | ;
1108 | ; Action bit: tells whether the macro ends (0) or acts on the sound registers (1)
1109 | ; req Pitch: requests Pitch field byte (PPPPPPPP), if it's 0 it won't update the pitch
1110 | ; req Wait: requests Wait field byte (WWWWWWWW), if it's 0 it will wait only 1 tick
1111 | ; Duty: literal duty value EXCEPT when it's
1112 | ; $1E: ignore duty change and use last duty
1113 | ; $1F: mute channel
1114 | ; pitch Mode: tells whether it's a fixed note (1) or an arpeggio offset (0)
1115 | ; when on fixed mode (1) it redirects the pulse width value to DutyOverride variable
1116 | ;
1117 | ; possible sizes:
1118 | ; 100DDDDD
1119 | ; 110DDDDD PPPPPPPP
1120 | ; 101DDDDD MFFFFFFF
1121 | ; 111DDDDD MFFFFFFF PPPPPPPP
1122 | ;
1123 | ;
1124 | ;
1125 | ; 0S------ BBBBBBBB
1126 | ; | ||||||||
1127 | ; | ++++++++---- go Back
1128 | ; +------------------- Stop bit
1129 | ;
1130 | ; Stop bit: don't look for a Loop parameter and disable the Gmacro
1131 | ; go Back: offset current Gmacro pos by bytes amount backwards (GmacroPos - BBBBBBBB)
1132 | ;
1133 | ; possible sizes:
1134 | ; 01000000
1135 | ; 00000000 BBBBBBBB
1136 | ;
1137 | ;
1138 | ; TODO: rewrite Gmacro and pitch pipe fixed part
1139 | ;
1140 | ;
1141 | ; ////////////////////// SONG HEADER FORMAT ///
1142 | ;
1143 | ; SONG_NAME label, it's used to know the start of the header, you feed its address to the setup code
1144 | ; .word time line location, tells where the time line data starts at
1145 | ; .word phrase index table location, tells where the list of Phrase locations is on Flash
1146 | ; .word Gmacro index table location, tells where the list of Gmacro locations is on Flash
1147 | ; .word Groove table location, tells where the groove table is on Flash
1148 | ; .byte Groove table start position
1149 | ;
1150 | ;
1151 | ; the driver setup code copies these words into the Position block of RAM (and the last byte into TickPreset)
1152 | ; access patterns for each local are:
1153 | ; TmLinePos <- TmLineLocal
1154 | ; PhrasePos <- Phrase data location <- Phrase index list
1155 | ; GmacroPos <- Gmacro data location <- Gmacro index list
1156 | ;
1157 | ;
1158 | ; Time line format:
1159 | ; PP TT (byte) - first byte (PP) contains a Phrase Index, second byte (TT) is a note transpose value
1160 | ;
1161 | ;
1162 | ;
1163 | ; ////////////////////// OTHER DOCUMENTATION ///
1164 | ;
1165 | ; FLAGS variable: C----GDK
1166 | ; | |||
1167 | ; | ||+--- Kill note counter enable
1168 | ; | |+---- Delay note is pending
1169 | ; | +----- Gmacro enable
1170 | ; +---------- Continuity check
1171 | ;
1172 | ; Continuity check: used for telling if a command that modifies step counter have been read already
1173 | ;
1174 | ;
1175 | ; SFXoverlay variable: 00-FC: new SFX pending, $FE: SFX being processed, $FF: no SFX running
1176 | ; FixedFreq variable: 00-FE: fixed T1 reload to play and ignore pitch pipe, FF: no pitch, run normally
1177 | ; if this variable is 00-FE the value at DutyOverlay variable is used, otherwise it's ignored
1178 | ;
1179 | ;
1180 | ;
1181 | ; ////////////////////// GROOVE TABLE ///
1182 | ;
1183 | ; the groove is a sequence of numbers to be used with the Block B tick counter, it affects the speed of the song
1184 | ; for example, a groove table that alternates between waiting 4 and 6 ticks will create a swing tempo
1185 | ;
1186 | ; in order to use the groove table just specify its start position in the song's header
1187 | ; you can change the position within the groove table using comand 7 (SetSpeed) with the lower bits cleared
1188 | ;
1189 | ; to tell the Tick counter to go back to a previous point you have to write $FF to mark an offset event
1190 | ; then the Tick counter will grab the next byte and add to its 8-bit position
1191 | ; this also means it overflows, so if you want to go back, all you have to do is to type a reaaally big number
1192 | ; like $FF to return one entry, $FE to return two, $FD to return three and so on...
1193 | ;
1194 | ; maybe add an example song that shows this better... todo?
1195 | ;
1196 | ;
1197 | ;
1198 | ; /////////////////////// SFX SUBDRIVER ///
1199 | ;
1200 | ; HOW TO SETUP LIST LOCAL
1201 | ; just write this
1202 | ; mov #yoursfxlist, ADVMSFX_ListLocalHigh
1204 | ; MAKING SURE THE LABEL IS THE LIST, NOT INDIVIDUAL SFX TRACKS
1205 | ;
1206 | ;
1207 | ;
1208 | ; ADVM SFX SUBDRIVER DIRECTORY:
1209 | ; Flash(data) <- Flash(index list)
1210 | ;
1211 | ;
1212 | ;
1213 | ; ADVM SFX SUBDRIVER FORMAT:
1214 | ; very similar to Gmacro but M bit doesn't exist, it's T1LR value directly
1215 | ; APWDDDDD PPPPPPPP WWWWWWWW
1216 | ; |||||||| |||||||| ||||||||
1217 | ; |||||||| |||||||| ++++++++--- wait
1218 | ; |||||||| ++++++++------------ T1 pitch
1219 | ; |||+++++--------------------- duty (1E = ignore; 1F = mute)
1220 | ; ||+-------------------------- Wait field request
1221 | ; |+--------------------------- Pitch field request
1222 | ; +---------------------------- Action bit
1223 | ;
1224 | ; if A is 0
1225 | ; end SFX and re-enable main sound, all other bits disregarded
--------------------------------------------------------------------------------