644 lines
15 KiB
PHP
644 lines
15 KiB
PHP
;###############################################################################
|
|
;
|
|
; GBT Player v3.1.0
|
|
;
|
|
; SPDX-License-Identifier: MIT
|
|
;
|
|
; Copyright (c) 2009-2021, Antonio Niño Díaz <antonio_nd@outlook.com>
|
|
;
|
|
;###############################################################################
|
|
|
|
INCLUDE "hardware.inc"
|
|
;###############################################################################
|
|
;
|
|
; GBT Player v3.1.0
|
|
;
|
|
; SPDX-License-Identifier: MIT
|
|
;
|
|
; Copyright (c) 2009-2020, Antonio Niño Díaz <antonio_nd@outlook.com>
|
|
;
|
|
;###############################################################################
|
|
|
|
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
|
|
|
|
;###############################################################################
|