Skip to content

Instantly share code, notes, and snippets.

@DeerTears
Created January 11, 2022 19:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DeerTears/f42cd912fd0b9df0ccc81c1b635c9ecb to your computer and use it in GitHub Desktop.
Save DeerTears/f42cd912fd0b9df0ccc81c1b635c9ecb to your computer and use it in GitHub Desktop.

Written by Emberlynn Bland.

How to alter GBT Player Banks for Channel 3

It is highly recomended you read the music documentation before continuing.

GBT Player can be compiled to use a different set of 8 pre-determined waveforms for Channel 3, compared to the defaults that are mentioned in the music docs. This lets you edit all 8 waveforms for Channel 3 on a per-game basis. These 8 waveforms can not be swapped-out during gameplay.

After using Engine Eject (2.0.0 and above) the file gbt_player_bank1.s can be found inside of assets/engine/src/core. Lines 40 to 47 contain sets of nibbles that determine the waveform shape for each of Channel 3's instruments.

Each line contains 32 nibbles joined together in groups of 2. Each nibble represents the amplitude of a sample from 0 to F.

Example:

gbt_wave: ; unofficial edit
	.DB	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; square wave
	.DB	0x79,0xBC,0xDE,0xEF,0xFF,0xEE,0xDC,0xB9,0x75,0x43,0x21,0x10,0x00,0x11,0x23,0x45 ; sine wave
	.DB	0xFF,0xEE,0xDD,0xCC,0xBB,0xAA,0x99,0x88,0x77,0x66,0x55,0x44,0x33,0x22,0x11,0x00 ; sawtooth
	.DB	0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x77,0x66,0x55,0x44,0x33,0x22,0x11 ; triangle
	.DB 0xFE,0xDC,0xBA,0x98,0x76,0x54,0x32,0x10,0x12,0x34,0x45,0x56,0x78,0x9A,0xBC,0xDE ; double-speed triangle

Example: 0xFF represents two samples of maximum amplitude. One set to F amplitude, the other set to F amplitude.
Example: 0x00 represents two samples of minimum amplitude. One set to 0 amplitude, the other set to 0 amplitude.
Example: 0x0F represents two samples of mixed amplitude. One set to 0 amplitude, the other set to F amplitude.

So long as the formatting of the nibbles remain the same, their values can be changed to any 16-bit value to create waveforms other than the GBT Player defaults. Changing the in-game samples here will not update the samples in template.mod, so adjust these samples with this in mind. hUGETracker features a sample-drawing tool that shows the necessary nibbles to create your desired waveform.

Here's a new waveform that can not be found in GBT Player by default:

gbt_wave: ; unofficial edit
	.DB	0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x77,0x66,0x55,0x44,0x33,0x22,0x11 ; triangle wave

Replacing one of the instruments with the above code will change the corrosponding instrument to your new waveform.


Example implementation

To cap this off, here's an example of me switching the default Sine wave with a Triangle wave:

;-------------------------------------------------------------------------------
;
; GBT Player v2.2.0
;
; SPDX-License-Identifier: MIT
;
; Copyright (c) 2009-2020, Antonio Niño Díaz <antonio_nd@outlook.com>
;
;-------------------------------------------------------------------------------

	.NR10 = 0xFF10
	.NR11 = 0xFF11
	.NR12 = 0xFF12
	.NR13 = 0xFF13
	.NR14 = 0xFF14
	.NR21 = 0xFF16
	.NR22 = 0xFF17
	.NR23 = 0xFF18
	.NR24 = 0xFF19
	.NR30 = 0xFF1A
	.NR31 = 0xFF1B
	.NR32 = 0xFF1C
	.NR33 = 0xFF1D
	.NR34 = 0xFF1E
	.NR41 = 0xFF20
	.NR42 = 0xFF21
	.NR43 = 0xFF22
	.NR44 = 0xFF23
	.NR50 = 0xFF24
	.NR51 = 0xFF25
	.NR52 = 0xFF26

;-------------------------------------------------------------------------------

	.area	_CODE_1

;-------------------------------------------------------------------------------

