;
; The Ultimate Lieves!Tuore Music Player
; for MSX computers
;
;   version 1.1
;
; coded by Yzi/L!T  2-8th April 1997
; updated by Yzi in July 2008
;
; *** CHANGE HISTORY ***
;
;   1997-08-12: V1.01: English fix & clean-up
; 
;   2008-07-24: V1.1: Added transposing for song order (TRACK_TRANSPOSE)
;               and parameter for instruments to be transposing/non-transposing
;               - Added track and pattern "sync" commands     
;   2008-07-26: - Code reorganization: inlined update_vibrato etc.
;               - Removed PSG IO port commands from inside process_channel. Now 
;                 channel tuning and volume values are written to channel_tuning
;                 and channel_volume variables, and PSG IO outs are done in the
;                 main player function. 
;   2008-07-28: - Added an echo feature from channel 2 to channel 3.
;
;
; "This is one of these funny projects hmm."
;                            -Marq
;
; features:
;       - 3 channels
;       - adsr -envelopes
;       - 16 instruments
;             - adsr, volume, arpeggio, noise/tone, slide
;       - over 5 octaves (64 notes)
;       - tracks and patterns
;       - small
;       - commands & misc.
;             - slide up&down
;             - delay
;             - portamento
;             - vibrato
;             - track loop
;             - tempo
;             - transposing
;       - developed with MSX emulators
;       - my very first Z80 program
;             - probably a lot of interesting code
;
; future improvements:
;       - PSG envelopes
;       - Ring buffer based delay line...
;

    .area _CODE

	
; PSG IO addresses
PSG_IO_PORT_REGISTER_SELECT = 0xA0
PSG_IO_PORT_DATA_WRITE = 0xA1
PSG_IO_PORT_DATA_READ = 0xA2
PSG_REG_CH_A_FREQ_FINE = 0
PSG_REG_CH_A_FREQ_COARSE = 1
PSG_REG_CH_B_FREQ_FINE = 2
PSG_REG_CH_B_FREQ_COARSE = 3
PSG_REG_CH_C_FREQ_FINE = 4
PSG_REG_CH_C_FREQ_COARSE = 5
PSG_REG_NOISE_PERIOD = 6
PSG_REG_MIXER = 7
PSG_REG_CH_A_VOLUME = 8
PSG_REG_CH_B_VOLUME = 9
PSG_REG_CH_C_VOLUME = 10
PSG_REG_ENVELOPE_FREQ_FINE = 11
PSG_REG_ENVELOPE_FREQ_COARSE = 12
PSG_REG_ENVELOPE_SHAPE = 13

; .db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
tempo_table_1:
.db 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
tempo_table_2:
.db 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30
tempo_table_3:
.db 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45
tempo_table_4:
.db 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60
tempo_table_5:
.db 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75
tempo_table_6:
.db 0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90
tempo_table_7:
.db 0, 7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 98, 105


;----------offsets to channel parameters
CHN_INSTRUMENT  =0
CHN_SONGPTR     =2
CHN_PATTERNPTR  =4
CHN_FINE        =6
CHN_COARSE      =7
CHN_TARGET_FINE =8
CHN_TARGET_COARSE =9
CHN_LEVEL       =10
;in which part of ADSR we're going
CHN_ADSR        =11
CHN_ARPIDX      =12
CHN_TEMPO       =13
CHN_DELAY       =14
CHN_STOPPED     =15
CHN_NOTE        =16
; delta tuning
CHN_DELTA_FINE  =17
CHN_DELTA_COARSE =18
CHN_VIBIDX      =19
CHN_VIBSPD      =20
; the loop point
CHN_REPEAT      =21
CHN_REPEAT_PTR  =22
; pointer to tunings table
; (if I implement a transpose command in the future)
CHN_NOTE_TABLE  =24
CHN_SLIDE_FINE  =26
CHN_SLIDE_COARSE =27
CHN_ECHO_ON		=28

;-------offsets to instrument parameters
INS_LEVEL       =0
INS_ATTACK      =1
INS_DECAY       =2
INS_SUSTAIN     =3
INS_RELEASE     =4
INS_MODE        =5
; the upper four bits of arplen is the upper four bits of slide (if 12 bit slide compiler define is used)
INS_ARPLEN      =6
INS_SLIDE       =7
INS_ARPLOOPSTART =8
INS_ARPEGGIO    =9
INS_TRANSPOSING =15

; commands in song order
TRACK_STOP      =255
TRACK_SETREPEAT =254
TRACK_REPEAT    =253
TRACK_TRANSPOSE = 252  ; this is a track command: after the command there's a new value for CHN_NOTE_TABLE pointer (16 bit word)
TRACK_SYNC      = 251 ; sync commmand: increase sync variable

LASTPATT        =((lastp-pattern_addr)/2)

; command names
.include "commands.inc"

; ******* variables *********
channel:        .db     0  ; channel number (0-2)

; process_channel sets these, and the main routine (which calls process_channel three times) reads
; the variables and performs PSG IO outs
channel_tuning:	.dw		0  ; processed channel's tuning value for PSG
channel_volume: .db     0  ; processed channel's volume for PSG
channel_a_mixer: .db	9
channel_b_mixer: .db	9
channel_c_mixer: .db	9
channel_mixer:  .db		0  ; processed channel's unshifted mixer bits (bit 0 and bit 3, possible values 0,1,8,9)   

save_hl:        .dw     0
chn_insptr:     .dw     instruments
chn_ptr:		.dw		0
tempword:       .dw     0
tempword2:      .dw     0

; ******* SYNC VARIABLES ******
; These variables are meant to be read and written by external music sync code.
; Track and pattern "sync" commands INCREASE the corresponding variables by one,
; so the music sync code can do something nice when it sees one of these has been
; increased. You need to add track/pattern sync commands to the places you want.
_music_sync_a::       .db     0 ; pattern sync variable "a"
_music_sync_b::       .db     0 ; pattern sync variable "b"
_music_sync_t::       .db     0 ; track sync variable


; ***** MUTE (ON/OFF) SWITCHES FOR CHANNELS *****
; The PSG mixer byte (register 7) is ORed with this before OUTing it. 
; FOR INTERACTIVE USER INTERFACE ONLY
_music_mixer_mute::	.db		0
; Volume bit masks for channels. Write zeros to these to make volumes zero.
_music_channel_a_volume_mask:: .db 15
_music_channel_b_volume_mask:: .db 15
_music_channel_c_volume_mask:: .db 15

; *** Tuning values for channels. FOR INTERACTIVE USER INTERFACE ONLY
_music_channel_0_tuning:: .dw 0
_music_channel_1_tuning:: .dw 0
_music_channel_2_tuning:: .dw 0

; ------------ the tuning table -----------
.include "msxfreq.inc"


; ----------- SET CHANNEL VOLUME -------------
; sets volume [a] on channel [channel]
; volume is 4.4 bit fixed point number
;
; destroys registers a, b
;set_channel_volume:
;        ld      b, a
;        ld      a, (channel)     ; PSG reg. 8-10: volume ch. 0-2
;	add	a, #8
;	out	(0xa0), a
;	ld	a, b
;	srl	a
;	srl	a
;	srl	a
;       srl     a               ; take the 4 upper bits
;	out	(0xa1), a
;	ret

; ----------- SET CHANNEL TUNING -------------
; sets [channel]
; frequency [b,c]  b=coarse, c=fine
;
; destroys a, d
;set_channel_tuning:
;        ld
;
;        ld      a, (channel)     ; PSG reg. 0-5: tuning ch. 0-2
;        sla     a               ; a=channel*2
;        ld      d, a            ; channel*2 talteen
;        out     (PSG_IO_PORT_REGISTER_SELECT), a       ; fine freq.
;	ld	a, c
;	out	(PSG_IO_PORT_DATA_WRITE), a
;	ld	a, d
;        inc     a               ; a=channel*2+1
;	out	(PSG_IO_PORT_REGISTER_SELECT), a	; coarse, PSG reg (ch+1)
;	ld	a, b
;	out	(PSG_IO_PORT_DATA_WRITE), a
;	ret

; --- iy := 2 * accumulator
iy_2a: ; just a little routine...
        sla     a       ; multiply by two (there's 16 bits/tuning)
        ld      (tempword), a
        sub     a
        ld      (tempword+1), a
        ld      iy, (tempword)
        ret

; ----------- SET NOTE -------------------
; FUNCTION: sets the frequency of the note in accumulator to current channel
; PARAMETERS:  ix = current channel
;              a = note number
; DESTROYS REGISTERS:  a, b, c, d, iy
set_note:
        ld      iy, (chn_insptr) 
		
; ---------- GET NOTE FREQUENCY ---------------
; FUNCTION: gets the frequency of the note in accumulator to bc
; PARAMETERS:   a = note number
;               ix = current channel
;               iy = current instrument
; DESTROYS REGISTERS:  a, b, c, iy
; get_note_frequency:
        ld      b, a ; save note number because we need accumulator for testing conditional jump
        ld      a, INS_TRANSPOSING(iy)
		cp      #1
		jp      z, do_transpose
dont_transpose: ; hard-coded table address for non-transposing instruments       
        ld      a, b
		ld      bc, #tunings
		jp      tuning_table_set_to_bc
; Transpose: note number is relative to CHN_NOTE_TABLE (which is #tunings, if not transposing)
do_transpose:
		ld      a, b
		ld      c, CHN_NOTE_TABLE(ix)
		ld      b, CHN_NOTE_TABLE+1(ix)
				
tuning_table_set_to_bc:		
        call    iy_2a  ; iy := 2 * a
        
		; fetch tuning values from table

        add     iy, bc          ;iy=offset tunings + note * 2
        ld      a, (iy)         ;bc=tuning (12 bit)
        add     a, CHN_DELTA_FINE(ix)
        ld      c, a
        ld      a, 1(iy)
        adc     a, CHN_DELTA_COARSE(ix)
        ld      b, a
; end - get_note_frequency         ---- destroys iy!

; ---------- ADD_VIBRATO_FREQUENCY ---------------
; adds vibrato frequency to  bc
; destroy a, b, c, iy
;add_vibrato_frequency:
        ld      a, CHN_VIBSPD(ix)
        cp      #0
        jp      z, vibrato_is_not_on_now
        ld      a, CHN_VIBIDX(ix)
        call    iy_2a ; iy := 2 * a
        ld      de, #vibrato_table
        add     iy, de          ;iy=offset vibrato_table + vibidx * 2
        ld      a, c
        add     a, (iy)
        ld      c, a
        ld      a, b
        adc     a, 1(iy)
        ld      b, a
vibrato_is_not_on_now:
; ---- end - add_vibrato_frequency      ---- destroys iy!
        ; call    set_channel_tuning
		ld      (channel_tuning), bc
        ret
; end - set_note


; .include "mul4.inc"


; --------------------------------------------------------------------------
; ---------------- PROCESS CHANNEL -----------------------------------------
; --------------------------------------------------------------------------
; IX points to channel parameters
; destroys all registers except SP
; 
save_sp_temp: .dw 0
process_channel_with_echo: ; here's the echo version... echo is checked first and if no echo is on
		ld		a, CHN_ECHO_ON(ix)   ; is echo on? 
		cp		#0
		jr		z, process_channel
		dec		a
		ld		CHN_ECHO_ON(ix), a

; ------ DO ECHO! -----		
;do_echo: ; we also come here in tick 0!

		ld		(save_sp_temp), sp
		ld		hl, (echo_read_pos)
		ld		sp, hl
		pop		hl
		ld		bc, #65534 ; tweak echo sound's tuning down a little bit... hopefully this creates a bit of chorusing
		add		hl, bc
		ld		(channel_tuning), hl

		pop		bc ; read volume from echo buffer
		ld		a, c
		ld		(channel_mixer), a
		ld		a, b
		sub		#2
		jr		nc, ok_no_overflow
		sub		a
ok_no_overflow:
        ld		(channel_volume), a
		ld		sp, (save_sp_temp)

		ld		a, #8 ; by default nothing is played on the channel
		ld		(channel_mixer), a
        ret     ; return from process_channel !!


process_channel:
;		ld		a, #9 ; by default nothing is played on the channel
;		ld		(channel_mixer), a
		
		ld      a, CHN_STOPPED(ix)
        cp      #1
        jp      nz, im_still_standing_yeah_yeah_yeah
        ret
im_still_standing_yeah_yeah_yeah:

; let's read the current instrument pointer to iy
        ld      l, (ix)         ; iy := (ix+0) (channel's instrument)
        ld      h, 1(ix)
        ld      (chn_insptr), hl   ; save it
        ld      iy, (chn_insptr)

; decrease delay, and if not zero, wait
        dec     CHN_DELAY(ix)
        jp      nz, lets_wait

; no more delay -> go on with the show
to_be_continued:
        ld      iy, (chn_insptr)   ; note! set_instr sets this also
        ld      l, CHN_PATTERNPTR(ix)    ; patternpointer from channel
        ld      h, CHN_PATTERNPTR+1(ix)  ; parameters
        ld      a, (hl) ; read the command
        ld      c, a    ; put it also to register c
        and     #192     ; take bits 6 and 7
        cp      #0       ; look at two upper bits
        jp      z, it_is_note
        cp      #64
        jp      z, it_is_portamento
        cp      #128 
        jp      z, it_is_echo
        cp      #192
        jp      z, it_is_command
        ; no other alternatives

lets_wait: ; --- CHN_DELAY was > 0 ---
	
; ------------ UPDATE VIBRATO -----------------
; destroys a
;update_vibrato:
		ld		hl, (chn_ptr)
		ld		de, #CHN_VIBIDX
		add		hl, de
        ld      a, (hl)		; vibidx = (vibidx+vibspd) AND 63
		inc		hl			; hl -> ix[CHN_VIBSPD]
        add     a, (hl)      
        and     #63
		dec		hl
        ld      (hl), a
; ---- end update vibrato ----

; ------ UPDATE INS SLIDE -------------------
; destroys a, b, c, hl
;update_ins_slide:
        ld      a, INS_SLIDE(iy)
        cp      #0
        jp      z, no_ins_slide
        ld      l, CHN_DELTA_FINE(ix)
        ld      h, CHN_DELTA_COARSE(ix)
        ld      c, a
; I thought that somebody would want 12 bit slides
;.if SLIDE_12BIT
;        ld      b, INS_ARPLEN(iy)
;        sra     b
;        sra     b
;        sra     b
;        sra     b
;.else
        sub     a
        ld      b, a
;.endif
        add     hl, bc
        ld      CHN_DELTA_FINE(ix), l
        ld      CHN_DELTA_COARSE(ix), h        ; delta_tuning += ins_slide
no_ins_slide:
; ------ end update_ins_slide

; ------ UPDATE SLIDE -------------------
; destroys a, b, c, hl
; update_slide:
        ld      a, CHN_SLIDE_FINE(ix)
        cp      #0
        jp      z, ei_mitaan_slidea
        ld      l, CHN_DELTA_FINE(ix)
        ld      h, CHN_DELTA_COARSE(ix)
        ld      c, a
        ld      b, CHN_SLIDE_COARSE(ix)
        add     hl, bc
        ld      CHN_DELTA_FINE(ix), l
        ld      CHN_DELTA_COARSE(ix), h        ; delta_tuning += chn_slide
ei_mitaan_slidea:
; ------- end update slide

; ------ UPDATE ARPEGGIO ---------------
; Goes forward with arpeggio and sets
; the correct note for the channel.
; destroys  a, b, c, d, hl, iy
; update_arpeggio:
        ld      a, CHN_ARPIDX(ix)
        inc     a
        cp      INS_ARPLEN(iy)
        jp      nz, ohi_tuskaista
        ld		a, INS_ARPLOOPSTART(iy)
ohi_tuskaista:
        ld      CHN_ARPIDX(ix), a

        ld      c, a
        sub     a
        ld      b, a    ; bc = offset to ins_arpeggio table

        add     iy, bc
        ld      a, CHN_NOTE(ix)
        add     a, INS_ARPEGGIO(iy)
        call    set_note ; destroys iy !
; --------- end update_arpeggio      DESTROYS IY !!!
		
waitywait: ; we come here also from tick 0, ie. arpeggio is not updated
          ; at tick 0, but adsr is (to prevent from getting a silent moment)
        ld      iy, (chn_insptr)
update_adsr:
; --------------- UPDATE ADSR ------------------
; IX:channel
; IY:instrument
        ld      a, CHN_ADSR(ix) ; ADSR phase
        cp      #0
        jp      nz, not_attack
; ---------- attack phase --------------
        ld      a, CHN_LEVEL(ix)
        add     a, INS_ATTACK(iy) ;overflow?
        jp      nc, n1ce
        ld      a, INS_LEVEL(iy) ;yes - set correct level.
        inc     CHN_ADSR(ix)
        jp      ookkei
n1ce:   cp      INS_LEVEL(iy) ;ins_level greater than accumulator(=chn_level)?
        jp      c, ookkei
        ;accumulator is greater - set ins_level and proceed with adsr
        ld      a, INS_LEVEL(iy)
        inc     CHN_ADSR(ix)
        jp      ookkei
not_attack:
        cp      #1 ; onko CHN_ADSR 1?
        jp      nz, not_decay
; ---------- decay phase ---------------
        ld      a, CHN_LEVEL(ix)
        sub     INS_DECAY(iy)
        jp      nc, n1ce2 ; did it go below zero?
        ld      a, INS_SUSTAIN(iy)
        inc     CHN_ADSR(ix)
        jp      ookkei
n1ce2:  cp      INS_SUSTAIN(iy)
        jp      nc, ookkei ; is ins_sustain less than chn_level?
        ; it went too high - set ins_sustain and proceed adsr
        ld      a, INS_SUSTAIN(iy)
        inc     CHN_ADSR(ix)
        jp      ookkei
not_decay:
        cp      #2 ; onko CHN_ADSR 2?
        jp      nz, not_sustain
; --- sustain phase (do nothing) --
        ld		a, CHN_LEVEL(ix)
		jp      ookkei
not_sustain:
        cp      #3 ; onko CHN_ADSR 3? (release)
        jp      nz, not_release
; release phase (decrease level to zero)
        ld      a, CHN_LEVEL(ix)
        sub     INS_RELEASE(iy)
        jp      nc, ookkei ; did it overflow?
        sub     a ; overflow, a:=0
        inc     CHN_ADSR(ix) ; forwards, CHN_ADSR := 4  (end)
        jp      ookkei
not_release: ; end of adsr, do nothing
; ŠŠŠ       jp		joojoo
ookkei: ld      CHN_LEVEL(ix), a
joojoo: 
		srl		a
		srl		a
		srl		a
		srl     a               ; take the 4 upper bits
		ld		(channel_volume), a
; ------ end update_adsr

        ret     ; return from process_channel !!
		
		
; ----------------------------------------

lets_see_now: ; ----- the command has now been processed ----
        ; not finished until CHN_DELAY > 0
        ld      a, CHN_DELAY(ix)
        cp      #0
        jp      z, to_be_continued   ; if CHN_DELAY = 0, jump
        jp      waitywait ; otherweise wait

; -------- NEW NOTE -------
it_is_note: ; two highest bits 00
        ld      a, c    ; note number is in accumulator
        ld      CHN_NOTE(ix), a        ; save note for arpeggio

; next_byte: ; increases hl and sets a new value to (ix+CHN_PATTERNPTR)
        inc     hl
        ld      CHN_PATTERNPTR(ix), l
        ld      CHN_PATTERNPTR+1(ix), h

        ld      a, (hl)
        ld      (save_hl), hl   ; set_channel_instrument destroys hl
        and     #240     ; four highest bits = instrument
        ; acc. doesn't need to be shifted
		
; -------------- SET CHANNEL INSTRUMENT ---------------
; sets instrument in a to current channel (the number is in
; the UPPER FOUR BITS of accumulator)
; IX points to start of channel parameters
; sets HL and IY to the instrument
set_channel_instrument:
        ld      c, a
        ld      b, #0
        ld      hl, #instruments
        add     hl, bc  ; hl now points to current instrument
        ld      (ix), l
        ld      1(ix), h
        ld      (chn_insptr), hl
        ld      iy, (chn_insptr)

; ---------- CLEAR ----------
        ; clear channel parameters
        sub     a ; a := 0
; clear_everything:
        ld      CHN_ADSR(ix), a
        ld      CHN_LEVEL(ix), a
        ld      CHN_ARPIDX(ix), a
        ld      CHN_VIBIDX(ix), a
        ld      CHN_VIBSPD(ix), a
; clear_delta_and_slide:
        ld      CHN_DELTA_FINE(ix), a
        ld      CHN_DELTA_COARSE(ix), a
        ld      CHN_SLIDE_FINE(ix), a
        ld      CHN_SLIDE_COARSE(ix), a
; ---- end - clear

; noise/tone enable?
        ld      a, INS_MODE(iy)
;        ld      b, #9    ; b = mask
;        and     b
		ld		(channel_mixer), a
;        ld      c, a    ; c = noise&tone enable

;        ld      a, #7 ; mixer
;        out     (0xA0), a

;        ld      a, (channel)
;        cp      #0
;        jp      z, ouagadougou
;        sla     c
;        sla     b
;        cp      #1
;        jp      z, ouagadougou
;        sla     c
;        sla     b
;ouagadougou:
;        ld      a, b
;        xor     #255
;        ld      b, a
;        in      a, (0xA2)
;        and     b
;        xor     c

;        out     (0xA1), a

; ------- end - set_channel_instrument
        ld      hl, (save_hl)

note_set: ; we may jump here from portamento command
        ld      a, (hl)
        and     #15      ; 4 lower bits = delay
; ---------- SET DELAY
; sets the delay in accumulator
; CHN_DELAY = CHN_TEMPO * a
; destroys a, b, c
;set_delay:
;        and     #15
		ld		(save_hl), hl
		ld		hl, #tempo_table_3 ; hard coded tempo...
        ld      c, a
		ld		b, #0
		add		hl, bc
		ld		a, (hl)
;        ld      b, CHN_TEMPO(ix)
        ld      CHN_DELAY(ix), a
		ld		hl, (save_hl)
;        ret

        ; the note has to be set after instrument, because
        ; we need the instrument info there
        ld      a, CHN_NOTE(ix)
        call    set_note
        ; iy is now destroyed

        ; after processing hl points to a new byte
; next_byte: ; increases hl and sets a new value to (ix+CHN_PATTERNPTR)
        inc     hl
        ld      CHN_PATTERNPTR(ix), l
        ld      CHN_PATTERNPTR+1(ix), h

        jp      lets_see_now
		
; -------- PORTAMENTO --------
it_is_portamento: ; just a simple portamento
                  ; sets note but not instrument
        ld      a, c
        and     #63
        ld      CHN_NOTE(ix), a
        sub     a

; clear_delta_and_slide:
        ld      CHN_DELTA_FINE(ix), a
        ld      CHN_DELTA_COARSE(ix), a
        ld      CHN_SLIDE_FINE(ix), a
        ld      CHN_SLIDE_COARSE(ix), a

; next_byte: ; increases hl and sets a new value to (ix+CHN_PATTERNPTR)
        inc     hl
        ld      CHN_PATTERNPTR(ix), l
        ld      CHN_PATTERNPTR+1(ix), h

        jp      note_set

; -------- ECHO --------
it_is_echo: ; start an echo
        ld      a, c
        and     #15
; set delay for echo
		ld		(save_hl), hl
		ld		hl, #tempo_table_3 ; hard coded tempo...
        ld      c, a
		ld		b, #0
		add		hl, bc
		ld		a, (hl)
		ld		hl, (save_hl)
		
        ld      CHN_ECHO_ON(ix), a
		ld		a, #1
		ld      CHN_DELAY(ix), a

; next_byte: ; increases hl and sets a new value to (ix+CHN_PATTERNPTR)
        inc     hl
        ld      CHN_PATTERNPTR(ix), l
        ld      CHN_PATTERNPTR+1(ix), h
		jp		process_channel_with_echo

; ---------- COMMAND ---------
it_is_command: ; the byte specifiec is a command (non-note)
        ld      a, c
        cp      #NOTE_OFF
        jp      z, do_note_off
        cp      #NOTE_OFF_VOL
        jp      z, do_note_off_vol
        cp      #DELAY
        jp      z, do_delay
        cp      #VIBRATO
        jp      z, do_vibrato
        cp      #SLIDE_UP
        jp      z, do_slide_up
        cp      #SLIDE_DOWN
        jp      z, do_slide_down
		cp      #SYNC_A
		jp      z, do_sync_a
		cp      #SYNC_B
		jp      z, do_sync_b
        cp      #END_PATTERN
        jp      z, end_pattern
		cp		#CHANGE_TEMPO
		jp		z, do_tempo
it_has_been_dealt_with: ; <--- we come here from the command processing <---
;next_byte: ; increases hl and sets a new value to (ix+CHN_PATTERNPTR)
        inc     hl
        ld      CHN_PATTERNPTR(ix), l
        ld      CHN_PATTERNPTR+1(ix), h

        jp      lets_see_now

end_pattern:
        ld      l, CHN_SONGPTR(ix)
        ld      h, CHN_SONGPTR+1(ix)
        ; hl points to pattern list
track_to_be_continued:

; song_next_byte:
        inc     hl ; next pattern, please!
        ld      CHN_SONGPTR(ix), l
        ld      CHN_SONGPTR+1(ix), h

        ld      d, #0
        ld      e, (hl)
        ld      a, e    ; is it a song order commang
        cp      #TRACK_STOP
        jp      z, end_of_the_world
        cp      #TRACK_SETREPEAT
        jp      z, trak_set_repeat
        cp      #TRACK_REPEAT
        jp      z, trak_repeat
		cp      #TRACK_TRANSPOSE
		jp      z, trak_transpose
		cp      #TRACK_SYNC
		jp      z, trak_sync

        ; it wasn't a song order command, so it is a pattern numbre
        sla     e       ; de=pattern number * 2

        ld      iy, #pattern_addr
        add     iy, de  ; iy -> pattern_addr[pnumero*2]

        ld      l, (iy)
        ld      h, 1(iy)       ; hl points to new pattern
;        ld      CHN_PATTERNPTR(ix), l
;        ld      CHN_PATTERNPTR+1(ix), h

; This is important:
        dec     hl

        jp      it_has_been_dealt_with

do_note_off_vol: ; this is like NOTE_OFF but also set data byte (delay) as channel volume
                 ; so we can use the high nibble for changing volume abruptly
        ld      CHN_ADSR(ix), #3 ; set chn_adsr := 3 (release)
		inc     hl      ; next byte: note off delay
        ld      a, (hl)
		ld		CHN_LEVEL(ix), a
        and     #15
		jp		do_set_delay

do_note_off: ; ----- NOTE OFF --------
        ld      CHN_ADSR(ix), #3 ; set chn_adsr := 3 (release)
		
do_delay:
        inc     hl      ; next byte: note off delay
        ld      a, (hl)
        and     #15
; ---------- SET DELAY
; sets the delay in accumulator
; CHN_DELAY = CHN_TEMPO * a
; destroys a, b, c
do_set_delay:
		ld		(save_hl), hl
		ld		hl, #tempo_table_3 ; hard-coded tempo...
        ld      c, a
		ld		b, #0
		add		hl, bc
		ld		a, (hl)
;        ld      b, CHN_TEMPO(ix)
        ld      CHN_DELAY(ix), a
		ld		hl, (save_hl)
        jp      it_has_been_dealt_with

do_vibrato: ; ------ VIBRATO -------
        inc     hl
        ld      a, (hl)
        ld      CHN_VIBSPD(ix), a
        jp      it_has_been_dealt_with

do_slide_up: ; ------- SLIDE UP -------
        inc     hl
        ld      a, (hl)
        neg
        ld      CHN_SLIDE_FINE(ix), a
        ld      a, #255
        ld      CHN_SLIDE_COARSE(ix), a
        jp      do_delay
do_slide_down: ; ------ SLIDE DOWN ------
        inc     hl
        ld      a, (hl)
        ld      CHN_SLIDE_FINE(ix), a
        sub     a
        ld      CHN_SLIDE_COARSE(ix), a
        jp      do_delay

do_sync_a: ; ------ SYNC A -------
        ld      a, (_music_sync_a)
        inc     a
		ld      (_music_sync_a), a
        jp      it_has_been_dealt_with

do_sync_b: ; ------ SYNC B -------
        ld      a, (_music_sync_b)
        inc     a
		ld      (_music_sync_b), a
        jp      it_has_been_dealt_with

do_tempo: ; ------- TEMPO -------
; next_byte: ; increases hl and sets a new value to (ix+CHN_PATTERNPTR)
        inc     hl
        ld      a, (hl)
		and		#15 ; tempo is only 4 bits wide
        ld      CHN_TEMPO(ix), a
        jp      it_has_been_dealt_with

; ------------- song order commands: ----------------------------

end_of_the_world: ; ---------- track ended
                ; we come here, when there is a TRACK_STOP
                ; in the song list. now we stop playing and
                ; set CHN_STOPPED := 1
        ld      a, #1
        ld      CHN_STOPPED(ix), a
        ret

trak_set_repeat:
; save value of HL for repeat.
; after this we continue with playing the patterns
        ld      CHN_REPEAT_PTR(ix), l
        ld      CHN_REPEAT_PTR+1(ix), h
        jp      track_to_be_continued

trak_repeat: ; now we have to jump backwards in the song order
        ld      a, CHN_REPEAT(ix)
        cp      #0
        jp      nz, decrement
        ; CHN_REPEAT = 0 : start loop
        inc     hl
        ld      a, (hl)
        jp      song_order_jump
decrement: ; CHN_REPEAT was not zero
        cp      #255 ; repeat 255: infinite repeat
        jp      z, song_order_jump
        dec     a
        jp      z, repeat_over  ; CHN_REPEAT was 1 : let's go forwards
        ; CHN_REPEAT oli >1 : do a jump in song order
song_order_jump:
        ld      CHN_REPEAT(ix), a
        ld      l, CHN_REPEAT_PTR(ix)  ; jump: get a new value for hl
        ld      h, CHN_REPEAT_PTR+1(ix)
;        inc     hl
        jp      track_to_be_continued

repeat_over:
        ld      CHN_REPEAT(ix), a ; accumulator is zero
        inc     hl ; skip the repeat parameter
        jp      track_to_be_continued


trak_transpose:
        ; assume: hl points to song list
		; Transpose! Let's load new value to CHN_NOTE_TABLE
		; from song order data 
		inc     hl
		ld      a, (hl)
		ld      CHN_NOTE_TABLE(ix), a
		inc     hl
		ld      a, (hl)
		ld      CHN_NOTE_TABLE+1(ix), a
		; and store value of CHN_SONGPTR
        ld      CHN_SONGPTR(ix), l
        ld      CHN_SONGPTR+1(ix), h
        jp      track_to_be_continued
		
trak_sync:
        ; assume: hl points to song list
		ld      a, (_music_sync_t)
		inc     a
		ld      (_music_sync_t), a
        jp      track_to_be_continued


; ************* INIT *************
_music_init:: 
        PUSH    AF
        PUSH    BC
        PUSH    DE
        PUSH    HL
        PUSH    IX
        PUSH    IY

		; init PSG mixer
		ld		a, #7
		out		(PSG_IO_PORT_REGISTER_SELECT), a
		in		a, (PSG_IO_PORT_DATA_READ)
		and     #192
		add		a, #63
		out     (PSG_IO_PORT_DATA_WRITE), a
		
        POP     IY
        POP     IX
        POP     HL
        POP     DE
        POP     BC
        POP     AF

		ret

; *****************************************************************************
; *************************** MUSIC PLAYER ROUTINE ****************************
; *****************************************************************************

save_sp: .dw 0
mixer_sum: .db 0 ; helper variable for summing PSG register 7 (Mixer) value 

_music_play:: 
        PUSH    AF
        PUSH    BC
        PUSH    DE
        PUSH    HL
        PUSH    IX
        PUSH    IY

		ld (save_sp), sp

		; **** Advance echo write/read buffers ****
		ld		hl, (echo_write_pos)
		ld		bc, #4
		add		hl, bc
		ld		a, #echo_buffer_end
		cp		l
		jr		nz, write_pos_not_wrapped
		ld		hl, #echo_buffer
write_pos_not_wrapped:
		ld		(echo_write_pos), hl

		ld		hl, (echo_read_pos)
		add		hl, bc
		ld		a, #echo_buffer_end
		cp		l
		jr		nz, read_pos_not_wrapped
		ld		hl, #echo_buffer
read_pos_not_wrapped:
		ld      (echo_read_pos), hl

        ; **** CHANNEL 1 a.k.a. CHANNEL A ****
		ld		a, (channel_a_mixer)
		ld		(channel_mixer), a
        sub     a
        ld      (channel), a
		ld		hl, #channel_0_parameters
		ld		(chn_ptr), hl
        ld      ix, #channel_0_parameters
;		ld		sp, (save_sp)
        call    process_channel
		; mixer
		ld		a, (channel_mixer)
		ld		(channel_a_mixer), a		
		ld		(mixer_sum), a
		; tuning
		sub		a ; channel A = 0
		out		(PSG_IO_PORT_REGISTER_SELECT), a
		ld		a, (channel_tuning)
		out		(PSG_IO_PORT_DATA_WRITE), a
.if 1==1
		ld		(_music_channel_0_tuning), a			; <-- for UI only
.endif
		ld		a, #1
		out		(PSG_IO_PORT_REGISTER_SELECT), a
		ld		a, (channel_tuning+1)
		out		(PSG_IO_PORT_DATA_WRITE), a
.if 1==1
		ld		(_music_channel_0_tuning+1), a			; <-- for UI only
.endif
		; volume
		ld		a, #8 ; channel A volume
		out		(PSG_IO_PORT_REGISTER_SELECT), a
.if 1==1
		ld		a, (_music_channel_a_volume_mask) ; <-- for UI only
		ld		b, a						      ; <-- for UI only
.endif
		ld		a, (channel_volume)
.if 1==1
		and		b							      ; <-- for UI only
.endif
		out		(PSG_IO_PORT_DATA_WRITE), a

        ; **** CHANNEL 2 a.k.a. CHANNEL B ****
		ld		a, (channel_b_mixer)
		ld		(channel_mixer), a
        ld      a, #1
        ld      (channel), a
		ld		hl, #channel_1_parameters
		ld		(chn_ptr), hl
        ld      ix, #channel_1_parameters
;		ld		sp, (save_sp)
        call    process_channel_with_echo
		; mixer
		ld		a, (channel_mixer)
		ld		(channel_b_mixer), a
		sla		a
		ld		hl, #mixer_sum
		add		a, (hl)
		ld		(mixer_sum), a
		; tuning
		ld		hl, (echo_write_pos)
		ld		sp, hl
		ld		a, #2
		out		(PSG_IO_PORT_REGISTER_SELECT), a
		ld		hl, (channel_tuning)
.if 1==1
		ld		(_music_channel_1_tuning), hl			; <-- for UI only
.endif
		ld		a, l
		out		(PSG_IO_PORT_DATA_WRITE), a
		ld		a, #3
		out		(PSG_IO_PORT_REGISTER_SELECT), a
		ld		a, h
		out		(PSG_IO_PORT_DATA_WRITE), a
		
		ex		(sp), hl ; write channel B's tone value to echo buffer
		; volume
		ld		a, #9 ; channel B volume
		out		(PSG_IO_PORT_REGISTER_SELECT), a
.if 1==1
		ld		a, (_music_channel_b_volume_mask) ; <-- for UI only
		ld		b, a						      ; <-- for UI only
.endif
		ld		a, (channel_volume)
.if 1==1
		and		b							      ; <-- for UI only
.endif
		out		(PSG_IO_PORT_DATA_WRITE), a
		inc     sp
		inc		sp
		ld		h, a ; write channel B's volume to echo buffer HIGH ORDER BYTE 
		ld		a, (channel_mixer)
		ld		l, a ; channel mixer to low order byte
	    ex		(sp), hl

        ; **** CHANNEL 3 a.k.a. CHANNEL C ****
		ld		a, (channel_c_mixer)
		ld		(channel_mixer), a
        ld      a, #2
        ld      (channel), a
		ld		hl, #channel_2_parameters
		ld		(chn_ptr), hl
        ld      ix, #channel_2_parameters
        ld		sp, (save_sp)
		call    process_channel_with_echo
		
		; mixer
		ld		a, (channel_mixer)
		ld		(channel_c_mixer), a
		sla		a
		sla		a
		ld		hl, #mixer_sum
		add		a, (hl)
		ld		(mixer_sum), a
				
		; tuning
		ld		a, #4
		out		(PSG_IO_PORT_REGISTER_SELECT), a
		ld		a, (channel_tuning)
		out		(PSG_IO_PORT_DATA_WRITE), a
.if 1==1
		ld		(_music_channel_2_tuning), a			; <-- for UI only
.endif
		ld		a, #5
		out		(PSG_IO_PORT_REGISTER_SELECT), a
		ld		a, (channel_tuning+1)
.if 1==1
		ld		(_music_channel_2_tuning+1), a			; <-- for UI only
.endif
		out		(PSG_IO_PORT_DATA_WRITE), a
		; volume
		ld		a, #10 ; channel C volume
		out		(PSG_IO_PORT_REGISTER_SELECT), a
.if 1==1
		ld		a, (_music_channel_c_volume_mask) ; <-- for UI only
		ld		b, a						      ; <-- for UI only
.endif
		ld		a, (channel_volume)
.if 1==1
		and		b							      ; <-- for UI only
.endif
		out		(PSG_IO_PORT_DATA_WRITE), a
		
		; set mixer for all channels
		ld		a, #7
		out		(PSG_IO_PORT_REGISTER_SELECT), a
		in		a, (PSG_IO_PORT_DATA_READ)
		and     #192
		ld		b, a
.if 1==1
		ld		a, (_music_mixer_mute)  ; <-- for UI only
		ld		c, a					; <-- for UI only
.endif
		ld		a, (mixer_sum)
		and		#63
		or		b
.if 1==1
		or      c						; <-- for UI only
.endif
		out     (PSG_IO_PORT_DATA_WRITE), a

		ld sp, (save_sp)

        POP     IY
        POP     IX
        POP     HL
        POP     DE
        POP     BC
        POP     AF

	ret

; ************* STOP SOUND *************
_music_stop_sound:: 
        PUSH    AF
        PUSH    BC
        PUSH    DE
        PUSH    HL
        PUSH    IX
        PUSH    IY

		; volume
		ld		a, #8 ; channel A volume
		out		(PSG_IO_PORT_REGISTER_SELECT), a
		sub		a
		out		(PSG_IO_PORT_DATA_WRITE), a
		; volume
		ld		a, #9 ; channel B volume
		out		(PSG_IO_PORT_REGISTER_SELECT), a
		sub		a
		out		(PSG_IO_PORT_DATA_WRITE), a
		; volume
		ld		a, #10 ; channel C volume
		out		(PSG_IO_PORT_REGISTER_SELECT), a
		sub		a
		out		(PSG_IO_PORT_DATA_WRITE), a
		
        POP     IY
        POP     IX
        POP     HL
        POP     DE
        POP     BC
        POP     AF

	ret

; ****** DELAY/ECHO ******

_music_echo_buffer::
echo_buffer:
.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 
.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
;.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
;.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
;.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
;.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
;.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
;.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
;.db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
echo_buffer_end:
_music_echo_write_pos::
echo_write_pos: .dw echo_buffer + 12 * 4
_music_echo_read_pos::
echo_read_pos: .dw echo_buffer


; INSTRUMENT FORMAT:
;   LVL, ATT, DCY, STN, RLS, MDE, 4SLDHI:4ARL, SLD, ARS, AR0, AR1, AR2,..,AR5, TRANSP ON
;

instruments:   ; 16*16 = 256 bytes
.db 0xF0,0xFF,0x0A,0x80,0x10,0x08,0x02,        0x00,0x01,24,0,0,0,0,0,1 ; i 0, basso
.db 0xC0,0xC0,0x20,0xB0,0x04,0x08,0x03,        0x00,0x02,12,0,0,0,0,0,1 ; i 1, melodia
.db 0xB0,0x80,0x12,0xA0,0x04,0x08,0x05,        0x00,0x00,8,8,0,0,5,5,1 ; i 2, molli/5
.db 0xB0,0x80,0x12,0xA0,0x04,0x08,0x03,        0x00,0x00,0,0,3,3,7,7,1 ; i 3, molli
.db 0xB0,0x80,0x12,0xA0,0x04,0x08,0x03,        0x00,0x00,0,3,8,0,0,0,1 ; i 4, duuri/3
.db 0xB0,0x80,0x12,0xA0,0x04,0x08,0x05,        0x00,0x00,9,9,0,0,5,5,1 ; i 5, duuri/5
.db 0xFF,0xFF,0x14,0x00,0x40,0x00,0x02,        0xD0,0x00,0,11,0,0,0,0,0 ; i 6, * virveli
.db 0xD0,0xF0,0x14,0x00,0x40,0x00,0x01,        0x30,0x00,0,12,0,0,0,0,0 ; i 7, * "rumpu2"
.db 0xA0,0xF0,0x44,0x00,0x40,0x00,0x01,        0x30,0x00,0,0,0,0,0,0,0 ; i 8, * "rumpu"
.db 0xC0,0xF0,0x09,0x00,0x40,0x00,0x01,        0x30,0x00,0,0,0,0,0,0,0 ; i 9, * "haikka"
.db 0xB0,0xFF,0x0A,0x40,0x50,0x08,0x01,        0x00,0x00,0,0,0,0,0,0,1 ; i A, pikkubas
.db 0xB0,0x80,0x18,0xA0,0x04,0x08,0x05,        0x00,0x00,9,9,0,0,4,4,1 ; i B, molli/3
.db 0xB0,0x80,0x17,0xA0,0x04,0x08,0x04,        0x00,0x00,0,4,5,9,0,0,1 ; i C, maj7/7
.db 0xB0,0x80,0x12,0xA0,0x04,0x08,0x06,        0x00,0x00,0,0,3,3,6,6,1 ; i D, dim
.db 0xE0,0xA0,0x0A,0x00,0x10,0x08,0x01,        0x00,0x00,0,0,0,0,0,0,1 ; i E, basso 2
.db 0xC0,0xF0,0x10,0x80,0x30,0x08,0x04,        0x00,0x02,12,12,0,0,0,0,1 ; i F, click-melodia
; ---- that's all... ----

; Instrument explanation:
;
; LVL = level
; ATT = attack rate
; DCY = decay rate
; STN = sustain level
; RLS = release rate
; MDE = mode (8=sound, 1=noise, 0=sound+noise);
; SLDHI = slide hi (CHANGE THE DEFINE)
; ARL = arpeggio length (MUST BE BETWEEN 1-6)
; SLD = slide
; ARS = arpeggio repeat start index (MUST BE BETWEEN 0-5 AND LESS THAN ARL)
; AR0..AR5 = arpeggio notes
; TRANSP ON = transposing instrument (1 = transposing, 0 = non-transposing like drums)

channel_0_parameters:				; ------- CH 1 / A
.dw instruments
_music_track_0_pos: 
.dw track0-1, starting_pattern
.db 0,0,0,0 ; first offs. 6,
_music_channel_0_level:: .db 0
.db 0,0,3 ; last offs. 13
.db 1,0,0,0,0,0,0,0 ; last offs. 21
.db 0,0 ; first 22
.dw #tunings ; ofs 24: note table pointer. Transposing changes this.
.db 0,0,0,0 ; first 26
channel_1_parameters:				; ------- CH 2 / B
.dw instruments
_music_track_1_pos: 
.dw track1-1, starting_pattern
.db 0,0,0,0 ; first offs. 6,
_music_channel_1_level:: .db 0
.db 0,0,3 ; last offs. 13
.db 1,0,0,0,0,0,0,0
.db 0,0 ; first 22
.dw #tunings ; 24
.db 0,0,0,0 ; first 26
channel_2_parameters:				; ------- CH 3 / C
.dw instruments
_music_track_2_pos: 
.dw track2-1, starting_pattern
.db 0,0,0,0 ; first offs. 6,
_music_channel_2_level:: .db 0
.db 0,0,3 ; last offs. 13
.db 1,0,0,0,0,0,0,0
.db 0,0 ; first 22
.dw #tunings ; 24
.db 0,0,0,0 ; first 26


; ------------------------------- SONG ORDER ------------------------------

_music_track_0_order:
track0:  ; ------ bass+drum
.db TRACK_TRANSPOSE ; --- INTRO 1 ---
.dw #transp0
.db TRACK_SYNC, 0, TRACK_SYNC, 0, TRACK_SYNC, 0, TRACK_SYNC, 0

.db TRACK_TRANSPOSE ; --- INTRO 2 ---
.dw #transpdown3
.db TRACK_SYNC, 0, TRACK_SYNC, 0, TRACK_SYNC, 0, TRACK_SYNC, 0

.db TRACK_SYNC
.db 03 ; INTRON LOPPUFILLI

; ----------------------------------------- A-osa
.db TRACK_SETREPEAT 
.db TRACK_SYNC
.db TRACK_TRANSPOSE
.dw #transp0
.db 1, TRACK_SYNC, 1 
.db TRACK_SYNC
.db TRACK_TRANSPOSE
.dw #transpdown3
.db 1, 1 
.db TRACK_TRANSPOSE
.dw #transpdown5
.db 1
.db TRACK_TRANSPOSE
.dw #transpdown3
.db 2
.db TRACK_SYNC
.db TRACK_TRANSPOSE
.dw #transpup2
.db 1,2
;-------------
.db TRACK_TRANSPOSE
.dw #transp0
.db TRACK_SYNC
.db 1,2

.db TRACK_TRANSPOSE
.dw #transpdown5
.db TRACK_SYNC
.db 1,2

.db TRACK_TRANSPOSE
.dw #transpdown7
.db TRACK_SYNC
.db 1

.db TRACK_TRANSPOSE
.dw #transpdown5
.db TRACK_SYNC
.db 2

.db TRACK_REPEAT, 1

.db TRACK_SYNC
.db 4 ; tauko + nosto B-osaan
; __--__--__--__-- B-osa lŠhtee tŠstŠ


.db TRACK_SETREPEAT 

.db TRACK_SYNC
.db TRACK_TRANSPOSE
.dw #transpdown3
.db 5, 6
.db TRACK_SYNC
.db TRACK_TRANSPOSE
.dw #transpdown7
.db 5, 6
.db TRACK_SYNC
.db TRACK_TRANSPOSE
.dw #transpdown2
.db 5, 6
.db TRACK_SYNC
.db TRACK_TRANSPOSE
.dw #transpdown4
.db 5, 6
.db TRACK_REPEAT, 1

.db TRACK_SYNC
.db TRACK_TRANSPOSE
.dw #transpdown3
.db 7

.db TRACK_SYNC
.db LASTPATT, TRACK_STOP

_music_track_1_order: ; *************************************** TRACK 1 / B
track1:  ; ------- INTRO

.db TRACK_TRANSPOSE
.dw #transp0
.db 40, 41, 40, 41, 40 ; 36 tickin patterneja
.db TRACK_TRANSPOSE
.dw #transpdown3
.db 41, 40, 41, 40, 41
.db 42 ; Tasaus ennen A-osaa

; tŠssŠ kohtaa vaihtuu arpeggiot kanavalle B

.db 24 ; INTRON LOPPUFILLI 

; ---- A-osa arpeggiot
.db TRACK_SETREPEAT 
.db TRACK_TRANSPOSE
.dw #transp0
.db 22, 23 
.db TRACK_TRANSPOSE
.dw #transpdown3
.db 22, 23 
.db TRACK_TRANSPOSE
.dw #transpdown5
.db 25
.db TRACK_TRANSPOSE
.dw #transpdown3
.db 25 
.db TRACK_TRANSPOSE
.dw #transpup2
.db 22,23
; -------------------------
.db TRACK_TRANSPOSE
.dw #transp0
.db 25, 25
.db TRACK_TRANSPOSE
.dw #transpdown5
.db 22, 23
.db TRACK_TRANSPOSE
.dw #transpdown7
.db 25
.db TRACK_TRANSPOSE
.dw #transpdown5
.db 25

.db TRACK_REPEAT, 1 ; ---------

.db 29 ; TRANSITIOFILLI TRIOLI --> SUORA, 32 ticks

; __--__--__--__-- B-osa lŠhtee tŠstŠ
.db TRACK_SETREPEAT 
.db TRACK_TRANSPOSE
.dw #transpdown3
.db 30
.db 30
.db TRACK_TRANSPOSE
.dw #transpup2
.db 31
.db TRACK_TRANSPOSE
.dw #transpdown4
.db 32  ; <-- dimiŠ
.db TRACK_REPEAT, 1

.db TRACK_TRANSPOSE
.dw #transpdown3
.db 33 ; <--- loppu

.db LASTPATT, TRACK_STOP

_music_track_2_order: ; ********************************** TRACK 2 / C
track2:  ; ------- INTRO arpeggiot -----

.db TRACK_TRANSPOSE ; --- INTRO 1 ---
.dw #transp0
.db 20, 21, 20, 21

.db TRACK_TRANSPOSE ; --- INTRO 2 ---
.dw #transpdown3
.db 20, 21, 20, 21

.db 43 ; INTRON LOPPUFILLI 

; ----------------------- A-osa melodia
.db TRACK_TRANSPOSE
.dw #transp0
.db 44 

; huom. repeat-hyppy tulee tŠhŠn eikŠ A-osan aloittavaan kohtaan, jotta saadaan taitos kivasti
.db TRACK_SETREPEAT 
.db 45

.db TRACK_TRANSPOSE
.dw #transpdown3
.db 44, 45

.db TRACK_TRANSPOSE
.dw #transpup7
.db 46
.db TRACK_TRANSPOSE
.dw #transpup9
.db 47

.db TRACK_TRANSPOSE
.dw #transpup2
.db 44, 45

.db TRACK_TRANSPOSE
.dw #transp0
.db 44, 48 ; huom. 48 on 6 yksikkšŠ normaalia pitempiŠ pattern! pitŠŠ olla tasoitus ---> 51 tasoittaa
 
.db TRACK_TRANSPOSE
.dw #transpdown5
.db 44, 49

.db TRACK_TRANSPOSE
.dw #transpdown10
.db 51
.db TRACK_TRANSPOSE
.dw #transpdown8
.db 45
.db TRACK_TRANSPOSE
.dw #transp0
.db 50

.db TRACK_REPEAT, 1

.db	52 ; tasaus

; tŠstŠ lŠhtee B-osa...

.db TRACK_SETREPEAT
.db 53, 53
.db TRACK_REPEAT,3

.db TRACK_TRANSPOSE
.dw #transpdown3
.db 54, 53
.db 54, 53
.db 54, 53
.db 55, 55

.db 56

.db LASTPATT, TRACK_STOP

starting_pattern:
.db NOTE_OFF, 4
.db END_PATTERN

.include "commands.inc"

; drum notes
nSD = 31
nDR = 48
nHH = 40

; -------------------------------- PATTERNS! -------------------------------

patt00: ; ------------- PATTERN 00 ------------ INTRO
        .db SYNC_A
        .db nD1,0x04, nE1,0x04, NOTE_OFF_VOL,0xB2, nE2,0xA2 ; 12 ticks
        .db SYNC_A
        .db nE2,0xA6, nD3,0xA2, nE3,0xA2, nG3,0xA2 ; 12 ticks
        .db nG3,0xA2, nHH,0x92, NOTE_OFF_VOL,0xB2, nB3,0xA2, nG3,0xA2, nE3,0xA2 ; 12 ticks
        .db nHH,0x92, nHH-10,0x92, NOTE_OFF_VOL,0xB2, nA3,0xA2, nFis3,0xA2, nD3,0xA2 ; 12 ticks
        .db END_PATTERN
patt01: ; ------------- PATTERN 01 ------------ A-osa
        .db SYNC_A
        .db nD1,0x04, nE1,0x02, nHH,0x92, nE2,0x02, nE1,0x02 
        .db nSD,0x62, nE1,0xE2, nG3,0xA2, nDR,0x72, nE1,0x02, nHH+8,0x92
        .db nDR,0x72, nE1,0xE2, nE2,0x02, nHH-2,0x92, nE2,0x02, nE1,0x02 
        .db SYNC_A
        .db nSD,0x64, nHH-14,0x92, nDR-10,0x72, nE1,0x02, nE2,0x02
        .db END_PATTERN
patt02: ; ------------- PATTERN 02 ------------ 
        .db SYNC_A
        .db nD1,0x04, nE1,0x02, nHH,0x92, nE2,0x02, nE1,0x02 
        .db nSD,0x62, nE1,0xE2, nG3,0xA2, nDR,0x72, nE1,0x02, nHH-8,0x72
        .db nE2,0x01, VIBRATO,5, DELAY,1, nSD-4,0x72, nE2,0x01, VIBRATO,5, DELAY,1, nHH-2,0x92, nE2,0x02, nE1,0x02 
        .db SYNC_A
        .db nSD,0x62, nE1,0x02, nSD-5,0x72, nDR-10,0x62, nE1,0x02, nE2,0x02
        .db END_PATTERN
patt03: ; ------------- PATTERN 03 ------------ INTRON LOPPUFILLI
		.db nE1,0x02, SLIDE_UP,2,4, NOTE_OFF,6
		.db nSD,0x62, 10,0x62, nSD-10,0x62
		.db nF1,0x02, SLIDE_UP,2,2, nSD-10,0x62
        .db END_PATTERN
patt04: ; transitiofilli A --> B                      		
		.db nE1,0x02, SLIDE_DOWN,2,10
		.db SLIDE_DOWN,1,12
		.db nC1,0x02, SLIDE_DOWN,5,10
		.db SLIDE_DOWN,1,12

		.db nHH,0x98, nHH,0x94, nHH-4,0x94		
		.db nHH,0x64, nHH-9,0x94, nHH-19,0x74, NOTE_OFF,4
		.db NOTE_OFF,4, nHH-10,0x94, NOTE_OFF,4, nHH-4,0x94
		.db nHH-20,0x64, nHH-22,0x94, nHH-24,0x64, NOTE_OFF,4
		
        .db END_PATTERN
patt05: ; ********** B-OSA                       
        .db SYNC_A
        .db nE1,0x04, NOTE_OFF_VOL,0x92, SLIDE_DOWN,1,2, nE1,0x02, NOTE_OFF,2, nE2,0x01, VIBRATO,6, DELAY,3
		.db nSD,0x64, nHH,0x94, nHH-10,0x94, nE1,0x02, NOTE_OFF,2
        .db END_PATTERN
patt06: 
	    .db SYNC_A
        .db nE1,0x01, NOTE_OFF,3, nE2,0xE1, NOTE_OFF,1, nE2,0xF1, NOTE_OFF,1, nHH-3,0x94, nE2,0x02, VIBRATO,6, DELAY,2
		.db nSD,0x64, nE1,0x02, NOTE_OFF,2, nE2,0x02, VIBRATO,6, DELAY,2, nE1,0xE2, NOTE_OFF,2
        .db END_PATTERN

patt07: 
	    .db SYNC_A
        .db nE1,0x01, NOTE_OFF,3, nE2,0xE1, NOTE_OFF,1, nE2,0xF1, NOTE_OFF,1, nHH-3,0x94, nE2,0x02, VIBRATO,6, DELAY,2
        .db END_PATTERN
patt08:
patt09: 
		.db END_PATTERN
patt10: 
patt11: 
patt12: 
patt13: 
patt14: 
patt15: 
patt16: 
patt17: 
patt18: 
patt19: 
patt20: ; ---------- pattern 10 ------------- ARPEGGIOITA
        .db nE1,0xE2, nE3,0xA2, nE2,0xE2, nB3,0x22, ECHO+4, nA3,0x52, nE2,0xF2, ECHO+2, nB3,0x24, nFis3,0xE2 ; 24 ticks
        .db SYNC_B
        .db ECHO+4, nE3,0xA2, ECHO+4, nE3,0xA2, ECHO+4, nB3,0x22, ECHO+4, nB4,0x22 ; 24 ticks
        .db END_PATTERN
patt21:
        .db nE2,0xF2, nE3,0xA2, nE2,0xE2, nB3,0x22, NOTE_OFF_VOL,0x62, nB3,0x22, nA3,0x52, ECHO+2, nB3,0x22, ECHO+2, nB3,0x22, ECHO+2
        .db SYNC_B
        .db ECHO+4, nE4,0xA2, ECHO+4, nE1,0xE2, ECHO+6, nDR+2,0x72, nDR-2,0x72, nHH+7,0x72 ; 24 ticks
        .db END_PATTERN
patt22: 
        .db nE1,0xE2, nE3,0xA2, nE2,0xE2, nB3,0x24, nDR,0x72, nA3,0x54, nHH+7,0x92, nB3,0x24, nHH+7,0x92 ; 24 ticks
        .db SYNC_B
        .db nE2,0xE1, VIBRATO,3, DELAY,3, nDR-7,0x72, nB3,0x24, nDR,0x72, nA3,0x54, nHH+7,0x92, nB3,0x24, nHH+7,0x92 ; 24 ticks
        .db END_PATTERN
patt23:
        .db nSD,0x62, nE3,0xA2, nE2,0xE2, nB3,0x24, nDR,0x72, nA3,0x54, nHH+7,0x92, nB3,0x24, nHH+7,0x92
        .db SYNC_B
        .db nC4,0xB1, VIBRATO,3, DELAY,3, nC4,0xA2, nB3,0x24, nDR,0x72, nA3,0x54, nDR-7,0x72, nB3,0x24, nDR-9,0x62
        .db END_PATTERN
patt24: ; - INTRON LOPPUFILLI
		.db nB2,0x22, SLIDE_UP,8,4, ECHO+4, SLIDE_UP,2,2
		.db 2,0x92, 4,0x92, 6,0x92
		.db 8,0x92, 10,0x92, 12,0x92
        .db END_PATTERN
patt25: ; Sama kuin 12, mutta duurisointu
        .db nE1,0xE2, nE3,0xA2, nE2,0xE2, nB3,0x54, nDR,0x72, nA3,0x54, nHH+7,0x92, nB3,0x54, nHH+7,0x92 ; 24 ticks
        .db SYNC_B
        .db nE2,0xE1, VIBRATO,3, DELAY,3, nDR-7,0x72, nB3,0x54, nDR,0x72, nA3,0x54, nHH-2,0x92, nB3,0x54, nHH-7,0x92 ; 24 ticks
        .db END_PATTERN
patt26: ; c-duuri /3
        .db END_PATTERN
patt27: ; d-duuri /3
        .db END_PATTERN
patt28: ; g-duuri /5
        .db END_PATTERN
patt29: ; - TRANSITIOFILLI... TRIOLI --> SUORA
		.db nHH,0x94, ECHO+8
		.db nSD,0x64, ECHO+4, nG4,0xF4
		.db ECHO+4, nG3,0xF4, nFis3,0xF4
		.db nG4,0xF4, ECHO+8

		.db nB4,0xF2, NOTE_OFF_VOL,0x42, nG4,0xF2, NOTE_OFF_VOL,0x42, nFis4,0xF2, NOTE_OFF_VOL,0x42, nG4,0xF2, NOTE_OFF_VOL,0x42
		.db ECHO+4, nG4,0xF2, NOTE_OFF_VOL,0x42, nFis4,0xF2, NOTE_OFF_VOL,0x42, nG4,0xF2, NOTE_OFF_VOL,0x42 
		.db nB4,0xA2, NOTE_OFF_VOL,0x82, nG4,0xA2, NOTE_OFF_VOL,0x82, nFis4,0xA2, NOTE_OFF_VOL,0x42, nG4,0xA2, NOTE_OFF_VOL,0x42
		.db ECHO+4, nG4,0xA2, NOTE_OFF_VOL,0x42, nFis4,0xA2, NOTE_OFF_VOL,0x42, nG4,0xA2, NOTE_OFF_VOL,0x42
        .db END_PATTERN
patt30: ; 64 ticks
        .db nG4,0xF2, NOTE_OFF_VOL,0x62, nHH-5,0x92, nE4,0xF1, ECHO+1, nB3,0x22, NOTE_OFF_VOL,0x66
        .db nB4,0xF1, ECHO+1, NOTE_OFF_VOL,0x62, nHH-5,0x92, nA4,0xF1, ECHO+1, nB3,0x22, NOTE_OFF_VOL,0x66
        .db SYNC_B
        .db nFis3,0x21, NOTE_OFF_VOL,0x63, nHH-5,0x92, nFis4,0xF1, ECHO+1, nB3,0x22, NOTE_OFF_VOL,0x66
        .db nG4,0xF2, NOTE_OFF_VOL,0x62, nHH-5,0x92, ECHO+2, nB3,0xF2, NOTE_OFF_VOL,0x66
		.db END_PATTERN
patt31:
        .db nG4,0xF2, NOTE_OFF_VOL,0x62, nHH-5,0x92, nE4,0xF1, ECHO+1, nB3,0x22, NOTE_OFF_VOL,0x66
        .db nB4,0xF1, ECHO+1, NOTE_OFF_VOL,0x62, nHH-5,0x92, nA4,0xF1, ECHO+1, nB3,0x22, NOTE_OFF_VOL,0x66
        .db SYNC_B
        .db nB3,0x22, NOTE_OFF_VOL,0x62, nHH-5,0x92, nE3,0xF1, ECHO+1, nB3,0x22, NOTE_OFF_VOL,0x66
        .db nB3,0x22, NOTE_OFF_VOL,0x62, nHH-5,0x92, nE3,0xF1, ECHO+1, nB3,0x22, NOTE_OFF_VOL,0x66
		.db END_PATTERN
patt32: ; dimiŠ ylšspŠin
        .db nCis3,0xD2, NOTE_OFF_VOL,0x62, nHH-5,0x92, nE3,0xF1, ECHO+1, nE3,0xD2, NOTE_OFF_VOL,0x66
        .db nE3,0xD2, NOTE_OFF_VOL,0x62, nHH-5,0x92, nG3,0xD1, ECHO+1, nAis3,0xD2, NOTE_OFF_VOL,0x66
        .db SYNC_B
        .db nCis4,0xD2, NOTE_OFF_VOL,0x62, nHH-5,0x92, nG3,0xF1, ECHO+1, nE4,0xD2, NOTE_OFF_VOL,0x66
        .db nE4,0xD2, NOTE_OFF_VOL,0x62, nHH-5,0x92, nE3,0xF1, ECHO+1, nG4,0xD2, NOTE_OFF_VOL,0x66
		.db END_PATTERN
patt33: ; ... loppupattern
        .db nB3,0x22, NOTE_OFF_VOL,0x62, nHH-5,0x92, nE3,0xF1, ECHO+1, nB3,0x22, NOTE_OFF_VOL,0x66
        .db SYNC_B
		.db END_PATTERN
patt34:
patt35:
patt36:
patt37:
patt38:
patt39: .db END_PATTERN
patt40: ; ---- intro 
        .db nFis4,0xF2, ECHO+2, nG4,0xF2, ECHO+2, VIBRATO,10, DELAY,2, ECHO+2
		.db ECHO + 6
        .db nFis3,0xF2, ECHO+2, nG3,0xF2, ECHO+2, VIBRATO,10, DELAY,2, ECHO+2
		.db ECHO + 6
        .db END_PATTERN ; 36 ticks
patt41: 
        .db nB3,0xF2, nG3,0xF2, ECHO+2, VIBRATO,10, DELAY,4, ECHO+2
		.db ECHO + 6
        .db nB4,0xF2, nG4,0xF2, ECHO+2, VIBRATO,10, DELAY,4, ECHO+2
		.db ECHO + 6
        .db END_PATTERN ; 36 ticks
patt42: 
        .db nB3,0xF2, nG3,0xF2, nE3,0xF2
		.db nB4,0xF2, nG4,0xF2, nE4,0xF2
        .db nE4,0xF2, nFis4,0xF2, nG4,0xF2, VIBRATO,10, DELAY,4, ECHO+2
        .db END_PATTERN ; 24 ticks TASAUS
patt43: ; INTRON LOPPUFILLI
		.db ECHO+12
		.db nHH,0x92, nHH-3,0x92, nHH-10,0x92
		.db ECHO+6
        .db END_PATTERN
patt44: ; ------ A-osa
	    ; mollimelodia A
		.db ECHO+6
		.db nA4,0x11, PORTAMENTO+nB4,3, NOTE_OFF_VOL, 0x82
		.db nA4,0x12, NOTE_OFF_VOL, 0x84
		.db nA4,0x11, PORTAMENTO+nB4,1, VIBRATO,10,DELAY,2, NOTE_OFF_VOL, 0x82
		.db ECHO+12
		.db ECHO+12
        .db END_PATTERN
patt45: ; mollimelodia B
		.db ECHO+6
		.db nA4,0x11, PORTAMENTO+nB4,3, NOTE_OFF_VOL, 0x82
		.db nA4,0x12, NOTE_OFF, 0x84
		.db nA4,0x11, PORTAMENTO+nB4,1, VIBRATO,10,DELAY,2, NOTE_OFF_VOL, 0x82
		.db nC5,0x12, VIBRATO,10,DELAY,2, NOTE_OFF, 0x82
		.db nC5,0x11, PORTAMENTO+nB4,1, NOTE_OFF, 1, SLIDE_DOWN,4,3
		.db nA4,0x14, NOTE_OFF_VOL, 0x82
		.db nA4,0x11, PORTAMENTO+nB4,1, VIBRATO,10,DELAY,2, NOTE_OFF_VOL, 0x82
        .db END_PATTERN
patt46: ; duurimelodia
		.db nFis4,0x11, PORTAMENTO+nGis4,1, VIBRATO,10,DELAY,4
		.db nB4,0x14, NOTE_OFF_VOL, 0x82
		.db nGis4,0x12, VIBRATO,10,DELAY,2, NOTE_OFF_VOL, 0x82
		.db nE4,0x12, NOTE_OFF, 0x82, SLIDE_DOWN,1,2
		.db nD4,0x12, VIBRATO,10,DELAY,4
		.db NOTE_OFF, 0x82, ECHO+2, nE4,0x12
		.db SLIDE_DOWN,1,2, NOTE_OFF, 2, ECHO+2
		.db nFis4,0x11, PORTAMENTO+nGis4,1, VIBRATO,10,DELAY,2, NOTE_OFF, 2
        .db END_PATTERN
patt47: ; duurimelodia
		.db nFis4,0x11, PORTAMENTO+nGis4,1, VIBRATO,10,DELAY,4
		.db nB4,0x14, NOTE_OFF_VOL, 0x82
		.db nGis4,0x12, VIBRATO,10,DELAY,2, NOTE_OFF_VOL, 0x82
		.db nE4,0x12, NOTE_OFF, 0x82, nD4,0x12
		.db VIBRATO,10,DELAY,2, NOTE_OFF_VOL,0x82, nGis3,0x11
		.db VIBRATO,10,DELAY,5, NOTE_OFF_VOL,0x82
		.db nA3,0x12, VIBRATO,10,DELAY,4
		.db nB3,0x12, NOTE_OFF_VOL,0x82, SLIDE_DOWN,2,2 
        .db END_PATTERN
patt48: ; kuten 45, mutta duurissa
		.db ECHO+6
		.db nA4,0x11, PORTAMENTO+nB4,1, VIBRATO,10,DELAY,4
		.db nA4,0x12, NOTE_OFF, 0x84
		.db nB4,0x12, VIBRATO,10,DELAY,2, NOTE_OFF_VOL, 0x82
		.db nE5,0x12, VIBRATO,10,DELAY,2, NOTE_OFF, 0x82
		.db nB4,0x12, NOTE_OFF_VOL, 0x22, SLIDE_DOWN,4,2
		.db nA4,0x14, NOTE_OFF_VOL, 0x82
		.db nGis4,0x12, VIBRATO,10,DELAY,2, nFis4,0x12
		.db VIBRATO,10,DELAY,4, NOTE_OFF_VOL,0x82
        .db END_PATTERN
patt49:
		.db nA4,0x11, PORTAMENTO+nB4,1, VIBRATO,10,DELAY,4
		.db nA4,0x12, NOTE_OFF, 0x84
		.db nB4,0x12, VIBRATO,10,DELAY,2, NOTE_OFF_VOL, 0x82
		.db nE5,0x12, VIBRATO,10,DELAY,2, NOTE_OFF, 0x82
		.db nB4,0x12, NOTE_OFF_VOL, 0x22, SLIDE_DOWN,4,2
		.db nA4,0x14, NOTE_OFF_VOL, 0x82
		.db nG4,0x12, VIBRATO,10,DELAY,2, nFis4,0x12
		.db VIBRATO,10,DELAY,4, NOTE_OFF_VOL,0x82
        .db END_PATTERN
patt50:
	    ; kuten 44, mutta alkuun ei taukoa vaan nuotti
		.db nE4,0x12, VIBRATO,10,DELAY,2, NOTE_OFF, 0x82
		.db nA4,0x11, PORTAMENTO+nB4,3, NOTE_OFF_VOL, 0x82
		.db nA4,0x12, NOTE_OFF_VOL, 0x84
		.db nA4,0x11, PORTAMENTO+nB4,1, VIBRATO,10,DELAY,2, NOTE_OFF_VOL, 0x82
		.db ECHO+12
		.db ECHO+12
        .db END_PATTERN
patt51:
	    ; kuten 44, mutta alusta napsaistu yksi rivi (6 aikayksikkšŠ) pois
		.db nA4,0x11, PORTAMENTO+nB4,3, NOTE_OFF_VOL, 0x82
		.db nA4,0x12, NOTE_OFF_VOL, 0x84
		.db nA4,0x11, PORTAMENTO+nB4,1, VIBRATO,10,DELAY,2, NOTE_OFF_VOL, 0x82
		.db ECHO+12
		.db ECHO+12
        .db END_PATTERN
patt52:
		.db ECHO+8 ; odotuspattern tasausta varten
		.db ECHO+12
		.db ECHO+12
		.db ECHO+8, ECHO+8
		.db ECHO+8, ECHO+8
        .db END_PATTERN
patt53:
        .db ECHO+8, ECHO+8
        .db ECHO+8, ECHO+8
        .db END_PATTERN
patt54:
        .db nB4,0x11,NOTE_OFF_VOL,0x81, nA4,0xF1,NOTE_OFF_VOL,0x81, nB4,0xF1,NOTE_OFF_VOL,0x81, nC5,0xF1,NOTE_OFF_VOL,0x81
        .db ECHO+8
        .db nB4,0xF1,NOTE_OFF_VOL,0x81, nG4,0xF1,NOTE_OFF_VOL,0x81, nFis4,0xF1,NOTE_OFF_VOL,0x81, nG4,0xF1,NOTE_OFF_VOL,0x81
        .db ECHO+8
        .db END_PATTERN
patt55:
        .db nB4,0xE1,NOTE_OFF_VOL,0x81, nA4,0xF1,NOTE_OFF_VOL,0x81, nFis4,0xF1,NOTE_OFF_VOL,0x81, nC5,0xF1,NOTE_OFF_VOL,0x81
        .db nA4,0xF1,NOTE_OFF_VOL,0x81, nFis4,0xF1,NOTE_OFF_VOL,0x81, nDis4,0xF1,NOTE_OFF_VOL,0x81, nC4,0xF1,NOTE_OFF_VOL,0x81
        .db nB4,0xE1,NOTE_OFF_VOL,0x81, nA4,0xF1,NOTE_OFF_VOL,0x81, nFis4,0xF1,NOTE_OFF_VOL,0x81, nC5,0xF1,NOTE_OFF_VOL,0x81
        .db nA4,0xF1,NOTE_OFF_VOL,0x81, nFis4,0xF1,NOTE_OFF_VOL,0x81, nDis4,0xF1,NOTE_OFF_VOL,0x81, nC4,0xF1,NOTE_OFF_VOL,0x81
        .db END_PATTERN
patt56:
        .db nE5,0xE1,NOTE_OFF_VOL,0x81, ECHO+2
        .db nE4,0xE1,NOTE_OFF_VOL,0x81, ECHO+2
        .db nE3,0xE1,NOTE_OFF_VOL,0x81, ECHO+2
        .db nE2,0xE1,NOTE_OFF_VOL,0x81, ECHO+2
        .db ECHO+8, ECHO+8
        .db END_PATTERN
patt57:
patt58:
patt59:
lastpat: .db NOTE_OFF,8, END_PATTERN

pattern_addr:
.dw     patt00
.dw     patt01
.dw     patt02
.dw     patt03
.dw     patt04
.dw     patt05
.dw     patt06
.dw     patt07
.dw     patt08
.dw     patt09
.dw     patt10
.dw     patt11
.dw     patt12
.dw     patt13
.dw     patt14
.dw     patt15
.dw     patt16
.dw     patt17
.dw     patt18
.dw     patt19
.dw     patt20
.dw     patt21
.dw     patt22
.dw     patt23
.dw     patt24
.dw     patt25
.dw     patt26
.dw     patt27
.dw     patt28
.dw     patt29
.dw     patt30
.dw     patt31
.dw     patt32
.dw     patt33
.dw     patt34
.dw     patt35
.dw     patt36
.dw     patt37
.dw     patt38
.dw     patt39
.dw     patt40
.dw     patt41
.dw     patt42
.dw     patt43
.dw     patt44
.dw     patt45
.dw     patt46
.dw     patt47
.dw     patt48
.dw     patt49
.dw     patt50
.dw     patt51
.dw     patt52
.dw     patt53
.dw     patt54
.dw     patt55
.dw     patt56
.dw     patt57
.dw     patt58
.dw     patt59
lastp:
.dw     lastpat

vibrato_table: ; quite stupid, but it was easy to implement
.dw    0,24/64,49/64,74/64,97/64,120/64,141/64,161/64
.dw    180/64,197/64,212/64,224/64,235/64,244/64,250/64,253/64
.dw    255/64,253/64,250/64,244/64,235/64,224/64,212/64,197/64
.dw    180/64,161/64,141/64,120/64,97/64,74/64,49/64,24/64
.dw    0,-24/64,-49/64,-74/64,-97/64,-120/64,-141/64,-161/64
.dw    -180/64,-197/64,-212/64,-224/64,-235/64
.dw    -244/64,-250/64,-253/64,-255/64,-253/64,-250/64
.dw    -244/64,-235/64,-224/64,-212/64,-197/64
.dw    -180/64,-161/64,-141/64,-120/64
.dw    -97/64,-74/64,-49/64,-24/64
;.dw    0,24,49,74,97,120,141,161,
;.dw    180,197,212,224,235,244,250,253
;.dw    255,253,250,244,235,224,212,197
;.dw    180,161,141,120,97,74,49,24
;.dw    0,-24,-49,-74,-97,-120,-141,-161
;.dw    -180,-197,-212,-224,-235
;.dw    -244,-250,-253,-255,-253,-250
;.dw    -244,-235,-224,-212,-197
;.dw    -180,-161,-141,-120
;.dw    -97,-74,-49,-24

; LOP-BU
