gb-tarot/gbt_player.inc

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
;###############################################################################