gbt_wave: ; 8 sounds
	.DB	0xA5,0xD7,0xC9,0xE1,0xBC,0x9A,0x76,0x31,0x0C,0xBA,0xDE,0x60,0x1B,0xCA,0x03,0x93 ; random :P
	.DB	0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x77,0x66,0x55,0x44,0x33,0x22,0x11 ; ember triangle
	.DB	0xFD,0xEC,0xDB,0xCA,0xB9,0xA8,0x97,0x86,0x79,0x68,0x57,0x46,0x35,0x24,0x13,0x02 ; little up-downs
	.DB	0xDE,0xFE,0xDC,0xBA,0x9A,0xA9,0x87,0x77,0x88,0x87,0x65,0x56,0x54,0x32,0x10,0x12
	.DB	0xAB,0xCD,0xEF,0xED,0xCB,0xA0,0x12,0x3E,0xDC,0xBA,0xBC,0xDE,0xFE,0xDC,0x32,0x10 ; triangular broken
	.DB	0xFF,0xEE,0xDD,0xCC,0xBB,0xAA,0x99,0x88,0x77,0x66,0x55,0x44,0x33,0x22,0x11,0x00 ; triangular
	.DB	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; square 50%
	.DB	0x79,0xBC,0xDE,0xEF,0xFF,0xEE,0xDC,0xB9,0x75,0x43,0x21,0x10,0x00,0x11,0x23,0x45 ; sine

; gbt_noise: ; Moved to Mod2GBT for better note range & performance
	; 7 bit, can adjust with pitch C D# F# A# C
	;.DB	0x5F,0x4E,0x3E,0x2F,0x2E,0x2C,0x1F,0x0F
	; 15 bit
	;.DB	0x64,0x54,0x44,0x24,0x00
	;.DB	0x67,0x56,0x46

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,#0
	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,#0x01
	jr	nz,channel1_enabled$

	; Channel is disabled. Increment pointer as needed

	ld	a,(de)
	inc	de
	bit	7,a
	jr	nz,ch1_more_bytes$
	bit	6,a
	jr	z,ch1_no_more_bytes_this_channel$

	jr	ch1_one_more_byte$

ch1_more_bytes$:

	ld	a,(de)
	inc	de
	bit	7,a
	jr	z,ch1_no_more_bytes_this_channel$

ch1_one_more_byte$:

	inc	de

ch1_no_more_bytes_this_channel$:

	ret

channel1_enabled$:

	; Channel 1 is enabled

	ld	a,(de)
	inc	de

	bit	7,a
	jr	nz,ch1_has_frequency$

	; Not frequency

	bit	6,a
	jr	nz,ch1_instr_effects$

	; Set volume or NOP

	bit	5,a
	jr	nz,ch1_just_set_volume$

	; NOP

	ret

ch1_just_set_volume$:

	; Set volume

	and	a,#0x0F
	swap	a
	; Preserve envelope data
	ld	b,a			; save byte
	ld	a,(gbt_vol+0)
	and	a,#0x0F		; mask envelope
	or	a,b

	ld	(gbt_vol+0),a

	jr	refresh_channel1_regs_trig$

ch1_instr_effects$:

	; Set instrument and effect

	ld	b,a ; save byte

	and	a,#0x30
	sla	a
	sla	a
	ld	(gbt_instr+0),a ; Instrument

	ld	a,b ; restore byte

	and	a,#0x0F ; a = effect

	call	gbt_channel_1_set_effect

	and a,a
	ret	z ; if 0, don't refresh registers
	jr	refresh_channel1_regs_notrig$

ch1_has_frequency$:

	; Has frequency

	and	a,#0x7F
	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,ch1_freq_instr_and_effect$

	; Freq + Instr + Volume

	ld	b,a ; save byte

	and	a,#0x30
	sla	a
	sla	a
	ld	(gbt_instr+0),a ; Instrument

	ld	a,b ; restore byte

	and	a,#0x0F ; a = volume

	swap	a
	; Preserve envelope data
	ld	b,a			; save byte
	ld	a,(gbt_vol+0)
	and	a,#0x0F		; mask envelope
	or	a,b

	ld	(gbt_vol+0),a

	jr	refresh_channel1_regs_trig$

ch1_freq_instr_and_effect$:

	; Freq + Instr + Effect

	ld	b,a ; save byte

	and	a,#0x30
	sla	a
	sla	a
	ld	(gbt_instr+0),a ; Instrument

	ld	a,b ; restore byte

	and	a,#0x0F ; a = effect

	call	gbt_channel_1_set_effect

	;jr	refresh_channel1_regs_trig$


	; fall through!

