├── 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 |

ADVM 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 --------------------------------------------------------------------------------