diff --git a/21TheWorld.inc b/21TheWorld.inc index 2bb4522..418dbc1 100644 --- a/21TheWorld.inc +++ b/21TheWorld.inc @@ -285,6 +285,8 @@ TheWorld: ld hl, MY_OAM ; go through MY_OAM and, for any which aren't still visible, set their ; y value to zero. + ; this could probably be rewritten as a simpler lookup table if we rewrote it + ; using jp hl and pointer math rather than explicitly listing all the possibilities .cleanUpLoop ld a, [hl] ; a is the y value and hl points to y : cp a, (2+1+5)*8 diff --git a/Async.inc b/Async.inc index 6a2ab67..bd51d43 100644 --- a/Async.inc +++ b/Async.inc @@ -1,15 +1,13 @@ ; async variables somewhere in RAM -def vAsyncAF equ ASYNC_VARS_START -def vAsyncHL EQU vAsyncAF+2 -def vAsyncDE EQU vAsyncHL+2 -def vAsyncBC equ vAsyncDE+2 -def vAsyncPC equ vAsyncBC+2 -def vAsyncNext equ vAsyncPC+2 -def vAsyncAfter equ vAsyncNext+2 -def vAsyncProgress equ vAsyncAfter+2 -def vAsyncMainSP equ vAsyncProgress+2 -def vAsyncThreadSP equ vAsyncMainSP+2 - +PUSHS "Async Variables", WRAM0[ASYNC_VARS_START] +vAsyncAF: dw +vAsyncHL: dw +vAsyncDE: dw +vAsyncBC: dw +vAsyncPC: dw +vAsyncMainSP: dw +vAsyncThreadSP: dw +POPS ; canonical ordering to push should be: AF, BC, DE, HL, def ASYNC_STACK_TOP equ $ffc0 @@ -18,6 +16,7 @@ def ASYNC_STACK_TOP equ $ffc0 ; second value is the destination of the async call, at ffbc = ffbe-2 = ffc0-2-2 def ASYNC_THREAD_CALL equ ASYNC_STACK_TOP - 2 - 2 +; timing for async execution, in scanlines def SAFE_ASYNC_START EQU 148 def SAFE_ASYNC_END EQU 153 diff --git a/Audio.inc b/Audio.inc index 150fa24..07db592 100644 --- a/Audio.inc +++ b/Audio.inc @@ -1,347 +1,534 @@ -PUSHS "Sound Variables", WRAM0[AUDIO_VARS_START] -ordersStart: dw -println "orders start is ", ordersStart -ordersLength: db - - -; audio player state -currentOrderNumber: db -currentRowNumber: db -println "currentRowNumber is ", currentRowNumber -currentTickNumber: db -currentPattern: dw -println "current pattern is ", currentPattern -audioReadHead: dw - -AUDIO_VARIABLES_BEGIN: - -speed: db - -macro channel ; macro to define all the channell data in parallel -channel_\1_note: db -channel_\1_duty: db -channel_\1_volume: db -channel_\1_slide: db -channel_\1_arp: db -channel_\1_pan: db -channel_\1_vibrato: db -channel_\1_trigger: db -endm - -channel 1 -channel 2 -channel 3 -channel 4 - -println "channel 1 note is ", channel_1_note -AUDIO_VARIABLES_END: -POPS - SoundSetup: - ld a, LOW(gbtarottheme) - ld [ordersStart], a - ld a, HIGH(gbtarottheme) - ld [ordersStart+1], a - - ld a, 0 - ld [currentOrderNumber], a - ld [currentRowNumber], a - ld [currentTickNumber], a - - ld a, $FF - ld [currentRowNumber], a - - ld a, [ordersStart] - ld l, a - ld a, [ordersStart+1] - ld h, a - inc hl - - ld a, [hl+] - ld [currentPattern], a - ld [audioReadHead], a - ld a, [hl+] - ld [currentPattern+1], a - ld [audioReadHead+1], a - - ld hl, ZEROES - ld de, AUDIO_VARIABLES_BEGIN - ld bc, AUDIO_VARIABLES_END - AUDIO_VARIABLES_BEGIN - call CopyRange - - ld a, 6 - ld [speed], a - - ld a, %1000_1111 - ld [rNR52], a - ld a, $ff - ld [rNR51], a - ld a, $11 - ld [rNR50], a + ld a, BANK(gbt_play) + ld [rROMB0], a + ld de,pythonrangetest + ld bc,BANK(instr_test_data) + ld a,$05 + call gbt_play ; Play song + + ld a, 1 + call gbt_loop + + ld a, [cvCardBank] + ld [rROMB0], a ret + SoundUpdate: - ld a, [speed] - ld b, a - ld a, [currentTickNumber] - inc a - call ArrayClampLoopingB - ld [currentTickNumber], a + ld a, BANK(gbt_play) + ld [rROMB0], a - cp a, 0 - jp nz, .doneAllPackets ; if we're not zero, then just update the things + call gbt_update - ld b, 64 - ld a, [currentRowNumber] - inc a - call ArrayClampLoopingB - ld [currentRowNumber], a - - cp a, 0 - jp nz, :+ ; if the new row number is zero, then we go to the next pattern. - ld a, AUDHIGH_RESTART - ld [rNR14], a - - - ; TODO: update for song orders - ld a, [currentPattern] - ld [audioReadHead], a - ld a, [currentPattern+1] - ld [audioReadHead+1], a -: - ld a, [audioReadHead] - ld l, a - ld a, [audioReadHead+1] - ld h, a - - ld a, [currentRowNumber] - ld b, a - ld a, [hl+] - and a, $F0 - cp a, 0 - ret nz - ld a, [hl+] - cp a, b - jp nz, .doneAllPackets ; return if the current row doesn't equal the row we're looking at - - ; else we're looking at packets -.packetExamine - ld a, l - ld [audioReadHead], a - ld a, h - ld [audioReadHead+1], a - - ld d, h - ld e, l - - ld hl, channel_1_note - - ld a, [de] - and a, $0F - cp a, 1 - jp nz, :+ - ld hl, channel_1_note -: cp a, 2 - jp nz, :+ - ld hl, channel_2_note -: cp a, 3 - jp nz, :+ - ld hl, channel_3_note -: cp a, 4 - jp nz, :+ - ld hl, channel_4_note -: - - ld a, [de] - and a, $F0 ; grab the top nibble of [hl], which holds the effect. - - ; if nibble is zero, then update our read head (already done) and return. - cp a, $00 - jp z, .doneAllPackets - - ld bc, channel_1_note - channel_1_note - cp a, $10 - jp z, .updateValue - - ld bc, channel_1_volume - channel_1_note - cp a, $20 - jp z, .updateVolume - - ld bc, channel_1_duty - channel_1_note - cp a, $30 - jp z, .updateValue - - ld bc, channel_1_pan - channel_1_note - cp a, $40 - jp z, .updateValue - - ld bc, channel_1_arp - channel_1_note - cp a, $50 - jp z, .updateValue - - ld bc, channel_1_vibrato - channel_1_note - cp a, $60 - jp z, .updateValue - - ld bc, channel_1_slide - channel_1_note - cp a, $70 - jp z, .updateValue - - ld bc, channel_1_volume - channel_1_note - cp a, $80 - jp z, .noteCut - - cp a, $90 - jp z, .patternJump - - cp a, $a0 - jp z, .breakSetStep - - cp a, $b0 - jp z, .setSpeed - - cp a, $c0 - jp z, .event - ; and now a bunch of code for handling all those cases! - -.updateVolume: - inc de - add hl, bc - ld a, 0 - ld [channel_1_trigger], a - ld a, [hl] - cp a, 0 - jp nz, :+ - ; if the volume is zero to start with, - ; and the new volume is nonzero, - ld a, [de] - ; set the trigger flag - cp a, 0 - jp z, :+ - ld a, AUDHIGH_RESTART - ld [channel_1_trigger], a -: - - ld a, [de] - ld [hl], a - - inc de - ld h, d - ld l, e - jp .packetExamine - -.updateValue: - inc de - - add hl, bc -: ld a, [de] - ld [hl], a - - inc de - ld h, d - ld l, e - - jp .packetExamine - -.noteCut: - inc de - add hl, bc - - ld [hl], 0 - - ld a, AUDHIGH_RESTART - ld [channel_1_trigger], a - - inc de - ld h, d - ld l, e - jp .packetExamine -.patternJump: - ; TODO - inc de - inc de - ld h, d - ld l, e - jp .packetExamine -.breakSetStep: - ; TODO - inc de - inc de - ld h, d - ld l, e - jp .packetExamine -.setSpeed: - inc de - ld a, [de] - ld [speed], a - - inc de - ld h, d - ld l, e - jp .packetExamine -.event: - ; TODO - inc de - inc de - ld h, d - ld l, e - jp .packetExamine - - -.doneAllPackets: - call Channel_1_Update - call Channel_2_Update - call Channel_3_Update - call Channel_4_Update + ld a, [cvCardBank] + ld [rROMB0], a ret - -Channel_1_Update: - ld a, [channel_1_duty] - or a, $0a - ld [rNR11], a - - ld a, [channel_1_volume] - sla a - sla a - sla a - sla a - and a, $F0 - ld [rNR12], a - - ld a, [channel_1_note] - ld hl, note_periods - ld b, 0 - ld c, a - add hl, bc - add hl, bc ; double width values - - ld a, [hl+] - ld [rNR13], a - - - ld a, [channel_1_trigger] - or a, [hl] - or a, AUDHIGH_LENGTH_OFF - ld [rNR14], a - - ld a, 0 - ld [channel_1_trigger], a - ret - -Channel_2_Update: -Channel_3_Update: -Channel_4_Update: - ret - -note_periods: - dw 44, 156, 262, 363, 457, 547, 631, 710, 786, 854, 923, 986, ; C3 to B3 - dw 1046,1102,1155,1205,1253,1297,1339,1379,1417,1452,1486,1517, ; C4 to B4 - dw 1546,1575,1602,1627,1650,1673,1694,1714,1732,1750,1767,1783, ; C5 to B5 - dw 1798,1812,1825,1837,1849,1860,1871,1881,1890,1899,1907,1915, ; C6 to B6 - dw 1923,1930,1936,1943,1949,1954,1959,1964,1969,1974,1978,1982, ; C7 to B7 - dw 1985,1988,1992,1995,1998,2001,2004,2006,2009,2011,2013,2015 ; C8 to B8 - -INCLUDE "theme.inc" +; File created by mod2gbt +; s3m2gbt modified by shoofle to output rgbds-compatible asm files +SECTION "pythonrangetest_0", ROMX +pythonrangetest_0: + db $BF,$00,$30,$10,$10,$10, + db $00,$00,$00,$00, + db $BF,$01,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$02,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$03,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$04,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$05,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$06,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$07,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$08,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$09,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$0A,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$0B,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$0C,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$0D,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$0E,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$0F,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$10,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$11,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$12,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$13,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$14,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$15,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$16,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$17,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$18,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$19,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$1A,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$1B,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$1C,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$1D,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$1E,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$1F,$30,$00,$00,$00, + db $00,$00,$00,$00, + + +SECTION "pythonrangetest_1", ROMX +pythonrangetest_1: + db $BF,$20,$30,$10,$10,$10, + db $00,$00,$00,$00, + db $BF,$21,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$22,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$23,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$24,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$25,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$26,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$27,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$28,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$29,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$2A,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$2B,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$2C,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$2D,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$2E,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$2F,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$30,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$31,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$32,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$33,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$34,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$35,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$36,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$37,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$38,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$39,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$3A,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$3B,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$3C,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$3D,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$3E,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$3F,$30,$00,$00,$00, + db $00,$00,$00,$00, + + +SECTION "pythonrangetest_2", ROMX +pythonrangetest_2: + db $BF,$40,$30,$10,$10,$10, + db $00,$00,$00,$00, + db $BF,$41,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$42,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$43,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$44,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$45,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$46,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$47,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + + +pythonrangetest_init_state: + db 0x01,0x06,0x02,0x11,0x22,0x44,0x88,0x00, + +SECTION "pythonrangetest", ROMX +pythonrangetest: + db BANK(pythonrangetest_0) + dw pythonrangetest_0 + + db BANK(pythonrangetest_1) + dw pythonrangetest_1 + + db BANK(pythonrangetest_2) + dw pythonrangetest_2 + + db $00 + dw $0000 + + + SECTION "instr_test_0", ROMX +instr_test_0: + DB $98, $1F, $20, $20, $4A, $07 + DB $2F, $00, $00, $20 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $98, $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $98, $3F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $98, $0F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $2F, $00, $00, $00 + DB $20, $00, $98, $10, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $98, $11, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $98, $12, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $98, $13, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + + SECTION "instr_test_1", ROMX +instr_test_1: + DB $00, $00, $98, $14, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $98, $15, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $98, $16, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $98, $17, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $21, $00 + DB $00, $00, $20, $80, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $81, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $82, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $83, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + + SECTION "instr_test_2", ROMX +instr_test_2: + DB $00, $00, $00, $84, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $85, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $86, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $87, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $88, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $89, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $8A, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $8B, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + + SECTION "instr_test_3", ROMX +instr_test_3: + DB $00, $00, $00, $8C, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $8D, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $8E, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $8F, $0F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $2F + DB $00, $00, $00, $20 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + DB $00, $00, $00, $00 + + SECTION "instr_test_data", ROM0 +instr_test_data:: + DB BANK(instr_test_0) + DW instr_test_0 + DB BANK(instr_test_1) + DW instr_test_1 + DB BANK(instr_test_2) + DW instr_test_2 + DB BANK(instr_test_3) + DW instr_test_3 + DB $00 + DW $0000 + + diff --git a/CardHelpers.inc b/CardHelpers.inc index 31985ca..6fc42a8 100644 --- a/CardHelpers.inc +++ b/CardHelpers.inc @@ -1,6 +1,8 @@ ; CARD_HELPER_VARS_START defines the beginning of the $100 bytes set aside for system usage -def cvCardBank equ 0 -def cvCardAddress equ 1 ; location of the current card's struct +PUSHS "Card Helper Vars", WRAM0[CARD_HELPER_VARS_START] +cvCardBank: db +cvCardAddress: dw +POPS LoadCardData: LoadCardDataAsync: @@ -33,14 +35,14 @@ LoadCardDataAsync: add hl, bc ; triple add bc entries are bank, addr, addr ld a, [hl+] - ld [CARD_HELPER_VARS_START + cvCardBank], a + ld [cvCardBank], a ld [rROMB0], a ; select the specified bank ; follow the pointer we're looking at, and write it to cvCardAddress ld a, [hl+] - ld [CARD_HELPER_VARS_START + cvCardAddress], a + ld [cvCardAddress], a ld c, a ld a, [hl+] - ld [CARD_HELPER_VARS_START + cvCardAddress + 1], a + ld [cvCardAddress + 1], a ld b, a ld h, b @@ -82,9 +84,9 @@ LoadCardDataAsync: call CopyTilesToMap - ld a, [CARD_HELPER_VARS_START + cvCardAddress] + ld a, [cvCardAddress] ld l, a - ld a, [CARD_HELPER_VARS_START + cvCardAddress + 1] + ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld b, 0 @@ -110,9 +112,9 @@ LoadCardDataAsync: call CardInit - ld a, [CARD_HELPER_VARS_START + cvCardAddress] + ld a, [cvCardAddress] ld l, a - ld a, [CARD_HELPER_VARS_START + cvCardAddress + 1] + ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld b, 0 @@ -135,9 +137,9 @@ LoadCardDataAsync: call CopyRange - ld a, [CARD_HELPER_VARS_START + cvCardAddress] + ld a, [cvCardAddress] ld l, a - ld a, [CARD_HELPER_VARS_START + cvCardAddress + 1] + ld a, [cvCardAddress + 1] ld h, a ; hl now contains the address of the card data. ld b, 0 diff --git a/CopyRangeSafe.inc b/CopyRange.inc similarity index 51% rename from CopyRangeSafe.inc rename to CopyRange.inc index 89ef549..b855437 100644 --- a/CopyRangeSafe.inc +++ b/CopyRange.inc @@ -1,18 +1,3 @@ -; variables for safe transfer async function -DEF vSafeCopySource EQU $ff80 -DEF vSafeCopyDest EQU vSafeCopySource + 2 -DEF vSafeCopyCount EQU vSafeCopyDest + 2 ; check this for safe transfer being complete -DEF vSafeCopyOriginalCount EQU vSafeCopyCount + 2 -; stash previous interrupt state before using the interrupts -DEF vSafeCopyLYC EQU vSafeCopyOriginalCount + 2 ; stashes $FF45, the LYC register -DEF vSafeCopySTAT EQU vSafeCopyLYC + 1 ; stashes $FF41, the STAT register -DEF vSafeCopyInterrupt EQU vSafeCopySTAT + 1 ; stashes the previous LCD interrupt -DEF vSafeCopyInterruptEnable EQU vSafeCopyInterrupt + 2 ; stashes $FFFF, which interrupts are enabled - -DEF SAFE_TRANSFER_START EQU 145 -DEF SAFE_TRANSFER_END EQU 153 - - CopyRange: CopyRangeUnsafe: ; hl is source diff --git a/CopyTilesSafe.inc b/CopyTiles.inc similarity index 100% rename from CopyTilesSafe.inc rename to CopyTiles.inc diff --git a/Random.inc b/Random.inc index 3e6dc2d..f0546f5 100644 --- a/Random.inc +++ b/Random.inc @@ -101,8 +101,7 @@ RandomUnderD: ; takes d as limit (inclusive) ret z ret c jr .inOne - - + SwapCards: ; takes hl as array, c and e as indices to swap (uses b and d) inc hl ; first element of array push hl ; save it for later @@ -122,10 +121,3 @@ SwapCards: ; takes hl as array, c and e as indices to swap (uses b and d) add hl, bc ld [hl], d ; put old [hl+0e] in [hl+0c] ret - - - - - - - \ No newline at end of file diff --git a/ScreenCardBrowse.inc b/ScreenCardBrowse.inc index ecdfe5e..6cc97ab 100644 --- a/ScreenCardBrowse.inc +++ b/ScreenCardBrowse.inc @@ -1,5 +1,4 @@ ; screen variables shared with screencardread -;DEF vPreviousCardIndex EQU VARIABLES_START ScreenCardBrowse: dw CardBrowseSetup diff --git a/ScreenCardRead.inc b/ScreenCardRead.inc index 1f374c1..c5f73eb 100644 --- a/ScreenCardRead.inc +++ b/ScreenCardRead.inc @@ -1,5 +1,7 @@ -; screen variables -DEF vPreviousCardIndex EQU SCREEN_VARS_START+16 +PUSHS UNION "Screen Variables", WRAM0[SCREEN_VARS_START] +ds 16 ; why are we putting this so far in? +vPreviousCardIndex: db +POPS ScreenCardRead: dw CardReadSetup diff --git a/ScreenMainMenu.inc b/ScreenMainMenu.inc index ba2e7bd..68b9aee 100644 --- a/ScreenMainMenu.inc +++ b/ScreenMainMenu.inc @@ -1,20 +1,22 @@ -; global variables at the start of hram -DEF vCurrentSpread EQU GLOBAL_VARS_START ; 16bit address of current spread, ff90 -DEF vSelectedSpreadIndex equ vCurrentSpread + 2 ; ff92 -def vSelectedSpreadCard equ vSelectedSpreadIndex + 1 ; ff93 -def vSelectedCardIndex equ vSelectedSpreadCard+1 ; ff94 -DEF vFrameCountSquares EQU vSelectedCardIndex+1 ; ff95 -DEF vTime EQU vFrameCountSquares+1 ; 16bit ; ff96 -def vBlocked EQU vTime+2 +PUSHS UNION "Global Variables", WRAM0[GLOBAL_VARS_START] +vCurrentSpread: dw +vSelectedSpreadIndex: db +vSelectedSpreadCard: db +vSelectedCardIndex: db +vFrameCountSquares: db +vTime: dw +vBlocked: db println "vBlocked is ", vBlocked ; - +POPS ; screen-specific variables -DEF vFrameCount1 EQU SCREEN_VARS_START -DEF vFrameCount2 equ vFrameCount1+1 -DEF vFrameCount3 EQU vFrameCount2+1 -DEF vMenuIndex equ vFrameCount3+1 -DEF vMenuIndexPrevious equ vMenuIndex + 1 +PUSHS UNION "Screen Variables", WRAM0[SCREEN_VARS_START] +vFrameCount1: db +vFrameCount2: db +vFrameCount3: db +vMenuIndex: db +vMenuIndexPrevious: db +POPS ScreenMainMenu: dw MainMenuSetup diff --git a/ScreenShuffle.inc b/ScreenShuffle.inc index ee95ef1..7b2a2cc 100644 --- a/ScreenShuffle.inc +++ b/ScreenShuffle.inc @@ -1,11 +1,10 @@ -; screen variables already ddefined in screencardread -;DEF vPreviousCardIndex EQU VARIABLES_START -;def vBlocked equ vPreviousCardIndex + 1 -def vAnimationFrame EQU SCREEN_VARS_START -def vState EQU vAnimationFrame+1 -def vCurrentAnimation EQU vState+1 ; 2 bytes -def vShuffleIndex equ vCurrentAnimation+2 -def vShuffleTime equ vShuffleIndex+1 ; 2 bytes +PUSHS UNION "Screen Variables", WRAM0[SCREEN_VARS_START] +vAnimationFrame: db ;def vAnimationFrame EQU SCREEN_VARS_START +vState: db ;def vState EQU vAnimationFrame+1 +vCurrentAnimation: dw ;def vCurrentAnimation EQU vState+1 ; 2 bytes +vShuffleIndex: db ;def vShuffleIndex equ vCurrentAnimation+2 +vShuffleTime: dw ;def vShuffleTime equ vShuffleIndex+1 ; 2 bytes +POPS def S_Center = 0 def S_RightOut = 1 diff --git a/ScreenSpreadSelect.inc b/ScreenSpreadSelect.inc index c12594a..517a4dc 100644 --- a/ScreenSpreadSelect.inc +++ b/ScreenSpreadSelect.inc @@ -1,5 +1,7 @@ -DEF vPreviousSpreadIndex EQU SCREEN_VARS_START -def vPreviousSpreadCard equ vPreviousSpreadIndex + 1 +PUSHS UNION "Screen Variables", WRAM0[SCREEN_VARS_START] +vPreviousSpreadIndex: db ; EQU SCREEN_VARS_START +vPreviousSpreadCard: db ; equ vPreviousSpreadIndex + 1 +POPS ScreenSpreadSelect: dw SpreadSelectSetup diff --git a/00thefool.aseprite b/card_art/00thefool.aseprite similarity index 100% rename from 00thefool.aseprite rename to card_art/00thefool.aseprite diff --git a/01themagician.aseprite b/card_art/01themagician.aseprite similarity index 100% rename from 01themagician.aseprite rename to card_art/01themagician.aseprite diff --git a/01themagician.asm b/card_art/01themagician.asm similarity index 100% rename from 01themagician.asm rename to card_art/01themagician.asm diff --git a/02thehighpriestess.aseprite b/card_art/02thehighpriestess.aseprite similarity index 100% rename from 02thehighpriestess.aseprite rename to card_art/02thehighpriestess.aseprite diff --git a/02thehighpriestess.asm b/card_art/02thehighpriestess.asm similarity index 100% rename from 02thehighpriestess.asm rename to card_art/02thehighpriestess.asm diff --git a/03theempress.aseprite b/card_art/03theempress.aseprite similarity index 100% rename from 03theempress.aseprite rename to card_art/03theempress.aseprite diff --git a/03theempress.asm b/card_art/03theempress.asm similarity index 100% rename from 03theempress.asm rename to card_art/03theempress.asm diff --git a/04theemperor.aseprite b/card_art/04theemperor.aseprite similarity index 100% rename from 04theemperor.aseprite rename to card_art/04theemperor.aseprite diff --git a/04theemperor.asm b/card_art/04theemperor.asm similarity index 100% rename from 04theemperor.asm rename to card_art/04theemperor.asm diff --git a/05thehierophant.aseprite b/card_art/05thehierophant.aseprite similarity index 100% rename from 05thehierophant.aseprite rename to card_art/05thehierophant.aseprite diff --git a/05thehierophant.asm b/card_art/05thehierophant.asm similarity index 100% rename from 05thehierophant.asm rename to card_art/05thehierophant.asm diff --git a/06thelovers.aseprite b/card_art/06thelovers.aseprite similarity index 100% rename from 06thelovers.aseprite rename to card_art/06thelovers.aseprite diff --git a/06thelovers.asm b/card_art/06thelovers.asm similarity index 100% rename from 06thelovers.asm rename to card_art/06thelovers.asm diff --git a/07thechariot.aseprite b/card_art/07thechariot.aseprite similarity index 100% rename from 07thechariot.aseprite rename to card_art/07thechariot.aseprite diff --git a/07thechariot.asm b/card_art/07thechariot.asm similarity index 100% rename from 07thechariot.asm rename to card_art/07thechariot.asm diff --git a/08strength.aseprite b/card_art/08strength.aseprite similarity index 100% rename from 08strength.aseprite rename to card_art/08strength.aseprite diff --git a/08strength.asm b/card_art/08strength.asm similarity index 100% rename from 08strength.asm rename to card_art/08strength.asm diff --git a/09thehermit.aseprite b/card_art/09thehermit.aseprite similarity index 100% rename from 09thehermit.aseprite rename to card_art/09thehermit.aseprite diff --git a/09thehermit.asm b/card_art/09thehermit.asm similarity index 100% rename from 09thehermit.asm rename to card_art/09thehermit.asm diff --git a/10wheeloffortune.aseprite b/card_art/10wheeloffortune.aseprite similarity index 100% rename from 10wheeloffortune.aseprite rename to card_art/10wheeloffortune.aseprite diff --git a/10wheeloffortune.asm b/card_art/10wheeloffortune.asm similarity index 100% rename from 10wheeloffortune.asm rename to card_art/10wheeloffortune.asm diff --git a/11justice.aseprite b/card_art/11justice.aseprite similarity index 100% rename from 11justice.aseprite rename to card_art/11justice.aseprite diff --git a/11justice.asm b/card_art/11justice.asm similarity index 100% rename from 11justice.asm rename to card_art/11justice.asm diff --git a/12thehangedman.aseprite b/card_art/12thehangedman.aseprite similarity index 100% rename from 12thehangedman.aseprite rename to card_art/12thehangedman.aseprite diff --git a/12thehangedman.asm b/card_art/12thehangedman.asm similarity index 100% rename from 12thehangedman.asm rename to card_art/12thehangedman.asm diff --git a/13death.aseprite b/card_art/13death.aseprite similarity index 100% rename from 13death.aseprite rename to card_art/13death.aseprite diff --git a/13death.asm b/card_art/13death.asm similarity index 100% rename from 13death.asm rename to card_art/13death.asm diff --git a/14temperance.aseprite b/card_art/14temperance.aseprite similarity index 100% rename from 14temperance.aseprite rename to card_art/14temperance.aseprite diff --git a/14temperance.asm b/card_art/14temperance.asm similarity index 100% rename from 14temperance.asm rename to card_art/14temperance.asm diff --git a/15thedevil.aseprite b/card_art/15thedevil.aseprite similarity index 100% rename from 15thedevil.aseprite rename to card_art/15thedevil.aseprite diff --git a/15thedevil.asm b/card_art/15thedevil.asm similarity index 100% rename from 15thedevil.asm rename to card_art/15thedevil.asm diff --git a/16thetower.aseprite b/card_art/16thetower.aseprite similarity index 100% rename from 16thetower.aseprite rename to card_art/16thetower.aseprite diff --git a/16thetower.asm b/card_art/16thetower.asm similarity index 100% rename from 16thetower.asm rename to card_art/16thetower.asm diff --git a/17thestar.aseprite b/card_art/17thestar.aseprite similarity index 100% rename from 17thestar.aseprite rename to card_art/17thestar.aseprite diff --git a/17thestar.asm b/card_art/17thestar.asm similarity index 100% rename from 17thestar.asm rename to card_art/17thestar.asm diff --git a/18themoon.aseprite b/card_art/18themoon.aseprite similarity index 100% rename from 18themoon.aseprite rename to card_art/18themoon.aseprite diff --git a/18themoon.asm b/card_art/18themoon.asm similarity index 100% rename from 18themoon.asm rename to card_art/18themoon.asm diff --git a/19thesun.aseprite b/card_art/19thesun.aseprite similarity index 100% rename from 19thesun.aseprite rename to card_art/19thesun.aseprite diff --git a/19thesun.asm b/card_art/19thesun.asm similarity index 100% rename from 19thesun.asm rename to card_art/19thesun.asm diff --git a/20judgement.aseprite b/card_art/20judgement.aseprite similarity index 100% rename from 20judgement.aseprite rename to card_art/20judgement.aseprite diff --git a/20judgement.asm b/card_art/20judgement.asm similarity index 100% rename from 20judgement.asm rename to card_art/20judgement.asm diff --git a/21theworld.aseprite b/card_art/21theworld.aseprite similarity index 100% rename from 21theworld.aseprite rename to card_art/21theworld.aseprite diff --git a/21theworld.asm b/card_art/21theworld.asm similarity index 100% rename from 21theworld.asm rename to card_art/21theworld.asm diff --git a/KeyArtTiles.asm b/card_art/KeyArtTiles.asm similarity index 100% rename from KeyArtTiles.asm rename to card_art/KeyArtTiles.asm diff --git a/SpriteTiles.asm b/card_art/SpriteTiles.asm similarity index 100% rename from SpriteTiles.asm rename to card_art/SpriteTiles.asm diff --git a/Sprites.asm b/card_art/Sprites.asm similarity index 100% rename from Sprites.asm rename to card_art/Sprites.asm diff --git a/SpritesLayer.asm b/card_art/SpritesLayer.asm similarity index 100% rename from SpritesLayer.asm rename to card_art/SpritesLayer.asm diff --git a/screendesigns.aseprite b/card_art/screendesigns.aseprite similarity index 100% rename from screendesigns.aseprite rename to card_art/screendesigns.aseprite diff --git a/screendesigns.asm b/card_art/screendesigns.asm similarity index 100% rename from screendesigns.asm rename to card_art/screendesigns.asm diff --git a/carillon.sav b/carillon.sav new file mode 100644 index 0000000..6ec3dbf Binary files /dev/null and b/carillon.sav differ diff --git a/gb_tarot_theme_2.mod b/gb_tarot_theme_2.mod new file mode 100644 index 0000000..75d6c2a Binary files /dev/null and b/gb_tarot_theme_2.mod differ diff --git a/gb_tarot_theme_2.mod~ b/gb_tarot_theme_2.mod~ new file mode 100644 index 0000000..89df4d4 Binary files /dev/null and b/gb_tarot_theme_2.mod~ differ diff --git a/gb_tarot_theme_2.s3m b/gb_tarot_theme_2.s3m new file mode 100644 index 0000000..4202458 Binary files /dev/null and b/gb_tarot_theme_2.s3m differ diff --git a/gbt_player.inc b/gbt_player.inc new file mode 100644 index 0000000..ddd96a2 --- /dev/null +++ b/gbt_player.inc @@ -0,0 +1,643 @@ +;############################################################################### +; +; GBT Player v3.1.0 +; +; SPDX-License-Identifier: MIT +; +; Copyright (c) 2009-2021, Antonio Niño Díaz +; +;############################################################################### + + INCLUDE "hardware.inc" +;############################################################################### +; +; GBT Player v3.1.0 +; +; SPDX-License-Identifier: MIT +; +; Copyright (c) 2009-2020, Antonio Niño Díaz +; +;############################################################################### + + IF !DEF(GBT_PLAYER_INC) + DEF GBT_PLAYER_INC = 1 + +;############################################################################### + + EXPORT gbt_play ; Starts playing a song. + ; de = pointer to song data + ; a = default speed. Careful, 0 = 256! + ; bc = data bank (b ignored if ROM with < 256 banks) + ; THIS WILL CHANGE ROM BANK!!! + + EXPORT gbt_pause ; Pauses or unpauses the song. + ; a = 0 to pause, anything else to unpause. + + EXPORT gbt_loop ; Enables/disables looping at the end of the song. + ; a = 0 to stop at the end, anything else to loop + + EXPORT gbt_stop ; Stops the song. + + EXPORT gbt_enable_channels ; Enables given channels. + ; a = channel flags ORed: + ; channel 1 = 1 + ; channel 2 = 2 + ; channel 3 = 4 + ; channel 4 = 8 + + EXPORT gbt_update ; Updates the player, must be called each VBL. + ; Note: This will change the active ROM bank! + +; - If the following value is uncomented, the total of banks allowed is 512 +; (or more), but it's a bit slower. MBC5 ONLY, DOESN'T WORK WITH OTHERS!!! +; YOU MUST USE THE -512-banks OPTION WHEN CONVERTING A SONG WITH mod2gbt!!! +; - If it's commented, only 256 banks are allowed, it's a little bit faster +; and saves a few bytes. MBC1, MBC3 and MBC5 (and others). + +; DEF GBT_USE_MBC5_512BANKS = 1 + +;############################################################################### + + ENDC ; GBT_PLAYER_INC + +;############################################################################### + + +;############################################################################### + + SECTION "GBT_VAR_1",WRAMX[$d800] + +;------------------------------------------------------------------------------- + +gbt_playing: DS 1 + +; pointer to the pattern pointer array +gbt_pattern_array_ptr: DS 2 ; LSB first +IF DEF(GBT_USE_MBC5_512BANKS) +gbt_pattern_array_bank: DS 2 ; LSB first +ELSE +gbt_pattern_array_bank: DS 1 +ENDC + +; playing speed +gbt_speed:: DS 1 + +; Up to 12 bytes per step are copied here to be handled in functions in bank 1 +gbt_temp_play_data:: DS 12 + +gbt_loop_enabled: DS 1 +gbt_ticks_elapsed:: DS 1 +gbt_current_step:: DS 1 +gbt_current_pattern:: DS 1 +gbt_current_step_data_ptr:: DS 2 ; pointer to next step data - LSB first +IF DEF(GBT_USE_MBC5_512BANKS) +gbt_current_step_data_bank:: DS 2 ; bank of current pattern data - LSB first +ELSE +gbt_current_step_data_bank:: DS 1 ; bank of current pattern data +ENDC + +gbt_channels_enabled:: DS 1 + +gbt_pan:: DS 4*1 ; Ch 1-4 +gbt_vol:: DS 4*1 ; Ch 1-4 +gbt_instr:: DS 4*1 ; Ch 1-4 +gbt_freq:: DS 3*2 ; Ch 1-3 + +gbt_channel3_loaded_instrument:: DS 1 ; current loaded instrument ($FF if none) + +; Arpeggio -> Ch 1-3 +gbt_arpeggio_freq_index:: DS 3*3 ; {base index, base index+x, base index+y} * 3 +gbt_arpeggio_enabled:: DS 3*1 ; if 0, disabled +gbt_arpeggio_tick:: DS 3*1 + +; Cut note +gbt_cut_note_tick:: DS 4*1 ; If tick == gbt_cut_note_tick, stop note. + +; Last step of last pattern this is set to 1 +gbt_have_to_stop_next_step:: DS 1 + +gbt_update_pattern_pointers:: DS 1 ; set to 1 by jump effects + +;############################################################################### + + SECTION "GBT_BANK0",ROM0 + +;------------------------------------------------------------------------------- + +gbt_get_pattern_ptr:: ; a = pattern number + + ; loads a pointer to pattern a into gbt_current_step_data_ptr and + ; gbt_current_step_data_bank + + ld e,a + ld d,0 + +IF DEF(GBT_USE_MBC5_512BANKS) + ld a,[gbt_pattern_array_bank+0] + ld [rROMB0],a ; MBC5 - Set bank + ld a,[gbt_pattern_array_bank+1] + ld [rROMB1],a ; MBC5 - Set bank +ELSE + ld a,[gbt_pattern_array_bank] + ld [rROMB0],a ; MBC1, MBC3, MBC5 - Set bank +ENDC + + ld hl,gbt_pattern_array_ptr + ld a,[hl+] + ld h,[hl] + ld l,a + + ; hl = pointer to list of pointers + ; de = pattern number + +IF DEF(GBT_USE_MBC5_512BANKS) + add hl,de +ENDC + add hl,de + add hl,de + add hl,de + + ; hl = pointer to pattern bank + + ld a,[hl+] + ld [gbt_current_step_data_bank+0],a +IF DEF(GBT_USE_MBC5_512BANKS) + ld a,[hl+] + ld [gbt_current_step_data_bank+1],a +ENDC + + ; hl = pointer to pattern data + + ld a,[hl+] + ld h,[hl] + ld l,a + + ld a,l + ld [gbt_current_step_data_ptr],a + ld a,h + ld [gbt_current_step_data_ptr+1],a + + ret + +;------------------------------------------------------------------------------- + +gbt_get_pattern_ptr_banked:: ; a = pattern number + + push de + call gbt_get_pattern_ptr + pop de + + ld hl,gbt_current_step_data_ptr + ld a,[hl+] + ld b,a + ld a,[hl] + or a,b + jr nz,.dont_loop + xor a,a + ld [gbt_current_pattern], a +.dont_loop: + +IF DEF(GBT_USE_MBC5_512BANKS) + xor a,a + ld [rROMB1],a +ENDC + ld a,$01 + ld [rROMB0],a ; MBC1, MBC3, MBC5 - Set bank 1 + + ret + +;------------------------------------------------------------------------------- + +gbt_play:: ; de = data, bc = bank, a = speed + + ld hl,gbt_pattern_array_ptr + ld [hl],e + inc hl + ld [hl],d + + ld [gbt_speed],a + + ld a,c + ld [gbt_pattern_array_bank+0],a +IF DEF(GBT_USE_MBC5_512BANKS) + ld a,b + ld [gbt_pattern_array_bank+1],a +ENDC + + ld a,0 + call gbt_get_pattern_ptr + + xor a,a + ld [gbt_current_step],a + ld [gbt_current_pattern],a + ld [gbt_ticks_elapsed],a + ld [gbt_loop_enabled],a + ld [gbt_have_to_stop_next_step],a + ld [gbt_update_pattern_pointers],a + + ld a,$FF + ld [gbt_channel3_loaded_instrument],a + + ld a,$0F + ld [gbt_channels_enabled],a + + ld hl,gbt_pan + ld a,$11 ; L and R + ld [hl+],a + add a,a + ld [hl+],a + add a,a + ld [hl+],a + add a,a + ld [hl],a + + ld hl,gbt_vol + ld a,$F0 ; 100% + ld [hl+],a + ld [hl+],a + ld a,$20 ; 100% + ld [hl+],a + ld a,$F0 ; 100% + ld [hl+],a + + ld a,0 + + ld hl,gbt_instr + ld [hl+],a + ld [hl+],a + ld [hl+],a + ld [hl+],a + + ld hl,gbt_freq + ld [hl+],a + ld [hl+],a + ld [hl+],a + ld [hl+],a + ld [hl+],a + ld [hl+],a + + ld [gbt_arpeggio_enabled+0],a + ld [gbt_arpeggio_enabled+1],a + ld [gbt_arpeggio_enabled+2],a + + ld a,$FF + ld [gbt_cut_note_tick+0],a + ld [gbt_cut_note_tick+1],a + ld [gbt_cut_note_tick+2],a + ld [gbt_cut_note_tick+3],a + + ld a,$80 + ld [rNR52],a + ld a,$00 + ld [rNR51],a + ld a,$00 ; 0% + ld [rNR50],a + + xor a,a + ld [rNR10],a + ld [rNR11],a + ld [rNR12],a + ld [rNR13],a + ld [rNR14],a + ld [rNR21],a + ld [rNR22],a + ld [rNR23],a + ld [rNR24],a + ld [rNR30],a + ld [rNR31],a + ld [rNR32],a + ld [rNR33],a + ld [rNR34],a + ld [rNR41],a + ld [rNR42],a + ld [rNR43],a + ld [rNR44],a + + ld a,$77 ; 100% + ld [rNR50],a + + ld a,$01 + ld [gbt_playing],a + + ret + +;------------------------------------------------------------------------------- + +gbt_pause:: ; a = pause/unpause + ld [gbt_playing],a + or a,a + jr nz,.gbt_pause_unmute + + ; Silence all channels + xor a,a + ld [rNR51],a + + ret + +.gbt_pause_unmute: ; Unmute sound if playback is resumed + + ; Restore panning status + ld hl,gbt_pan + ld a,[hl+] + or a,[hl] + inc hl + or a,[hl] + inc hl + or a,[hl] + ld [rNR51],a + + ret + +;------------------------------------------------------------------------------- + +gbt_loop:: ; a = loop/don't loop + ld [gbt_loop_enabled],a + ret + +;------------------------------------------------------------------------------- + +gbt_stop:: + xor a,a + ld [gbt_playing],a + ld [rNR50],a + ld [rNR51],a + ld [rNR52],a + ret + +;------------------------------------------------------------------------------- + +gbt_enable_channels:: ; a = channel flags (channel flag = (1<<(channel_num-1))) + ld [gbt_channels_enabled],a + ret + +;------------------------------------------------------------------------------- + + EXPORT gbt_update_bank1 + +gbt_update:: + + ld a,[gbt_playing] + or a,a + ret z ; If not playing, return + + ; Handle tick counter + + ld hl,gbt_ticks_elapsed + ld a,[gbt_speed] ; a = total ticks + ld b,[hl] ; b = ticks elapsed + inc b + ld [hl],b + cp a,b + jr z,.dontexit + + ; Tick != Speed, update effects and exit +IF DEF(GBT_USE_MBC5_512BANKS) + xor a,a + ld [rROMB1],a +ENDC + ld a,$01 + ld [rROMB0],a ; MBC1, MBC3, MBC5 - Set bank 1 + ; Call update function in bank 1 (in gbt_player_bank1.s) + call gbt_update_effects_bank1 + + ret + +.dontexit: + ld [hl],$00 ; reset tick counter + + ; Clear tick-based effects + ; ------------------------ + + xor a,a + ld hl,gbt_arpeggio_enabled ; Disable arpeggio + ld [hl+],a + ld [hl+],a + ld [hl],a + dec a ; a = $FF + ld hl,gbt_cut_note_tick ; Disable cut note + ld [hl+],a + ld [hl+],a + ld [hl+],a + ld [hl],a + + ; Update effects + ; -------------- + +IF DEF(GBT_USE_MBC5_512BANKS) + xor a,a + ld [rROMB1],a +ENDC + ld a,$01 + ld [rROMB0],a ; MBC1, MBC3, MBC5 - Set bank 1 + ; Call update function in bank 1 (in gbt_player_bank1.s) + call gbt_update_effects_bank1 + + ; Check if last step + ; ------------------ + + ld a,[gbt_have_to_stop_next_step] + or a,a + jr z,.dont_stop + + call gbt_stop + ld a,0 + ld [gbt_have_to_stop_next_step],a + ret + +.dont_stop: + + ; Get this step data + ; ------------------ + + ; Change to bank with song data + +IF DEF(GBT_USE_MBC5_512BANKS) + ld a,[gbt_current_step_data_bank+0] + ld [rROMB0],a ; MBC5 - Set bank + ld a,[gbt_current_step_data_bank+1] + ld [rROMB1],a ; MBC5 - Set bank +ELSE + ld a,[gbt_current_step_data_bank] + ld [rROMB0],a ; MBC1, MBC3, MBC5 - Set bank +ENDC + + ; Get step data + + ld a,[gbt_current_step_data_ptr] + ld l,a + ld a,[gbt_current_step_data_ptr+1] + ld h,a ; hl = pointer to data + + ld de,gbt_temp_play_data + + ld b,4 +.copy_loop: ; copy as bytes as needed for this step + + ld a,[hl+] + ld [de],a + inc de + bit 7,a + jr nz,.more_bytes + bit 6,a + jr z,.no_more_bytes_this_channel + + jr .one_more_byte + +.more_bytes: + + ld a,[hl+] + ld [de],a + inc de + bit 7,a + jr z,.no_more_bytes_this_channel + +.one_more_byte: + + ld a,[hl+] + ld [de],a + inc de + +.no_more_bytes_this_channel: + dec b + jr nz,.copy_loop + + ld a,l + ld [gbt_current_step_data_ptr],a + ld a,h + ld [gbt_current_step_data_ptr+1],a ; save pointer to data + + ; Increment step/pattern + ; ---------------------- + + ; Increment step + + ld a,[gbt_current_step] + inc a + ld [gbt_current_step],a + cp a,64 + jr nz,.dont_increment_pattern + + ; Increment pattern + + ld a,0 + ld [gbt_current_step],a ; Step 0 + + ld a,[gbt_current_pattern] + inc a + ld [gbt_current_pattern],a + + call gbt_get_pattern_ptr + + ld a,[gbt_current_step_data_ptr] + ld b,a + ld a,[gbt_current_step_data_ptr+1] + or a,b + jr nz,.not_ended ; if pointer is 0, song has ended + + ld a,[gbt_loop_enabled] + and a,a + + jr z,.loop_disabled + + ; If loop is enabled, jump to pattern 0 + + ld a,0 + ld [gbt_current_pattern],a + + call gbt_get_pattern_ptr + + jr .end_handling_steps_pattern + +.loop_disabled: + + ; If loop is disabled, stop song + ; Stop it next step, if not this step won't be played + + ld a,1 + ld [gbt_have_to_stop_next_step],a + +.not_ended: + +.dont_increment_pattern: + +.end_handling_steps_pattern: + +IF DEF(GBT_USE_MBC5_512BANKS) + xor a,a + ld [rROMB1],a ; MBC5 +ENDC + ld a,$01 + ld [rROMB0],a ; MBC1, MBC3, MBC5 - Set bank 1 + ; Call update function in bank 1 (in gbt_player_bank1.s) + call gbt_update_bank1 + + ; Check if any effect has changed the pattern or step + + ld a,[gbt_update_pattern_pointers] + and a,a + ret z + ; if any effect has changed the pattern or step, update + + xor a,a + ld [gbt_update_pattern_pointers],a ; clear update flag + + ld [gbt_have_to_stop_next_step],a ; clear stop flag + + ld a,[gbt_current_pattern] + call gbt_get_pattern_ptr ; set ptr to start of the pattern + + ; Search the step + + ; Change to bank with song data + +IF DEF(GBT_USE_MBC5_512BANKS) + ld a,[gbt_pattern_array_bank+1] + ld [rROMB1],a ; MBC5 +ENDC + ld a,[gbt_pattern_array_bank+0] + ld [rROMB0],a ; MBC1, MBC3, MBC5 + + ld a,[gbt_current_step_data_ptr] + ld l,a + ld a,[gbt_current_step_data_ptr+1] + ld h,a ; hl = pointer to data + + ld a,[gbt_current_step] + and a,a + ret z ; if changing to step 0, exit + + add a,a + add a,a + ld b,a ; b = iterations = step * 4 (number of channels) +.next_channel: + + ld a,[hl+] + bit 7,a + jr nz,.next_channel_more_bytes + bit 6,a + jr z,.next_channel_no_more_bytes_this_channel + + jr .next_channel_one_more_byte + +.next_channel_more_bytes: + + ld a,[hl+] + bit 7,a + jr z,.next_channel_no_more_bytes_this_channel + +.next_channel_one_more_byte: + + ld a,[hl+] + +.next_channel_no_more_bytes_this_channel: + dec b + jr nz,.next_channel + + ld a,l + ld [gbt_current_step_data_ptr],a + ld a,h + ld [gbt_current_step_data_ptr+1],a ; save pointer to data + + ret + +;############################################################################### diff --git a/gbt_player_bank1.inc b/gbt_player_bank1.inc new file mode 100644 index 0000000..14093ba --- /dev/null +++ b/gbt_player_bank1.inc @@ -0,0 +1,1389 @@ +;############################################################################### +; +; GBT Player v3.1.0 +; +; SPDX-License-Identifier: MIT +; +; Copyright (c) 2009-2020 Antonio Niño Díaz +; +;############################################################################### + + INCLUDE "hardware.inc" + INCLUDE "gbt_player.inc" + +;############################################################################### + + SECTION "GBT_BANK1",ROMX,BANK[1] + +;------------------------------------------------------------------------------- + +gbt_wave: ; 8 sounds +DB $A5,$D7,$C9,$E1,$BC,$9A,$76,$31,$0C,$BA,$DE,$60,$1B,$CA,$03,$93 ; random +DB $F0,$E1,$D2,$C3,$B4,$A5,$96,$87,$78,$69,$5A,$4B,$3C,$2D,$1E,$0F +DB $FD,$EC,$DB,$CA,$B9,$A8,$97,$86,$79,$68,$57,$46,$35,$24,$13,$02 ; up-downs +DB $DE,$FE,$DC,$BA,$9A,$A9,$87,$77,$88,$87,$65,$56,$54,$32,$10,$12 +DB $AB,$CD,$EF,$ED,$CB,$A0,$12,$3E,$DC,$BA,$BC,$DE,$FE,$DC,$32,$10 ; tri. broken +DB $FF,$EE,$DD,$CC,$BB,$AA,$99,$88,$77,$66,$55,$44,$33,$22,$11,$00 ; triangular +DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00 ; square +DB $79,$BC,$DE,$EF,$FF,$EE,$DC,$B9,$75,$43,$21,$10,$00,$11,$23,$45 ; sine + +gbt_noise: ; 16 sounds + ; 7 bit + DB $5F,$5B,$4B,$2F,$3B,$58,$1F,$0F + ; 15 bit + DB $90,$80,$70,$50,$00 + DB $67,$63,$53 + +gbt_frequencies: + DW 44, 156, 262, 363, 457, 547, 631, 710, 786, 854, 923, 986 + DW 1046, 1102, 1155, 1205, 1253, 1297, 1339, 1379, 1417, 1452, 1486, 1517 + DW 1546, 1575, 1602, 1627, 1650, 1673, 1694, 1714, 1732, 1750, 1767, 1783 + DW 1798, 1812, 1825, 1837, 1849, 1860, 1871, 1881, 1890, 1899, 1907, 1915 + DW 1923, 1930, 1936, 1943, 1949, 1954, 1959, 1964, 1969, 1974, 1978, 1982 + DW 1985, 1988, 1992, 1995, 1998, 2001, 2004, 2006, 2009, 2011, 2013, 2015 + +;------------------------------------------------------------------------------- + +_gbt_get_freq_from_index: ; a = index, bc = returned freq + ld hl,gbt_frequencies + ld c,a + ld b,$00 + add hl,bc + add hl,bc + ld c,[hl] + inc hl + ld b,[hl] + ret + +;------------------------------------------------------------------------------- +; ---------------------------------- Channel 1 --------------------------------- +;------------------------------------------------------------------------------- + +gbt_channel_1_handle:: ; de = info + + ld a,[gbt_channels_enabled] + and a,$01 + jr nz,.channel1_enabled + + ; Channel is disabled. Increment pointer as needed + + ld a,[de] + inc de + bit 7,a + jr nz,.more_bytes + bit 6,a + jr z,.no_more_bytes_this_channel + + jr .one_more_byte + +.more_bytes: + + ld a,[de] + inc de + bit 7,a + jr z,.no_more_bytes_this_channel + +.one_more_byte: + + inc de + +.no_more_bytes_this_channel: + + ret + +.channel1_enabled: + + ; Channel 1 is enabled + + ld a,[de] + inc de + + bit 7,a + jr nz,.has_frequency + + ; Not frequency + + bit 6,a + jr nz,.instr_effects + + ; Set volume or NOP + + bit 5,a + jr nz,.just_set_volume + + ; NOP + + ret + +.just_set_volume: + + ; Set volume + + and a,$0F + swap a + ld [gbt_vol+0],a + + jr .refresh_channel1_regs + +.instr_effects: + + ; Set instrument and effect + + ld b,a ; save byte + + and a,$30 + add a,a + add a,a + ld [gbt_instr+0],a ; Instrument + + ld a,b ; restore byte + + and a,$0F ; a = effect + + call gbt_channel_1_set_effect + + jr .refresh_channel1_regs + +.has_frequency: + + ; Has frequency + + and a,$7F + ld [gbt_arpeggio_freq_index+0*3],a + ; This destroys hl and a. Returns freq in bc + call _gbt_get_freq_from_index + + ld a,c + ld [gbt_freq+0*2+0],a + ld a,b + ld [gbt_freq+0*2+1],a ; Get frequency + + ld a,[de] + inc de + + bit 7,a + jr nz,.freq_instr_and_effect + + ; Freq + Instr + Volume + + ld b,a ; save byte + + and a,$30 + add a,a + add a,a + ld [gbt_instr+0],a ; Instrument + + ld a,b ; restore byte + + and a,$0F ; a = volume + + swap a + ld [gbt_vol+0],a + + jr .refresh_channel1_regs + +.freq_instr_and_effect: + + ; Freq + Instr + Effect + + ld b,a ; save byte + + and a,$30 + add a,a + add a,a + ld [gbt_instr+0],a ; Instrument + + ld a,b ; restore byte + + and a,$0F ; a = effect + + call gbt_channel_1_set_effect + + ;jr .refresh_channel1_regs + +.refresh_channel1_regs: + + ; fall through!!!!! + +; ----------------- + +channel1_refresh_registers: + + xor a,a + ld [rNR10],a + ld a,[gbt_instr+0] + ld [rNR11],a + ld a,[gbt_vol+0] + ld [rNR12],a + ld a,[gbt_freq+0*2+0] + ld [rNR13],a + ld a,[gbt_freq+0*2+1] + or a,$80 ; start + ld [rNR14],a + + ret + +; ------------------ + +channel1_update_effects: ; returns 1 in a if it needed to update sound registers + + ; Cut note + ; -------- + + ld a,[gbt_cut_note_tick+0] + ld hl,gbt_ticks_elapsed + cp a,[hl] + jp nz,.dont_cut + + dec a ; a = $FF + ld [gbt_cut_note_tick+0],a ; disable cut note + + xor a,a ; vol = 0 + ld [rNR12],a + ld a,$80 ; start + ld [rNR14],a + +.dont_cut: + + ; Arpeggio + ; -------- + + ld a,[gbt_arpeggio_enabled+0] + and a,a + ret z ; a is 0, return 0 + + ; If enabled arpeggio, handle it + + ld a,[gbt_arpeggio_tick+0] + and a,a + jr nz,.not_tick_0 + + ; Tick 0 - Set original frequency + + ld a,[gbt_arpeggio_freq_index+0*3+0] + + call _gbt_get_freq_from_index + + ld a,c + ld [gbt_freq+0*2+0],a + ld a,b + ld [gbt_freq+0*2+1],a ; Set frequency + + ld a,1 + ld [gbt_arpeggio_tick+0],a + + ret ; ret 1 + +.not_tick_0: + + cp a,1 + jr nz,.not_tick_1 + + ; Tick 1 + + ld a,[gbt_arpeggio_freq_index+0*3+1] + + call _gbt_get_freq_from_index + + ld a,c + ld [gbt_freq+0*2+0],a + ld a,b + ld [gbt_freq+0*2+1],a ; Set frequency + + ld a,2 + ld [gbt_arpeggio_tick+0],a + + dec a + ret ; ret 1 + +.not_tick_1: + + ; Tick 2 + + ld a,[gbt_arpeggio_freq_index+0*3+2] + + call _gbt_get_freq_from_index + + ld a,c + ld [gbt_freq+0*2+0],a + ld a,b + ld [gbt_freq+0*2+1],a ; Set frequency + + xor a,a + ld [gbt_arpeggio_tick+0],a + + inc a ; ret 1 + ret + +; ----------------- + +; returns a = 1 if needed to update registers, 0 if not +gbt_channel_1_set_effect: ; a = effect, de = pointer to data. + + ld hl,.gbt_ch1_jump_table + ld c,a + ld b,0 + add hl,bc + add hl,bc + + ld a,[hl+] + ld h,[hl] + ld l,a + + ld a,[de] ; load args + inc de + + jp hl + +.gbt_ch1_jump_table: + DW .gbt_ch1_pan + DW .gbt_ch1_arpeggio + DW .gbt_ch1_cut_note + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_jump_pattern + DW gbt_ch1234_jump_position + DW gbt_ch1234_speed + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + +.gbt_ch1_pan: + and a,$11 + ld [gbt_pan+0],a + xor a,a + ret ; ret 0 do not update registers, only NR51 at end. + +.gbt_ch1_arpeggio: + ld b,a ; b = params + + ld hl,gbt_arpeggio_freq_index+0*3 + ld c,[hl] ; c = base index + inc hl + + ld a,b + swap a + and a,$0F + add a,c + + ld [hl+],a ; save first increment + + ld a,b + and a,$0F + add a,c + + ld [hl],a ; save second increment + + ld a,1 + ld [gbt_arpeggio_enabled+0],a + ld [gbt_arpeggio_tick+0],a + + ret ; ret 1 + +.gbt_ch1_cut_note: + ld [gbt_cut_note_tick+0],a + xor a,a ; ret 0 + ret + +;------------------------------------------------------------------------------- +; ---------------------------------- Channel 2 --------------------------------- +;------------------------------------------------------------------------------- + +gbt_channel_2_handle:: ; de = info + + ld a,[gbt_channels_enabled] + and a,$02 + jr nz,.channel2_enabled + + ; Channel is disabled. Increment pointer as needed + + ld a,[de] + inc de + bit 7,a + jr nz,.more_bytes + bit 6,a + jr z,.no_more_bytes_this_channel + + jr .one_more_byte + +.more_bytes: + + ld a,[de] + inc de + bit 7,a + jr z,.no_more_bytes_this_channel + +.one_more_byte: + + inc de + +.no_more_bytes_this_channel: + + ret + +.channel2_enabled: + + ; Channel 2 is enabled + + ld a,[de] + inc de + + bit 7,a + jr nz,.has_frequency + + ; Not frequency + + bit 6,a + jr nz,.instr_effects + + ; Set volume or NOP + + bit 5,a + jr nz,.just_set_volume + + ; NOP + + ret + +.just_set_volume: + + ; Set volume + + and a,$0F + swap a + ld [gbt_vol+1],a + + jr .refresh_channel2_regs + +.instr_effects: + + ; Set instrument and effect + + ld b,a ; save byte + + and a,$30 + add a,a + add a,a + ld [gbt_instr+1],a ; Instrument + + ld a,b ; restore byte + + and a,$0F ; a = effect + + call gbt_channel_2_set_effect + + jr .refresh_channel2_regs + +.has_frequency: + + ; Has frequency + + and a,$7F + ld [gbt_arpeggio_freq_index+1*3],a + ; This destroys hl and a. Returns freq in bc + call _gbt_get_freq_from_index + + ld a,c + ld [gbt_freq+1*2+0],a + ld a,b + ld [gbt_freq+1*2+1],a ; Get frequency + + ld a,[de] + inc de + + bit 7,a + jr nz,.freq_instr_and_effect + + ; Freq + Instr + Volume + + ld b,a ; save byte + + and a,$30 + add a,a + add a,a + ld [gbt_instr+1],a ; Instrument + + ld a,b ; restore byte + + and a,$0F ; a = volume + + swap a + ld [gbt_vol+1],a + + jr .refresh_channel2_regs + +.freq_instr_and_effect: + + ; Freq + Instr + Effect + + ld b,a ; save byte + + and a,$30 + add a,a + add a,a + ld [gbt_instr+1],a ; Instrument + + ld a,b ; restore byte + + and a,$0F ; a = effect + + call gbt_channel_2_set_effect + + ;jr .refresh_channel2_regs + +.refresh_channel2_regs: + + ; fall through!!!!! + +; ----------------- + +channel2_refresh_registers: + + ld a,[gbt_instr+1] + ld [rNR21],a + ld a,[gbt_vol+1] + ld [rNR22],a + ld a,[gbt_freq+1*2+0] + ld [rNR23],a + ld a,[gbt_freq+1*2+1] + or a,$80 ; start + ld [rNR24],a + + ret + +; ------------------ + +channel2_update_effects: ; returns 1 in a if it needed to update sound registers + + ; Cut note + ; -------- + + ld a,[gbt_cut_note_tick+1] + ld hl,gbt_ticks_elapsed + cp a,[hl] + jp nz,.dont_cut + + dec a ; a = $FF + ld [gbt_cut_note_tick+1],a ; disable cut note + + xor a,a ; vol = 0 + ld [rNR22],a + ld a,$80 ; start + ld [rNR24],a + +.dont_cut: + + ; Arpeggio + ; -------- + + ld a,[gbt_arpeggio_enabled+1] + and a,a + ret z ; a is 0, return 0 + + ; If enabled arpeggio, handle it + + ld a,[gbt_arpeggio_tick+1] + and a,a + jr nz,.not_tick_0 + + ; Tick 0 - Set original frequency + + ld a,[gbt_arpeggio_freq_index+1*3+0] + + call _gbt_get_freq_from_index + + ld a,c + ld [gbt_freq+1*2+0],a + ld a,b + ld [gbt_freq+1*2+1],a ; Set frequency + + ld a,1 + ld [gbt_arpeggio_tick+1],a + + ret ; ret 1 + +.not_tick_0: + + cp a,1 + jr nz,.not_tick_1 + + ; Tick 1 + + ld a,[gbt_arpeggio_freq_index+1*3+1] + + call _gbt_get_freq_from_index + + ld a,c + ld [gbt_freq+1*2+0],a + ld a,b + ld [gbt_freq+1*2+1],a ; Set frequency + + ld a,2 + ld [gbt_arpeggio_tick+1],a + + dec a + ret ; ret 1 + +.not_tick_1: + + ; Tick 2 + + ld a,[gbt_arpeggio_freq_index+1*3+2] + + call _gbt_get_freq_from_index + + ld a,c + ld [gbt_freq+1*2+0],a + ld a,b + ld [gbt_freq+1*2+1],a ; Set frequency + + xor a,a + ld [gbt_arpeggio_tick+1],a + + inc a ; ret 1 + ret + +; ----------------- + +; returns a = 1 if needed to update registers, 0 if not +gbt_channel_2_set_effect: ; a = effect, de = pointer to data + + ld hl,.gbt_ch2_jump_table + ld c,a + ld b,0 + add hl,bc + add hl,bc + + ld a,[hl+] + ld h,[hl] + ld l,a + + ld a,[de] ; load args + inc de + + jp hl + +.gbt_ch2_jump_table: + DW .gbt_ch2_pan + DW .gbt_ch2_arpeggio + DW .gbt_ch2_cut_note + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_jump_pattern + DW gbt_ch1234_jump_position + DW gbt_ch1234_speed + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + +.gbt_ch2_pan: + and a,$22 + ld [gbt_pan+1],a + xor a,a + ret ; ret 0 do not update registers, only NR51 at end. + +.gbt_ch2_arpeggio: + ld b,a ; b = params + + ld hl,gbt_arpeggio_freq_index+1*3 + ld c,[hl] ; c = base index + inc hl + + ld a,b + swap a + and a,$0F + add a,c + + ld [hl+],a ; save first increment + + ld a,b + and a,$0F + add a,c + + ld [hl],a ; save second increment + + ld a,1 + ld [gbt_arpeggio_enabled+1],a + ld [gbt_arpeggio_tick+1],a + + ret ; ret 1 + +.gbt_ch2_cut_note: + ld [gbt_cut_note_tick+1],a + xor a,a ; ret 0 + ret + +;------------------------------------------------------------------------------- +; ---------------------------------- Channel 3 --------------------------------- +;------------------------------------------------------------------------------- + +gbt_channel_3_handle:: ; de = info + + ld a,[gbt_channels_enabled] + and a,$04 + jr nz,.channel3_enabled + + ; Channel is disabled. Increment pointer as needed + + ld a,[de] + inc de + bit 7,a + jr nz,.more_bytes + bit 6,a + jr z,.no_more_bytes_this_channel + + jr .one_more_byte + +.more_bytes: + + ld a,[de] + inc de + bit 7,a + jr z,.no_more_bytes_this_channel + +.one_more_byte: + + inc de + +.no_more_bytes_this_channel: + + ret + +.channel3_enabled: + + ; Channel 3 is enabled + + ld a,[de] + inc de + + bit 7,a + jr nz,.has_frequency + + ; Not frequency + + bit 6,a + jr nz,.effects + + ; Set volume or NOP + + bit 5,a + jr nz,.just_set_volume + + ; NOP + + ret + +.just_set_volume: + + ; Set volume + + and a,$0F + swap a + ld [gbt_vol+2],a + + jr .refresh_channel3_regs + +.effects: + + ; Set effect + + and a,$0F ; a = effect + + call gbt_channel_3_set_effect + and a,a + ret z ; if 0, don't refresh registers + + jr .refresh_channel3_regs + +.has_frequency: + + ; Has frequency + + and a,$7F + ld [gbt_arpeggio_freq_index+2*3],a + ; This destroys hl and a. Returns freq in bc + call _gbt_get_freq_from_index + + ld a,c + ld [gbt_freq+2*2+0],a + ld a,b + ld [gbt_freq+2*2+1],a ; Get frequency + + ld a,[de] + inc de + + bit 7,a + jr nz,.freq_instr_and_effect + + ; Freq + Instr + Volume + + ld b,a ; save byte + + and a,$0F + ld [gbt_instr+2],a ; Instrument + + ld a,b ; restore byte + + and a,$30 ; a = volume + add a,a + ld [gbt_vol+2],a + + jr .refresh_channel3_regs + +.freq_instr_and_effect: + + ; Freq + Instr + Effect + + ld b,a ; save byte + + and a,$0F + ld [gbt_instr+2],a ; Instrument + + ld a,b ; restore byte + + and a,$70 + swap a ; a = effect (only 0-7 allowed here) + + call gbt_channel_3_set_effect + + ;jr .refresh_channel3_regs + +.refresh_channel3_regs: + + ; fall through!!!!! + +; ----------------- + +channel3_refresh_registers: + + xor a,a + ld [rNR30],a ; disable + + ld a,[gbt_channel3_loaded_instrument] + ld b,a + ld a,[gbt_instr+2] + cp a,b + call nz,gbt_channel3_load_instrument ; a = instrument + + ld a,$80 + ld [rNR30],a ; enable + + xor a,a + ld [rNR31],a + ld a,[gbt_vol+2] + ld [rNR32],a + ld a,[gbt_freq+2*2+0] + ld [rNR33],a + ld a,[gbt_freq+2*2+1] + or a,$80 ; start + ld [rNR34],a + + ret + +; ------------------ + +gbt_channel3_load_instrument: + + ld [gbt_channel3_loaded_instrument],a + + swap a ; a = a * 16 + ld c,a + ld b,0 + ld hl,gbt_wave + add hl,bc + + ld c,$30 + ld b,16 +.loop: + ld a,[hl+] + ld [$FF00+c],a + inc c + dec b + jr nz,.loop + + ret + +; ------------------ + +channel3_update_effects: ; returns 1 in a if it needed to update sound registers + + ; Cut note + ; -------- + + ld a,[gbt_cut_note_tick+2] + ld hl,gbt_ticks_elapsed + cp a,[hl] + jp nz,.dont_cut + + dec a ; a = $FF + ld [gbt_cut_note_tick+2],a ; disable cut note + + ld a,$80 + ld [rNR30],a ; enable + + xor a,a ; vol = 0 + ld [rNR32],a + ld a,$80 ; start + ld [rNR34],a + +.dont_cut: + + ; Arpeggio + ; -------- + + ld a,[gbt_arpeggio_enabled+2] + and a,a + ret z ; a is 0, return 0 + + ; If enabled arpeggio, handle it + + ld a,[gbt_arpeggio_tick+2] + and a,a + jr nz,.not_tick_0 + + ; Tick 0 - Set original frequency + + ld a,[gbt_arpeggio_freq_index+2*3+0] + + call _gbt_get_freq_from_index + + ld a,c + ld [gbt_freq+2*2+0],a + ld a,b + ld [gbt_freq+2*2+1],a ; Set frequency + + ld a,1 + ld [gbt_arpeggio_tick+2],a + + ret ; ret 1 + +.not_tick_0: + + cp a,1 + jr nz,.not_tick_1 + + ; Tick 1 + + ld a,[gbt_arpeggio_freq_index+2*3+1] + + call _gbt_get_freq_from_index + + ld a,c + ld [gbt_freq+2*2+0],a + ld a,b + ld [gbt_freq+2*2+1],a ; Set frequency + + ld a,2 + ld [gbt_arpeggio_tick+2],a + + dec a + ret ; ret 1 + +.not_tick_1: + + ; Tick 2 + + ld a,[gbt_arpeggio_freq_index+2*3+2] + + call _gbt_get_freq_from_index + + ld a,c + ld [gbt_freq+2*2+0],a + ld a,b + ld [gbt_freq+2*2+1],a ; Set frequency + + xor a,a + ld [gbt_arpeggio_tick+2],a + + inc a + ret ; ret 1 + +; ----------------- + +; returns a = 1 if needed to update registers, 0 if not +gbt_channel_3_set_effect: ; a = effect, de = pointer to data + + ld hl,.gbt_ch3_jump_table + ld c,a + ld b,0 + add hl,bc + add hl,bc + + ld a,[hl+] + ld h,[hl] + ld l,a + + ld a,[de] ; load args + inc de + + jp hl + +.gbt_ch3_jump_table: + DW .gbt_ch3_pan + DW .gbt_ch3_arpeggio + DW .gbt_ch3_cut_note + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_jump_pattern + DW gbt_ch1234_jump_position + DW gbt_ch1234_speed + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + +.gbt_ch3_pan: + and a,$44 + ld [gbt_pan+2],a + xor a,a + ret ; ret 0 do not update registers, only NR51 at end. + +.gbt_ch3_arpeggio: + ld b,a ; b = params + + ld hl,gbt_arpeggio_freq_index+2*3 + ld c,[hl] ; c = base index + inc hl + + ld a,b + swap a + and a,$0F + add a,c + + ld [hl+],a ; save first increment + + ld a,b + and a,$0F + add a,c + + ld [hl],a ; save second increment + + ld a,1 + ld [gbt_arpeggio_enabled+2],a + ld [gbt_arpeggio_tick+2],a + + ret ; ret 1 + +.gbt_ch3_cut_note: + ld [gbt_cut_note_tick+2],a + xor a,a ; ret 0 + ret + +;------------------------------------------------------------------------------- +; ---------------------------------- Channel 4 --------------------------------- +;------------------------------------------------------------------------------- + +gbt_channel_4_handle:: ; de = info + + ld a,[gbt_channels_enabled] + and a,$08 + jr nz,.channel4_enabled + + ; Channel is disabled. Increment pointer as needed + + ld a,[de] + inc de + bit 7,a + jr nz,.more_bytes + bit 6,a + jr z,.no_more_bytes_this_channel + + jr .one_more_byte + +.more_bytes: + + ld a,[de] + inc de + bit 7,a + jr z,.no_more_bytes_this_channel + +.one_more_byte: + + inc de + +.no_more_bytes_this_channel: + + ret + +.channel4_enabled: + + ; Channel 4 is enabled + + ld a,[de] + inc de + + bit 7,a + jr nz,.has_instrument + + ; Not instrument + + bit 6,a + jr nz,.effects + + ; Set volume or NOP + + bit 5,a + jr nz,.just_set_volume + + ; NOP + + ret + +.just_set_volume: + + ; Set volume + + and a,$0F + swap a + ld [gbt_vol+3],a + + jr .refresh_channel4_regs + +.effects: + + ; Set effect + + and a,$0F ; a = effect + + call gbt_channel_4_set_effect + and a,a + ret z ; if 0, don't refresh registers + + jr .refresh_channel4_regs + +.has_instrument: + + ; Has instrument + + and a,$0F + ld hl,gbt_noise + ld c,a + ld b,0 + add hl,bc + ld a,[hl] ; a = instrument data + + ld [gbt_instr+3],a + + ld a,[de] + inc de + + bit 7,a + jr nz,.instr_and_effect + + ; Instr + Volume + + and a,$0F ; a = volume + + swap a + ld [gbt_vol+3],a + + jr .refresh_channel4_regs + +.instr_and_effect: + + ; Instr + Effect + + and a,$0F ; a = effect + + call gbt_channel_4_set_effect + + ;jr .refresh_channel4_regs + +.refresh_channel4_regs: + + ; fall through!!!!! + +; ----------------- + +channel4_refresh_registers: + + xor a,a + ld [rNR41],a + ld a,[gbt_vol+3] + ld [rNR42],a + ld a,[gbt_instr+3] + ld [rNR43],a + ld a,$80 ; start + ld [rNR44],a + + ret + +; ------------------ + +channel4_update_effects: ; returns 1 in a if it needed to update sound registers + + ; Cut note + ; -------- + + ld a,[gbt_cut_note_tick+3] + ld hl,gbt_ticks_elapsed + cp a,[hl] + jp nz,.dont_cut + + dec a ; a = $FF + ld [gbt_cut_note_tick+3],a ; disable cut note + + xor a,a ; vol = 0 + ld [rNR42],a + ld a,$80 ; start + ld [rNR44],a + +.dont_cut: + + xor a,a + ret ; a is 0, return 0 + +; ----------------- + +; returns a = 1 if needed to update registers, 0 if not +gbt_channel_4_set_effect: ; a = effect, de = pointer to data + + ld hl,.gbt_ch4_jump_table + ld c,a + ld b,0 + add hl,bc + add hl,bc + + ld a,[hl+] + ld h,[hl] + ld l,a + + ld a,[de] ; load args + inc de + + jp hl + +.gbt_ch4_jump_table: + DW .gbt_ch4_pan + DW gbt_ch1234_nop ; gbt_ch4_arpeggio + DW .gbt_ch4_cut_note + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_jump_pattern + DW gbt_ch1234_jump_position + DW gbt_ch1234_speed + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + DW gbt_ch1234_nop + +.gbt_ch4_pan: + and a,$88 + ld [gbt_pan+3],a + xor a,a + ret ; ret 0 do not update registers, only NR51 at end. + +.gbt_ch4_cut_note: + ld [gbt_cut_note_tick+3],a + xor a,a ; ret 0 + ret + +;------------------------------------------------------------------------------- +;------------------------------------------------------------------------------- +;------------------------------------------------------------------------------- + +; Common effects go here: + +gbt_ch1234_nop: + xor a,a ;ret 0 + ret + +gbt_ch1234_jump_pattern: + ld [gbt_current_pattern],a + xor a,a + ld [gbt_current_step],a + ld [gbt_have_to_stop_next_step],a ; clear stop flag + ld a,1 + ld [gbt_update_pattern_pointers],a + xor a,a ;ret 0 + ret + +gbt_ch1234_jump_position: + ld [gbt_current_step],a + ld hl,gbt_current_pattern + inc [hl] + + ; Check to see if jump puts us past end of song + ld a,[hl] + call gbt_get_pattern_ptr_banked + ld a,1 + ld [gbt_update_pattern_pointers],a + xor a,a ;ret 0 + ret + +gbt_ch1234_speed: + ld [gbt_speed],a + xor a,a + ld [gbt_ticks_elapsed],a + ret ;ret 0 + +;------------------------------------------------------------------------------- + +gbt_update_bank1:: + + ld de,gbt_temp_play_data + + ; each function will return in de the pointer to next byte + + call gbt_channel_1_handle + + call gbt_channel_2_handle + + call gbt_channel_3_handle + + call gbt_channel_4_handle + + ; end of channel handling + + ld hl,gbt_pan + ld a,[hl+] + or a,[hl] + inc hl + or a,[hl] + inc hl + or a,[hl] + ld [rNR51],a ; handle panning... + + ret + +;------------------------------------------------------------------------------- + +gbt_update_effects_bank1:: + + call channel1_update_effects + and a,a + call nz,channel1_refresh_registers + + call channel2_update_effects + and a,a + call nz,channel2_refresh_registers + + call channel3_update_effects + and a,a + call nz,channel3_refresh_registers + + call channel4_update_effects + and a,a + call nz,channel4_refresh_registers + + ret + +;############################################################################### diff --git a/justice.asm b/justice.asm deleted file mode 100644 index dd90e6e..0000000 --- a/justice.asm +++ /dev/null @@ -1,101 +0,0 @@ - ; original export script by gabriel reis, modified by shoofle - - -KeyArtTiles: - - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - db $00,$00,$00,$60,$20,$5c,$3c,$43,$3c,$43,$20,$5c,$00,$60,$00,$00 - db $3f,$40,$3f,$40,$3f,$40,$3f,$40,$3f,$40,$3f,$40,$3f,$40,$3f,$40 - db $ff,$00,$ff,$00,$ff,$00,$ff,$00,$ff,$00,$ff,$00,$ff,$00,$ff,$00 - db $fc,$02,$fc,$02,$fc,$02,$fc,$02,$fc,$02,$fc,$02,$fc,$02,$fc,$02 - db $00,$00,$00,$06,$04,$3a,$3c,$c2,$3c,$c2,$04,$3a,$00,$06,$00,$00 - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$03,$03 - db $1f,$20,$0f,$30,$07,$18,$03,$0c,$01,$06,$00,$03,$00,$01,$00,$00 - db $ff,$00,$ff,$00,$ff,$00,$ff,$00,$ff,$00,$ff,$00,$7f,$80,$00,$ff - db $ff,$00,$ff,$00,$ff,$00,$ff,$00,$ff,$00,$ff,$00,$fc,$03,$00,$ff - db $f8,$04,$f0,$0c,$e0,$18,$c0,$30,$80,$60,$00,$c0,$00,$80,$00,$00 - db $04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04 - db $80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80 - db $00,$04,$00,$1c,$08,$74,$78,$84,$38,$44,$08,$34,$00,$0c,$00,$04 - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$0f,$0f - db $00,$00,$00,$18,$08,$14,$0c,$12,$1c,$22,$18,$27,$00,$38,$c1,$c1 - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$0f,$3f,$f8,$ff - db $00,$00,$00,$00,$00,$00,$00,$00,$03,$03,$fe,$ff,$03,$ff,$1c,$fc - db $00,$00,$00,$00,$00,$00,$00,$00,$f8,$f8,$18,$f8,$f8,$f8,$08,$08 - db $18,$1f,$33,$3c,$67,$78,$43,$7c,$4f,$71,$7f,$7f,$7f,$7f,$78,$7f - db $67,$ef,$f3,$33,$f3,$13,$f3,$13,$f3,$f3,$f3,$f3,$f4,$f4,$f4,$34 - db $1f,$ff,$fa,$fb,$82,$83,$02,$03,$01,$01,$01,$01,$81,$81,$81,$81 - db $f0,$f0,$10,$f0,$10,$f0,$10,$f0,$10,$f0,$10,$f0,$10,$f0,$10,$f0 - db $14,$14,$14,$14,$14,$14,$14,$14,$14,$14,$22,$22,$22,$22,$22,$22 - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$01 - db $7f,$60,$5f,$60,$38,$27,$2f,$30,$17,$18,$08,$0f,$00,$0f,$00,$ff - db $f4,$14,$f4,$14,$78,$98,$d8,$38,$a8,$68,$48,$c8,$10,$d0,$1f,$ff - db $81,$81,$80,$80,$40,$40,$40,$40,$40,$40,$40,$40,$20,$20,$e0,$e0 - db $10,$f0,$90,$f0,$90,$f0,$90,$f0,$50,$70,$50,$70,$48,$78,$48,$78 - db $22,$22,$21,$21,$41,$41,$41,$41,$7f,$7f,$41,$7f,$3e,$3e,$00,$00 - db $04,$04,$04,$04,$3c,$3c,$3f,$3f,$07,$07,$02,$1d,$0f,$30,$18,$27 - db $80,$80,$80,$80,$f0,$f0,$f0,$f0,$80,$80,$00,$c0,$00,$c0,$80,$40 - db $00,$07,$00,$04,$00,$08,$00,$08,$00,$18,$00,$10,$00,$10,$00,$30 - db $10,$1f,$0f,$0f,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - db $20,$f0,$c0,$dc,$00,$04,$00,$06,$00,$02,$00,$02,$00,$03,$00,$03 - db $48,$78,$28,$38,$28,$38,$28,$38,$28,$38,$24,$3c,$14,$1c,$14,$1c - db $1f,$20,$18,$27,$1f,$20,$18,$27,$0f,$10,$00,$0f,$07,$07,$07,$07 - db $80,$40,$80,$40,$00,$e3,$00,$ff,$70,$8f,$fc,$83,$ff,$80,$ff,$80 - db $00,$20,$00,$40,$00,$c1,$00,$81,$00,$e1,$c0,$21,$c0,$21,$c0,$31 - db $00,$01,$00,$21,$00,$21,$00,$21,$00,$21,$00,$23,$01,$22,$01,$22 - db $14,$1c,$14,$9c,$14,$9c,$12,$9e,$0a,$de,$00,$ff,$8f,$70,$c1,$3e - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$80,$00,$c0 - db $7f,$80,$3f,$c0,$3f,$c0,$3f,$c0,$0f,$f0,$00,$ff,$00,$7f,$00,$7f - db $e0,$11,$e0,$11,$e0,$11,$e0,$11,$c0,$31,$00,$f1,$00,$f1,$00,$f2 - db $01,$22,$01,$22,$01,$22,$01,$22,$01,$22,$00,$23,$00,$23,$00,$23 - db $9f,$60,$83,$7c,$bf,$40,$c2,$3d,$de,$23,$0a,$fe,$0a,$fe,$0a,$fe - db $80,$40,$80,$40,$00,$c0,$00,$80,$00,$00,$00,$00,$00,$00,$00,$00 - db $00,$7f,$00,$7f,$00,$7f,$00,$7f,$00,$7f,$00,$3f,$00,$3f,$00,$3f - db $00,$f2,$00,$e2,$00,$e6,$00,$ec,$00,$dc,$00,$f4,$00,$e4,$00,$c4 - db $00,$33,$00,$31,$00,$39,$00,$2d,$00,$25,$00,$27,$00,$23,$00,$23 - db $0a,$fe,$0a,$fe,$0a,$fe,$0a,$fe,$09,$ff,$09,$ff,$09,$ff,$09,$ff - db $00,$04,$00,$04,$00,$04,$00,$04,$00,$04,$00,$0c,$00,$08,$00,$08 - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$60,$00,$30 - db $00,$21,$00,$20,$00,$31,$00,$11,$00,$10,$00,$10,$00,$10,$00,$10 - db $09,$ff,$09,$ff,$09,$ff,$09,$8f,$0f,$0f,$00,$06,$00,$00,$00,$00 - db $00,$08,$00,$08,$00,$08,$00,$08,$00,$18,$00,$10,$00,$10,$00,$10 - db $00,$1c,$00,$06,$00,$01,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 - db $00,$00,$00,$00,$00,$80,$00,$c0,$00,$00,$00,$00,$00,$00,$00,$00 - db $00,$08,$00,$08,$00,$08,$00,$0c,$00,$04,$00,$06,$00,$0e,$00,$1a - db $00,$10,$00,$10,$00,$30,$00,$38,$00,$24,$00,$22,$00,$21,$00,$21 - db $00,$00,$00,$00,$00,$01,$00,$07,$00,$00,$00,$00,$00,$00,$00,$00 - db $00,$32,$00,$c2,$00,$82,$00,$03,$00,$01,$00,$01,$00,$00,$00,$00 - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$80,$00,$80,$00,$c0 - db $00,$21,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$40,$00,$40 - db $00,$80,$00,$80,$00,$40,$00,$60,$00,$30,$00,$18,$00,$07,$00,$00 - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$c0,$00,$00 - db $00,$80,$00,$80,$00,$80,$00,$40,$00,$40,$00,$40,$00,$60,$00,$20 - db $00,$40,$00,$40,$00,$40,$00,$40,$00,$60,$00,$3c,$00,$3f,$00,$39 - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$c0,$00,$ff - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$ff,$00,$01 - db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$07,$00,$fc,$00,$bf - db $00,$20,$00,$10,$00,$10,$00,$10,$00,$f0,$00,$b0,$00,$30,$00,$f0 - db $00,$00,$00,$7f,$00,$30,$00,$18,$00,$0c,$00,$06,$00,$03,$00,$03 - db $00,$00,$00,$ff,$00,$1a,$00,$32,$00,$62,$00,$c2,$00,$82,$00,$82 - db $00,$03,$00,$06,$00,$0c,$00,$18,$00,$30,$00,$20,$00,$7f,$00,$00 - db $00,$82,$00,$c2,$00,$62,$00,$32,$00,$12,$00,$1a,$00,$ff,$00,$00 - - - -BackgroundCopy: ; tiles start at 27 - db $1b, $1c, $1d, $1e, $1e, $1f, $20, $1b - db $21, $1b, $22, $23, $24, $25, $1b, $1b - db $26, $27, $28, $29, $2a, $2b, $2c, $2d - db $26, $27, $1b, $2e, $2f, $30, $31, $32 - db $26, $27, $33, $34, $35, $36, $37, $38 - db $39, $3a, $3b, $1b, $3c, $3d, $3e, $1b - db $3f, $40, $41, $1b, $1b, $42, $43, $44 - db $1b, $45, $46, $1b, $1b, $47, $48, $49 - db $1b, $4a, $4b, $1b, $1b, $4c, $4d, $1b - db $1b, $1b, $4e, $4f, $1b, $50, $51, $1b - db $1b, $1b, $52, $53, $54, $55, $1b, $1b - db $1b, $1b, $56, $1b, $57, $58, $59, $1b - db $1b, $1b, $5a, $5b, $5c, $1b, $5d, $1b - db $1b, $1b, $5e, $5f, $60, $61, $62, $1b - db $1b, $1b, $1b, $63, $64, $1b, $1b, $1b - db $1b, $1b, $1b, $65, $66, $1b, $1b, $1b diff --git a/main.asm b/main.asm index 2cef85f..e7b2b6f 100644 --- a/main.asm +++ b/main.asm @@ -42,16 +42,20 @@ def ZEROES equ $D000 def ONES equ $D200 ; allocating $8 spaces for system variables, currently only using $4 bytes -DEF rMYBTN EQU SYSTEM_VARS_START -DEF rMYBTNP EQU rMYBTN + 1 -DEF rDELTAT EQU rMYBTNP + 1 ; delta_t where $1000 = 1 second -def rLFSR equ rDELTAT + 1 ; 16 bit +PUSHS "System Variables", WRAM0[SYSTEM_VARS_START] +rMYBTN: db ; EQU SYSTEM_VARS_START +rMYBTNP: db ;EQU rMYBTN + 1 +rDELTAT: db ; EQU rMYBTNP + 1 ; delta_t where $1000 = 1 second +rLFSR: dw ; equ rDELTAT + 1 ; 16 bit +POPS def SAFE_DMA_LOCATION equ $ffc0 -def VARIABLE_TILES_START equ 26 +def VARIABLE_TILES_START equ 26 ; where in VRAM the variable tiles start +; (i.e. we allocate VARIABLE_TILES_START-1 slots out of that block of 128 +; for ever-present ui tiles) @@ -558,8 +562,8 @@ LetterTiles: INCLUDE "Async.inc" INCLUDE "Audio.inc" INCLUDE "Random.inc" -INCLUDE "CopyRangeSafe.inc" -INCLUDE "CopyTilesSafe.inc" +INCLUDE "CopyRange.inc" +INCLUDE "CopyTiles.inc" INCLUDE "ScreenMainMenu.inc" INCLUDE "ScreenSpreadSelect.inc" INCLUDE "CardHelpers.inc" @@ -569,3 +573,4 @@ INCLUDE "ScreenShuffle.inc" INCLUDE "CardLibrary.inc" +include "gbt_player_bank1.inc" \ No newline at end of file diff --git a/mod2gbt b/mod2gbt new file mode 100755 index 0000000..09ae777 Binary files /dev/null and b/mod2gbt differ diff --git a/pythonrangetest.c b/pythonrangetest.c new file mode 100644 index 0000000..1cad6b3 --- /dev/null +++ b/pythonrangetest.c @@ -0,0 +1,217 @@ +; s3m2gbt modified by shoofle to output rgbds-compatible asm filespythonrangetest_0: + db 0xBF,0x00,0x30,0x10,0x10,0x10, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x01,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x02,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x03,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x04,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x05,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x06,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x07,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x08,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x09,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x0A,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x0B,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x0C,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x0D,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x0E,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x0F,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x10,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x11,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x12,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x13,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x14,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x15,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x16,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x17,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x18,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x19,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x1A,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x1B,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x1C,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x1D,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x1E,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x1F,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + + +pythonrangetest_1: + db 0xBF,0x20,0x30,0x10,0x10,0x10, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x21,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x22,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x23,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x24,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x25,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x26,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x27,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x28,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x29,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x2A,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x2B,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x2C,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x2D,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x2E,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x2F,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x30,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x31,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x32,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x33,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x34,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x35,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x36,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x37,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x38,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x39,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x3A,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x3B,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x3C,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x3D,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x3E,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x3F,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + + +pythonrangetest_2: + db 0xBF,0x40,0x30,0x10,0x10,0x10, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x41,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x42,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x43,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x44,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x45,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x46,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0xBF,0x47,0x30,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + db 0x00,0x00,0x00,0x00, + + +pythonrangetest_init_state: + db 0x01,0x06,0x02,0x11,0x22,0x44,0x88,0x00, + +pythonrangetest: + db BANK(pythonrangetest_0) + dw pythonrangetest_0 + + db BANK(pythonrangetest_1) + dw pythonrangetest_1 + + db BANK(pythonrangetest_2) + dw pythonrangetest_2 + + db $00 + dw $0000 + diff --git a/pythonrangetest.inc b/pythonrangetest.inc new file mode 100644 index 0000000..354236b --- /dev/null +++ b/pythonrangetest.inc @@ -0,0 +1,222 @@ +; s3m2gbt modified by shoofle to output rgbds-compatible asm files +SECTION "pythonrangetest_0", ROMX +pythonrangetest_0: + db $BF,$00,$30,$10,$10,$10, + db $00,$00,$00,$00, + db $BF,$01,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$02,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$03,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$04,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$05,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$06,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$07,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$08,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$09,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$0A,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$0B,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$0C,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$0D,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$0E,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$0F,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$10,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$11,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$12,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$13,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$14,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$15,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$16,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$17,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$18,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$19,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$1A,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$1B,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$1C,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$1D,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$1E,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$1F,$30,$00,$00,$00, + db $00,$00,$00,$00, + + +SECTION "pythonrangetest_1", ROMX +pythonrangetest_1: + db $BF,$20,$30,$10,$10,$10, + db $00,$00,$00,$00, + db $BF,$21,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$22,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$23,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$24,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$25,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$26,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$27,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$28,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$29,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$2A,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$2B,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$2C,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$2D,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$2E,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$2F,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$30,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$31,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$32,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$33,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$34,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$35,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$36,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$37,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$38,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$39,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$3A,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$3B,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$3C,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$3D,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$3E,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$3F,$30,$00,$00,$00, + db $00,$00,$00,$00, + + +SECTION "pythonrangetest_2", ROMX +pythonrangetest_2: + db $BF,$40,$30,$10,$10,$10, + db $00,$00,$00,$00, + db $BF,$41,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$42,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$43,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$44,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$45,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$46,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $BF,$47,$30,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + db $00,$00,$00,$00, + + +pythonrangetest_init_state: + db 0x01,0x06,0x02,0x11,0x22,0x44,0x88,0x00, + +SECTION "pythonrangetest", ROMX +pythonrangetest: + db BANK(pythonrangetest_0) + dw pythonrangetest_0 + + db BANK(pythonrangetest_1) + dw pythonrangetest_1 + + db BANK(pythonrangetest_2) + dw pythonrangetest_2 + + db $00 + dw $0000 + diff --git a/range_test.s3m b/range_test.s3m new file mode 100644 index 0000000..a55bcb2 Binary files /dev/null and b/range_test.s3m differ diff --git a/rangetest.mod b/rangetest.mod new file mode 100644 index 0000000..680be11 Binary files /dev/null and b/rangetest.mod differ diff --git a/rangetest.s3m b/rangetest.s3m new file mode 100644 index 0000000..a3d188d Binary files /dev/null and b/rangetest.s3m differ diff --git a/s3m2gbt.py b/s3m2gbt.py old mode 100644 new mode 100755 index d7f03a8..71d74c6 --- a/s3m2gbt.py +++ b/s3m2gbt.py @@ -1,39 +1,11 @@ #!/usr/bin/env python3 -# i (shoofle) have heavily modified this script to output a totally different format -# in order to use it with my tarot project. much credit to the original author, -# whose intro title/copyright block is preserved below. -# sourced this from https://github.com/AntonioND/gbt-player/tree/master/gba/s3m2gbt -# in april of 2025 - # s3m2gbt v4.4.1 (Part of GBT Player) # # SPDX-License-Identifier: MIT # # Copyright (c) 2022 Antonio Niño Díaz -""" - SAMPLE PERIOD LUT - MOD values - C C# D D# E F F# G G# A A# B -Octave 0:1712,1616,1525,1440,1357,1281,1209,1141,1077,1017, 961, 907 // C3 to B3 -Octave 1: 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453 // C4 to B4 -Octave 2: 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226 // C5 to B5 -Octave 3: 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113 // C6 to B6 -Octave 4: 107, 101, 95, 90, 85, 80, 76, 71, 67, 64, 60, 57 // C7 to B7 -Octave 5: 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28 // C8 to B8 - -//From C3 to B8 | A5 = 1750 = 440.00Hz | C5 = 1546 -const UWORD GB_frequencies[] = { - 44, 156, 262, 363, 457, 547, 631, 710, 786, 854, 923, 986, // C3 to B3 - 1046,1102,1155,1205,1253,1297,1339,1379,1417,1452,1486,1517, // C4 to B4 - 1546,1575,1602,1627,1650,1673,1694,1714,1732,1750,1767,1783, // C5 to B5 - 1798,1812,1825,1837,1849,1860,1871,1881,1890,1899,1907,1915, // C6 to B6 - 1923,1930,1936,1943,1949,1954,1959,1964,1969,1974,1978,1982, // C7 to B7 - 1985,1988,1992,1995,1998,2001,2004,2006,2009,2011,2013,2015 // C8 to B8 -}; - -""" - class RowConversionError(Exception): def __init__(self, message, pattern = -1, row = -1, channel = -1): self.pattern = pattern @@ -313,13 +285,8 @@ def s3m_note_to_gb(note): # Note cut with ^^ if note == 0xFE: return 0xFE - if note == 0xFF: - return 0xFF # Note off and ^^ note cut should be handled before reaching this point - #note = note & 0x7F - if note > 0x7F: - print(note) assert note <= 0x7F note -= 32 @@ -351,16 +318,15 @@ def s3m_pan_to_gb(pan, channel): return val -# masks for how to define an effect -EFFECT_PAN = 0x40 -EFFECT_ARPEGGIO = 0x50 -EFFECT_VIBRATO = 0x60 -EFFECT_VOLUME_SLIDE = 0x70 -EFFECT_NOTE_CUT = 0x80 -EFFECT_PATTERN_JUMP = 0x90 -EFFECT_BREAK_SET_STEP = 0xA0 -EFFECT_SPEED = 0xB0 -EFFECT_EVENT = 0xC0 +EFFECT_PAN = 0 +EFFECT_ARPEGGIO = 1 +EFFECT_NOTE_CUT = 2 +EFFECT_VIBRATO = 3 +EFFECT_VOLUME_SLIDE = 4 +EFFECT_PATTERN_JUMP = 8 +EFFECT_BREAK_SET_STEP = 9 +EFFECT_SPEED = 10 +EFFECT_EVENT = 15 # Returns (converted_num, converted_params) if there was a valid effect. If # there is none, it returns (None, None). Note that it is needed to pass the @@ -443,31 +409,229 @@ def effect_s3m_to_gb(channel, effectnum, effectparams): raise RowConversionError(f"Unsupported effect: {effectnum}{effectparams:02X}") -def convert_channel(channel, note_index, samplenum, volume, effectnum, effectparams): - commands = [] +HAS_VOLUME = 1 << 4 +HAS_INSTRUMENT = 1 << 5 +HAS_EFFECT = 1 << 6 +HAS_NOTE = 1 << 7 +HAS_KIT = 1 << 7 + +def convert_channel1(note_index, samplenum, volume, effectnum, effectparams): + command = [ 0, 0, 0, 0 ] # NOP + command_ptr = 1 # Check if it's needed to add a note - if note_index != -1 and note_index != 0xFF and note_index != 0xFE: + if note_index != -1: note_index = s3m_note_to_gb(note_index) - commands.append([0x10 | channel, note_index]) - if note_index == 0xFF or note_index == 0xFE: - commands.append([0x80 | channel, note_index]) - - if volume > -1: - commands.append([0x20 | channel, s3m_volume_to_gb(volume) & 0x0F]) + command[0] |= HAS_NOTE + command[command_ptr] = note_index + command_ptr = command_ptr + 1 # Check if there is a sample defined if samplenum > 0: instrument = samplenum & 3 - commands.append([0x30 | channel, instrument << 6]) + + command[0] |= HAS_INSTRUMENT + command[command_ptr] = (instrument << 4) & 0x30 if effectnum is not None: [num, params] = effect_s3m_to_gb(1, effectnum, effectparams) if num is not None: - commands.append([num | channel, params]) + command[0] |= HAS_EFFECT + command[command_ptr] |= num & 0x0F + command_ptr += 1 + command[command_ptr] = params & 0xFF - return commands + # Check if it's needed to add a volume + if volume > -1: + command[0] |= HAS_VOLUME + command[0] |= s3m_volume_to_gb(volume) & 0x0F + + # Note: The volume bit doesn't affect the final size. + sizes = [ 1, 2, 3, 3, 2, 3, 4, 4 ] + command_size = sizes[command[0] >> 5] + + return command[:command_size] + +def convert_channel2(note_index, samplenum, volume, effectnum, effectparams): + command = [ 0, 0, 0, 0 ] # NOP + command_ptr = 1 + + # Check if it's needed to add a note + if note_index != -1: + note_index = s3m_note_to_gb(note_index) + command[0] |= HAS_NOTE + command[command_ptr] = note_index + command_ptr = command_ptr + 1 + + # Check if there is a sample defined + if samplenum > 0: + instrument = samplenum & 3 + + command[0] |= HAS_INSTRUMENT + command[command_ptr] = (instrument << 4) & 0x30 + + if effectnum is not None: + [num, params] = effect_s3m_to_gb(2, effectnum, effectparams) + + if num is not None: + command[0] |= HAS_EFFECT + command[command_ptr] |= num & 0x0F + command_ptr += 1 + command[command_ptr] = params & 0xFF + + # Check if it's needed to add a volume + if volume > -1: + command[0] |= HAS_VOLUME + command[0] |= s3m_volume_to_gb(volume) & 0x0F + + # Note: The volume bit doesn't affect the final size. + sizes = [ 1, 2, 3, 3, 2, 3, 4, 4 ] + command_size = sizes[command[0] >> 5] + + return command[:command_size] + +def convert_channel3(note_index, samplenum, volume, effectnum, effectparams): + command = [ 0, 0, 0, 0 ] # NOP + command_ptr = 1 + + # Check if it's needed to add a note + if note_index != -1: + note_index = s3m_note_to_gb(note_index) + command[0] |= HAS_NOTE + command[command_ptr] = note_index + command_ptr = command_ptr + 1 + + # Check if there is a sample defined + if samplenum > 0: + instrument = samplenum & 7 + + command[0] |= HAS_INSTRUMENT + command[command_ptr] = (instrument << 4) & 0xF0 + + if effectnum is not None: + [num, params] = effect_s3m_to_gb(3, effectnum, effectparams) + + if num is not None: + command[0] |= HAS_EFFECT + command[command_ptr] |= num & 0x0F + command_ptr += 1 + command[command_ptr] = params & 0xFF + + # Check if it's needed to add a volume + if volume > -1: + command[0] |= HAS_VOLUME + command[0] |= s3m_volume_to_gb_ch3(volume) & 0x0F + + # Note: The volume bit doesn't affect the final size. + sizes = [ 1, 2, 3, 3, 2, 3, 4, 4 ] + command_size = sizes[command[0] >> 5] + + return command[:command_size] + +def convert_channel4(note_index, samplenum, volume, effectnum, effectparams): + command = [ 0, 0, 0, 0 ] # NOP + command_ptr = 1 + + # Note cut using ^^ as note + if note_index == 0xFE: + if samplenum > 0: + # This limitation is only for channel 4. It should never happen in a + # regular song. + raise("Note cut + Sample in same row: Not supported in channel 4") + samplenum = 0xFE + + # Check if there is a sample defined + if samplenum > 0: + if samplenum == 0xFE: + kit = 0xFE; + else: + kit = samplenum & 0xF; + command[0] |= HAS_KIT + command[command_ptr] = kit + command_ptr += 1 + + if effectnum is not None: + [num, params] = effect_s3m_to_gb(4, effectnum, effectparams) + + if num is not None: + command[0] |= HAS_EFFECT + command[command_ptr] |= num & 0x0F + command_ptr += 1 + command[command_ptr] = params & 0xFF + + # Check if it's needed to add a volume + if volume > -1: + command[0] |= HAS_VOLUME + command[0] |= s3m_volume_to_gb(volume) & 0x0F + + # Note: The volume bit doesn't affect the final size. + sizes = [ 1, 2, 3, 3, 2, 3, 4, 4 ] + command_size = sizes[command[0] >> 5] + + return command[:command_size] + +STARTUP_CMD_DONE = 0 +STARTUP_CMD_SPEED = 1 +STARTUP_CMD_PANING = 2 +STARTUP_CMD_CHANNEL3_INSTRUMENT = 3 + +SAMPLE_64_ENTRIES = 1 << 7 + +def initial_state_array(speed, panning_array, instruments): + array = [] + + # Initial speed + # ------------- + + array.extend([STARTUP_CMD_SPEED, speed]) + + # Initial panning + # --------------- + + array.extend([STARTUP_CMD_PANING]) + array.extend(panning_array) + + # Channel 3 instruments + # --------------------- + + if instruments is not None: + print("Exporting instruments...") + count = 0 + for inst in instruments: + # In the tracker, instruments start at index 1, but they start at + # index 0 in the S3M file. + count += 1 + + # Only handle instruments assigned to channel 3 + if count < 8 or count > 15: + continue + + name = inst.sample_name + + size = inst.length + if size != 32 and size != 64: + raise S3MFormatError(f"Sample '{name}': Invalid sample length: {size}") + else: + flags = count - 8 # The low bits are the instrument index + if size == 64: + flags |= SAMPLE_64_ENTRIES + + array.extend([STARTUP_CMD_CHANNEL3_INSTRUMENT, flags]) + + # Convert from 8 bit to 4 bit + for i in range(0, size, 2): + sample_hi = inst.sample_data[i + 0] >> 4 + sample_lo = inst.sample_data[i + 1] >> 4 + value = (sample_hi << 4) | sample_lo + array.extend([value]) + + # End commands + # ------------ + + array.extend([STARTUP_CMD_DONE]) + + return array def convert_file(module_path, song_name, output_path, export_instruments): @@ -481,7 +645,7 @@ def convert_file(module_path, song_name, output_path, export_instruments): with open(output_path, "w") as fileout: - fileout.write("; File created by shoofle's s3m2gbt edit\n\n") + fileout.write("; s3m2gbt modified by shoofle to output rgbds-compatible asm files\n") # Export patterns # --------------- @@ -498,15 +662,18 @@ def convert_file(module_path, song_name, output_path, export_instruments): print(f"Pattern {pattern} not exported: Not in the order list") continue + fileout.write(f"SECTION \"{song_name}_{pattern}\", ROMX\n") fileout.write(f"{song_name}_{pattern}:\n") row = 0 - commands = [[0x00, 0x00]] + cmd1 = [0] + cmd2 = [0] + cmd3 = [0] + cmd4 = [0] for c in p.cells: - # If an end of row marker is reached, print the previous row. # Trust that the S3M file is generated in a valid way and it # doesn't have markers at weird positions, and that there is one @@ -514,19 +681,21 @@ def convert_file(module_path, song_name, output_path, export_instruments): if c.empty: # Write row - fileout.write(" ") + fileout.write(" db ") - for cmd in commands: - fileout.write("db ") - for b in cmd: - fileout.write(f"${b:02X}, ") - fileout.write("\n") + cmd = cmd1 + cmd2 + cmd3 + cmd4 + for b in cmd: + fileout.write(f"${b:02X},") fileout.write("\n") row = row + 1 - commands = [[0x0F, row & 0xFF]] + # Clear commands + cmd1 = [0] + cmd2 = [0] + cmd3 = [0] + cmd4 = [0] # Next iteration continue @@ -557,11 +726,19 @@ def convert_file(module_path, song_name, output_path, export_instruments): channel = c.channel + 1 try: - commands.extend(convert_channel(channel, - note, instrument, volume, - effectnum, effectparams)) - - if channel > 4: + if channel == 1: + cmd1 = convert_channel1(note, instrument, volume, + effectnum, effectparams) + elif channel == 2: + cmd2 = convert_channel2(note, instrument, volume, + effectnum, effectparams) + elif channel == 3: + cmd3 = convert_channel3(note, instrument, volume, + effectnum, effectparams) + elif channel == 4: + cmd4 = convert_channel4(note, instrument, volume, + effectnum, effectparams) + else: raise S3MFormatError(f"Too many channels: {channel}") except RowConversionError as e: e.row = row @@ -569,27 +746,77 @@ def convert_file(module_path, song_name, output_path, export_instruments): e.channel = channel raise e + fileout.write("\n") + fileout.write("\n") # Export initial state # -------------------- - print(f"Exporting initial state... or not...") + print(f"Exporting initial state...") + + fileout.write(f"{song_name}_init_state:\n") + + default_pan = [8, 8, 8, 8] + for i in range(0, 4): + default_pan[i] = s3m.channel_pan[i] + + gb_default_pan = [ + s3m_pan_to_gb(default_pan[0], 1), + s3m_pan_to_gb(default_pan[1], 2), + s3m_pan_to_gb(default_pan[2], 3), + s3m_pan_to_gb(default_pan[3], 4) + ] + + instr = None + if export_instruments: + instr = s3m.instruments + + state_array = initial_state_array(s3m.initial_speed, gb_default_pan, instr) + + # Write rows of 8 bytes until the end of the array + while True: + left = len(state_array) + + write = [] + if left == 0: + break + elif left <= 8: + write = state_array + state_array = [] + else: + write = state_array[0:8] + state_array = state_array[8:] + + fileout.write(" db ") + for s in write: + fileout.write(f"0x{s:02X},") + fileout.write("\n") + + fileout.write("\n") - # Export orders # ------------- print(f"Exporting orders...") + fileout.write(f"SECTION \"{song_name}\", ROMX\n") fileout.write(f"{song_name}:\n") - fileout.write(f"\tdb {len(s3m.song_orders)}\n") + #fileout.write(f" {song_name}_init_state,") + #fileout.write("\n") + for o in s3m.song_orders: pattern = int(o) if pattern >= s3m.num_patterns: # TODO: Warn if the pattern goes over the limit? continue - fileout.write(f"\tdw {song_name}_{pattern}\n") + fileout.write(f" db BANK({song_name}_{pattern})\n") + fileout.write(f" dw {song_name}_{pattern}\n") + fileout.write("\n") + + fileout.write(" db $00\n") + fileout.write(" dw $0000\n") + fileout.write("\n") if __name__ == "__main__": diff --git a/s3m2shoofmt.py b/s3m2shoofmt.py new file mode 100644 index 0000000..d7f03a8 --- /dev/null +++ b/s3m2shoofmt.py @@ -0,0 +1,627 @@ +#!/usr/bin/env python3 + +# i (shoofle) have heavily modified this script to output a totally different format +# in order to use it with my tarot project. much credit to the original author, +# whose intro title/copyright block is preserved below. +# sourced this from https://github.com/AntonioND/gbt-player/tree/master/gba/s3m2gbt +# in april of 2025 + +# s3m2gbt v4.4.1 (Part of GBT Player) +# +# SPDX-License-Identifier: MIT +# +# Copyright (c) 2022 Antonio Niño Díaz + +""" + SAMPLE PERIOD LUT - MOD values + C C# D D# E F F# G G# A A# B +Octave 0:1712,1616,1525,1440,1357,1281,1209,1141,1077,1017, 961, 907 // C3 to B3 +Octave 1: 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453 // C4 to B4 +Octave 2: 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226 // C5 to B5 +Octave 3: 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113 // C6 to B6 +Octave 4: 107, 101, 95, 90, 85, 80, 76, 71, 67, 64, 60, 57 // C7 to B7 +Octave 5: 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28 // C8 to B8 + +//From C3 to B8 | A5 = 1750 = 440.00Hz | C5 = 1546 +const UWORD GB_frequencies[] = { + 44, 156, 262, 363, 457, 547, 631, 710, 786, 854, 923, 986, // C3 to B3 + 1046,1102,1155,1205,1253,1297,1339,1379,1417,1452,1486,1517, // C4 to B4 + 1546,1575,1602,1627,1650,1673,1694,1714,1732,1750,1767,1783, // C5 to B5 + 1798,1812,1825,1837,1849,1860,1871,1881,1890,1899,1907,1915, // C6 to B6 + 1923,1930,1936,1943,1949,1954,1959,1964,1969,1974,1978,1982, // C7 to B7 + 1985,1988,1992,1995,1998,2001,2004,2006,2009,2011,2013,2015 // C8 to B8 +}; + +""" + +class RowConversionError(Exception): + def __init__(self, message, pattern = -1, row = -1, channel = -1): + self.pattern = pattern + self.row = row + self.channel = channel + 1 + self.message = message + + def __str__(self): + return f"Pattern {self.pattern} | Row {self.row} | Channel {self.channel} | {self.message}" + +class S3MFormatError(Exception): + pass + +class S3MFormatReader: + + def read_u8(self): + offset = self.read_ptr + self.read_ptr += 1 + return int(self.data[offset]) + + def read_u16(self): + offset = self.read_ptr + self.read_ptr += 2 + return int((self.data[offset + 1] << 8) | self.data[offset]) + + def read_memseg(self): + offset = self.read_ptr + self.read_ptr += 3 + part1 = self.data[offset + 0] + part2 = self.data[offset + 1] + part3 = self.data[offset + 2] + return int((part1 << 16) | (part3 << 8) | part2) + + def read_string(self, size): + offset = self.read_ptr + self.read_ptr += size + return self.data[offset:offset+size] + +class S3MFileInstrument(S3MFormatReader): + + def __init__(self, data, offset): + self.data = data + self.read_ptr = offset + + instrument_type = self.read_u8() + if instrument_type != 1: + self.exists = False + return + self.exists = True + + self.dos_filename = self.read_string(12).decode("utf-8") + + self.sample_data_offset = self.read_memseg() * 16 + + self.length = self.read_u16() + self.length |= self.read_u16() << 16 + + self.read_ptr += 4 + 4 # Skip loop begin and loop end + + self.default_volume = self.read_u8() + + self.read_ptr = offset + 0x30 + + self.sample_name = self.read_string(28).decode("utf-8") + + if self.read_string(4) != b'SCRS': + raise S3MFormatError("Invalid magic string in instrument") + + start = self.sample_data_offset + end = start + self.length + self.sample_data = self.data[start:end] + +class S3MFilePatternCell(): + + def __init__(self, header, channel, note, instrument, volume, + effect, effect_args): + + if header == 0: + self.empty = True + return + + self.empty = False + + self.channel = channel + + if (note != None) or (instrument != None): + self.has_note_and_instrument = True + self.note = note + self.instrument = instrument + else: + self.has_note_and_instrument = False + + if volume != None: + self.has_volume = True + self.volume = volume + else: + self.has_volume = False + + if (effect != None) or (effect_args != None): + self.has_effect = True + self.effect = effect + self.effect_args = effect_args + else: + self.has_effect = False + +class S3MFilePattern(S3MFormatReader): + + def __init__(self, data, offset): + + # Check if we have asked to generate an empty pattern + if data == None: + cell = S3MFilePatternCell(0, 0, 0, 0, 0, 0, 0) + self.cells = [] + for i in range(0, 64): + self.cells.append(cell) + return + + self.data = data + self.read_ptr = offset + + length = self.read_u16() - 2 + + self.cells = [] + + while length > 0: + header = self.read_u8() + length -= 1 + + channel = header & 31 + + note = None + instrument = None + volume = None + effect = None + effect_args = None + + if (header & (1 << 5)) != 0: # Has note and instrument + note = self.read_u8() + instrument = self.read_u8() + length -= 2 + + if (header & (1 << 6)) != 0: # Has volume + volume = self.read_u8() + length -= 1 + + if (header & (1 << 7)) != 0: # Has effect + effect = self.read_u8() + effect_args = self.read_u8() + length -= 2 + + cell = S3MFilePatternCell(header, channel, note, instrument, volume, + effect, effect_args) + self.cells.append(cell) + +class S3MFile(S3MFormatReader): + + def __init__(self, data): + + # Save data for now + + self.data = data + self.read_ptr = 0 + + self.name = self.read_string(28).decode("utf-8") + print(f"Song Name: '{self.name}'") + + self.read_ptr += 1 + 1 + 2 # Ignore fields + + self.song_length = self.read_u16() + print(f"Song Length: {self.song_length}") + + self.num_instruments = self.read_u16() + self.num_patterns = self.read_u16() + + self.read_ptr += 6 # Ignore fields + + if self.read_string(4) != b'SCRM': + raise S3MFormatError("Invalid magic string in file") + + self.read_ptr += 1 # Ignore global volume + + self.initial_speed = self.read_u8() + + if self.read_u8() != 150: + raise S3MFormatError("Invalid tempo: It must be 150") + + self.read_ptr += 2 # Ignore master volume and ultraclick removal + + # Save this for later + has_custom_pan = False + if self.read_u8() == 252: + has_custom_pan = True + + self.read_ptr = 0x40 + channel_settings = self.read_string(4) + if channel_settings[0] >= 16 or channel_settings[1] >= 16 or \ + channel_settings[2] >= 16 or channel_settings[3] >= 16: + raise S3MFormatError("Invalid channel settings: Channels 0-3 must be enabled") + + # Read orders + + self.read_ptr = 0x60 + + self.song_orders = self.read_string(self.song_length) + if self.song_length % 2 == 1: + self.read_ptr += 1 # Align to 2 + + # Read instrument parapointers + + self.instrument_offsets = [None] * self.num_instruments + for i in range(0, self.num_instruments): + self.instrument_offsets[i] = self.read_u16() * 16 + + # Read pattern parapointers + + self.pattern_offsets = [None] * self.num_patterns + for i in range(0, self.num_patterns): + self.pattern_offsets[i] = self.read_u16() * 16 + + # Read default panning + + if has_custom_pan: + self.channel_pan = [b & 0xF for b in self.read_string(4)] + else: + self.channel_pan = [8, 8, 8, 8] + + # Load instruments + + self.instruments = [None] * self.num_instruments + for i in range(0, len(self.instrument_offsets)): + offset = self.instrument_offsets[i] + if offset != 0: + instr = S3MFileInstrument(self.data, offset) + if instr.exists: + self.instruments[i] = instr + + # Load patterns + + self.patterns = [None] * self.num_patterns + for i in range(0, len(self.pattern_offsets)): + offset = self.pattern_offsets[i] + if offset != 0: + self.patterns[i] = S3MFilePattern(self.data, offset) + else: + # A NULL pointer means that the pattern is empty + self.patterns[i] = S3MFilePattern(None, 0) + + # The file data is no longer needed + + self.data = [] + +# Channels 1, 2, 4 +def s3m_volume_to_gb(s3m_vol): + if s3m_vol >= 64: + return 15 + else: + return s3m_vol >> 2; + +# Channel 3 +def s3m_volume_to_gb_ch3(s3m_vol): + vol = s3m_volume_to_gb(s3m_vol) + + if vol >= 0 and vol <= 3: + return 0 # 0% + elif vol >= 4 and vol <= 6: + return 3 # 25% + elif vol >= 7 and vol <= 9: + return 2 # 50% + elif vol >= 10 and vol <= 12: + return 4 # 75% + elif vol >= 13 and vol <= 15: + return 1 # 100% + else: + return 0 + +def s3m_note_to_gb(note): + # Note cut with ^^ + if note == 0xFE: + return 0xFE + if note == 0xFF: + return 0xFF + + # Note off and ^^ note cut should be handled before reaching this point + #note = note & 0x7F + if note > 0x7F: + print(note) + assert note <= 0x7F + + note -= 32 + if note < 0: + raise RowConversionError("Note too low") + elif note > 32 + 16 * 6: + raise RowConversionError("Note too high") + + note = (note & 0xF) + ((note & 0xF0) >> 4) * 12 + return note + +def s3m_pan_to_gb(pan, channel): + left = False + right = False + + if pan >= 0 and pan <= 3: + left = True + elif pan >= 4 and pan <= 11: + left = True + right = True + elif pan >= 12 and pan <= 15: + right = True + + val = 0 + if left: + val |= 1 << (3 + channel) + if right: + val |= 1 << (channel - 1) + + return val + +# masks for how to define an effect +EFFECT_PAN = 0x40 +EFFECT_ARPEGGIO = 0x50 +EFFECT_VIBRATO = 0x60 +EFFECT_VOLUME_SLIDE = 0x70 +EFFECT_NOTE_CUT = 0x80 +EFFECT_PATTERN_JUMP = 0x90 +EFFECT_BREAK_SET_STEP = 0xA0 +EFFECT_SPEED = 0xB0 +EFFECT_EVENT = 0xC0 + +# Returns (converted_num, converted_params) if there was a valid effect. If +# there is none, it returns (None, None). Note that it is needed to pass the +# channel to this function because some effects behave differently depending on +# the channel (like panning). +def effect_s3m_to_gb(channel, effectnum, effectparams): + + if effectnum == 'A': # Set Speed + if effectparams == 0: + raise RowConversionError("Speed must not be zero") + + return (EFFECT_SPEED, effectparams) + + if effectnum == 'B': # Pattern jump + # TODO: Fail if this jumps out of bounds + return (EFFECT_PATTERN_JUMP, effectparams) + + elif effectnum == 'C': # Break + Set row + # Effect value is BCD, convert to integer + val = (((effectparams & 0xF0) >> 4) * 10) + (effectparams & 0x0F) + return (EFFECT_BREAK_SET_STEP, val) + + elif effectnum == 'D': # Volume Slide + if channel == 3: + raise RowConversionError("Volume slide not supported in channel 3") + + if effectparams == 0: + # Ignore volume slide commands that just continue the effect, + # they are only needed for the S3M player. + return (None, None) + + upper = (effectparams >> 4) & 0xF + lower = effectparams & 0xF + + if upper == 0xF or lower == 0xF: + raise RowConversionError("Fine volume slide not supported") + + elif lower == 0: # Volume goes up + params = 1 << 3 # Increase + delay = 7 - upper + 1 + if delay <= 0: + raise RowConversionError("Volume slide too steep") + params |= delay + return (EFFECT_VOLUME_SLIDE, params) + elif upper == 0: # Volume goes down + params = 0 << 3 # Decrease + delay = 7 - lower + 1 + if delay <= 0: + raise RowConversionError("Volume slide too steep") + params = delay + return (EFFECT_VOLUME_SLIDE, params) + else: + raise RowConversionError("Invalid volume slide arguments") + + return (EFFECT_VOLUME_SLIDE, effectparams) + + elif effectnum == 'H': # Vibrato + return (EFFECT_VIBRATO, effectparams) + + elif effectnum == 'J': # Arpeggio + return (EFFECT_ARPEGGIO, effectparams) + + elif effectnum == 'S': # This effect is subdivided into many + + subeffectnum = (effectparams & 0xF0) >> 4 + subeffectparams = effectparams & 0x0F + + if subeffectnum == 0x8: # Pan position + val = s3m_pan_to_gb(subeffectparams, channel) + return (EFFECT_PAN, val) + + elif subeffectnum == 0xC: # Notecut + return (EFFECT_NOTE_CUT, subeffectparams) + + elif subeffectnum == 0xF: # Funkrepeat? Set active macro? + # This effect is either unused, or it's the "set active macro" + # command, which doesn't have any effect if you don't use the macro + # afterwards. It can safely be overloaded for event callbacks. + return (EFFECT_EVENT, subeffectparams) + + raise RowConversionError(f"Unsupported effect: {effectnum}{effectparams:02X}") + +def convert_channel(channel, note_index, samplenum, volume, effectnum, effectparams): + commands = [] + + # Check if it's needed to add a note + if note_index != -1 and note_index != 0xFF and note_index != 0xFE: + note_index = s3m_note_to_gb(note_index) + commands.append([0x10 | channel, note_index]) + if note_index == 0xFF or note_index == 0xFE: + commands.append([0x80 | channel, note_index]) + + if volume > -1: + commands.append([0x20 | channel, s3m_volume_to_gb(volume) & 0x0F]) + + # Check if there is a sample defined + if samplenum > 0: + instrument = samplenum & 3 + commands.append([0x30 | channel, instrument << 6]) + + if effectnum is not None: + [num, params] = effect_s3m_to_gb(1, effectnum, effectparams) + + if num is not None: + commands.append([num | channel, params]) + + return commands + +def convert_file(module_path, song_name, output_path, export_instruments): + + with open(module_path, "rb") as file: + file_byte_array = bytearray(file.read()) + + s3m = S3MFile(file_byte_array) + + if output_path == None: + output_path = song_name + ".inc" + + with open(output_path, "w") as fileout: + + fileout.write("; File created by shoofle's s3m2gbt edit\n\n") + + # Export patterns + # --------------- + + print(f"Exporting patterns...") + + pattern = -1 + for p in s3m.patterns: + pattern += 1 + + # Check if pattern is actually used in the order list. If it isn't + # used, don't export it. + if pattern not in s3m.song_orders: + print(f"Pattern {pattern} not exported: Not in the order list") + continue + + fileout.write(f"{song_name}_{pattern}:\n") + + row = 0 + + commands = [[0x00, 0x00]] + + for c in p.cells: + + + # If an end of row marker is reached, print the previous row. + # Trust that the S3M file is generated in a valid way and it + # doesn't have markers at weird positions, and that there is one + # marker right at the end of each pattern. + if c.empty: + + # Write row + fileout.write(" ") + + for cmd in commands: + fileout.write("db ") + for b in cmd: + fileout.write(f"${b:02X}, ") + fileout.write("\n") + + fileout.write("\n") + + row = row + 1 + + commands = [[0x0F, row & 0xFF]] + + # Next iteration + continue + + volume = -1 + if c.has_volume: + volume = c.volume + + note = -1 + instrument = 0 + if c.has_note_and_instrument: + note = c.note + instrument = c.instrument + + # Rows with note and instrument but no volume use the + # default volume of the sample. + if instrument > 0 and volume == -1: + this_instr = s3m.instruments[instrument - 1] + volume = this_instr.default_volume + + effectnum = None + effectparams = None + if c.has_effect: + # Convert type to ASCII to match the documentation + effectnum = chr(c.effect + ord('A') - 1) + effectparams = c.effect_args + + channel = c.channel + 1 + + try: + commands.extend(convert_channel(channel, + note, instrument, volume, + effectnum, effectparams)) + + if channel > 4: + raise S3MFormatError(f"Too many channels: {channel}") + except RowConversionError as e: + e.row = row + e.pattern = pattern + e.channel = channel + raise e + + + # Export initial state + # -------------------- + + print(f"Exporting initial state... or not...") + + + # Export orders + # ------------- + + print(f"Exporting orders...") + + fileout.write(f"{song_name}:\n") + + fileout.write(f"\tdb {len(s3m.song_orders)}\n") + for o in s3m.song_orders: + pattern = int(o) + if pattern >= s3m.num_patterns: + # TODO: Warn if the pattern goes over the limit? + continue + fileout.write(f"\tdw {song_name}_{pattern}\n") + +if __name__ == "__main__": + + import argparse + import sys + + print("s3m2gbt v4.4.1 (part of GBT Player)") + print("Copyright (c) 2022 Antonio Niño Díaz ") + print("All rights reserved") + print("") + + parser = argparse.ArgumentParser(description='Convert S3M files into GBT format.') + parser.add_argument("--input", default=None, required=True, + help="input file") + parser.add_argument("--name", default=None, required=True, + help="output song name") + parser.add_argument("--output", default=None, required=False, + help="output file") + parser.add_argument("--instruments", default=False, required=False, + action='store_true', help="export channel 3 instruments") + + args = parser.parse_args() + + try: + convert_file(args.input, args.name, args.output, args.instruments) + except RowConversionError as e: + print("ERROR: " + str(e)) + sys.exit(1) + except S3MFormatError as e: + print("ERROR: Invalid S3M file: " + str(e)) + sys.exit(1) + + print("Done!") + + sys.exit(0) diff --git a/source.zip b/source.zip index 2f25319..dde3c62 100644 Binary files a/source.zip and b/source.zip differ