; -----------------

refresh_channel1_regs_trig$:

channel1_refresh_registers_trig:

	xor	a,a
	ld	(#.NR10),a
	ld	a,(gbt_instr+0)
	ld	(#.NR11),a
	ld	a,(gbt_vol+0)
	ld	(#.NR12),a
	ld	a,(gbt_freq+0*2+0)
	ld	(#.NR13),a
	ld	a,(gbt_freq+0*2+1)
	or	a,#0x80 ; start
	ld	(#.NR14),a

	ret

refresh_channel1_regs_notrig$:

channel1_refresh_registers_notrig:

	xor	a,a
	ld	(#.NR10),a
	ld	a,(gbt_instr+0)
	ld	(#.NR11),a
	ld	a,(gbt_freq+0*2+0)
	ld	(#.NR13),a
	ld	a,(gbt_freq+0*2+1)
	ld	(#.NR14),a

	ret

; ------------------

channel1_update_effects: ; returns 1 in a if it is needed to update sound registers

	; Cut note
	; --------

	ld	a,(gbt_cut_note_tick+0)
	ld	hl,#gbt_ticks_elapsed
	cp	a,(hl)
	jp	nz,ch1_dont_cut$

	dec	a ; a = 0xFF
	ld	(gbt_cut_note_tick+0),a ; disable cut note

	xor	a,a ; vol = 0
	ld	(#.NR12),a
	ld	a,#0x80 ; start
	ld	(#.NR14),a

ch1_dont_cut$:

	; Arpeggio or Sweep
	; --------

	ld	a,(gbt_arpeggio_enabled+0)
	and	a,a
	ret	z ; a is 0, return 0

	; Check if Sweep or Arpeggio (4-5 cycles)
	and a,#1
	jr z,gbt_ch1_sweep_run$

	; If enabled arpeggio, handle it

	ld	a,(gbt_arpeggio_tick+0)
	and	a,a
	jr	nz,ch1_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

ch1_not_tick_0$:

	cp	a,#1
	jr	nz,ch1_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

ch1_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 ; ret 1

gbt_ch1_sweep_run$:

	; PortA Pitch Sweep
	; -----------
	ld	hl,#(gbt_freq+0*2+0)
	ld	a,(gbt_sweep+0)
	bit 7,a ; bit 7, if nz, sweep up.
	jr  z,gbt_ch1_sweep_up$

	; Sweep down -
	sub	a,#0x80
	ld	b,a
	ld	a,(hl)		; Get frequency small (gbt_freq+0*2+0)
	sub	a,b			; subtract b from a
	ld	(hl+),a		; Set frequency small (gbt_freq+0*2+0)
	ld	a,#1
	jr	c,gbt_ch1_sweep_dec$
	ret				; ret 1, update without trigger
	; Sweep down --
gbt_ch1_sweep_dec$:
	dec	(hl)		; DEC frequency large (gbt_freq+0*2+1) 3cy
	ld	a,(hl)
	inc a			; find if decremented past 0 to exactly 255
	ret	nz			; ret/update unless 0
	ld	(hl-),a		; fix frequency large 0x0
	ld	(hl),a		; fix frequency small 0x0
	ld	(gbt_arpeggio_enabled+0),a	; disable sweep
	ret				; ret 0, no update

	; Sweep up +
gbt_ch1_sweep_up$:
	add	a,(hl)		; add frequency small (gbt_freq+0*2+0)
	ld	(hl+),a		; Set frequency small (gbt_freq+0*2+0)
	jr	c,gbt_ch1_sweep_inc$
	ld	a,#1
	ret				; ret 1, update without trigger
	; Sweep up ++
gbt_ch1_sweep_inc$:
	inc	(hl)		; inc frequency large (gbt_freq+0*2+1) 2cy
	ld	a,(hl-)
	and	a,#0x07		; check if wrapped to 0x08 00001000
	ret	nz			; ret/update unless 0
	ld	(gbt_arpeggio_enabled+0),a	; disable sweep
	ld	(#.NR12),a ; vol = 0
	ld	a,#0x80 ; start
	ld	(#.NR14),a
	ret 			; ret 0x80, update without trigger

; -----------------

; 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_ch1_sweep$
	.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_ch1_NRx2_VolEnv$

gbt_ch1_pan$:
	and	a,#0x11
	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,#0x0F
	add	a,c

	ld	(hl+),a ; save first increment

	ld	a,b
	and	a,#0x0F
	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

gbt_ch1_NRx2_VolEnv$:	; Raw data into volume, VVVV APPP, bits 4-7 vol
	ld	(gbt_vol+0),a	; bit 3 true = add, bits 0-2 wait period 
	xor	a,a	; ret 0		; 0xF1 = max volume, sub 1 every 1 tick.
	ret					; 0x0A = min volume, add 1 every 2 ticks.

gbt_ch1_sweep$:
	ld 	(gbt_sweep+0),a
	ld	a,#2
	ld	(gbt_arpeggio_enabled+0),a
	xor	a,a	; ret 0
	ret

;-------------------------------------------------------------------------------
;---------------------------------- Channel 2 ----------------------------------
;-------------------------------------------------------------------------------

gbt_channel_2_handle:: ; de = info

	ld	a,(gbt_channels_enabled)
	and	a,#0x02
	jr	nz,channel2_enabled$

	; Channel is disabled. Increment pointer as needed

	ld	a,(de)
	inc	de
	bit	7,a
	jr	nz,ch2_more_bytes$
	bit	6,a
	jr	z,ch2_no_more_bytes_this_channel$

	jr	ch2_one_more_byte$

ch2_more_bytes$:

	ld	a,(de)
	inc	de
	bit	7,a
	jr	z,ch2_no_more_bytes_this_channel$

ch2_one_more_byte$:

	inc	de

ch2_no_more_bytes_this_channel$:

	ret

channel2_enabled$:

	; Channel 2 is enabled

	ld	a,(de)
	inc	de

	bit	7,a
	jr	nz,ch2_has_frequency$

	; Not frequency

	bit	6,a
	jr	nz,ch2_instr_effects$

	; Set volume or NOP

	bit	5,a
	jr	nz,ch2_just_set_volume$

	; NOP

	ret

ch2_just_set_volume$:

	; Set volume

	and	a,#0x0F
	swap	a
	; Preserve envelope data
	ld	b,a			; save byte
	ld	a,(gbt_vol+1)
	and	a,#0x0F		; mask envelope
	or	a,b

	ld	(gbt_vol+1),a

	jr	refresh_channel2_regs_trig$

ch2_instr_effects$:

	; Set instrument and effect

	ld	b,a ; save byte

	and	a,#0x30
	sla	a
	sla	a
	ld	(gbt_instr+1),a ; Instrument

	ld	a,b ; restore byte

	and	a,#0x0F ; a = effect

	call	gbt_channel_2_set_effect

	and a,a
	ret	z ; if 0, don't refresh registers
	jr	refresh_channel2_regs_notrig$

ch2_has_frequency$:

	; Has frequency

	and	a,#0x7F
	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,ch2_freq_instr_and_effect$

	; Freq + Instr + Volume

	ld	b,a ; save byte

	and	a,#0x30
	sla	a
	sla	a
	ld	(gbt_instr+1),a ; Instrument

	ld	a,b ; restore byte

	and	a,#0x0F ; a = volume

	swap	a
	; Preserve envelope data
	ld	b,a			; save byte
	ld	a,(gbt_vol+1)
	and	a,#0x0F		; mask envelope
	or	a,b

	ld	(gbt_vol+1),a

	jr	refresh_channel2_regs_trig$

ch2_freq_instr_and_effect$:

	; Freq + Instr + Effect

	ld	b,a ; save byte

	and	a,#0x30
	sla	a
	sla	a
	ld	(gbt_instr+1),a ; Instrument

	ld	a,b ; restore byte

	and	a,#0x0F ; a = effect

	call	gbt_channel_2_set_effect

	;jr	.refresh_channel2_regs_trig

	; fall through!

; -----------------

refresh_channel2_regs_trig$:

channel2_refresh_registers_trig:

	ld	a,(gbt_instr+1)
	ld	(#.NR21),a
	ld	a,(gbt_vol+1)
	ld	(#.NR22),a
	ld	a,(gbt_freq+1*2+0)
	ld	(#.NR23),a
	ld	a,(gbt_freq+1*2+1)
	or	a,#0x80 ; start
	ld	(#.NR24),a

	ret

refresh_channel2_regs_notrig$:

channel2_refresh_registers_notrig:

	ld	a,(gbt_instr+1)
	ld	(#.NR21),a
	ld	a,(gbt_freq+1*2+0)
	ld	(#.NR23),a
	ld	a,(gbt_freq+1*2+1)
	ld	(#.NR24),a

	ret

; ------------------

channel2_update_effects: ; returns 1 in a if it is needed to update sound regs

	; Cut note
	; --------

	ld	a,(gbt_cut_note_tick+1)
	ld	hl,#gbt_ticks_elapsed
	cp	a,(hl)
	jp	nz,ch2_dont_cut$

	dec	a ; a = 0xFF
	ld	(gbt_cut_note_tick+1),a ; disable cut note

	xor	a,a ; vol = 0
	ld	(#.NR22),a
	ld	a,#0x80 ; start
	ld	(#.NR24),a

ch2_dont_cut$:

	; Arpeggio or Sweep
	; --------

	ld	a,(gbt_arpeggio_enabled+1)
	and	a,a
	ret	z ; a is 0, return 0

	; Check if Sweep or Arpeggio (5-6 cycles)
	and a,#1
	jr z,gbt_ch2_sweep_run$
	; If enabled arpeggio, handle it

	ld	a,(gbt_arpeggio_tick+1)
	and	a,a
	jr	nz,ch2_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

ch2_not_tick_0$:

	cp	a,#1
	jr	nz,ch2_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

ch2_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 ; ret 1

gbt_ch2_sweep_run$:

	; PortA Pitch Sweep
	; -----------
	ld	hl,#(gbt_freq+1*2+0)
	ld	a,(gbt_sweep+1)
	bit 7,a ; bit 7, if nz, sweep up.
	jr  z,gbt_ch2_sweep_up$

	; Sweep down -
	sub	a,#0x80
	ld	b,a
	ld	a,(hl)		; Get frequency small (gbt_freq+0*2+0)
	sub	a,b			; subtract b from a
	ld	(hl+),a		; Set frequency small (gbt_freq+0*2+0)
	ld	a,#1
	jr	c,gbt_ch2_sweep_dec$
	ret				; ret 1, update without trigger
	; Sweep down --
gbt_ch2_sweep_dec$:
	dec	(hl)		; DEC frequency large (gbt_freq+0*2+1) 3cy
	ld	a,(hl)
	inc a			; find if decremented past 0 to exactly 255
	ret	nz			; ret/update unless 0
	ld	(hl-),a		; fix frequency large 0x0
	ld	(hl),a		; fix frequency small 0x0
	ld	(gbt_arpeggio_enabled+1),a	; disable sweep
	ret				; ret 0, no update

	; Sweep up +
gbt_ch2_sweep_up$:
	add	a,(hl)		; add frequency small (gbt_freq+0*2+0)
	ld	(hl+),a		; Set frequency small (gbt_freq+0*2+0)
	jr	c,gbt_ch2_sweep_inc$
	ld	a,#1
	ret				; ret 1, update without trigger
	; Sweep up ++
gbt_ch2_sweep_inc$:
	inc	(hl)		; inc frequency large (gbt_freq+0*2+1) 2cy
	ld	a,(hl-)
	and	a,#0x07		; check if wrapped to 0x08 00001000
	ret nz			; ret/update unless 0
	ld	(gbt_arpeggio_enabled+1),a	; disable sweep
	ld	(#.NR22),a ; vol = 0
	ld	a,#0x80 ; start
	ld	(#.NR24),a
	ret 			; ret 0x80, update without trigger

; -----------------

; 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_ch2_sweep$
	.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_ch2_NRx2_VolEnv$

gbt_ch2_pan$:
	and	a,#0x22
	ld	(gbt_pan+1),a
	xor	a,a ; ret 0
	ret ; Should 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,#0x0F
	add	a,c

	ld	(hl+),a ; save first increment

	ld	a,b
	and	a,#0x0F
	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

gbt_ch2_NRx2_VolEnv$:	; raw volumeEnv, VVVV APPP, bits 7-4 vol
	ld	(gbt_vol+1),a	; bit 3 true = add, bits 2-0 wait period 
	xor	a,a	; ret 0		; 0xF1 = max volume, sub 1 every 1 tick.
	ret					; 0x0A = min volume, add 1 every 2 ticks.

gbt_ch2_sweep$:
	ld 	(gbt_sweep+1),a
	ld	a,#2
	ld	(gbt_arpeggio_enabled+1),a
	xor	a,a	; ret 0
	ret

;-------------------------------------------------------------------------------
;---------------------------------- Channel 3 ----------------------------------
;-------------------------------------------------------------------------------

gbt_channel_3_handle:: ; de = info

	ld	a,(gbt_channels_enabled)
	and	a,#0x04
	jr	nz,channel3_enabled$

	; Channel is disabled. Increment pointer as needed

	ld	a,(de)
	inc	de
	bit	7,a
	jr	nz,ch3_more_bytes$
	bit	6,a
	jr	z,ch3_no_more_bytes_this_channel$

	jr	ch3_one_more_byte$

ch3_more_bytes$:

	ld	a,(de)
	inc	de
	bit	7,a
	jr	z,ch3_no_more_bytes_this_channel$

ch3_one_more_byte$:

	inc	de

ch3_no_more_bytes_this_channel$:

	ret

channel3_enabled$:

	; Channel 3 is enabled

	ld	a,(de)
	inc	de

	bit	7,a
	jr	nz,ch3_has_frequency$

	; Not frequency

	bit	6,a
	jr	nz,ch3_effects$

	; Set volume or NOP

	bit	5,a
	jr	nz,ch3_just_set_volume$

	; NOP

	ret

ch3_just_set_volume$:

	; Set volume

	swap	a
	ld	(gbt_vol+2),a

	jr	refresh_channel3_regs_trig$

ch3_effects$:

	; Set effect

	and	a,#0x0F ; a = effect

	call	gbt_channel_3_set_effect
	and	a,a
	ret	z ; if 0, don't refresh registers

	jr	refresh_channel3_regs_notrig$

ch3_has_frequency$:

	; Has frequency

	and	a,#0x7F
	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,ch3_freq_instr_and_effect$

	; Freq + Instr + Volume

	ld	b,a ; save byte

	and	a,#0x0F
	ld	(gbt_instr+2),a ; Instrument

	ld	a,b ; restore byte

	and	a,#0x30 ; a = volume
	sla	a
	ld	(gbt_vol+2),a

	jr	refresh_channel3_regs_trig$

ch3_freq_instr_and_effect$:

	; Freq + Instr + Effect

	ld	b,a ; save byte

	and	a,#0x0F
	ld	(gbt_instr+2),a ; Instrument

	ld	a,b ; restore byte

	and	a,#0x70
	swap	a	; a = effect (only 0-7 allowed here)

	call	gbt_channel_3_set_effect

	;jr	.refresh_channel3_regs

	; fall through!

; -----------------
refresh_channel3_regs_trig$:

channel3_refresh_registers_trig:

	xor	a,a
	ld	(#.NR30),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,#0x80
	ld	(#.NR30),a ; enable

	xor	a,a
	ld	(#.NR31),a
	ld	a,(gbt_vol+2)
	ld	(#.NR32),a
	ld	a,(gbt_freq+2*2+0)
	ld	(#.NR33),a
	ld	a,(gbt_freq+2*2+1)
	or	a,#0x80 ; start
	ld	(#.NR34),a

	ret

refresh_channel3_regs_notrig$:
	; Don't Restart Waveform!
channel3_refresh_registers_notrig:

	ld	a,(gbt_freq+2*2+0)
	ld	(#.NR33),a
	ld	a,(gbt_freq+2*2+1)
	ld	(#.NR34),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,#0x30
	ld	b,#16
ch3_loop$:
	ld	a,(hl+)
	ldh	(c),a
	inc	c
	dec	b
	jr	nz,ch3_loop$

	ret

; ------------------

channel3_update_effects: ; returns 1 in a if it is needed to update sound regs

	; Cut note
	; --------

	ld	a,(gbt_cut_note_tick+2)
	ld	hl,#gbt_ticks_elapsed
	cp	a,(hl)
	jp	nz,ch3_dont_cut$

	dec	a ; a = 0xFF
	ld	(gbt_cut_note_tick+2),a ; disable cut note

	xor	a,a ; vol = 0
	ld	(#.NR30),a ; disable
	ld	(#.NR32),a
	ld	a,#0x80 ; start
	ld	(#.NR34),a

ch3_dont_cut$:

	; Arpeggio or Sweep
	; --------

	ld	a,(gbt_arpeggio_enabled+2)
	and	a,a
	ret	z ; a is 0, return 0

	; Check if Sweep or Arpeggio (5-6 cycles)
	and a,#1
	jp z,gbt_ch3_sweep_run$

	; If enabled arpeggio, handle it

	ld	a,(gbt_arpeggio_tick+2)
	and	a,a
	jr	nz,ch3_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

ch3_not_tick_0$:

	cp	a,#1
	jr	nz,ch3_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

ch3_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

gbt_ch3_sweep_run$:

	; PortA Pitch Sweep
	; -----------
	ld	hl,#(gbt_freq+2*2+0)
	ld	a,(gbt_sweep+2)
	bit 7,a ; bit 7, if nz, sweep up.
	jr  z,gbt_ch3_sweep_up$

	; Sweep down -
	sub	a,#0x80
	ld	b,a
	ld	a,(hl)		; Get frequency small (gbt_freq+0*2+0)
	sub	a,b			; subtract b from a
	ld	(hl+),a		; Set frequency small (gbt_freq+0*2+0)
	ld	a,#1
	jr	c,gbt_ch3_sweep_dec$
	ret				; ret 1, update without trigger
	; Sweep down --
gbt_ch3_sweep_dec$:
	dec	(hl)		; DEC frequency large (gbt_freq+0*2+1) 3cy
	ld	a,(hl)
	inc a			; find if decremented past 0 to exactly 255
	ret	nz			; ret/update unless 0
	ld	(hl-),a		; fix frequency large 0x0
	ld	(hl),a		; fix frequency small 0x0
	ld	(gbt_arpeggio_enabled+2),a	; disable sweep
	ret				; ret 0, no update

	; Sweep up +
gbt_ch3_sweep_up$:
	add	a,(hl)		; add frequency small (gbt_freq+0*2+0)
	ld	(hl+),a		; Set frequency small (gbt_freq+0*2+0)
	jr	c,gbt_ch3_sweep_inc$
	ld	a,#1
	ret				; ret 1, update without trigger
	; Sweep up ++
gbt_ch3_sweep_inc$:
	inc	(hl)		; inc frequency large (gbt_freq+0*2+1) 2cy
	ld	a,(hl-)
	and	a,#0x07		; check if wrapped to 0x08 00001000
	ret	nz			; ret/update unless 0
	ld	(gbt_arpeggio_enabled+2),a	; disable sweep
	ld	(#.NR32),a ; vol = 0
	ld	a,#0x80 ; start
	ld	(#.NR34),a
	ret 			; ret 1, update without trigger

; -----------------

; 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_ch3_sweep$
	.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,#0x44
	ld	(gbt_pan+2),a
	xor	a,a ; ret 0
	ret ; 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,#0x0F
	add	a,c

	ld	(hl+),a ; save first increment

	ld	a,b
	and	a,#0x0F
	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

gbt_ch3_sweep$:
	ld 	(gbt_sweep+2),a
	ld	a,#2
	ld	(gbt_arpeggio_enabled+2),a
	xor	a,a	; ret 0
	ret

;-------------------------------------------------------------------------------
;---------------------------------- Channel 4 ----------------------------------
;-------------------------------------------------------------------------------

gbt_channel_4_handle:: ; de = info

	ld	a,(gbt_channels_enabled)
	and	a,#0x08
	jr	nz,channel4_enabled$

	; Channel is disabled. Increment pointer as needed

	ld	a,(de)
	inc	de
	bit	7,a
	jr	nz,ch4_more_bytes$
	bit	6,a
	jr	z,ch4_no_more_bytes_this_channel$

	jr	ch4_one_more_byte$

ch4_more_bytes$:

	ld	a,(de)
	inc	de
	bit	7,a
	jr	z,ch4_no_more_bytes_this_channel$

ch4_one_more_byte$:

	inc	de

ch4_no_more_bytes_this_channel$:

	ret

channel4_enabled$:

	; Channel 4 is enabled

	ld	a,(de)
	inc	de

	bit	7,a
	jr	nz,ch4_has_instrument$

	; Not instrument

	bit	6,a
	jr	nz,ch4_effects$

	; Set volume or NOP

	bit	5,a
	jr	nz,ch4_just_set_volume$

	; NOP

	ret

ch4_just_set_volume$:

	; Set volume

	and	a,#0x0F
	swap	a
	; Preserve envelope data
	ld	b,a			; save byte
	ld	a,(gbt_vol+3)
	and	a,#0x0F		; mask envelope
	or	a,b

	ld	(gbt_vol+3),a

	jr	refresh_channel4_regs$

ch4_effects$:

	; Set effect

	and	a,#0x0F ; a = effect

	call	gbt_channel_4_set_effect
	and	a,a
	ret	z ; if 0, don't refresh registers

	jr	refresh_channel4_regs$

ch4_has_instrument$:

	; Has instrument raw frequency data

	and	a,#0x7F ; mask out bit 7
	ld	b,a

	ld	a,(de)	; load next byte
	inc	de
	ld	c,a
	rla
	and a,#0x80	; Mask only bit 7
	or	a,b		; Append noise bit
	ld	(gbt_instr+3),a
	ld	a,c		; restore byte2
	bit	7,a
	jr	nz,ch4_instr_and_effect$

	; Instr + Volume

	and	a,#0x0F ; a = volume

	swap	a
	; Preserve envelope data
	ld	b,a			; save byte
	ld	a,(gbt_vol+3)
	and	a,#0x0F		; mask envelope
	or	a,b

	ld	(gbt_vol+3),a

	jr	refresh_channel4_regs$

ch4_instr_and_effect$:

	; Instr + Effect

	and	a,#0x0F ; a = effect

	call	gbt_channel_4_set_effect

	;jr	ch4_refresh_channel4_regs$

refresh_channel4_regs$:

	; fall through!

; -----------------

channel4_refresh_registers:

	xor	a,a
	ld	(#.NR41),a
	ld	a,(gbt_vol+3)
	ld	(#.NR42),a
	ld	a,(gbt_instr+3)
	ld	(#.NR43),a
	ld	a,#0x80 ; start
	ld	(#.NR44),a

	ret

; ------------------

channel4_update_effects: ; returns 1 in a if it is needed to update sound regs

	; Cut note
	; --------

	ld	a,(gbt_cut_note_tick+3)
	ld	hl,#gbt_ticks_elapsed
	cp	a,(hl)
	jp	nz,ch4_dont_cut$

	dec	a ; a = 0xFF
	ld	(gbt_cut_note_tick+3),a ; disable cut note

	xor	a,a ; vol = 0
	ld	(#.NR42),a
	ld	a,#0x80 ; start
	ld	(#.NR44),a

ch4_dont_cut$:

	xor	a,a
	ret ; a is 0, return

; -----------------

; 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_ch4_NRx2_VolEnv$

gbt_ch4_pan$:
	and	a,#0x88
	ld	(gbt_pan+3),a
	xor	a,a ; ret 0
	ret ; 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

gbt_ch4_NRx2_VolEnv$:	; Raw data into volume, VVVV APPP, bits 4-7 vol
	ld	(gbt_vol+3),a	; bit 3 true = add, bits 0-2 wait period 
	xor	a,a	; ret 0		; 0xF1 = max volume, sub 1 every 1 tick.
	ret					; 0x0A = min volume, add 1 every 2 ticks.

;-------------------------------------------------------------------------------

; 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 ; tell gbt_player.s to do this next cycle
	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	(#.NR51),a ; handle panning...

	ret

;-------------------------------------------------------------------------------

gbt_update_effects_bank1::

	call	channel1_update_effects
	and	a,a
	call	nz,channel1_refresh_registers_notrig

	call	channel2_update_effects
	and	a,a
	call	nz,channel2_refresh_registers_notrig

	call	channel3_update_effects
	and	a,a
	call	nz,channel3_refresh_registers_notrig

	call	channel4_update_effects
	and	a,a
	call	nz,channel4_refresh_registers

	ret

;-------------------------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment