; ======================================
; Odin Tracker 1.00 for C64 by Zed on 15 Feb 2000.
;
; This is the complete source including all data (font, etc) as well.
; Use DASM to compile.
;
; Hacks, unfinished portions are marked with !!!.
;
; Wotan mit uns.
; ======================================

; ======================================
; Memory layout
;
; $0810-$3800     editor code and data
; $3800-$3a00     font
; $3a00-$3fc0       [unused, maybe for some editor hack]
; $3fc0-$4000     sprite cursor
; $4000-$4100     orderlist
; $4100-$4120     song title
; $4120-$4200       [unused, maybe for song info]
; $4200-$4800     patterns
; $4800-$4a00     instruments
; $4a00-$4c00     instrument names
; $4c00-$4d00     wave table
; $4d00-$4e00     arpeggio table
; $4e00-$5000       [unused, maybe for some filter table]
; $5000-$b000     tracks
; $b000-$d000     help text
; ======================================

BASIC           = $0801

EDITOR          = $0810
PLAYER          = $2e00
FONT            = $3800
SPRITE          = $3fc0

; Locations of music data.
ORDERLIST       = $4000         ; 256 bytes for orderlist.
SONGTITLE       = $4100         ; 32 bytes for song title.
PATTERNS        = $4200         ; 256 pattern, 3 track numbers + transpose per pattern, total $600 bytes.
INSTRUMENTS     = $4800         ; 32 instruments, each 16 bytes.
INSTRUMENTNAMES = $4a00         ; 32 instrument names, each 16 bytes.
WAVETABLE       = $4c00         ; 256 bytes for wave table.
ARPEGGIOTABLE   = $4d00         ; 256 bytes for arpeggio table.
TRACKS_BASE     = $5000         ; 128 tracks, each 64*3 bytes.
HELPTEXT        = $b000         ; $2000 bytes for help text.

MAX_ORDERS      = 256
MAX_PATTERS     = 256
MAX_TRACKS      = 128
MAX_INSTRUMENTS = 32
SONGTITLELEN    = 32
INSTRUMENTNAMELEN = 16

; ======================================
; Defines for editor only.
; ======================================

; Kernal data.
status          = $90
shiftflags      = $028d
caseswitch      = $0291

;Kernal routines.
scnkey          = $ff9f
getin           = $ffe4
setnam          = $ffbd
setlfs          = $ffba
load            = $ffd5
save            = $ffd8
chrin           = $ffcf
open            = $ffc0
close           = $ffc3
chkin           = $ffc6
chkout          = $ffc9
ciout           = $ffa8
acptr           = $ffa5
clrchn          = $ffcc
chrout          = $ffd2
plot            = $fff0

; Keycodes returned by getin.
;KEY_                   = $00
;KEY_                   = $01
;KEY_                   = $02
KEY_RUNSTOP             = $03
;KEY_                   = $04
KEY_CTRL_2              = $05
KEY_CTRL_LEFTARROW      = $06
;KEY_                   = $07
;KEY_                   = $08
;KEY_                   = $09
;KEY_                   = $0A
;KEY_                   = $0B
;KEY_                   = $0C
KEY_RETURN              = $0D
;KEY_                   = $0E
;KEY_                   = $0F
;KEY_                   = $10
KEY_DOWN                = $11
KEY_CTRL_9              = $12
KEY_HOME                = $13
KEY_DELETE              = $14
;KEY_                   = $15
;KEY_                   = $16
;KEY_                   = $17
;KEY_                   = $18
;KEY_                   = $19
;KEY_                   = $1A
;KEY_                   = $1B
KEY_CTRL_3              = $1C
KEY_RIGHT               = $1D
KEY_CTRL_6              = $1E
KEY_CTRL_7              = $1F
KEY_SPACE               = $20
KEY_EXCL                = $21
KEY_DOUBLEQUOT          = $22
KEY_HASHMARK            = $23
KEY_DOLLAR              = $24
KEY_PERCENT             = $25
KEY_AMP                 = $26
KEY_QUOT                = $27
KEY_LBRACKET            = $28
KEY_RBRACKET            = $29
KEY_ASTERISK            = $2A
KEY_PLUS                = $2B
KEY_COMMA               = $2C
KEY_MINUS               = $2D
KEY_PERIOD              = $2E
KEY_SLASH               = $2F
KEY_0                   = $30
KEY_1                   = $31
KEY_2                   = $32
KEY_3                   = $33
KEY_4                   = $34
KEY_5                   = $35
KEY_6                   = $36
KEY_7                   = $37
KEY_8                   = $38
KEY_9                   = $39
KEY_COLON               = $3A
KEY_SEMICOLON           = $3B
KEY_LESS                = $3C
KEY_EQUALS              = $3D
KEY_GREATER             = $3E
KEY_QUEST               = $3F
KEY_AT                  = $40                   ; @
KEY_A                   = $41
KEY_B                   = $42
KEY_C                   = $43
KEY_D                   = $44
KEY_E                   = $45
KEY_F                   = $46
KEY_G                   = $47
KEY_H                   = $48
KEY_I                   = $49
KEY_J                   = $4A
KEY_K                   = $4B
KEY_L                   = $4C
KEY_M                   = $4D
KEY_N                   = $4E
KEY_O                   = $4F
KEY_P                   = $50
KEY_Q                   = $51
KEY_R                   = $52
KEY_S                   = $53
KEY_T                   = $54
KEY_U                   = $55
KEY_V                   = $56
KEY_W                   = $57
KEY_X                   = $58
KEY_Y                   = $59
KEY_Z                   = $5A
KEY_LSQRBRACKET         = $5B
KEY_POUND               = $5C
KEY_RSQRBRACKET         = $5D
KEY_UPARROW             = $5E
KEY_LEFTARROW           = $5F
;KEY_                   = $60
;KEY_                   = $61
;KEY_                   = $62
;KEY_                   = $63
;KEY_                   = $64
;KEY_                   = $65
;KEY_                   = $66
;KEY_                   = $67
;KEY_                   = $68
;KEY_                   = $69
;KEY_                   = $6A
;KEY_                   = $6B
;KEY_                   = $6C
;KEY_                   = $6D
;KEY_                   = $6E
;KEY_                   = $6F
;KEY_                   = $70
;KEY_                   = $71
;KEY_                   = $72
;KEY_                   = $73
;KEY_                   = $74
;KEY_                   = $75
;KEY_                   = $76
;KEY_                   = $77
;KEY_                   = $78
;KEY_                   = $79
;KEY_                   = $7A
;KEY_                   = $7B
;KEY_                   = $7C
;KEY_                   = $7D
;KEY_                   = $7E
;KEY_                   = $7F
;KEY_                   = $80
KEY_COMM_1              = $81
;KEY_                   = $82
KEY_SH_RUNSTOP          = $83
;KEY_                   = $84
KEY_F1                  = $85
KEY_F3                  = $86
KEY_F5                  = $87
KEY_F7                  = $88
KEY_F2                  = $89
KEY_F4                  = $8A
KEY_F6                  = $8B
KEY_F8                  = $8C
KEY_SH_RETURN           = $8D
;KEY_                   = $8E
;KEY_                   = $8F
KEY_CTRL_1              = $90
KEY_UP                  = $91
KEY_CTRL_0              = $92
KEY_SH_HOME             = $93
KEY_INSERT              = $94
KEY_COMM_2              = $95
KEY_COMM_3              = $96
KEY_COMM_4              = $97
KEY_COMM_5              = $98
KEY_COMM_6              = $99
KEY_COMM_7              = $9A
KEY_COMM_8              = $9B
KEY_CTRL_5              = $9C
KEY_LEFT                = $9D
KEY_CTRL_8              = $9E
KEY_CTRL_4              = $9F
;KEY_                   = $A0
KEY_COMM_K              = $A1
KEY_COMM_I              = $A2
KEY_COMM_T              = $A3
;KEY_                   = $A4
KEY_COMM_G              = $A5
;KEY_                   = $A6
KEY_COMM_M              = $A7
;KEY_                   = $A8
;KEY_                   = $A9
KEY_COMM_N              = $AA
KEY_COMM_Q              = $AB
KEY_COMM_D              = $AC
KEY_COMM_Z              = $AD
KEY_COMM_S              = $AE
KEY_COMM_P              = $AF
KEY_COMM_A              = $B0
KEY_COMM_E              = $B1
KEY_COMM_R              = $B2
KEY_COMM_W              = $B3
KEY_COMM_H              = $B4
KEY_COMM_J              = $B5
KEY_COMM_L              = $B6
KEY_COMM_Y              = $B7
KEY_COMM_U              = $B8
KEY_COMM_O              = $B9
KEY_SH_AT               = $BA
KEY_COMM_F              = $BB
KEY_COMM_C              = $BC
KEY_COMM_X              = $BD
KEY_COMM_V              = $BE
KEY_COMM_B              = $BF
;KEY_                   = $C0
KEY_SH_A                = $C1
KEY_SH_B                = $C2
KEY_SH_C                = $C3
KEY_SH_D                = $C4
KEY_SH_E                = $C5
KEY_SH_F                = $C6
KEY_SH_G                = $C7
KEY_SH_H                = $C8
KEY_SH_I                = $C9
KEY_SH_J                = $CA
KEY_SH_K                = $CB
KEY_SH_L                = $CC
KEY_SH_M                = $CD
KEY_SH_N                = $CE
KEY_SH_O                = $CF
KEY_SH_P                = $D0
KEY_SH_Q                = $D1
KEY_SH_R                = $D2
KEY_SH_S                = $D3
KEY_SH_T                = $D4
KEY_SH_U                = $D5
KEY_SH_V                = $D6
KEY_SH_W                = $D7
KEY_SH_X                = $D8
KEY_SH_Y                = $D9
KEY_SH_Z                = $DA
;KEY_                   = $DB
;KEY_                   = $DC
;KEY_                   = $DD
KEY_SH_UPARROW          = $DE
;KEY_                   = $DF

; Editor's zero page variables.
timer_hundreds          = $50   ; Timer that measures playing time.
timer_seconds           = $51   ; Timer that measures playing time.
timer_minutes           = $52   ; Timer that measures playing time.

editor_trackptr         = $60   ; Temp 16 bit pointer to track.
editor_temptrackptr     = $62   ; Used when inserting/deleting in track.
editor_instptr          = $64   ; Temp 16 bit pointer to instrument.
editor_patternptr       = $66   ; Pointer to pattern.
stringptr               = $68   ; for console_writestring.
display_datasrc         = $6a   ; For write_list

; Screen identifiers for editor.
EDITOR_ORDER_ID         = 0
EDITOR_PATTERN_ID       = 1
EDITOR_INSTRUMENT_ID    = 2
EDITOR_CONFIGMENU_ID    = 3
EDITOR_SPECMENU_ID      = 4
EDITOR_DISKMENU_ID      = 5

; Block status flags.
BLOCK_BEGINSET          = $01
BLOCK_ENDSET            = $02

; Temporary pointers for track, instruments and patterns.
; The player destroys these.
player_trackptr = $fb           ; Temp 16 bit pointer to track.
player_instptr  = $fd           ; Temp 16 bit pointer to instrument.
player_patternptr = player_instptr     ; Pattern and instrument are never accessed simoultaneously.
; Temporary zeropage variables for multiplication routine.
mul8result      = $02          ; 16 bit result.
mul8n           = $04          ; 16 bit multiplicant.
mul8m           = $06          ; 8 bit multiplicant.

; Offsets in instrument structure.
INST_AD                 = $00  ; Attack/Decay.
INST_SR                 = $01  ; Sustain/Release.
INST_WAVETABLEINDEX     = $02  ; Wave table start.
INST_WAVETABLEEND       = $03  ; Length of wave table.
INST_WAVETABLELOOP      = $04  ; Wave table loop position.
INST_ARPTABLEINDEX      = $05  ; Arpeggio table start
INST_ARPTABLEEND        = $06  ; Length of arpeggio table.
INST_ARPTABLELOOP       = $07  ; Arpeggio table loop position.
INST_VIBDELAY           = $08  ; Number of ticks before starting vibrato.
INST_VIBDEPTH_SPEED     = $09  ; Vibrato speed*$10 + vibrato depth
INST_PULSEWIDTH         = $0a  ; Pulse low/high ratio bits 11..4
INST_PULSESPEED         = $0b  ; Pulse variation speed bits 7..0.
INST_PULSELIMITS        = $0c  ; Pulse variation limit.
                               ;   Low nybble is lower limit's bits 11.8,
                               ;   high nybble is high limit's bits 11.8.
INST_FILTERCUTOFF       = $0d  ; Filter cutoff frequency high byte.
INST_FILTERSPEED        = $0e  ; Cutoff frequency variation speed bits 7..0.
INST_FILTERLIMITS       = $0f  ; Cutoff frequency  variation limits.
                               ;   Low nybble is lower limit's bits 11.8,
                               ;   high nybble is high limit's bits 11.8.

        processor 6502

;        org BASIC
; Basic tokens for the line '2000 SYS2064'
;        .byte $0B, $08, $D0, $07, $9E, $32, $30, $36, $34, $00, $00

; ======================================
; Start of editor.
; ======================================

        org EDITOR

        jsr editor_init

; Main editor loop.
editor_loop:
        jsr getin
        sta editor_currentkey
        cmp #0
        beq el_keyprocessed             ; No key pressed.
        lda editor_whichscreen
        cmp #EDITOR_ORDER_ID
        beq el_editorder
        cmp #EDITOR_PATTERN_ID
        beq el_editpattern
        cmp #EDITOR_INSTRUMENT_ID
        beq el_editinstrument
        cmp #EDITOR_CONFIGMENU_ID
        beq el_configmenu
        cmp #EDITOR_SPECMENU_ID
        beq el_specmenu
        cmp #EDITOR_DISKMENU_ID
        beq el_diskmenu
        brk                             ; !!!! impossible

; Edit orderlist.
el_editorder:
        jsr editor_editorder
        jmp el_processglobalkeys

; Edit pattern.
el_editpattern:
        jsr editor_editpattern
        jmp el_processglobalkeys

; Edit instrument.
el_editinstrument:
        jsr editor_editinstrument
        jmp el_processglobalkeys

; Handle config screen.
el_configmenu:
        jsr editor_configmenu
        jmp el_processglobalkeys

; Handle specials menu.
el_specmenu:
        jsr editor_specmenu
        jmp el_processglobalkeys

; Handle diskmenu.
el_diskmenu:
        jsr editor_diskmenu

; Process global keys.
; If the key was processed by some handler before, editor_currentkey==0
el_processglobalkeys:
        lda editor_currentkey
        beq el_keyprocessed
; Try to find action for this key.
        ldx #0
el_findkey:
        cmp global_keys,x
        beq el_keyfound
        inx
        inx
        inx
        cpx #GLOBAL_KEYS_NUM
        bne el_findkey
        beq el_keyprocessed

; Call event handler for the key found.
el_keyfound:
        lda global_keys+1,x
        ldy global_keys+2,x
        jsr editor_executekey

el_keyprocessed:
        jsr editor_updatescreen
        jmp editor_loop

; Clear editor_currentkey to indicate it is processed.
editor_clearkey:
        lda #0
        sta editor_currentkey
        rts

; ======================================
editor_executekey:
; Call procedure in Y:A.
; ======================================
;  Input: Y:A: address of routine,
; ======================================
        sta editor_executekey0+1
        sty editor_executekey0+2
editor_executekey0:
        jmp $ffff

; ======================================
; Events for global keys.
; ======================================

; Start/stop playing.
eg_playonoff:
        lda editor_isplaying
        beq eg_playonoffstart
; Stop playing.
eg_stopplaying:
; Stop player.
        lda #$00
        sta editor_isplaying
        jsr PLAYER+6
        rts
; Start playing.
eg_playonoffstart:
; Zero timer.
        lda #$00
        sta timer_hundreds
        sta timer_seconds
        sta timer_minutes
        sta editor_currentraster        ; Clear current rastertime.
        sta editor_maxraster            ; Clear max rastertime.
        lda ordernumber
        jsr PLAYER
        lda #$01
        sta editor_isplaying
        rts

; Show context help.
eg_writecontexthelp:
        ldx editor_whichscreen
        lda #12                 ; Page where orderlist help starts.
        cpx #EDITOR_ORDER_ID
        beq eg_writecontexthelp1
        lda #6                  ; Page where pattern editor help starts.
        cpx #EDITOR_PATTERN_ID
        beq eg_writecontexthelp1
        lda #3                  ; Page where inst editor help starts.
        cpx #EDITOR_INSTRUMENT_ID
        beq eg_writecontexthelp1
        lda #13                 ; Page where config menu help starts.
        cpx #EDITOR_CONFIGMENU_ID
        beq eg_writecontexthelp1
        lda #14                 ; Page where specials menu help starts.
        cpx #EDITOR_SPECMENU_ID
        beq eg_writecontexthelp1
        lda #15                 ; Page where disk menu help starts.
        cpx #EDITOR_DISKMENU_ID
        beq eg_writecontexthelp1
; Show all help.
eg_writehelp:
        lda #0
eg_writecontexthelp1:
        ldx #$00
        stx editor_makerowhighlight
        stx $d020
        stx $d021
        ldx #$1b
        stx $d011                       ; Disable ECM.
        ldx #$16
        stx $d018                       ; Set ROM font.
        ldx #$00
        stx $d015                       ; Disable sprite.
        jsr write_help
        jmp editor_reinit

; Go to order editor.
eg_gotoordereditor:
        lda #EDITOR_ORDER_ID
eg_gotoscreen:
        sta editor_whichscreen
        jmp editor_initscreen

; Go to pattern editor.
eg_gotopatterneditor:
        lda #EDITOR_PATTERN_ID
        bne eg_gotoscreen

; Go to instrument editor.
eg_gotoinsteditor:
        lda #EDITOR_INSTRUMENT_ID
        bne eg_gotoscreen

; Go to config menu.
eg_configmenu:
        jsr cm_getcolors
        lda #EDITOR_CONFIGMENU_ID
        bne eg_gotoscreen

; Go to disk menu.
eg_specmenu:
        lda #EDITOR_SPECMENU_ID
        bne eg_gotoscreen

; Go to disk menu.
eg_diskmenu:
        lda #EDITOR_DISKMENU_ID
        bne eg_gotoscreen

; Dec current instrument.
eg_instdec:
        ldx editor_currentinst
        dex
        jmp eg_instinc1

; Inc current instrument.
eg_instinc:
        ldx editor_currentinst
        inx
eg_instinc1:
        txa
        and #MAX_INSTRUMENTS-1
        sta editor_currentinst
        rts

; Dec pattern for current order position.
eg_keypatterndec:
        ldx ordernumber
        dec ORDERLIST,x
        rts

; Inc pattern for current order position.
eg_keypatterninc:
        ldx ordernumber
        inc ORDERLIST,x
        rts

; Move backward in orderlist.
eg_keyorderdec:
        dec ordernumber
        jmp eg_keyorderinc0

; Move forward in orderlist.
eg_keyorderinc:
        inc ordernumber
eg_keyorderinc0:
        ldy editor_isplaying
        lda #0                          ; Avoid calling player inbetween!
        sta editor_isplaying
        lda ordernumber
        sta nextordernumber
        lda #$80
        sta forcenewpattern
        sty editor_isplaying
        rts

; Mute/unmute all channels.
eg_muteall:
        ldx editor_patternchannel
        lda chn_muted,x
        eor #$80
        sta chn_muted+0
        sta chn_muted+1
        sta chn_muted+2
        rts

; Mute/unmute channel 1.
eg_mutechannel1:
        ldx #0
        beq eg_domute
; Mute/unmute channel 2.
eg_mutechannel2:
        ldx #1
        bne eg_domute
; Mute/unmute channel 3.
eg_mutechannel3:
        ldx #2
eg_domute:
        lda chn_muted,x
        eor #$80
        sta chn_muted,x
        rts

; ======================================
editor_editorder:
; Edit orders.
; ======================================
;  Input: editor_currentkey: key from getin.
; ======================================
        lda editor_currentkey
; Try to find action for this key.
        ldx #0
eo_findkey:
        cmp editorder_keys,x
        beq eo_keyfound
        inx
        inx
        inx
        cpx #EDITORDER_KEYS_NUM
        bne eo_findkey

        lda editor_currentkey
        jsr ascii2hexdigit
        bmi eo_done
        ldx editor_ordercol
        php
        ldy ordernumber
        ldx ORDERLIST,y
        plp
        beq eo_highnybble
        jsr replacelownybble
        jmp eo_replaced
eo_highnybble:
        jsr replacehighnybble
eo_replaced:
        sta ORDERLIST,y
        jmp eo_keyprocessed

; Call event handler for the key found.
eo_keyfound:
        jsr editor_clearkey             ; Indicate that key is processed.
        lda editorder_keys+1,x
        ldy editorder_keys+2,x
        jmp editor_executekey

eo_keyprocessed:
        jsr editor_clearkey             ; Indicate that key is processed.
        lda editor_ordercol
        eor #$01
        sta editor_ordercol
        bne eo_done
        jsr eg_keyorderinc
eo_done:
        rts

; ======================================
; Events for keys in orderlist editor.
; ======================================

eo_keyleft:
        lda #0
eo_keyleft1:
        sta editor_ordercol
        rts

eo_keyright:
        lda #1
        bne eo_keyleft1

; Move up 16 lines in orders.
eo_keypgup:
        lda #$f0
eo_keypgup1:
        clc
        adc ordernumber
        sta ordernumber
        rts

; Move down 16 lines in orders.
eo_keypgdn:
        lda #$10
        bne eo_keypgup1

; Goto row 0.
eo_keygotoline00:
        lda #$00
eo_keygotolinea:
        sta ordernumber
        rts
; Goto row $40.
eo_keygotoline40:
        lda #$40
        bne eo_keygotolinea
; Goto row $80.
eo_keygotoline80:
        lda #$80
        bne eo_keygotolinea
; Goto row $c0.
eo_keygotolinec0:
        lda #$c0
        bne eo_keygotolinea

; Insert into order list.
eo_insert:
        lda #MAX_ORDERS-1
        sta list_length
        lda ordernumber
        ldx #<ORDERLIST
        ldy #>ORDERLIST
        jmp list_insert

; Delete from orderlist.
eo_delete:
        lda ordernumber
        beq eo_deletedone
        jsr eg_keyorderdec
        lda #MAX_ORDERS-1
        sta list_length
        lda #$00
        sta list_defaultelement
        txa
        ldx #<ORDERLIST
        ldy #>ORDERLIST
        jmp list_delete
eo_deletedone:
        rts

; ======================================
ep_storeundobuffer:
; Store current track into undo buffer.
; ======================================
;  Input: editor_patternchannel
;         editor_tracks[]
; Output: editor_trackundochannel: = editor_patternchannel
;         editor_trackundobuffer[]
; ======================================
        ldx editor_patternchannel
        stx editor_trackundochannel
        lda editor_tracks,x
        jsr editor_maketrackpointer
        ldy #0
epsub_loop:
        lda (editor_trackptr),y
        sta editor_trackundobuffer,y
        iny
        cpy #192
        bne epsub_loop
        rts

; ======================================
ep_restoreundobuffer:
; Restore track from undo buffer.
; ======================================
;  Input: editor_trackundochannel
;         editor_tracks[]
; Output: editor_trackundochannel: always $80
; ======================================
        ldx editor_trackundochannel
        bmi eprub_done
        lda editor_tracks,x
        jsr editor_maketrackpointer
        ldy #0
eprub_loop:
        lda editor_trackundobuffer,y
        sta (editor_trackptr),y
        iny
        cpy #192
        bne eprub_loop
        lda #$80
        sta editor_trackundochannel
eprub_done:
        rts

; ======================================
editor_editpattern:
; Edit pattern.
; ======================================
;  Input: editor_currentkey: key from getin.
; ======================================
        jsr editor_gettracks
        lda trackrow
        sta editor_getputtrackrow
        jsr editor_gettrackrow
        lda editor_currentkey
; Try to find action for this key.
        ldx #0
ep_findkey:
        cmp editpattern_keys,x
        beq ep_keyfound
        inx
        inx
        inx
        cpx #EDITPATTERN_KEYS_NUM
        bne ep_findkey

        lda editor_currentkey
        ldy editor_patternfield
        beq ep_note
        jsr ascii2hexdigit
        bmi ep_done                     ; Not a hex digit.
        dey
        beq ep_insthi
        dey
        beq ep_instlo
        dey
        beq ep_effect
        dey
        beq ep_effectparhi
        dey
        beq ep_effectparlo
        brk                       ; !!!!! impossible

; Try to identify a note key.
ep_note:
        ldx #NOTEKEYS_NUM-1
ep_findnotekeys:
        cmp notekeys,x
        beq ep_notekey
        dex
        bpl ep_findnotekeys
ep_done:
        rts

; Edit various hex field. A is a hex digit. [$00-$0f]
ep_insthi:
        ldx editor_trackinst
        jsr replacehighnybble
        jmp ep_instmodified
ep_instlo:
        ldx editor_trackinst
        jsr replacelownybble
ep_instmodified:
        cmp #MAX_INSTRUMENTS            ; Do not accept instrument>=$20
        bcs ep_done
        sta editor_trackinst
        jmp ep_patternrowmodified

ep_effect:
        sta editor_trackeffect
        jmp ep_patternrowmodified

ep_effectparhi:
        ldx editor_trackeffectpar
        jsr replacehighnybble
        jmp ep_effectmodified
ep_effectparlo:
        ldx editor_trackeffectpar
        jsr replacelownybble
ep_effectmodified:
        sta editor_trackeffectpar
        jmp ep_patternrowmodified

; Call event handler for the key found.
ep_keyfound:
        jsr editor_clearkey             ; Indicate that key is processed.
        lda editpattern_keys+1,x
        ldy editpattern_keys+2,x
        jmp editor_executekey

ep_notekey:
        inx
        txa
        clc
        adc editor_currentoctave
        cmp #96                         ; Do not allow anything above B-7.
        bcc ep_notekeyok
        lda #96
ep_notekeyok:
        sta editor_tracknote
        lda editor_currentinst          ; Do not enter instrument 0.
        beq ep_patternrowmodified
        sta editor_trackinst
ep_patternrowmodified:
        lda trackrow
        sta editor_getputtrackrow
        jsr editor_puttrackrow
        jsr ep_advancecursor
        jmp editor_clearkey             ; Indicate that key is processed.

; ======================================
ep_advancecursor:
; Advance cursor by number of rows in editor_editstep.
; ======================================
        lda trackrow
        clc
        adc editor_editstep
        and #$3f
        sta trackrow
        rts

; ======================================
; Events for keys in pattern editor.
; ======================================

ep_keyup:
        ldx trackrow
        dex
ep_keyup1:
        txa
        and #$3f
        sta trackrow
        rts

ep_keydown:
        ldx trackrow
        inx
        jmp ep_keyup1

ep_keyleft:
        ldx editor_patternfield
        ldy editor_patternchannel
        dex
        bpl ep_keyleft1
        ldx #5
ep_keyleft2:
        dey
        bpl ep_keyleft1
        ldy #2
ep_keyleft1:
        stx editor_patternfield
        sty editor_patternchannel
        rts

ep_keyright:
        ldx editor_patternfield
        ldy editor_patternchannel
        inx
        cpx #6
        bmi ep_keyright1
ep_keyright2:
        ldx #0
        iny
        cpy #3
        bmi ep_keyright1
        ldy #0
ep_keyright1:
        stx editor_patternfield
        sty editor_patternchannel
        rts

; Move to note field in next channel.
ep_keynextchannel:
        ldy editor_patternchannel
        bpl ep_keyright2                ; short jump

; Move to note field in previous channel.
ep_keyprevchannel:
        ldx #0
        ldy editor_patternchannel
        bpl ep_keyleft2                 ; short jump

; Move up 16 lines in pattern.
ep_keypgup:
        lda trackrow
        sec
        sbc #$10
        and #$3f
        sta trackrow
        rts

; Move down 16 lines in pattern.
ep_keypgdn:
        lda trackrow
        clc
        adc #$10
        and #$3f
        sta trackrow
        rts

; Goto row 0.
ep_keygotoline00:
        lda #0
ep_keygotolinea:
        sta trackrow
        rts
; Goto row 16.
ep_keygotoline16:
        lda #16
        bne ep_keygotolinea
; Goto row 32.
ep_keygotoline32:
        lda #32
        bne ep_keygotolinea
; Goto row 48.
ep_keygotoline48:
        lda #48
        bne ep_keygotolinea

; Inc track for current pattern.
ep_keytrackdec:
; Get pointer to pattern.
        ldy ordernumber
        lda ORDERLIST,y                 ; Get current pattern.
        jsr editor_makepatternpointer
        lda editor_patternchannel
        asl
        tay
        lda (editor_patternptr),y
        sec
        sbc #$01
        and #MAX_TRACKS-1
        sta (editor_patternptr),y
        rts

; Dec track for current pattern.
ep_keytrackinc:
; Get pointer to pattern.
        ldy ordernumber
        lda ORDERLIST,y                 ; Get current pattern.
        jsr editor_makepatternpointer
        lda editor_patternchannel
        asl
        tay
        lda (editor_patternptr),y
        clc
        adc #$01
        and #MAX_TRACKS-1
        sta (editor_patternptr),y
        rts

; Enter track number.
ep_edittracknum:
        lda color_static
        ldx #$80
        jsr console_setcolorandmask
        ldy editor_patternchannel
        lda editor_channelxpositions,y
        clc
        adc #3
        tax
        lda editor_tracks,y
        ldy #6
        jsr console_gethexatxy
        cmp #MAX_TRACKS
        bcc ep_edittracknumvalid
        rts
ep_edittracknumvalid:
        ldx editor_patternchannel
        sta editor_tracks,x
        jmp editor_puttracks

; Clear note or effect.
ep_keyclearnote:
        lda #0
        ldx editor_patternfield
        cpx #3
        bmi ep_keyclearnote1
        sta editor_trackeffect
        sta editor_trackeffectpar
        jmp ep_patternrowmodified
ep_keyclearnote1:
        sta editor_tracknote
        sta editor_trackinst
        jmp ep_patternrowmodified

ep_keyoff:
        lda #97                         ; B-7 + 1
        bne ep_keyclearnote1

; Select octaves.
ep_octave1:
        lda #0
ep_octavedone:
        sta editor_currentoctave
        rts
ep_octave2:
        lda #12
        bne ep_octavedone               ; short jump
ep_octave3:
        lda #24
        bne ep_octavedone               ; short jump
ep_octave4:
        lda #36
        bne ep_octavedone               ; short jump
ep_octave5:
        lda #48
        bne ep_octavedone               ; short jump
ep_octave6:
        lda #60
        bne ep_octavedone               ; short jump
ep_octave7:
        lda #72
        bne ep_octavedone               ; short jump

; Insert trackrow.
ep_insert:
        jsr ep_storeundobuffer
        ldx editor_patternchannel
        lda editor_tracks,x             ; Get track index from current channel.
        jsr editor_maketrackpointer
        lda trackrow
        asl
        adc trackrow
        sta trackrow3
        cmp #63*3
        beq ep_insert1                  ; Nothing to move on last row.
; editor_temptrackptr=editor_trackptr+3
        lda editor_trackptr
        clc
        adc #3
        sta editor_temptrackptr
        lda editor_trackptr+1
        adc #0
        sta editor_temptrackptr+1
        ldy #63*3
ep_insertloop:
        dey
        lda (editor_trackptr),y
        sta (editor_temptrackptr),y
        dey
        lda (editor_trackptr),y
        sta (editor_temptrackptr),y
        dey
        lda (editor_trackptr),y
        sta (editor_temptrackptr),y
        cpy trackrow3
        bne ep_insertloop
ep_insert1:
        ldy trackrow3
        jmp ep_insdelclear

; Backspace patternrow.
ep_delete:
        lda trackrow
        beq ep_delete1                  ; Nothing to do on first row.
        jsr ep_storeundobuffer
        dec trackrow
        ldx editor_patternchannel
        lda editor_tracks,x             ; Get track index from current channel.
        jsr editor_maketrackpointer
        lda trackrow
        asl
        adc trackrow
        tay
; editor_temptrackptr=editor_trackptr+3
        lda editor_trackptr
        clc
        adc #3
        sta editor_temptrackptr
        lda editor_trackptr+1
        adc #0
        sta editor_temptrackptr+1
ep_deleteloop:
        lda (editor_temptrackptr),y
        sta (editor_trackptr),y
        iny
        lda (editor_temptrackptr),y
        sta (editor_trackptr),y
        iny
        lda (editor_temptrackptr),y
        sta (editor_trackptr),y
        iny
        cpy #64*3                       ; Do move until last row.
        bne ep_deleteloop
        dey
        dey
        dey
; Clear row pointed by Y.
ep_insdelclear:
        lda #$00
        sta (editor_trackptr),y
        iny
        sta (editor_trackptr),y
        iny
        sta (editor_trackptr),y
ep_delete1:
        rts

; Grab effect under cursor to effect buffer.
ep_grabeffect:
        lda editor_trackeffect
        sta editor_trackeffectbuf
        lda editor_trackeffectpar
        sta editor_trackeffectparbuf
        rts

; Enter effect from effect buffer to cursor.
ep_dropeffect:
        jsr ep_storeundobuffer
        lda editor_trackeffectbuf
        sta editor_trackeffect
        lda editor_trackeffectparbuf
        sta editor_trackeffectpar
        jmp ep_patternrowmodified

; Set block begin.
ep_setblockbeg:
        lda trackrow
        sta editor_blockbeg
        lda editor_blockflags
        ora #BLOCK_BEGINSET
        ldx editor_patternchannel
        cpx editor_blockchannel
        beq ep_checkblocklimits
        lda #BLOCK_BEGINSET
        bne ep_checkblocklimits         ; short jump

; Set block end.
ep_setblockend:
        lda trackrow
        sta editor_blockend
        lda editor_blockflags
        ora #BLOCK_ENDSET
        ldx editor_patternchannel
        cpx editor_blockchannel
        beq ep_checkblocklimits
        lda #BLOCK_ENDSET
; Force blockbeg<=blockend.
ep_checkblocklimits:
        sta editor_blockflags
        stx editor_blockchannel
        lda editor_blockflags
        and #BLOCK_BEGINSET+BLOCK_ENDSET
        cmp #BLOCK_BEGINSET+BLOCK_ENDSET
        bne ep_checkblocklimits1        ; Begin or end unset -> nothing to do.
        lda editor_blockbeg
        cmp editor_blockend
        bcc ep_checkblocklimits1        ; beg<end -> nothing to do.
        ldy editor_blockend
        sta editor_blockend
        sty editor_blockbeg
ep_checkblocklimits1:
        rts

; Select the whole track.
ep_selecttrack:
        lda editor_patternchannel
        sta editor_blockchannel
        lda #0
        sta editor_blockbeg
        lda #63
        sta editor_blockend
        lda #BLOCK_BEGINSET+BLOCK_ENDSET
        sta editor_blockflags
        rts

; Unselect block.
ep_unselectblock:
        lda #$00
        sta editor_blockflags
        rts

; Cut block.
ep_cutblock:
; Proceed only if block start and end are defined.
        lda editor_blockflags
        cmp #BLOCK_BEGINSET+BLOCK_ENDSET
        beq ep_cutblockvalid
        rts
ep_cutblockvalid:
        jsr ep_storeundobuffer
        lda #$00
        sta editor_trackcopybufferpos
        sta editor_trackcopybufferlen
        lda editor_blockbeg
        sta ep_execblockbeg
        lda editor_blockend
        sta ep_execblockend
        lda editor_blockchannel
        ldx #<ep_cutblockcb
        ldy #>ep_cutblockcb
        jmp ep_execblockop

; Cut block callback for each row.
ep_cutblockcb:
        jsr ep_copyblockcb
        lda #0
        sta editor_tracknote
        sta editor_trackinst
        sta editor_trackeffect
        sta editor_trackeffectpar
        rts

; Copy block.
ep_copyblock:
; Proceed only if block start and end are defined.
        lda editor_blockflags
        cmp #BLOCK_BEGINSET+BLOCK_ENDSET
        beq ep_copyblockvalid
        rts
ep_copyblockvalid:
        lda #$00
        sta editor_trackcopybufferpos
        sta editor_trackcopybufferlen
        lda editor_blockbeg
        sta ep_execblockbeg
        lda editor_blockend
        sta ep_execblockend
        lda editor_blockchannel
        ldx #<ep_copyblockcb
        ldy #>ep_copyblockcb
        jmp ep_execblockop

; Copy block callback for each row.
ep_copyblockcb:
        lda editor_trackcopybufferlen
        asl
        asl
        tax
        lda editor_tracknote
        sta editor_trackcopybuffer,x
        inx
        lda editor_trackinst
        sta editor_trackcopybuffer,x
        inx
        lda editor_trackeffect
        sta editor_trackcopybuffer,x
        inx
        lda editor_trackeffectpar
        sta editor_trackcopybuffer,x
        inc editor_trackcopybufferlen
        rts

; Paste block.
ep_pasteblock:
        jsr ep_storeundobuffer
        lda #$00
        sta editor_trackcopybufferpos
        lda trackrow
        sta ep_execblockbeg
        lda #63
        sta ep_execblockend
        lda editor_patternchannel
        ldx #<ep_pasteblockcb
        ldy #>ep_pasteblockcb
        jmp ep_execblockop

; Paste block callback for each row.
ep_pasteblockcb:
        lda editor_trackcopybufferpos
        cmp editor_trackcopybufferlen   ; Do we still have block data?
        bcs ep_pasteblockcb1
        asl
        asl
        tax
        lda editor_trackcopybuffer,x
        sta editor_tracknote
        inx
        lda editor_trackcopybuffer,x
        sta editor_trackinst
        inx
        lda editor_trackcopybuffer,x
        sta editor_trackeffect
        inx
        lda editor_trackcopybuffer,x
        sta editor_trackeffectpar
        inc editor_trackcopybufferpos
ep_pasteblockcb1:
        rts

; Mix block.
ep_mixblock:
        jsr ep_storeundobuffer
        lda #$00
        sta editor_trackcopybufferpos
        lda trackrow
        sta ep_execblockbeg
        lda #63
        sta ep_execblockend
        lda editor_patternchannel
        ldx #<ep_mixblockcb
        ldy #>ep_mixblockcb
        jmp ep_execblockop

; Mix block callback for each row.
ep_mixblockcb:
        lda editor_trackcopybufferpos
        cmp editor_trackcopybufferlen   ; Do we still have block data?
        bcs ep_mixblockcb1
        asl
        asl
        tax
        lda editor_tracknote
        bne ep_mixblockcbkeepnote
        lda editor_trackcopybuffer,x
        sta editor_tracknote
ep_mixblockcbkeepnote:
        inx
        lda editor_trackinst
        bne ep_mixblockcbkeepinst
        lda editor_trackcopybuffer,x
        sta editor_trackinst
ep_mixblockcbkeepinst:
        inx
        lda editor_trackeffect
        bne ep_mixblockcbkeepeff
        lda editor_trackcopybuffer,x
        sta editor_trackeffect
ep_mixblockcbkeepeff:
        inx
        lda editor_trackeffectpar
        bne ep_mixblockcbkeeppar
        lda editor_trackcopybuffer,x
        sta editor_trackeffectpar
ep_mixblockcbkeeppar:
        inc editor_trackcopybufferpos
ep_mixblockcb1:
        rts

; Transpose block up.
ep_transposeup:
; Proceed only if block start and end are defined.
        lda editor_blockflags
        cmp #BLOCK_BEGINSET+BLOCK_ENDSET
        beq ep_transposeupvalid
        rts
ep_transposeupvalid:
        jsr ep_storeundobuffer
        lda editor_blockbeg
        sta ep_execblockbeg
        lda editor_blockend
        sta ep_execblockend
        lda editor_blockchannel
        ldx #<ep_transposeupcb
        ldy #>ep_transposeupcb
        jmp ep_execblockop

; Transpose block up callback for each row.
ep_transposeupcb:
        lda editor_tracknote
        cmp #2                          ; Do not slide below C-0.
        bcc ep_transposeupcb0
        cmp #96                         ; Do not slide above B-7.
        bcs ep_transposeupcb0
        inc editor_tracknote
ep_transposeupcb0:
        rts

; Transpose block down.
ep_transposedown:
; Proceed only if block start and end are defined.
        lda editor_blockflags
        cmp #BLOCK_BEGINSET+BLOCK_ENDSET
        beq ep_transposedownvalid
        rts
ep_transposedownvalid:
        jsr ep_storeundobuffer
        lda editor_blockbeg
        sta ep_execblockbeg
        lda editor_blockend
        sta ep_execblockend
        lda editor_blockchannel
        ldx #<ep_transposedowncb
        ldy #>ep_transposedowncb
        jmp ep_execblockop

; Transpose block down callback for each row.
ep_transposedowncb:
        lda editor_tracknote
        cmp #2                          ; Do not slide below C-0.
        bcc ep_transposedowncb0
        cmp #97                         ; Do not slide note off.
        beq ep_transposedowncb0
        dec editor_tracknote
ep_transposedowncb0:
        rts

; Inc/dec edit step.
ep_editstepincdec:
        ldx editor_editstep
        inx
        lda shiftflags
        and #$01
        beq ep_editstep1
        dex
        dex
ep_editstep1:
        txa
        and #$3f
        sta editor_editstep
        rts

; Increment track transpose.
ep_transposeinc:
        ldx editor_patternchannel
        inc editor_tracktransposes,x
        jmp editor_puttracks

; Decrement track transpose.
ep_transposedec:
        ldx editor_patternchannel
        dec editor_tracktransposes,x
        jmp editor_puttracks

; Edit track transpose.
ep_edittranspose:
        lda color_static
        ldx #$80
        jsr console_setcolorandmask
        ldy editor_patternchannel
        lda editor_channelxpositions,y
        clc
        adc #8
        tax
        lda editor_tracktransposes,y
        ldy #6
        jsr console_gethexatxy
        tax                             ; Convert transpose from sign bit
        bpl ep_edittransposeup          ; binary to 2's complement.
        sec
        sbc #$01
        eor #$7f
ep_edittransposeup:
        ldx editor_patternchannel
        sta editor_tracktransposes,x
        jmp editor_puttracks

; Channel number and first and last rows touched in block operations.
ep_execblockchannel:    .byte 0
ep_execblockbeg:        .byte 0
ep_execblockend:        .byte 0

; ======================================
ep_execblockop:
; Execute block operation.
; The address of the callback function executed for each row is in Y:X.
; The callback function gets note, instrument and effect data in
; editor_tracknote, editor_trackinst, editor_trackeffect,
; editor_trackeffectpar and should update them if necessary.
; ======================================
;  Input: A: channel where operation is done
;         Y:X: callback function address
;         ep_execblockbeg: first row to touch
;         ep_execblockend: last row to touch
; ======================================
        sta ep_execblockchannel
        stx ep_execblockop0+1
        sty ep_execblockop0+2
epebo_loop:
        lda ep_execblockbeg
        sta editor_getputtrackrow
        ldx ep_execblockchannel
        jsr editor_gettrackrowanychannel
ep_execblockop0:
        jsr $ffff
        ldx ep_execblockchannel
        jsr editor_puttrackrowanychannel
        inc ep_execblockbeg
        lda ep_execblockbeg
        cmp ep_execblockend
        bcc epebo_loop
        beq epebo_loop
        rts

; ======================================
editor_editinstrument:
; Edit instrument.
; ======================================
;  Input: editor_currentkey: key from getin.
; ======================================
        lda editor_currentinst
        beq ei_done1
        jsr editor_makeinstptr

        lda editor_currentkey
; Try to find action for this key.
        ldx #0
ei_findkey:
        cmp editinstrument_keys,x
        beq ei_keyfound
        inx
        inx
        inx
        cpx #EDITINSTRUMENT_KEYS_NUM
        bne ei_findkey

        lda editor_currentkey
        jsr ascii2hexdigit
        bpl ei_validdigit
ei_done1:
        jmp ei_done                     ; Not a hex digit
ei_validdigit:
        ldx editor_instfield
        beq ei_instparams
        dex
        beq ei_wavetable
        dex
        beq ei_arptable
        brk                             ; impossible

ei_instparams:
        ldy editor_instrow
        ldx editor_instcol
        php
        pha
        lda (editor_instptr),y
        tax
        pla
        plp
        beq ei_instparamshigh
        jsr replacelownybble
        jmp ei_instparams0
ei_instparamshigh:
        jsr replacehighnybble
ei_instparams0:
        sta (editor_instptr),y
        jmp ei_keyprocessed

ei_wavetable:
        ldy editor_waveindex
        ldx editor_instcol
        php
        ldx WAVETABLE,y
        plp
        beq ei_wavetablehigh
        jsr replacelownybble
        jmp ei_wavetable0
ei_wavetablehigh:
        jsr replacehighnybble
ei_wavetable0:
        sta WAVETABLE,y
        jmp ei_keyprocessed

ei_arptable:
        ldy editor_arpindex
        ldx editor_instcol
        php
        ldx ARPEGGIOTABLE,y
        plp
        beq ei_arptablehigh
        jsr replacelownybble
        jmp ei_arptable0
ei_arptablehigh:
        jsr replacehighnybble
ei_arptable0:
        sta ARPEGGIOTABLE,y
        jmp ei_keyprocessed

; Call event handler for the key found.
ei_keyfound:
        jsr editor_clearkey             ; Indicate that key is processed.
        lda editinstrument_keys+1,x
        ldy editinstrument_keys+2,x
        jmp editor_executekey

ei_keyprocessed:
        jsr editor_clearkey             ; Indicate that key is processed.
        lda editor_instcol
        eor #$01
        sta editor_instcol
        bne ei_done
        jsr ei_keydown
ei_done:
        rts

; ======================================
; Events for keys in instrument editor.
; ======================================

ei_keyup:
        lda #$ff
        clc
        ldx editor_instfield
        dex
        beq ei_keypgupwave
        dex
        beq ei_keypguparp
        ldx editor_instrow
        dex
ei_keyup1:
        txa
        and #$0f
        sta editor_instrow
        rts

ei_keydown:
        lda #$01
        clc
        ldx editor_instfield
        dex
        beq ei_keypgupwave
        dex
        beq ei_keypguparp
        ldx editor_instrow
        inx
        jmp ei_keyup1

ei_keyleft:
        lda #0
ei_keyleft1:
        sta editor_instcol
        rts

ei_keyright:
        lda #1
        bne ei_keyleft1

; Move to next field.
ei_keynextfield:
        ldx editor_instfield
        inx
        cpx #3
        bmi ei_keynextfield1
        ldx #0
ei_keynextfield1:
        stx editor_instfield
        rts

; Move to previous field.
ei_keyprevfield:
        ldx editor_instfield
        dex
        bpl ei_keyprevfield1
        ldx #2
ei_keyprevfield1:
        stx editor_instfield
        rts

; Move up 16 lines in table.
ei_keypgup:
        lda #$f0
        clc
        ldx editor_instfield
        dex
        beq ei_keypgupwave
        dex
        beq ei_keypguparp
        rts
ei_keypgupwave:
        adc editor_waveindex
        sta editor_waveindex
        rts
ei_keypguparp:
        adc editor_arpindex
        sta editor_arpindex
        rts

; Move down 16 lines in table.
ei_keypgdn:
        lda #$10
        clc
        ldx editor_instfield
        dex
        beq ei_keypgupwave
        dex
        beq ei_keypguparp
        rts

; Goto row 0.
ei_keygotoline00:
        lda #$00
ei_keygotolinea:
        ldx editor_instfield
        dex
        beq ei_keygotolineawave
        dex
        beq ei_keygotolineaarp
        rts
ei_keygotolineawave:
        sta editor_waveindex
        rts
ei_keygotolineaarp:
        sta editor_arpindex
        rts
; Goto row $40.
ei_keygotoline40:
        lda #$40
        bne ei_keygotolinea
; Goto row $80.
ei_keygotoline80:
        lda #$80
        bne ei_keygotolinea
; Goto row $c0.
ei_keygotolinec0:
        lda #$c0
        bne ei_keygotolinea

; Go to wave or instrument table to position under cursor.
ei_keygototable:
        ldx #0
        lda editor_instfield            ; If not in inst params, do nothing.
        bne ei_keygototable1
        lda editor_currentinst
        jsr editor_makeinstptr
        ldy editor_instrow
        cpy #INST_WAVETABLEINDEX
        bmi ei_keygototable0            ; If not in table pointers, do nothing.
        cpy #INST_ARPTABLELOOP+1
        bpl ei_keygototable0            ; If not in table pointers, do nothing.
        lda #0
        sta editor_instcol
        ldx #1
        lda (editor_instptr),y
        cpy #INST_ARPTABLEINDEX
        bmi ei_keygototablewave
        sta editor_arpindex
        inx
        bne ei_keygototable1
ei_keygototablewave:
        sta editor_waveindex
ei_keygototable1:
        stx editor_instfield
ei_keygototable0:
        rts

; Insert into wave or arpeggio table.
ei_insert:
        ldx editor_instfield
        dex
        beq ei_insertwave
        dex
        beq ei_insertarp
        rts
ei_insertwave:
        lda #$ff
        sta list_length
        lda editor_waveindex
        ldx #<WAVETABLE
        ldy #>WAVETABLE
        jsr list_insert
; Update all indices that affected by the insertion.
        lda editor_waveindex
        ldy #INST_WAVETABLEINDEX
        jsr ei_insertfix
        lda editor_waveindex
        ldy #INST_WAVETABLEEND
        jsr ei_insertfix
        lda editor_waveindex
        ldy #INST_WAVETABLELOOP
        jmp ei_insertfix
ei_insertarp:
        lda #$ff
        sta list_length
        lda editor_arpindex
        ldx #<ARPEGGIOTABLE
        ldy #>ARPEGGIOTABLE
        jsr list_insert
; Update all indices that affected by the insertion.
        lda editor_arpindex
        ldy #INST_ARPTABLEINDEX
        jsr ei_insertfix
        lda editor_arpindex
        ldy #INST_ARPTABLEEND
        jsr ei_insertfix
        lda editor_arpindex
        ldy #INST_ARPTABLELOOP
        jmp ei_insertfix

; Update wave and arpeggio table indices that have affected by insertion.
; A: editor_waveindex or editor_arpindex
; Y: INST_WAVETABLEINDEX or INST_WAVETABLEEND or INST_WAVETABLELOOP
;    INST_ARPTABLEINDEX  or INST_ARPTABLEEND    or INST_ARPTABLELOOP
ei_insertfix:
        sta ei_insertfixindex+1
        sty ei_insertfixfield+1
        ldx #MAX_INSTRUMENTS-1
ei_insertfixloop:
        txa
        jsr editor_makeinstptr
ei_insertfixfield:
        ldy #$ff
        lda (editor_instptr),y
        cmp #$ff                        ; If index was $ff, never increment.
        beq ei_insertfix1
ei_insertfixindex:
        cmp #$ff
        beq ei_insertfix1
        bcc ei_insertfix1
        clc
        adc #1
        sta (editor_instptr),y
ei_insertfix1:
        dex
        bne ei_insertfixloop
        rts

; Delete from wave or arpeggio table.
ei_delete:
        ldx editor_instfield
        dex
        beq ei_deletewave
        dex
        beq ei_deletearp
ei_deletedone:
        rts
ei_deletewave:
        ldx editor_waveindex
        beq ei_deletedone
        dex
        stx editor_waveindex
        lda #$ff
        sta list_length
        lda #$00
        sta list_defaultelement
        txa
        ldx #<WAVETABLE
        ldy #>WAVETABLE
        jsr list_delete
; Update all indices that affected by the deletion.
        lda editor_waveindex
        ldy #INST_WAVETABLEINDEX
        jsr ei_deletefix
        lda editor_waveindex
        ldy #INST_WAVETABLEEND
        jsr ei_deletefix
        lda editor_waveindex
        ldy #INST_WAVETABLELOOP
        jmp ei_deletefix
ei_deletearp:
        ldx editor_arpindex
        beq ei_deletedone
        dex
        stx editor_arpindex
        lda #$ff
        sta list_length
        lda #$00
        sta list_defaultelement
        txa
        ldx #<ARPEGGIOTABLE
        ldy #>ARPEGGIOTABLE
        jsr list_delete
; Update all indices that affected by the deletion.
        lda editor_arpindex
        ldy #INST_ARPTABLEINDEX
        jsr ei_deletefix
        lda editor_arpindex
        ldy #INST_ARPTABLEEND
        jsr ei_deletefix
        lda editor_arpindex
        ldy #INST_ARPTABLELOOP
        jmp ei_deletefix

; Update wave and arpeggio table indices that have affected by deletion.
; A: editor_waveindex or editor_arpindex
; Y: INST_WAVETABLEINDEX or INST_WAVETABLEEND or INST_WAVETABLELOOP
;    INST_ARPTABLEINDEX  or INST_ARPTABLEEND    or INST_ARPTABLELOOP
ei_deletefix:
        sta ei_deletefixindex+1
        sty ei_deletefixfield+1
        ldx #MAX_INSTRUMENTS-1
ei_deletefixloop:
        txa
        jsr editor_makeinstptr
ei_deletefixfield:
        ldy #$ff
        lda (editor_instptr),y
ei_deletefixindex:
        cmp #$ff
        beq ei_deletefix1
        bcc ei_deletefix1
        sec
        sbc #1
        sta (editor_instptr),y
ei_deletefix1:
        dex
        bne ei_deletefixloop
        rts

; Edit instrument name.
ei_editname:
        lda editor_currentinst
        jsr editor_makeinstnameptr
        lda #INSTRUMENTNAMELEN
        ldx #5
        ldy #6
        jmp console_getstringatxy

; Copy instrument to buffer.
ei_copyinst:
; Copy instrument data and name in one go, both is 16 bytes.
        lda editor_currentinst
        jsr editor_makeinstnameptr
        lda editor_currentinst
        jsr editor_makeinstptr
        ldy #INSTRUMENTNAMELEN-1
ei_copyinstloop:
        lda (editor_instptr),y
        sta editor_instcopybuffer,y
        lda (stringptr),y
        sta editor_instnamecopybuffer,y
        dey
        bpl ei_copyinstloop
; Indicate valid data in instrument copy boffer.
        lda #1
        sta editor_instcopybuffervalid
        rts

; Paste instrument from buffer.
ei_pasteinst:
; If there is no valid data in instrument copy boffer, do nothing.
        lda editor_instcopybuffervalid
        beq ei_pasteinstdone
; Copy instrument data and name in one go, both is 16 bytes.
        lda editor_currentinst
        jsr editor_makeinstnameptr
        lda editor_currentinst
        jsr editor_makeinstptr
        ldy #INSTRUMENTNAMELEN-1
ei_pasteinstloop:
        lda editor_instcopybuffer,y
        sta (editor_instptr),y
        lda editor_instnamecopybuffer,y
        sta (stringptr),y
        dey
        bpl ei_pasteinstloop
ei_pasteinstdone:
        rts

; ======================================
editor_configmenu:
; ======================================
;  Input: editor_currentkey: key from getin.
; ======================================
        lda editor_currentkey
; Try to find action for this key.
        ldx #0
cm_findkey:
        cmp configmenu_keys,x
        beq cm_keyfound
        inx
        inx
        inx
        cpx #CONFIGMENU_KEYS_NUM
        bne cm_findkey

        lda editor_currentkey
        jsr ascii2hexdigit
        bmi cm_done                     ; Not a hex digit
        ldy editor_configrow
        ldx editor_configcolors,y
        jsr replacelownybble
        sta editor_configcolors,y
        jmp cm_done

; Call event handler for the key found.
cm_keyfound:
        jsr editor_clearkey             ; Indicate that key is processed.
        lda configmenu_keys+1,x
        ldy configmenu_keys+2,x
        jmp editor_executekey

cm_done:
        rts

; ======================================
; Events for keys in config menu.
; ======================================

cm_keyup:
        ldx editor_configrow
        dex
        bpl cm_keyup1
        ldx #EDITOR_COLORSNUM-1
cm_keyup1:
        stx editor_configrow
        rts

cm_keydown:
        ldx editor_configrow
        inx
        cpx #EDITOR_COLORSNUM
        bmi cm_keydown1
        ldx #0
cm_keydown1:
        stx editor_configrow
        rts

; Copy colors to edit buffer.
cm_getcolors:
        ldx #EDITOR_COLORSNUM-1
cm_getcolorsloop:
        lda editor_colors,x
        sta editor_configcolors,x
        dex
        bpl cm_getcolorsloop
        rts

; Copy colors to real colors and reinit screen to take effect.
cm_putcolors:
        ldx #EDITOR_COLORSNUM-1
cm_putcolorsloop:
        lda editor_configcolors,x
        sta editor_colors,x
        dex
        bpl cm_putcolorsloop
        jmp editor_reinit

; Get default color sets .
cm_colorset1:
        ldx #EDITOR_COLORSNUM-1
        bne cm_colorsetx
cm_colorset2:
        ldx #EDITOR_COLORSNUM+EDITOR_COLORSNUM-1
        bne cm_colorsetx
cm_colorset3:
        ldx #2*EDITOR_COLORSNUM+EDITOR_COLORSNUM-1
        bne cm_colorsetx
cm_colorset4:
        ldx #3*EDITOR_COLORSNUM+EDITOR_COLORSNUM-1
cm_colorsetx:
        ldy #EDITOR_COLORSNUM-1
cm_colorsetxloop:
        lda editor_colorsets,x
        sta editor_colors,y
        dex
        dey
        bpl cm_colorsetxloop
        jmp editor_reinit

; ======================================
editor_specmenu:
; ======================================
;  Input: editor_currentkey: key from getin.
; ======================================
        lda editor_currentkey
; Try to find action for this key.
        ldx #0
sm_findkey:
        cmp specmenu_keys,x
        beq sm_keyfound
        inx
        inx
        inx
        cpx #SPECMENU_KEYS_NUM
        bne sm_findkey
        beq sm_done

; Call event handler for the key found.
sm_keyfound:
        jsr editor_clearkey             ; Indicate that key is processed.
        lda specmenu_keys+1,x
        ldy specmenu_keys+2,x
        jmp editor_executekey

sm_done:
        rts

; ======================================
; Events for keys in specials menu.
; ======================================

; Edit song title.
sm_edittitle:
        lda #<SONGTITLE
        sta stringptr
        lda #>SONGTITLE
        sta stringptr+1
        lda #SONGTITLELEN
        ldx #6
        ldy #6
        jmp console_getstringatxy

; Clear order list, patterns, tracks.
sm_clearsong:
        ldx #<clearsongtext
        ldy #>clearsongtext
        jsr sm_getconfirmation
        tax
        beq sm_clearsong0
; Stop player.
        lda #$00
        sta editor_isplaying
        jsr PLAYER+6
        jsr clear_song
sm_clearsong0:
        rts

; Clear instruments.
sm_clearinstruments:
        ldx #<clearinsttext
        ldy #>clearinsttext
        jsr sm_getconfirmation
        tax
        beq sm_clearinstruments0
; Stop player.
        lda #$00
        sta editor_isplaying
        jsr PLAYER+6
        jsr clear_instruments
sm_clearinstruments0:
        rts

; Used in replace instruments in tracks/patterns.
sm_replaceinstnum:      .byte 0
sm_replacewithinstnum:  .byte 0

; Replace instrument in tracks.
sm_replaceinsttrack:
; Get instrument numbers.
        jsr sm_getreplaceinstnum
        jsr sm_getreplacewithinstnum
; Get first and last track number.
        jsr sm_getfirsttrack
        jsr sm_getlasttrack
; If last<first, do nothing.
        lda sm_lasttrack
        cmp sm_firsttrack
        bcc smrit_done
; Get confirmation.
        ldx #<areyousuretext
        ldy #>areyousuretext
        jsr sm_getconfirmation
        tax
        beq smrit_done
; Stop player.
        lda #$00
        sta editor_isplaying
        jsr PLAYER+6
; Do for all tracks between sm_firsttrack and sm_lasttrack.
smrit_trackloop:
        lda sm_firsttrack
        jsr sm_replaceinst
; Advance to next track.
        lda sm_firsttrack
        cmp sm_lasttrack
        beq smrit_done
        inc sm_firsttrack
        bne smrit_trackloop
smrit_done:
        rts

; Replace instrument in patterns.
; sm_firsttrack and sm_lasttrack are pattern numbers here!
sm_replaceinstpattern:
; Get instrument numbers.
        jsr sm_getreplaceinstnum
        jsr sm_getreplacewithinstnum
; Get first and last pattern number.
        jsr sm_getfirstpattern
        jsr sm_getlastpattern
; If last<first, do nothing.
        lda sm_lasttrack
        cmp sm_firsttrack
        bcc smrip_done
; Get confirmation.
        ldx #<areyousuretext
        ldy #>areyousuretext
        jsr sm_getconfirmation
        tax
        beq smrip_done
; Stop player.
        lda #$00
        sta editor_isplaying
        jsr PLAYER+6
; Do for all patterns between sm_firsttrack and sm_lasttrack.
smrip_patternloop:
        lda sm_firsttrack
        jsr editor_makepatternpointer
        ldy #0
        lda (editor_patternptr),y
        jsr sm_replaceinst
        ldy #2
        lda (editor_patternptr),y
        jsr sm_replaceinst
        ldy #4
        lda (editor_patternptr),y
        jsr sm_replaceinst
; Advance to next pattern.
        lda sm_firsttrack
        cmp sm_lasttrack
        beq smrip_done
        inc sm_firsttrack
        bne smrip_patternloop
smrip_done:
        rts

; Replace instruments in track number A.
sm_replaceinst:
        jsr editor_maketrackpointer
; Do for rows 0..63 in each track.
        lda #0
        sta editor_getputtrackrow
smri_rowloop:
        lda editor_getputtrackrow
        cmp #64
        beq smri_done
        jsr editor_gettrackrowany
        lda editor_trackinst
        cmp sm_replaceinstnum
        bne smri_cont
        lda sm_replacewithinstnum
        sta editor_trackinst
        jsr editor_puttrackrowany
smri_cont:
        inc editor_getputtrackrow
        bne smri_rowloop
smri_done:
        rts

; Swap tracks 1-2 in patterns.
; sm_firsttrack and sm_lasttrack are pattern numbers here!
sm_swap12:
; Get first and last pattern number.
        jsr sm_getfirstpattern
        jsr sm_getlastpattern
; If last<first, do nothing.
        lda sm_lasttrack
        cmp sm_firsttrack
        bcc sm_swap12done
; Get confirmation.
        ldx #<areyousuretext
        ldy #>areyousuretext
        jsr sm_getconfirmation
        tax
        beq sm_swap12done
; Stop player.
        lda #$00
        sta editor_isplaying
        jsr PLAYER+6
sm_swap12loop:
        lda sm_firsttrack
        jsr editor_makepatternpointer
        ldy #0
        lda (editor_patternptr),y
        pha
        iny
        lda (editor_patternptr),y
        pha
        ldy #2
        lda (editor_patternptr),y
        ldy #0
        sta (editor_patternptr),y
        ldy #3
        lda (editor_patternptr),y
        ldy #1
        sta (editor_patternptr),y
        pla
        ldy #3
        sta (editor_patternptr),y
        pla
        dey
        sta (editor_patternptr),y
; Advance to next pattern.
        lda sm_firsttrack
        cmp sm_lasttrack
        beq sm_swap12done
        inc sm_firsttrack
        bne sm_swap12loop
sm_swap12done:
        rts

; Swap tracks 2-3 in patterns.
; sm_firsttrack and sm_lasttrack are pattern numbers here!
sm_swap23:
; Get first and last pattern number.
        jsr sm_getfirstpattern
        jsr sm_getlastpattern
; If last<first, do nothing.
        lda sm_lasttrack
        cmp sm_firsttrack
        bcc sm_swap23done
; Get confirmation.
        ldx #<areyousuretext
        ldy #>areyousuretext
        jsr sm_getconfirmation
        tax
        beq sm_swap23done
; Stop player.
        lda #$00
        sta editor_isplaying
        jsr PLAYER+6
sm_swap23loop:
        lda sm_firsttrack
        jsr editor_makepatternpointer
        ldy #2
        lda (editor_patternptr),y
        pha
        iny
        lda (editor_patternptr),y
        pha
        ldy #4
        lda (editor_patternptr),y
        ldy #2
        sta (editor_patternptr),y
        ldy #5
        lda (editor_patternptr),y
        ldy #3
        sta (editor_patternptr),y
        pla
        ldy #5
        sta (editor_patternptr),y
        pla
        dey
        sta (editor_patternptr),y
; Advance to next pattern.
        lda sm_firsttrack
        cmp sm_lasttrack
        beq sm_swap23done
        inc sm_firsttrack
        bne sm_swap23loop
sm_swap23done:
        rts

; Swap tracks 3-1 in patterns.
; sm_firsttrack and sm_lasttrack are pattern numbers here!
sm_swap31:
; Get first and last pattern number.
        jsr sm_getfirstpattern
        jsr sm_getlastpattern
; If last<first, do nothing.
        lda sm_lasttrack
        cmp sm_firsttrack
        bcc sm_swap31done
; Get confirmation.
        ldx #<areyousuretext
        ldy #>areyousuretext
        jsr sm_getconfirmation
        tax
        beq sm_swap31done
; Stop player.
        lda #$00
        sta editor_isplaying
        jsr PLAYER+6
sm_swap31loop:
        lda sm_firsttrack
        jsr editor_makepatternpointer
        ldy #4
        lda (editor_patternptr),y
        pha
        iny
        lda (editor_patternptr),y
        pha
        ldy #0
        lda (editor_patternptr),y
        ldy #4
        sta (editor_patternptr),y
        ldy #1
        lda (editor_patternptr),y
        ldy #5
        sta (editor_patternptr),y
        pla
        ldy #1
        sta (editor_patternptr),y
        pla
        dey
        sta (editor_patternptr),y
; Advance to next pattern.
        lda sm_firsttrack
        cmp sm_lasttrack
        beq sm_swap31done
        inc sm_firsttrack
        bne sm_swap31loop
sm_swap31done:
        rts

; ======================================
sm_getconfirmation:
; Ask for confirmation for destructive operation.
; ======================================
;  Input: Y:X: pointer to prompt
; Output: A: 0: no
;            1: yes
; ======================================
        stx stringptr
        sty stringptr+1
        lda color_static
        ldx #$00
        jsr console_setcolorandmask
        ldx #0
        ldy #24
        jsr console_setcursorpos
        jsr console_writestring
        ldx #<confirmationtext
        ldy #>confirmationtext
        jsr console_writestringxy
        jsr console_hidespritecursor
        jsr console_waitkey
        tax
        lda #$01
        cpx #KEY_Y
        beq smgc_done
        lda #$00
smgc_done:
        pha
        jsr editor_initscreen
        pla
        rts

; First and last track number used when reading/writing tracks
; and replacing instruments.
; Also first/last pattern numbers when replacing instruments
; and swapping track.
sm_firsttrack:  .byte 0
sm_lasttrack:   .byte 0

; ======================================
sm_getfirsttrack:
; Get first track number for read/write tracks and replace instrument ops.
; ======================================
; Output: sm_firsttrack
;         A: =sm_firsttrack
; ======================================
        lda color_static
        ldx #$00
        jsr console_setcolorandmask
        ldx #0
        ldy #23
        jsr console_setcursorpos
        ldx #<firsttracktext
        ldy #>firsttracktext
        jsr console_writestringxy
        lda #$80
        jsr console_setcolormask
        lda #$00
        ldx #13
        ldy #23
        jsr console_gethexatxy
        sta sm_firsttrack
        rts

; ======================================
sm_getlasttrack:
; Get last track number for write tracks and replace instrument ops.
; ======================================
; Output: sm_lasttrack
;         A: =sm_lasttrack
; ======================================
        lda color_static
        ldx #$00
        jsr console_setcolorandmask
        ldx #20
        ldy #23
        jsr console_setcursorpos
        ldx #<lasttracktext
        ldy #>lasttracktext
        jsr console_writestringxy
        lda #$80
        jsr console_setcolormask
        lda #$00
        ldx #31
        ldy #23
        jsr console_gethexatxy
        sta sm_lasttrack
        rts

; ======================================
sm_getfirstpattern:
; Get first pattern number for read/write patterns and replace instrument ops.
; ======================================
; Output: sm_firsttrack
;         A: =sm_firsttrack
; ======================================
        lda color_static
        ldx #$00
        jsr console_setcolorandmask
        ldx #0
        ldy #23
        jsr console_setcursorpos
        ldx #<firstpatterntext
        ldy #>firstpatterntext
        jsr console_writestringxy
        lda #$80
        jsr console_setcolormask
        lda #$00
        ldx #13
        ldy #23
        jsr console_gethexatxy
        sta sm_firsttrack
        rts

; ======================================
sm_getlastpattern:
; Get last pattern number for write patterns and replace instrument ops.
; ======================================
; Output: sm_lasttrack
;         A: =sm_lasttrack
; ======================================
        lda color_static
        ldx #$00
        jsr console_setcolorandmask
        ldx #20
        ldy #23
        jsr console_setcursorpos
        ldx #<lastpatterntext
        ldy #>lastpatterntext
        jsr console_writestringxy
        lda #$80
        jsr console_setcolormask
        lda #$00
        ldx #31
        ldy #23
        jsr console_gethexatxy
        sta sm_lasttrack
        rts

; ======================================
sm_getreplaceinstnum:
; Get instrument number that will be replaced.
; ======================================
; Output: sm_replaceinstnum
;         A: =sm_replaceinstnum
; ======================================
        lda color_static
        ldx #$00
        jsr console_setcolorandmask
        ldx #0
        ldy #22
        jsr console_setcursorpos
        ldx #<replaceinsttext
        ldy #>replaceinsttext
        jsr console_writestringxy
        lda #$80
        jsr console_setcolormask
        lda #$00
        ldx #13
        ldy #22
        jsr console_gethexatxy
        sta sm_replaceinstnum
        rts

; ======================================
sm_getreplacewithinstnum:
; Get instrument number that replaces.
; ======================================
; Output: sm_replacewithinstnum
;         A: =sm_replacewithinstnum
; ======================================
        lda color_static
        ldx #$00
        jsr console_setcolorandmask
        ldx #20
        ldy #22
        jsr console_setcursorpos
        ldx #<replacewithinsttext
        ldy #>replacewithinsttext
        jsr console_writestringxy
        lda #$80
        jsr console_setcolormask
        lda #$00
        ldx #36
        ldy #22
        jsr console_gethexatxy
        sta sm_replacewithinstnum
        rts

; ======================================
editor_diskmenu:
; ======================================
;  Input: editor_currentkey: key from getin.
; ======================================
        lda editor_currentkey
; Try to find action for this key.
        ldx #0
dm_findkey:
        cmp diskmenu_keys,x
        beq dm_keyfound
        inx
        inx
        inx
        cpx #DISKMENU_KEYS_NUM
        bne dm_findkey
        beq dm_done

; Call event handler for the key found.
dm_keyfound:
        jsr editor_clearkey             ; Indicate that key is processed.
        lda diskmenu_keys+1,x
        ldy diskmenu_keys+2,x
        jmp editor_executekey

dm_done:
        rts

; ======================================
; Events for keys in diskmenu.
; ======================================

; Cycle through device numbers 8, 9, 10, 11.
dm_changedevice:
        ldx editor_diskdevice
        inx
        cpx #12
        bmi dm_changedevice0
        ldx #8
dm_changedevice0:
        stx editor_diskdevice
        rts

; Temporary data for directory routine.
dm_directoryname:       .byte "$"

dm_directory:
; Stop playing.
        lda #$00
        sta editor_isplaying
        jsr PLAYER+6
; Set colors, ROM font, sprite off.
        ldx #$00
        stx editor_makerowhighlight
        stx $d020
        stx $d021
        ldx #$1b
        stx $d011                       ; Disable ECM.
        ldx #$14
        stx $d018                       ; Set ROM font.
        ldx #$00
        stx $d015                       ; Disable sprite.
; Clear screen.
        lda #155                        ; Light grey characters.
        jsr chrout
        lda #147                        ; Clear screen.
        jsr chrout
; Open directory file.
        lda #$02
        ldy #$00
        ldx editor_diskdevice
        jsr setlfs
        lda #$01
        ldx #<dm_directoryname
        ldy #>dm_directoryname
        jsr setnam
        jsr open
        ldx #$02
        jsr chkin
        jsr acptr
        jsr acptr
dmd_loop:
        lda shiftflags                  ; Pause while SHIFT is pressed.
        lsr
        bcs dmd_loop
        lda status
        bne dmd_end
        jsr acptr
        cmp #$01
        bne dmd_loop
        jsr acptr
        lda status
        bne dmd_end
        jsr acptr
        sta stringptr
        jsr acptr
        sta stringptr+1
        jsr dmd_printdec
dmd_nameloop:
        lda status
        bne dmd_end
        jsr acptr
        cmp #$00
        beq dmd_nameend
        jsr chrout
        jmp dmd_nameloop
dmd_nameend:
        lda #13
        jsr chrout
        jmp dmd_loop
dmd_end:
        lda #$02
        jsr close
        jsr clrchn
        jsr console_waitkey
        jmp editor_reinit

; Flag indicating if we have already printed a digit.
dm_digitprinted:       .byte 0

; Print 16 bit number in stringptr, stringptr+1 in dec.
dmd_printdec:
        ldx #0
        stx dm_digitprinted     ; Indicates if we have already printed a digit.
dmdpd_100loop:
        lda stringptr+1
        bne dmdpd_100sub
        lda stringptr
        cmp #<100
        bcc dmdpd_100ok
dmdpd_100sub:
        lda stringptr
        sec
        sbc #<100
        sta stringptr
        lda stringptr+1
        sbc #0
        sta stringptr+1
        inx
        bne dmdpd_100loop                 ; short jump.
dmdpd_100ok:
        txa
        beq dmdpd_10
        inc dm_digitprinted
        clc
        adc #"0"
        jsr chrout
dmdpd_10:
        ldx #0
dmdpd_10loop:
        lda stringptr+1
        bne dmdpd_10sub
        lda stringptr
        cmp #<10
        bcc dmdpd_10ok
dmdpd_10sub:
        lda stringptr
        sec
        sbc #<10
        sta stringptr
        lda stringptr+1
        sbc #0
        sta stringptr+1
        inx
        bne dmdpd_10loop                 ; short jump.
dmdpd_10ok:
        txa
        clc
        adc #"0"
        ldx dm_digitprinted
        bne dmdpd_10print
        cmp #"0"
        beq dmdpd_1
dmdpd_10print:
        jsr chrout
dmdpd_1:
        lda stringptr
        clc
        adc #"0"
        jsr chrout
        lda #$20
        jmp chrout

; Read filename, if non-empty, clear current song and load.
dm_loadsong:
        jsr dm_getfilename
        lda editor_filenamelen          ; No name given.
        beq dm_loadsongdone
; Stop player.
        lda #$00
        sta editor_isplaying
        jsr PLAYER+6
        jsr dm_setdeviceandfilename
; Clear everything from the current song.
        jsr clear_song
        jsr clear_instruments
; Load into $4000.
        lda #0
        ldx #<ORDERLIST
        ldy #>ORDERLIST
        jsr load
; Redraw screen to clear the 'enter filename' line.
dm_loadsongdone:
        jmp editor_initscreen

; Temporary variables used when finding out last track to save.
dm_savesongpattern:     .byte 0
dm_savesonglasttrack:   .byte 0

; Read filename, if non-empty, find last track and save unpacked memory.
dm_savesong:
        jsr dm_getfilename
        lda editor_filenamelen          ; No name given.
        beq dm_savesongdone
; Stop player.
        lda #$00
        sta editor_isplaying
        jsr PLAYER+6
; Find out last track used.
        lda #$00
        sta dm_savesongpattern
        sta dm_savesonglasttrack
dmss_findlasttrack:
        lda dm_savesongpattern
        jsr editor_makepatternpointer
        ldy #0
        lda (editor_patternptr),y
        cmp dm_savesonglasttrack
        bcc dmss_findlasttrack1
        sta dm_savesonglasttrack
dmss_findlasttrack1:
        iny
        iny
        lda (editor_patternptr),y
        cmp dm_savesonglasttrack
        bcc dmss_findlasttrack2
        sta dm_savesonglasttrack
dmss_findlasttrack2:
        iny
        iny
        lda (editor_patternptr),y
        cmp dm_savesonglasttrack
        bcc dmss_findlasttrack3
        sta dm_savesonglasttrack
dmss_findlasttrack3:
        inc dm_savesongpattern
        bne dmss_findlasttrack
        jsr dm_setdeviceandfilename
; Save everything from $4000 to the address of the last track.
        lda dm_savesonglasttrack
        jsr editor_maketrackpointer
; Add last track's length to actually save it.
        lda editor_trackptr
        clc
        adc #192
        tax
        lda editor_trackptr+1
        adc #0
        tay
        lda #<ORDERLIST
        sta stringptr
        lda #>ORDERLIST
        sta stringptr+1
        lda #stringptr
        jsr save
; Redraw screen to clear the 'enter filename' line.
dm_savesongdone:
        jmp editor_initscreen

; Read tracks.
dm_readtracks:
        jsr dm_getfilename
        lda editor_filenamelen          ; No name given.
        beq dm_readtracksdone
; Get the number of track it will be loaded to.
        lda color_static
        ldx #$00
        jsr console_setcolorandmask
        ldx #1
        ldy #23
        jsr console_setcursorpos
        ldx #<loadattracktext
        ldy #>loadattracktext
        jsr console_writestringxy
        lda #$80
        jsr console_setcolormask
        lda #$00
        ldx #15
        ldy #23
        jsr console_gethexatxy
        sta sm_firsttrack
; Stop player.
        lda #$00
        sta editor_isplaying
        jsr PLAYER+6
        jsr dm_setdeviceandfilename
; Calculate load address.
        lda sm_firsttrack
        jsr editor_maketrackpointer
        lda #0                          ; Indicate load.
        ldx editor_trackptr
        ldy editor_trackptr+1
        jsr load
; Redraw screen to clear the 'enter filename' and 'load to track' line.
dm_readtracksdone:
        jmp editor_initscreen

; Write tracks.
dm_writetracks:
        jsr dm_getfilename
        lda editor_filenamelen          ; No name given.
        beq dm_writetracksdone
; Get first and last track number.
        jsr sm_getfirsttrack
        jsr sm_getlasttrack
; If last<first, do nothing.
        lda sm_lasttrack
        cmp sm_firsttrack
        bcc dm_writetracksdone
; Stop player.
        lda #$00
        sta editor_isplaying
        jsr PLAYER+6
        jsr dm_setdeviceandfilename
; Save all tracks between sm_firsttrack and sm_lasttrack.
        lda sm_firsttrack
        jsr editor_maketrackpointer
        lda editor_trackptr
        sta stringptr
        lda editor_trackptr+1
        sta stringptr+1
        lda sm_lasttrack
        jsr editor_maketrackpointer
        lda editor_trackptr
        clc
        adc #192
        tax
        lda editor_trackptr+1
        adc #0
        tay
        lda #stringptr
        jsr save
; Redraw screen to clear the 'enter filename' and first/last tracks lines.
dm_writetracksdone:
        jmp editor_initscreen

; ======================================
dm_setdeviceandfilename:
; Set device number and filename. (Always the same.)
; ======================================
; Set device number.
        lda #$02
        ldy #$00
        ldx editor_diskdevice
        jsr setlfs
; Set name.
        lda editor_filenamelen
        ldx #<editor_filename
        ldy #>editor_filename
        jmp setnam

; ======================================
dm_getfilename:
; Read file name from keyboard.
; ======================================
; Output: editor_filename[]
;         editor_filenamelen: number of valid characters in
; ======================================
; Clear old filename.
        lda #$20
        ldx #15
dmgfn_clear:
        sta editor_filename,x
        dex
        bpl dmgfn_clear
; Write 'ENTER FILENAME:'
        lda color_static
        ldx #$00
        jsr console_setcolorandmask
        ldx #0
        ldy #22
        jsr console_setcursorpos
        ldx #<enternametext
        ldy #>enternametext
        jsr console_writestringxy
        lda #$80
        jsr console_setcolormask
        lda #<editor_filename
        sta stringptr
        lda #>editor_filename
        sta stringptr+1
        lda #$10
        ldx #15
        ldy #22
        jsr console_getstringatxy
; Convert from screen codes to ASCII.
        ldx #15
dmgfn_convloop:
        lda editor_filename,x
        cmp #$20
        bcs dmgfn_noconv
        ora #$40
        sta editor_filename,x
dmgfn_noconv:
        dex
        bpl dmgfn_convloop
; Find out name length.
        ldx #15
        lda #$20
dmgfn_len:
        cmp editor_filename,x
        bne dmgfn_lenfound
        dex
        bpl dmgfn_len
dmgfn_lenfound:
        inx
        stx editor_filenamelen
        rts

; ======================================
editor_initscreen:
; Draw static stuff in editor screen.
; Draws patterns and orders or instrument depending on editor_whichscreen.
; ======================================
; Clear everything on screen except for the panel.
        ldx #$00
eis_clearscreen:
        lda #$20
        sta $04f0,x
        sta $04f0+190,x
        sta $04f0+380,x
        sta $04f0+570,x
        lda color_pattern
        sta $d8f0,x
        sta $d8f0+190,x
        sta $d8f0+380,x
        sta $d8f0+570,x
        inx
        cpx #190
        bne eis_clearscreen

; Init colors to a known state.
        lda color_static
        ldx #$00
        jsr console_setcolorandmask
; Move cursor directly below panel.
        ldx #0
        ldy #6
        jsr console_setcursorpos

; See what sort of screen we have to draw.
        lda editor_whichscreen
        cmp #EDITOR_ORDER_ID
        beq eis_pattern
        cmp #EDITOR_PATTERN_ID
        beq eis_pattern
        cmp #EDITOR_INSTRUMENT_ID
        beq eis_instrument
        cmp #EDITOR_CONFIGMENU_ID
        beq eis_configmenu
        cmp #EDITOR_SPECMENU_ID
        beq eis_specmenu
        cmp #EDITOR_DISKMENU_ID
        beq eis_diskmenu
        brk                      ; !!!! This is impossible

eis_pattern:
        lda #$01
        sta editor_makerowhighlight
        ldx #<patterntext
        ldy #>patterntext
        jmp console_writepackedstringxy

eis_instrument:
        lda #$00
        sta editor_makerowhighlight
        ldx #<instrumenttext
        ldy #>instrumenttext
        jmp console_writepackedstringxy

eis_specmenu:
        lda #$00
        sta editor_makerowhighlight
        ldx #<specmenutext
        ldy #>specmenutext
        jmp console_writepackedstringxy

eis_diskmenu:
        lda #$00
        sta editor_makerowhighlight
        ldx #<diskmenutext
        ldy #>diskmenutext
        jmp console_writepackedstringxy

eis_configmenu:
        lda #$00
        sta editor_makerowhighlight
        ldx #<configmenutext
        ldy #>configmenutext
        jmp console_writepackedstringxy

; ======================================
editor_updatescreen:
; Update changing stuff in editor screen.
; Draws patterns and orders or instrument depending on editor_whichscreen.
; ======================================
        lda color_static
        ldx #$80
        jsr console_setcolorandmask

; Write current rastertime.
        lda editor_currentraster
        ldx #15
        ldy #3
        jsr console_writehexatxy
; Write max rastertime.
        jsr console_cursorright
        lda editor_maxraster
        jsr console_writehex

; Print editor step.
        lda editor_editstep
        ldx #18
        ldy #1
        jsr console_writehexatxy

; Print speed.
        lda speed
        ldx #18
        ldy #2
        jsr console_writehexatxy

; Draw timer.
        lda timer_minutes
        ldx #12
        ldy #4
        jsr console_writehexatxy
        lda timer_seconds
        ldx #15
        ldy #4
        jsr console_writehexatxy
        lda timer_hundreds
        ldx #18
        ldy #4
        jsr console_writehexatxy

; Display orderlist.
        lda #0
        sta display_x
        lda #0
        sta display_y
        lda #5
        sta display_height
        lda ordernumber
        sta display_index
        lda #2
        sta display_line
        lda #<ORDERLIST
        sta display_datasrc
        lda #>ORDERLIST
        sta display_datasrc+1
        lda #$40
        sta display_indexcolormask
        lda #$80
        sta display_datacolormask
        jsr write_list

; Write instrument names.
        jsr write_instrumentnames

; See what sort of screen we have to draw.
        lda editor_whichscreen
        cmp #EDITOR_ORDER_ID
        beq eus_order
        cmp #EDITOR_PATTERN_ID
        beq eus_pattern
        cmp #EDITOR_INSTRUMENT_ID
        beq eus_instrument
        cmp #EDITOR_CONFIGMENU_ID
        beq eus_configmenu
        cmp #EDITOR_SPECMENU_ID
        beq eus_specmenu
        cmp #EDITOR_DISKMENU_ID
        beq eus_diskmenu
        brk                      ; !!!! This is impossible

eus_order:
        jsr write_pattern
; Set sprite cursor.
; X=3+editor_ordercol
        ldx editor_ordercol
        inx
        inx
        inx
        ldy #2
        jmp console_setspritecursor

eus_pattern:
        jsr write_pattern
; Set sprite cursor.
; X=5+editor_patternchannel*12+patterncursorpositions[editor_patternfield]
        lda editor_patternchannel
        asl
        adc editor_patternchannel
        asl
        asl
        adc #5
        ldy editor_patternfield
        adc editor_patternfieldxpositions,y
        tax
        ldy #16
        jmp console_setspritecursor

eus_instrument:
        jsr write_instrument
        ldx #41                         ; Move cursor out of screen.
        ldy #0
        lda editor_currentinst
        beq eus_instrument0
; Set cursor position into middle of table or into instrument parameters.
        ldy #16
        lda editor_instfield
        bne eusdi_intable
        lda #8
        clc
        adc editor_instrow
        tay
eusdi_intable:
        ldx editor_instfield
        lda editor_instfieldxpositions,x
        clc
        adc editor_instcol
        tax
eus_instrument0:
        jmp console_setspritecursor

eus_configmenu:
        jsr write_config
        ldx #24
        lda #8
        clc
        adc editor_configrow
        tay
        jmp console_setspritecursor

eus_specmenu:
; Display song title.
        jsr console_hidespritecursor
        lda #$80
        jsr console_setcolormask
        ldx #6
        ldy #6
        jsr console_setcursorpos
        lda #<SONGTITLE
        sta stringptr
        lda #>SONGTITLE
        sta stringptr+1
        ldx #SONGTITLELEN
        jmp console_writestringlen

eus_diskmenu:
        jsr console_hidespritecursor
        lda #$80
        jsr console_setcolormask
; Quick hack to display a decimal-looking device number.
        lda editor_diskdevice
        cmp #10
        bmi eus_diskmenu1
        clc
        adc #6
eus_diskmenu1
        ldx #15
        ldy #6
        jsr console_writehexatxy
        rts

; ======================================
editor_makeinstptr:
; editor_instptr=16*A+INSTRUMENTS
; ======================================
;  Input: A: instrument number
; Output: editor_instptr, editor_instptr+1
; ======================================
        sec
        sbc #$01
        ldy #$00
        asl
        asl
        asl
        asl
        bcc emip_noof
        iny
emip_noof:
        clc
        adc #<INSTRUMENTS
        sta editor_instptr
        tya
        adc #>INSTRUMENTS
        sta editor_instptr+1
        rts

; ======================================
editor_makeinstnameptr:
; stringptr=16*stringptr+INSTRUMENTNAMES
; ======================================
;  Input: A: instrument number
; Output: stringptr, stringptr+1
; ======================================
        sec
        sbc #$01
        ldy #$00
        asl
        asl
        asl
        asl
        bcc eminp_noof
        iny
eminp_noof:
        clc
        adc #<INSTRUMENTNAMES
        sta stringptr
        tya
        adc #>INSTRUMENTNAMES
        sta stringptr+1
        rts

; ======================================
editor_init:
; Initialize editor. Set up screen, colors, sprites, etc.
; ======================================
        lda #$36
        sta $01                         ; Hide BASIC ROM.
        lda #$80
        sta $028a                       ; Enable keyrepeat.
        sta $0291                       ; Disable case-switching.
        lda #$00                        ; Go to program mode
        sta $9d                         ; to hide kernal messages.

; Init editor state.
        ldy #8
        sty editor_diskdevice
        lda #EDITOR_PATTERN_ID
        sta editor_whichscreen
        lda #$00
        sta editor_isplaying            ; Stop playing.
        sta editor_ordercol             ; Cursor on high nybble in orderlist.
        sta editor_patternchannel       ; Go to channel 1
        sta editor_patternfield         ; Go to note field.
        sta editor_instfield            ; Cursor on instrument parameters.
        sta editor_instrow              ; Cursor on first row in inst editor.
        sta editor_instcol              ; Cursor on high nybble.
        sta editor_waveindex
        sta editor_arpindex
        sta editor_configrow            ; Cursor on first row in config menu.
        sta editor_trackeffectbuf       ; Clear effect copy buffer.
        sta editor_trackeffectparbuf    ; Clear effect parameter copy buffer.
        ldy #1
        sty editor_currentinst          ; Set current instrument.
        sty editor_editstep             ; Initialize edit step to 1.
        ldy #4*12
        sty editor_currentoctave        ; Default to octave 4.
; Invalidate undo buffer.
        ldy #$80
        sty editor_trackundochannel
; Clear track copy buffer.
        sta editor_trackcopybufferpos
        sta editor_trackcopybufferlen
; Clear instrument copy buffer.
        sta editor_instcopybuffervalid
; No block highlight.
        jsr ep_unselectblock

; Load default color set 1.
        jsr cm_colorset1

; Init interrupts.
        jsr initirq
        lda #$00
        sta editor_maxraster            ; Clear max rastertime.

editor_reinit:
        lda #$5b
        sta $d011                       ; Enable ECM.

; Init colors.
        lda color_d021
        sta $d020
        sta $d021
        lda color_d022
        sta $d022
        lda color_d023
        sta $d023
        lda color_d024
        sta $d024

; Create static things in the panel.
        lda color_static
        ldx #$00
        jsr console_setcolorandmask
        ldx #0
        ldy #0
        jsr console_setcursorpos
        ldx #<paneltext
        ldy #>paneltext
        jsr console_writepackedstringxy

; Init static stuff on screen.
        jsr editor_initscreen

; Init sprite cursor.
        lda #SPRITE/64
        sta $07f8                       ; Set sprite pointer.
        lda #$01
        sta $d015                       ; Enable sprite.
        sta $d01d                       ; Double horizontal
        lda color_cursor
        sta $d027                       ; Sprite color.

; Init font.
        lda #$10+FONT/1024
        sta $d018
        rts

; ======================================
editor_gettracks:
; Get track numbers and transposes into
; editor_tracks[], editor_tracktransposes[].
; ======================================
;  Input: ordernumber, orderlist[], patterns[]
; Output: editor_tracks+0, editor_tracks+1, editor_tracks+2
;         editor_tracktransposes+0, editor_tracktransposes+1, editor_tracktransposes+2
; ======================================
; Get pointer to pattern.
        ldy ordernumber
        lda ORDERLIST,y                 ; Get current pattern.
        jsr editor_makepatternpointer
        ldx #0
        ldy #0
egt_loop:
        lda (editor_patternptr),y
        sta editor_tracks,x
        iny
        lda (editor_patternptr),y
        sta editor_tracktransposes,x
        iny
        inx
        cpx #3
        bne egt_loop
        rts

; ======================================
editor_puttracks:
; Put track numbers and transposes from
; editor_tracks[], editor_tracktransposes[] to pattern.
; ======================================
;  Input: ordernumber, editor_tracks[], editor_tracktransposes[], orderlist[]
; Output: patterns[]
; ======================================
; Get pointer to pattern.
        ldy ordernumber
        lda ORDERLIST,y                 ; Get current pattern.
        jsr editor_makepatternpointer
        ldx #0
        ldy #0
ept_loop:
        lda editor_tracks,x
        sta (editor_patternptr),y
        iny
        lda editor_tracktransposes,x
        sta (editor_patternptr),y
        iny
        inx
        cpx #3
        bne ept_loop
        rts

; ======================================
editor_makepatternpointer:
; Calculate 6*A+PATTERNS.
; ======================================
;  Input: A: pattern number
; Output: patternptr: -> pattern
; ======================================
        sta emtp_noof1+1
        ldx #0
        asl
        bcc emtp_noof1
        inx
        clc
emtp_noof1:
        adc #$ff                        ; !!!! Self-modifying.
        bcc emtp_noof2
        inx
emtp_noof2:
        stx editor_patternptr+1
        asl
        rol editor_patternptr+1
        adc #<PATTERNS
        sta editor_patternptr
        lda editor_patternptr+1
        adc #>PATTERNS
        sta editor_patternptr+1
        rts

; ======================================
editor_maketrackpointer:
; Calculate 192*track_number+tracks_base
; ======================================
;  Input: A: track number
; Output: editor_trackptr: -> track
; ======================================
        sta editor_maketrackpointer0+1
        asl
editor_maketrackpointer0:
        adc #$00                        ; !!!! Self-modifying.
        sta editor_trackptr+1
        lda #$00
        ror editor_trackptr+1
        ror
        lsr editor_trackptr+1
        ror
        adc #<TRACKS_BASE
        sta editor_trackptr
        lda editor_trackptr+1
        adc #>TRACKS_BASE
        sta editor_trackptr+1
        rts

; Row that editor_gettrackrow and editor_puttrackrow work on.
editor_getputtrackrow:  .byte 0

; ======================================
editor_gettrackrow:
; Read note, instrument, effect and parameter from track visible in editor.
; ======================================
;  Input: editor_tracks+0, editor_tracks+1, editor_tracks+2
;         editor_getputtrackrow: row number to read from
; Output: editor_tracknote
;         editor_trackinst
;         editor_trackeffect
;         editor_trackeffectpar
; ======================================
        ldx editor_patternchannel

; Get from track visible in channel X in editor.
editor_gettrackrowanychannel:
        lda editor_tracks,x
        jsr editor_maketrackpointer

; Entry point to read any track.
editor_gettrackrowany:
        lda editor_getputtrackrow
        asl
        adc editor_getputtrackrow
        tay
; Read note.
        lda (editor_trackptr),y
        and #$7f
        sta editor_tracknote
; Read effect and instrument.
        lda (editor_trackptr),y
        lsr
        lsr
        lsr
        lsr
        and #$08
        sta editor_trackeffect
        iny
        lda (editor_trackptr),y
        and #$1f
        sta editor_trackinst
        lda (editor_trackptr),y
        lsr
        lsr
        lsr
        lsr
        lsr
        ora editor_trackeffect
        sta editor_trackeffect
; Read effect parameter.
        iny
        lda (editor_trackptr),y
        sta editor_trackeffectpar
        rts

; ======================================
editor_puttrackrow:
; Write note, instrument, effect and parameter to track visible in editor.
; ======================================
;  Input: editor_tracks+0, editor_tracks+1, editor_tracks+2
;         editor_getputtrackrow: row number to read from
;         editor_tracknote
;         editor_trackinst
;         editor_trackeffect
;         editor_trackeffectpar
; ======================================
        ldx editor_patternchannel

; Put to track visible in channel X in editor.
editor_puttrackrowanychannel:
        lda editor_tracks,x
        jsr editor_maketrackpointer

; Entry point to write any track.
editor_puttrackrowany:
        lda editor_getputtrackrow
        asl
        adc editor_getputtrackrow
        tay
; Put note and bit 3 of effect.
        lda editor_trackeffect
        and #$08
        asl
        asl
        asl
        asl
        ora editor_tracknote
        sta (editor_trackptr),y
; Put bit 0-2 of effect and instrument.
        lda editor_trackeffect
        asl
        asl
        asl
        asl
        asl
        ora editor_trackinst
        iny
        sta (editor_trackptr),y
; Put effect parameter.
        lda editor_trackeffectpar
        iny
        sta (editor_trackptr),y
        rts

; ======================================
write_instrumentnames:
; Display instrument name list
; ======================================
        lda #0
        sta display_y
        lda #5
        sta display_height
        lda editor_currentinst
        sec
        sbc #2
        sta display_index
        lda #0
        sbc #0
        sta display_index+1

win_next:
; Move cursor to current line.
        lda color_static
        ldy display_y
        cpy #2
        bne win_nohighligth
        lda color_highlight
win_nohighligth:
        jsr console_setcolor
        ldx #21
        jsr console_setcursorpos
; Display empty line if display_index<0 or display_index>=$20
        lda display_index+1
        bne win_emptyline
        lda display_index
        cmp #MAX_INSTRUMENTS
        bpl win_emptyline
; Display valid line in instrument names list.
        lda #$40
        jsr console_setcolormask
        lda display_index
        jsr console_writehex
        lda #":"
        jsr console_writechar
        lda #$80
        jsr console_setcolormask
        lda display_index
        beq win_instrument0
        jsr editor_makeinstnameptr
        ldx #INSTRUMENTNAMELEN
        jsr console_writestringlen
        jmp win_cont
win_instrument0:
        ldx #<instrument0name
        ldy #>instrument0name
        jsr console_writestringxy
        jmp win_cont
; Disply empty line in instrument name list.
win_emptyline:
        lda #$40
        jsr console_setcolormask
        ldy #3
        jsr console_writespaces
        lda #$80
        jsr console_setcolormask
        ldy #INSTRUMENTNAMELEN
        jsr console_writespaces
win_cont:
        inc display_index
        bne win_nohi
        inc display_index+1
win_nohi:
        inc display_y
        dec display_height
        bne win_next
        rts

; ======================================
write_instrument:
; Display instrument parameters.
; ======================================
        lda color_static
        ldx #$80
        jsr console_setcolorandmask
        lda editor_currentinst
        bne wi_validinstrument

; When displaying instrument 0 clear everything and
; write default instrument name.
        ldx #5
        ldy #6
        jsr console_setcursorpos
        ldx #<instrument0name
        ldy #>instrument0name
        jsr console_writestringxy
; Clear instrument parameters.
        ldx #21
        ldy #8
        jsr console_setcursorpos
        lda #$20
        ldx #2
        ldy #16
        jsr console_fillblock
; Clear wave table.
        ldx #25
        ldy #8
        jsr console_setcursorpos
        lda #$20
        ldx #5
        ldy #17
        jsr console_fillblock
; Clear arpeggio table.
        ldx #34
        ldy #8
        jsr console_setcursorpos
        lda #$20
        ldx #5
        ldy #17
        jmp console_fillblock

; Write a valid (non-zero) instrument's data.
wi_validinstrument:
        jsr editor_makeinstptr

; Write instrument name.
        lda editor_currentinst
        jsr editor_makeinstnameptr
        ldx #5
        ldy #6
        jsr console_setcursorpos
        ldx #INSTRUMENTNAMELEN
        jsr console_writestringlen

; Write normal instrument parameters.
        lda #0
        sta display_index
        lda #8
        sta display_y
wi_loop:
        ldy display_index
        lda (editor_instptr),y
        ldx #21
        ldy display_y
        jsr console_writehexatxy
        inc display_index
        inc display_y
        lda display_index
        cmp #16
        bne wi_loop

; Display wave table.
        lda #25
        sta display_x
        lda #8
        sta display_y
        lda #17
        sta display_height
        lda editor_waveindex
        sta display_index
        lda #8
        sta display_line
        lda #<WAVETABLE
        sta display_datasrc
        lda #>WAVETABLE
        sta display_datasrc+1
        lda #$40
        sta display_indexcolormask
        lda #$80
        sta display_datacolormask
        jsr write_list
; Display arpeggio table.
        lda #34
        sta display_x
        lda #8
        sta display_y
        lda #17
        sta display_height
        lda editor_arpindex
        sta display_index
        lda #8
        sta display_line
        lda #<ARPEGGIOTABLE
        sta display_datasrc
        lda #>ARPEGGIOTABLE
        sta display_datasrc+1
        jmp write_list

; ======================================
write_pattern:
; ======================================
        lda color_static
        ldx #$80
        jsr console_setcolorandmask

; Write track headers.
        jsr editor_gettracks
; Write track number for channel 1.
        lda editor_tracks+0
        ldx #8
        ldy #6
        jsr console_writehexatxy
; Write track transpose for channel 1.
        lda editor_tracktransposes+0    ; Convert transpose from 2's
        bpl wp_transposeup1             ; complement to sign bit binary.
        eor #$7f
        clc
        adc #$01
wp_transposeup1:
        ldx #13
        ldy #6
        jsr console_writehexatxy
; Write track number for channel 2.
        lda editor_tracks+1
        ldx #20
        ldy #6
        jsr console_writehexatxy
; Write track transpose for channel 2.
        lda editor_tracktransposes+1    ; Convert transpose from 2's
        bpl wp_transposeup2             ; complement to sign bit binary.
        eor #$7f
        clc
        adc #$01
wp_transposeup2:
        ldx #25
        ldy #6
        jsr console_writehexatxy
; Write track number for channel 3.
        lda editor_tracks+2
        ldx #32
        ldy #6
        jsr console_writehexatxy
; Write track transpose for channel 3.
        lda editor_tracktransposes+2    ; Convert transpose from 2's
        bpl wp_transposeup3             ; complement to sign bit binary.
        eor #$7f
        clc
        adc #$01
wp_transposeup3:
        ldx #37
        ldy #6
        jsr console_writehexatxy

; Write row numbers and 3 tracks for the pattern.
        ldx #1
        ldy #8
        stx display_x
        sty display_y
        lda trackrow
        sec
        sbc #8
        sta display_row

; Repeat this for each row on screen.
wp_nextrow:
; Write row numbers on the left.
        ldx display_x
        ldy display_y
        jsr console_setcursorpos
        lda display_row
        cmp #64
        bcc wp_validline
        lda #$00
        jsr console_setcolormask
        ldy #38
        jsr console_writespaces
        jmp wp_cont
; Write non-empty line.
wp_validline:
; First write row number.
        lda color_pattern
        ldx #$00
        jsr console_setcolorandmask
        lda display_row
        jsr console_writehex
; Create index into track.
        lda display_row
        asl
        adc display_row
        sta display_row3
        lda #0
        sta display_chn
; Repeat this for each track.
wp_nexttrack:
        jsr write_trackrow
        inc display_chn
        lda display_chn
        cmp #3
        bne wp_nexttrack
wp_cont:
        inc display_row
        inc display_y
        lda display_y
        cmp #25
        bmi wp_nextrow
        rts

; ======================================
write_trackrow:
; Write 1 row of a track.
; ======================================
;  Input: display_chn: channel number
;         display_x: screen x position
;         display_y: screen y position
;         display_row3: track row counter*3
;         editor_tracks[]
;         ... and more ...
; ======================================
        jsr console_cursorright
        jsr console_cursorright
        ldx display_chn
        lda editor_tracks,x
        jsr editor_maketrackpointer
; Set color to indicate muted/unmuted channels.
        ldy display_chn
        lda color_pattern
        ldx chn_muted,y
        beq wtr_chnunmuted
        lda color_muted
wtr_chnunmuted:
; Find out if we are in a block. If so, highlight it.
        ldx #$00
        cpy editor_blockchannel
        bne wtr_colorok                 ; Block is in an other channel.
        ldy editor_blockflags
        cpy #BLOCK_BEGINSET+BLOCK_ENDSET
        bne wtr_colorok                 ; Begin or end unset -> no block.
        ldy editor_blockbeg
        cpy display_row
        beq wtr_colorok1
        bpl wtr_colorok
        ldy editor_blockend
        cpy display_row
        bmi wtr_colorok
wtr_colorok1:
        ldx #$c0
wtr_colorok:
        jsr console_setcolorandmask
        ldy display_row3
; Write note.
        lda (editor_trackptr),y
        and #$7f
        sta wtr_note+1
        ldy #$00
        asl
wtr_note:
        adc #$ff
        bcc wtr_notestringptrok
        iny
wtr_notestringptrok:
        clc
        adc #<notestrings
        sta stringptr
        tya
        adc #>notestrings
        sta stringptr+1
        ldx #3
        jsr console_writestringlen
        jsr console_writespace
; Write instrument.
        ldy display_row3
        iny
        lda (editor_trackptr),y
        and #$1f
        bne wtr_validinst
        lda #$2e
        jsr console_writechar
        lda #$2e
        jsr console_writechar
        jmp wtr_instdone
wtr_validinst:
        jsr console_writehex
wtr_instdone:
        jsr console_writespace
; Write effect.
        ldy display_row3
        lda (editor_trackptr),y
        lsr
        lsr
        lsr
        lsr
        and #$08
        sta wtr_eff+1
        iny
        lda (editor_trackptr),y
        lsr
        lsr
        lsr
        lsr
        lsr
wtr_eff:
        ora #$00
        tay
        lda hexdigitsscreen,y
        jsr console_writechar
; Write effect parameter.
        ldy display_row3
        iny
        iny
        lda (editor_trackptr),y
        jmp console_writehex

; ======================================
write_config:
; Display config parameters.
; ======================================
        lda color_static
        ldx #$80
        jsr console_setcolorandmask

        lda #0
        sta display_index
        lda #8
        sta display_y
wc_loop:
        ldy display_index
        lda editor_configcolors,y
        ldx #23
        ldy display_y
        jsr console_writehexatxy
        inc display_index
        inc display_y
        lda display_index
        cmp #EDITOR_COLORSNUM
        bne wc_loop
        rts

; ======================================
write_list:
; Display orderlist, wave or arpeggio table.
; ======================================
;  Input: display_x: screen X position
;         display_y: screen Y position
;         display_height: number of lines to display
;         display_index: index of selected element
;         display_line: line where selected element appears
;         display_datasrc: 16 bit ptr to data
;         display_indexcolormask:
;         display_datacolormask:
; ======================================
        lda display_index
        sec
        sbc display_line
        sta display_index
        lda #0
        sbc #0
        sta display_index+1
; Calculate highlighted line Y position in screen coordinates.
        lda display_y
        clc
        adc display_line
        sta display_highlightline

wl_next:
; Set color to indicate highlight if necessary.
        lda color_static
        ldy display_y
        cpy display_highlightline
        bne wl_nohighligth
        lda color_highlight
wl_nohighligth:
        jsr console_setcolor
        ldx display_x
        jsr console_setcursorpos
; Move cursor to current line.
        ldx display_x
        ldy display_y
        jsr console_setcursorpos
; Display empty line if display_index<0 or display_index>=$100
        lda display_index+1
        bne wl_emptyline
; Display valid line in orderlist.
        lda display_indexcolormask
        jsr console_setcolormask
        lda display_index
        jsr console_writehex
        lda #":"
        jsr console_writechar
        lda display_datacolormask
        jsr console_setcolormask
        ldy display_index
        lda (display_datasrc),y
        jsr console_writehex
        jmp wl_cont
; Disply empty line in list.
wl_emptyline:
        lda display_indexcolormask
        jsr console_setcolormask
        ldy #3
        jsr console_writespaces
        lda display_datacolormask
        jsr console_setcolormask
        ldy #2
        jsr console_writespaces
wl_cont:
        inc display_index
        bne wl_nohi
        inc display_index+1
wl_nohi:
        inc display_y
        dec display_height
        bne wl_next
        rts

; ======================================
initirq:
; Init IRQs.
; ======================================
        sei
        lda #$7f
        sta $dc0d
        lda #$81
        sta $d01a
        lda #$5b
        sta $d011
        lda #$32
        ldx #<irqhandler1
        ldy #>irqhandler1
        jsr setirq
        cli
        rts

irqhandler1:
        lda $d019
        sta $d019
        lda editor_isplaying
        beq ih1_noplay
        bit $ffff
        dec $d020
; Save zeropage variables.
        lda mul8m
        pha
        lda mul8n
        pha
        lda mul8n+1
        pha
        lda mul8result
        pha
        lda mul8result+1
        pha
        jsr PLAYER+3
; Restore zeropage variables.
        pla
        sta mul8result+1
        pla
        sta mul8result
        pla
        sta mul8n+1
        pla
        sta mul8n
        pla
        sta mul8m
        inc $d020
        jsr timer_update                ; Update timer once a frame.
ih1_noplay:
        lda $d012
        sec
        sbc #$32
        sta editor_currentraster
        cmp editor_maxraster
        bcc ih1_notmax
        sta editor_maxraster
ih1_notmax:
        jsr scnkey                      ; Scan keyboard once a frame.
        lda #$b2
        ldx #<irqhandler2
        ldy #>irqhandler2
        jsr setirq
        jmp $ea81

irqhandler2:
        lda $d019
        sta $d019
        lda editor_makerowhighlight
        beq irqhandler2no
        lda color_currentrow
        sta $d020
        sta $d021
irqhandler2no:
        lda #$ba
        ldx #<irqhandler3
        ldy #>irqhandler3
        jsr setirq
        jmp $ea81

irqhandler3:
        lda $d019
        sta $d019
        lda editor_makerowhighlight
        beq irqhandler3no
        lda color_d021
        sta $d020
        sta $d021
irqhandler3no:
        lda #$32
        ldx #<irqhandler1
        ldy #>irqhandler1
        jsr setirq
        jmp $ea81

; ======================================
timer_update:
; Update the system timer. Call in every frame.
; ======================================
;  Input: timer_hundreds: 1/100th seconds, 0.02s precision
;         timer_seconds:  seconds
;         timer_minutes:  minutes
; Output: timer_hundreds: 1/100th seconds, 0.02s precision
;         timer_seconds:  seconds
;         timer_minutes:  minutes
; ======================================
        sed
        lda timer_hundreds
        clc
        adc #2
        sta timer_hundreds
        lda timer_seconds
        adc #0
        cmp #$60
        bcc tu_nominitupdate
        lda #0
        sta timer_seconds
        lda timer_minutes
        clc
        adc #1
        sta timer_minutes
        cld
        rts
tu_nominitupdate:
        sta timer_seconds
        cld
        rts

; ======================================
setirq:
; Set IRQ handler and rasterline.
; ======================================
;  Input: A: rasterline
;         X: handler low
;         Y: handler high
; ======================================
        stx $0314
        sty $0315
        sta $d012
        rts

; ======================================
write_help:
; Help reader.
; ======================================
;  Input: A: number of page to start at.
; ======================================
        tax
        jmp wh_displaypage
wh_loop:
        jsr console_waitkey
        cmp #KEY_DOWN
        beq wh_nextpage
        cmp #KEY_UP
        beq wh_prevpage
        cmp #KEY_LEFT
        beq wh_prevpage
        cmp #KEY_RIGHT
        beq wh_nextpage
; Exit help reader.
        rts

; Go to next page.
wh_nextpage:
        ldx editor_currenthelppage
        inx
        cpx #EDITOR_HELPPAGESNUM
        bcc wh_displaypage
        bcs wh_cont

; Go to previous page.
wh_prevpage:
        ldx editor_currenthelppage
        dex
        bpl wh_displaypage
        bmi wh_cont

; Write page in X.
wh_displaypage:
        stx editor_currenthelppage
        lda #155                        ; Light grey characters.
        jsr chrout
        lda #147                        ; Clear screen.
        jsr chrout
        lda editor_currenthelppage
        jsr write_helppage

wh_cont:
        jmp wh_loop

; ======================================
write_helppage:
; Display help page number A.
; Page end is marked with $00.
; ======================================
;  Input: A: help page number
; ======================================
        asl
        tax
        lda helppagepointers,x
        sta whp_loop+1
        lda helppagepointers+1,x
        sta whp_loop+2
whp_loop:
        lda $ffff
        beq whp_done
        jsr chrout
        inc whp_loop+1
        bne whp_loop
        inc whp_loop+2
        bne whp_loop
whp_done:
        rts

; ======================================
console_waitkey:
; ======================================
;  Output: A: keycode (always non-zero)
; ======================================
        jsr getin
        cmp #0
        beq console_waitkey
        rts

; ======================================
console_hidespritecursor:
; Hide sprite cursor by moving it out of screen.
; ======================================
        ldx #41
        ldy #0

; ======================================
console_setspritecursor:
;  Input: X: X position
;         Y: Y position
; ======================================
; Set Y*8+$31
        tya
        asl
        asl
        asl
        adc #$31
        sta $d001
; Set X*8+$16
        txa
        ldx #0
        asl
        asl
        asl
        bcc css_nooverflow1
        inx
css_nooverflow1:
        clc
        adc #$16
        bcc css_nooverflow2
        inx
css_nooverflow2:
        sta $d000
        stx $d010
        rts

; ======================================
console_setcolor:
;  Input: A: color
; Output: console_color+1
; ======================================
        sta console_color+1
        rts

; ======================================
console_setcolormask:
;  Input: A: color mask
; Output: console_colormask+1
; ======================================
        sta console_colormask+1
        rts

; ======================================
console_setcolorandmask:
;  Input: A: color
;         X: color mask
; Output: console_color+1
;         console_colormask+1
; ======================================
        sta console_color+1
        stx console_colormask+1
        rts

; ======================================
console_setcursorpos:
;  Input: X: X position
;         Y: Y position
; Output: console_screenptr+1, console_screenptr+2
; ======================================
        stx csc_x+1
        tya
        asl
        tay
        lda screenptrs,y
csc_x:
        adc #$00
        sta console_screenptr+1
        sta console_colramptr+1
        lda screenptrs+1,y
        adc #$00
        sta console_screenptr+2
        adc #$d4                        ; !!!! Bloody hack. $d400=$d800-$0400
        sta console_colramptr+2
        rts

; ======================================
console_writespace:
; Print a space.
; ======================================
        lda #$20

; ======================================
console_writechar:
; Print a character.
; ======================================
;  Input: A: byte
; ======================================
console_colormask:
        ora #$00
console_screenptr:
        sta $ffff
console_color:
        lda #$ff
console_colramptr:
        sta $ffff

; ======================================
console_cursorright:
; Move cursor right.
; ======================================
        inc console_screenptr+1
        bne ccr_noof1
        inc console_screenptr+2
ccr_noof1:
        inc console_colramptr+1
        bne ccr_noof2
        inc console_colramptr+2
ccr_noof2:
        rts

; ======================================
console_writespaces:
; Print Y spaces.
; ======================================
;  Input: Y: number of spaces
; ======================================
        jsr console_writespace
        dey
        bne console_writespaces
        rts

; ======================================
console_writestringxy:
; Print a string terminated by $ff.
; ======================================
;  Input: X: string pointer low byte
;         Y: string pointer high byte
; ======================================
        stx stringptr
        sty stringptr+1

; ======================================
console_writestring:
; Print a string terminated by $ff.
; ======================================
;  Input: stringptr: String pointer (16 bit)
; ======================================
        ldy #$00
cws_next:
        lda (stringptr),y
        cmp #$ff
        beq cws_done
        jsr console_writechar
        inc stringptr
        bne cws_nohi
        inc stringptr+1
cws_nohi:
        jmp cws_next
cws_done:
        rts

; ======================================
console_writestringlen:
; Print a string whose length is given in X.
; ======================================
;  Input: stringptr: String pointer (16 bit)
;         X: length of string
; ======================================
        ldy #$00
cwsl_next:
        lda (stringptr),y
        jsr console_writechar
        iny
        dex
        bne cwsl_next
        rts

; ======================================
console_writepackedstringxy:
; Print an RLE coded string terminated by $ff.
; $fe is RLE escape byte followed by count and char.
; ======================================
;  Input: X: string pointer low byte
;         Y: string pointer high byte
; ======================================
        stx stringptr
        sty stringptr+1
        ldy #0
cwps_loop:
        jsr cwps_getchar                ; Get character into A.
        cmp #$ff
        beq cwps_done                   ; End marked found.
        cmp #$fe
        beq cwps_depack                 ; RLE escape byte found.
        jsr console_writechar           ; Write simple character.
        jmp cwps_cont
; Depack RLE.
cwps_depack:
        jsr cwps_getchar                ; Get character into A.
        tax
        jsr cwps_getchar                ; Get character into A.
cwps_depackloop:
        pha
        jsr console_writechar
        pla
        dex
        bne cwps_depackloop
cwps_cont:
        jmp cwps_loop
; Advance string pointer.
cwps_getchar:
        lda (stringptr),y
cwps_advance:
        inc stringptr
        bne cwps_nohi
        inc stringptr+1
cwps_nohi:
cwps_done:
        rts

; ======================================
console_writehex:
; Print a byte in hex.
; ======================================
;  Input: A: byte
; ======================================
        pha
        lsr
        lsr
        lsr
        lsr
        tax
        lda hexdigitsscreen,x
        jsr console_writechar
        pla
        and #$0f
        tax
        lda hexdigitsscreen,x
        jsr console_writechar
        rts

; ======================================
console_writecharatxy:
; Print a character in hex at X,Y.
; ======================================
;  Input: A: character
;         X: X position
;         Y: Y position
; ======================================
        pha
        jsr console_setcursorpos
        pla
        jmp console_writechar

; ======================================
console_writehexatxy:
; Print a byte in hex at X,Y.
; ======================================
;  Input: A: byte
;         X: X position
;         Y: Y position
; ======================================
        pha
        jsr console_setcursorpos
        pla
        jmp console_writehex

; ======================================
console_fillblock:
; Fill a rectangular block with character in A at current cursor position.
; ======================================
;  Input: A: character
;         X: width
;         Y: height
; ======================================
        sta cfb_char+1
        dex
        stx cfb_width+1
        lda console_screenptr+1
        sta cfb_screenptr+1
        lda console_screenptr+2
        sta cfb_screenptr+2
cfb_char:
        lda #$ff
cfb_width:
        ldx #$ff
cfb_screenptr:
        sta $ffff,x
        dex
        bpl cfb_screenptr
        lda cfb_screenptr+1
        clc
        adc #40
        sta cfb_screenptr+1
        lda cfb_screenptr+2
        adc #$00
        sta cfb_screenptr+2
        dey
        bne cfb_char
        rts

; ======================================
console_gethexatxy:
; Get a hex byte at X,Y.
; ======================================
;  Input: A: byte
;         X: X position
;         Y: Y position
; Output: A: byte
; ======================================
        sta cgh_temp
        stx display_x
        sty display_y
        jsr console_writehexatxy
        ldx display_x
        ldy display_y
        jsr console_setspritecursor
cgh_loop1:
        jsr getin
        cmp #KEY_RETURN
        beq cgh_done
        jsr ascii2hexdigit
        bmi cgh_loop1
        ldx cgh_temp
        jsr replacehighnybble
        sta cgh_temp
        ldx display_x
        ldy display_y
        jsr console_writehexatxy
        ldx display_x
        inx
        ldy display_y
        jsr console_setspritecursor
cgh_loop2:
        jsr getin
        cmp #KEY_RETURN
        beq cgh_done
        jsr ascii2hexdigit
        bmi cgh_loop2
        ldx cgh_temp
        jsr replacelownybble
        sta cgh_temp
        ldx display_x
        ldy display_y
        jsr console_writehexatxy

cgh_done:
        lda cgh_temp
        rts

cgh_temp:       .byte 0

; ======================================
console_getstringatxy:
; Read a string at X,Y.
; ======================================
;  Input: A: string length
;         X: X position
;         Y: Y position
;         stringptr: String pointer (16 bit)
; ======================================
        stx display_x
        sty display_y
        sta cgs_length
        tax
        dex
        stx list_length
        lda #0
        sta cgs_cursorpos
        lda #$20                        ; Add space when deleting.
        sta list_defaultelement
cgs_loop:
        ldx display_x
        ldy display_y
        jsr console_setcursorpos
        ldx cgs_length
        jsr console_writestringlen
        lda display_x
        clc
        adc cgs_cursorpos
        tax
        ldy display_y
        jsr console_setspritecursor
        jsr console_waitkey
        cmp #KEY_RETURN
        beq cgs_done
        cmp #KEY_LEFT
        beq cgs_left
        cmp #KEY_RIGHT
        beq cgs_right
        cmp #KEY_DELETE
        beq cgs_delete
; Insert character into string.
        pha
        lda cgs_cursorpos
        ldx stringptr
        ldy stringptr+1
        jsr list_insert
        ldy cgs_cursorpos
        pla
        and #$3f
        sta (stringptr),y
; Move cursor right.
cgs_right:
        ldy cgs_cursorpos
        iny
        cpy cgs_length
        bcs cgs_right0
cgs_right1:
        sty cgs_cursorpos
cgs_right0:
        jmp cgs_loop
; Move cursor left.
cgs_left:
        ldy cgs_cursorpos
        dey
        bpl cgs_right1
        bmi cgs_loop
; Backspace.
cgs_delete:
        ldy cgs_cursorpos
        dey
        bmi cgs_loop
        sty cgs_cursorpos
        tya
        ldx stringptr
        ldy stringptr+1
        jsr list_delete
        jmp cgs_loop

cgs_done:
        rts

cgs_length:     .byte 0
cgs_cursorpos:  .byte 0

; ======================================
ascii2hexdigit:
; Convert ASCII charactor code to hex digit, like this:
; '0'->$00, ... 'A'->$0a, ... 'F'->$0f
; ======================================
;  Input: A: character representing a hex digit
; Output: A: hex digit value or $ff if input was in [0-9] or [A-Z]
; ======================================
        ldx #$0f
a2h_loop:
        cmp hexdigitsascii,x
        beq a2h_break
        dex
        bpl a2h_loop
a2h_break:
        txa
        rts

; ======================================
replacelownybble:
; Replace low nybble of X by A and return result in A.
; ======================================
        sta replacelownybble1+1
        txa
        and #$f0
replacelownybble1:
        ora #$00
        rts

; ======================================
replacehighnybble:
; Replace high nybble of X by A and return result in A.
; ======================================
        asl
        asl
        asl
        asl
        sta replacehighnybble1+1
        txa
        and #$0f
replacehighnybble1:
        ora #$00
        rts

; Data for list insert/delete.
list_length:            .byte 0
list_defaultelement:    .byte 0

; ======================================
list_insert:
; Insert into orderlist, wave or arpeggio table.
; ======================================
;  Input: A: index in list where insertion happens
;         X: pointer to list low byte
;         Y: pointer to list high byte
;         list_length: last valid element in list ($ff means 256 elements)
; ======================================
; Does not make much sense to insert at last element, so avoid that.
        cmp list_length
        beq li_last
        sta li_index+1
        stx editor_temptrackptr
        sty editor_temptrackptr+1
        ldy list_length
li_loop:
        dey
        lda (editor_temptrackptr),y
        iny
        sta (editor_temptrackptr),y
        dey
li_index:
        cpy #$ff
        bne li_loop
li_last:
        rts

; ======================================
list_delete:
; Delete from orderlist, wave or arpeggio table.
; ======================================
;  Input: A: index in list where deletion happens
;         X: pointer to list low byte
;         Y: pointer to list high byte
;         list_length: last valid element in list ($ff means 256 elements)
;         list_defaultelement: byte that is inserted at list end
; ======================================
; When deleting from last element, nothing has to be moved.
        stx editor_temptrackptr
        sty editor_temptrackptr+1
        tay
        cpy list_length
        beq ld_last
ld_loop:
        iny
        lda (editor_temptrackptr),y
        dey
        sta (editor_temptrackptr),y
        iny
        cpy list_length
        bne ld_loop
ld_last:
        lda list_defaultelement
        sta (editor_temptrackptr),y
ld_first:
        rts

; ======================================
clear_song:
; Clear orderlist, patterns, track.
; ======================================
; Nuke orderlist.
        lda #$00
        ldx #>ORDERLIST
        ldy #$01
        jsr memsetpages
; Nuke patterns.
        ldx #>PATTERNS
        ldy #$06
        jsr memsetpages
; Nuke tracks.
        ldx #>TRACKS_BASE
        ldy #$60
        jsr memsetpages
; Nuke title.
        lda #$20
        ldx #SONGTITLELEN-1
csct_loop:
        sta SONGTITLE,x
        dex
        bpl csct_loop
        rts

; ======================================
clear_instruments:
; Clear all instrument data.
; ======================================
; Nuke instruments.
        ldx #>INSTRUMENTS
        ldy #$02
        jsr memsetpages
; Nuke wave table.
        ldx #>WAVETABLE
        ldy #$01
        jsr memsetpages
; Nuke arpeggio table.
        ldx #>ARPEGGIOTABLE
        ldy #$01
        jsr memsetpages
; Nuke instrument names.
        lda #$20
        ldx #>INSTRUMENTNAMES
        ldy #$02
        jmp memsetpages

; ======================================
memsetpages:
; Fill memory with a given byte.
; ======================================
;  Input: A: fill byte
;         X: page number
;         Y: number of pages
; ======================================
        stx msp_fillpage+2
        ldx #0
msp_fillpage:
        sta $ff00,x
        inx
        bne msp_fillpage
        inc msp_fillpage+2
        dey
        bne msp_fillpage
        rts

; ======================================
; Editor's readonly data.
; ======================================

; X positions for channels in pattern display.
editor_channelxpositions:       .byte 5,17,29

; Cursor X positions in pattern editor.
editor_patternfieldxpositions:  .byte 0, 4, 5, 7, 8, 9
editor_instfieldxpositions:     .byte 21, 28, 37

screenptrs:     .word $0400,$0428,$0450,$0478,$04a0,$04c8,$04f0,$0518
                .word $0540,$0568,$0590,$05b8,$05e0,$0608,$0630,$0658
                .word $0680,$06a8,$06d0,$06f8,$0720,$0748,$0770,$0798
                .word $07c0

; Hex digits in screen codes and in ASCII codes.
hexdigitsscreen:.byte $30, $31, $32, $33, $34, $35, $36, $37, $38, $39, $01, $02, $03, $04, $05, $06
hexdigitsascii: .byte KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7
                .byte KEY_8, KEY_9, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F

notestrings:    .byte $2e, $2e, $2e             ; ...
; Octave 0.
                .byte $03, $2d, $30             ; C-0
                .byte $03, $23, $30             ; C#0
                .byte $04, $2d, $30             ; D-0
                .byte $04, $23, $30             ; D#0
                .byte $05, $2d, $30             ; E-0
                .byte $06, $2d, $30             ; F-0
                .byte $06, $23, $30             ; F#0
                .byte $07, $2d, $30             ; G-0
                .byte $07, $23, $30             ; G#0
                .byte $01, $2d, $30             ; A-0
                .byte $01, $23, $30             ; A#0
                .byte $02, $2d, $30             ; B-0
; Octave 1.
                .byte $03, $2d, $31             ; C-1
                .byte $03, $23, $31             ; C#1
                .byte $04, $2d, $31             ; D-1
                .byte $04, $23, $31             ; D#1
                .byte $05, $2d, $31             ; E-1
                .byte $06, $2d, $31             ; F-1
                .byte $06, $23, $31             ; F#1
                .byte $07, $2d, $31             ; G-1
                .byte $07, $23, $31             ; G#1
                .byte $01, $2d, $31             ; A-1
                .byte $01, $23, $31             ; A#1
                .byte $02, $2d, $31             ; B-1
; Octave 2.
                .byte $03, $2d, $32             ; C-2
                .byte $03, $23, $32             ; C#2
                .byte $04, $2d, $32             ; D-2
                .byte $04, $23, $32             ; D#2
                .byte $05, $2d, $32             ; E-2
                .byte $06, $2d, $32             ; F-2
                .byte $06, $23, $32             ; F#2
                .byte $07, $2d, $32             ; G-2
                .byte $07, $23, $32             ; G#2
                .byte $01, $2d, $32             ; A-2
                .byte $01, $23, $32             ; A#2
                .byte $02, $2d, $32             ; B-2
; Octave 3.
                .byte $03, $2d, $33             ; C-3
                .byte $03, $23, $33             ; C#3
                .byte $04, $2d, $33             ; D-3
                .byte $04, $23, $33             ; D#3
                .byte $05, $2d, $33             ; E-3
                .byte $06, $2d, $33             ; F-3
                .byte $06, $23, $33             ; F#3
                .byte $07, $2d, $33             ; G-3
                .byte $07, $23, $33             ; G#3
                .byte $01, $2d, $33             ; A-3
                .byte $01, $23, $33             ; A#3
                .byte $02, $2d, $33             ; B-3
; Octave 4.
                .byte $03, $2d, $34             ; C-4
                .byte $03, $23, $34             ; C#4
                .byte $04, $2d, $34             ; D-4
                .byte $04, $23, $34             ; D#4
                .byte $05, $2d, $34             ; E-4
                .byte $06, $2d, $34             ; F-4
                .byte $06, $23, $34             ; F#4
                .byte $07, $2d, $34             ; G-4
                .byte $07, $23, $34             ; G#4
                .byte $01, $2d, $34             ; A-4
                .byte $01, $23, $34             ; A#4
                .byte $02, $2d, $34             ; B-4
; Octave 5.
                .byte $03, $2d, $35             ; C-5
                .byte $03, $23, $35             ; C#5
                .byte $04, $2d, $35             ; D-5
                .byte $04, $23, $35             ; D#5
                .byte $05, $2d, $35             ; E-5
                .byte $06, $2d, $35             ; F-5
                .byte $06, $23, $35             ; F#5
                .byte $07, $2d, $35             ; G-5
                .byte $07, $23, $35             ; G#5
                .byte $01, $2d, $35             ; A-5
                .byte $01, $23, $35             ; A#5
                .byte $02, $2d, $35             ; B-5
; Octave 6.
                .byte $03, $2d, $36             ; C-6
                .byte $03, $23, $36             ; C#6
                .byte $04, $2d, $36             ; D-6
                .byte $04, $23, $36             ; D#6
                .byte $05, $2d, $36             ; E-6
                .byte $06, $2d, $36             ; F-6
                .byte $06, $23, $36             ; F#6
                .byte $07, $2d, $36             ; G-6
                .byte $07, $23, $36             ; G#6
                .byte $01, $2d, $36             ; A-6
                .byte $01, $23, $36             ; A#6
                .byte $02, $2d, $36             ; B-6
; Octave 7.
                .byte $03, $2d, $37             ; C-7
                .byte $03, $23, $37             ; C#7
                .byte $04, $2d, $37             ; D-7
                .byte $04, $23, $37             ; D#7
                .byte $05, $2d, $37             ; E-7
                .byte $06, $2d, $37             ; F-7
                .byte $06, $23, $37             ; F#7
                .byte $07, $2d, $37             ; G-7
                .byte $07, $23, $37             ; G#7
                .byte $01, $2d, $37             ; A-7
                .byte $01, $23, $37             ; A#7
                .byte $02, $2d, $37             ; B-7
                .byte $3d, $3d, $3d             ; ===

; Instrument 0 really means no instrument. This is the default name for that.
instrument0name .byte $1b,$0e,$0f,$20,$20,$09,$0e,$13,$14,$12,$15,$0d,$05,$0e,$14,$1d,$ff ; [no  instrument]

; RLE coded static panel text.
paneltext:      .byte $fe,$08,$60
                .byte $46,$71,$60,$46,$4f,$52,$60,$48,$45,$4c,$50       ; f1 for help
                .byte $fe,$1d,$60
                .byte $45,$44,$49,$54,$60,$53,$54,$45,$50,$7a           ; edit step:
                .byte $fe,$22,$60
                .byte $53,$50,$45,$45,$44,$7a                           ; speed:
                .byte $fe,$1e,$60
                .byte $52,$41,$53,$54,$45,$52,$7a,$a0,$a0,$6f           ; raster:  /
                .byte $fe,$1d,$60
                .byte $54,$49,$4d,$45,$7a,$a0,$a0,$ba,$a0,$a0,$ae       ; time:
                .byte $fe,$3e,$60
                .byte $ff

; RLE coded static pattern editor text. (Only the channel headers.)
patterntext:    .byte $fe,$05,$60
                .byte $54,$52,$4b,$60,$60,$60,$54,$52,$fe,$04,$60       ; trk.. tr..
                .byte $54,$52,$4b,$60,$60,$60,$54,$52,$fe,$04,$60       ; trk.. tr..
                .byte $54,$52,$4b,$60,$60,$60,$54,$52,$60,$60,$60       ; trk.. tr..
                .byte $ff

; RLE coded static instrument editor text.
instrumenttext: .byte $4e,$41,$4d,$45,$7a,$fe,$10,$a0   ; name:
                .byte $fe,$04,$60
                .byte $57,$41,$56,$45                   ; wave
                .byte $fe,$05,$60
                .byte $41,$52,$50,$60,$60,$60           ; arp
                .byte $fe,$30,$20
                .byte $01,$14,$14,$01,$03,$0b,$2f,$04,$05,$03,$01,$19,$3a                               ; attack/decay
                .byte $fe,$18,$20
                .byte $13,$15,$13,$14,$01,$09,$0e,$2f,$12,$05,$0c,$05,$01,$13,$05,$3a                   ; sustain/release
                .byte $fe,$17,$20
                .byte $17,$01,$16,$05,$20,$14,$01,$02,$0c,$05,$20,$13,$14,$01,$12,$14,$3a               ; wave table start
                .byte $fe,$19,$20
                .byte $17,$01,$16,$05,$20,$14,$01,$02,$0c,$05,$20,$05,$0e,$04,$3a                       ; wave table end
                .byte $fe,$18,$20
                .byte $17,$01,$16,$05,$20,$14,$01,$02,$0c,$05,$20,$0c,$0f,$0f,$10,$3a                   ; wave table loop
                .byte $fe,$13,$20
                .byte $01,$12,$10,$05,$07,$07,$09,$0f,$20,$14,$01,$02,$0c,$05,$20,$13,$14,$01,$12,$14,$3a ;arpeggio table start
                .byte $fe,$15,$20
                .byte $01,$12,$10,$05,$07,$07,$09,$0f,$20,$14,$01,$02,$0c,$05,$20,$05,$0e,$04,$3a       ; arpeggio table end
                .byte $fe,$14,$20
                .byte $01,$12,$10,$05,$07,$07,$09,$0f,$20,$14,$01,$02,$0c,$05,$20,$0c,$0f,$0f,$10,$3a   ; arpeggio table loop
                .byte $fe,$1a,$20
                .byte $16,$09,$02,$12,$01,$14,$0f,$20,$04,$05,$0c,$01,$19,$3a                           ; vibrato delay
                .byte $fe,$14,$20
                .byte $16,$09,$02,$12,$01,$14,$0f,$20,$04,$05,$10,$14,$08,$2f,$13,$10,$05,$05,$04,$3a   ; vibrato depth/speed
                .byte $fe,$1c,$20
                .byte $10,$15,$0c,$13,$05,$20,$17,$09,$04,$14,$08,$3a                                   ; pulse width
                .byte $fe,$1c,$20
                .byte $10,$15,$0c,$13,$05,$20,$13,$10,$05,$05,$04,$3a                                   ; pulse speed
                .byte $fe,$1b,$20
                .byte $10,$15,$0c,$13,$05,$20,$0c,$09,$0d,$09,$14,$13,$3a                               ; pulse limits
                .byte $fe,$15,$20
                .byte $06,$09,$0c,$14,$05,$12,$20,$03,$15,$14,$0f,$06,$06,$20,$06,$12,$05,$11,$3a                           ; filter cutoff freq:
                .byte $fe,$14,$20
                .byte $06,$09,$0c,$14,$05,$12,$20,$03,$15,$14,$0f,$06,$06,$20,$13,$10,$05,$05,$04,$3a   ; filter cutoff speed:
                .byte $fe,$13,$20
                .byte $06,$09,$0c,$14,$05,$12,$20,$03,$15,$14,$0f,$06,$06,$20,$0c,$09,$0d,$09,$14,$13,$3a ; filter cutoff limits:
                .byte $ff

; RLE coded static config menu text.
configmenutext:
                .byte $50,$52,$45,$53,$53,$60,$52,$45,$54,$55,$52,$4e,$60,$54,$4f,$60,$55,$50,$44,$41,$54,$45,$60,$43,$4f,$4c,$4f,$52,$53,$6e ; press return to update colors.
                .byte $fe,$0a,$60
                .byte $fe,$2e,$20
                .byte $10,$01,$0e,$05,$0c,$20,$02,$01,$03,$0b,$07,$12,$0f,$15,$0e,$04,$3a       ; panel background:
                .byte $fe,$13,$20
                .byte $16,$01,$12,$09,$01,$02,$0c,$05,$13,$20,$02,$01,$03,$0b,$07,$12,$0f,$15,$0e,$04,$3a ; variables background:
                .byte $fe,$1c,$20
                .byte $13,$14,$01,$14,$09,$03,$20,$14,$05,$18,$14,$3a                           ; static text:
                .byte $fe,$17,$20
                .byte $08,$09,$07,$08,$0c,$09,$07,$08,$14,$05,$04,$20,$14,$05,$18,$14,$3a       ; highlighted text:
                .byte $fe,$15,$20
                .byte $10,$01,$14,$14,$05,$12,$0e,$20,$02,$01,$03,$0b,$07,$12,$0f,$15,$0e,$04,$3a ; pattern background:
                .byte $fe,$11,$20
                .byte $03,$15,$12,$12,$05,$0e,$14,$20,$12,$0f,$17,$20,$02,$01,$03,$0b,$07,$12,$0f,$15,$0e,$04,$3a ; current row background:
                .byte $fe,$1b,$20
                .byte $10,$01,$14,$14,$05,$12,$0e,$20,$14,$05,$18,$14,$3a ; pattern text:
                .byte $fe,$1a,$20
                .byte $0d,$15,$14,$05,$04,$20,$03,$08,$01,$0e,$0e,$05,$0c,$3a ; muted channel:
                .byte $fe,$19,$20
                .byte $13,$05,$0c,$05,$03,$14,$05,$04,$20,$02,$0c,$0f,$03,$0b,$3a ; selected block:
                .byte $fe,$21,$20
                .byte $03,$15,$12,$13,$0f,$12,$3a ; cursor:
                .byte $ff

; RLE coded static specials menu text.
specmenutext:   .byte $54,$49,$54,$4c,$45,$7a,$fe,$20,$a0,$60,$60       ; title:
                .byte $fe,$2b,$20
                .byte $05,$20,$20,$20,$05,$04,$09,$14,$20,$13,$0f,$0e,$07,$20,$14,$09,$14,$0c,$05   ; e   edit song title
                .byte $fe,$15,$20
                .byte $03,$20,$20,$20,$03,$0c,$05,$01,$12,$20,$13,$0f,$0e,$07   ; c   clear song
                .byte $fe,$1a,$20
                .byte $09,$20,$20,$20,$03,$0c,$05,$01,$12,$20,$09,$0e,$13,$14,$12,$15,$0d,$05,$0e,$14,$13       ; i   clear instruments
                .byte $fe,$13,$20
                .byte $14,$20,$20,$20,$12,$05,$10,$0c,$01,$03,$05,$20,$09,$0e,$13,$14,$12,$15,$0d,$05,$0e,$14,$20,$09,$0e,$20,$14,$12,$01,$03,$0b,$13 ; t   replace instrument in tracks
                .byte $fe,$08,$20
                .byte $10,$20,$20,$20,$12,$05,$10,$0c,$01,$03,$05,$20,$09,$0e,$13,$14,$12,$15,$0d,$05,$0e,$14,$20,$09,$0e,$20,$10,$01,$14,$14,$05,$12,$0e,$13 ; p   replace instrument in patterns
                .byte $fe,$06,$20
                .byte $31,$20,$20,$20,$13,$17,$01,$10,$20,$14,$12,$01,$03,$0b,$13,$20,$31,$2d,$32               ; 1   swap tracks 1-2
                .byte $fe,$15,$20
                .byte $32,$20,$20,$20,$13,$17,$01,$10,$20,$14,$12,$01,$03,$0b,$13,$20,$32,$2d,$33               ; 2   swap tracks 2-3
                .byte $fe,$15,$20
                .byte $33,$20,$20,$20,$13,$17,$01,$10,$20,$14,$12,$01,$03,$0b,$13,$20,$33,$2d,$31               ; 3   swap tracks 3-2
                .byte $ff

; RLE coded static disk menu text.
diskmenutext:   .byte $43,$55,$52,$52,$45,$4E,$54,$60,$44,$45,$56,$49,$43,$45,$7A       ; current device:
                .byte $fe,$19,$60
                .byte $fe,$33,$20
                .byte $24,$20,$20,$20,$04,$09,$12,$05,$03,$14,$0f,$12,$19       ; $   directory
                .byte $fe,$1b,$20
                .byte $04,$20,$20,$20,$03,$08,$01,$0E,$07,$05,$20,$04,$05,$16,$09,$03,$05       ; D   change device
                .byte $fe,$17,$20
                .byte $0c,$20,$20,$20,$0c,$0f,$01,$04,$20,$13,$0f,$0e,$07   ; L   load song
                .byte $fe,$1b,$20
                .byte $13,$20,$20,$20,$13,$01,$16,$05,$20,$13,$0f,$0e,$07   ; S   save song
                .byte $fe,$1b,$20
                .byte $12,$20,$20,$20,$12,$05,$01,$04,$20,$14,$12,$01,$03,$0b,$13       ; R   read tracks
                .byte $fe,$19,$20
                .byte $17,$20,$20,$20,$17,$12,$09,$14,$05,$20,$14,$12,$01,$03,$0b,$13   ; W   write tracks
                .byte $ff

enternametext:  .byte $05,$0e,$14,$05,$12,$20,$06,$09,$0c,$05,$0e,$01,$0d,$05,$3A       ; enter filename:
                .byte $ff

clearsongtext:  .byte $03,$0c,$05,$01,$12,$20,$13,$0f,$0e,$07,$3f       ; clear song?
                .byte $ff

clearinsttext:  .byte $03,$0c,$05,$01,$12,$20,$09,$0e,$13,$14,$12,$15,$0d,$05,$0e,$14,$13,$3f       ; clear instruments?
                .byte $ff

areyousuretext: .byte $01,$12,$05,$20,$19,$0f,$15,$20,$13,$15,$12,$05,$3f ; are you sure?
                .byte $ff

confirmationtext:       .byte $20,$10,$12,$05,$13,$13,$20,$27,$19,$27,$20,$14,$0f,$20,$03,$0f,$0e,$06,$09,$12,$0d,$2e       ; press 'y' to confirm.
                        .byte $ff

firsttracktext: .byte $06,$09,$12,$13,$14,$20,$14,$12,$01,$03,$0b,$3a   ; first track:
                .byte $ff

lasttracktext:  .byte $0c,$01,$13,$14,$20,$14,$12,$01,$03,$0b,$3a       ; last track:
                .byte $ff

firstpatterntext:       .byte $06,$09,$12,$13,$14,$20,$10,$01,$14,$14,$05,$12,$0e,$3a   ; first pattern:
                        .byte $ff

lastpatterntext:        .byte $0c,$01,$13,$14,$20,$10,$01,$14,$14,$05,$12,$0e,$3a       ; last pattern:
                        .byte $ff

loadattracktext:        .byte $0c,$0f,$01,$04,$20,$01,$14,$20,$14,$12,$01,$03,$0b,$3a       ; load at track:
                        .byte $ff

replaceinsttext:        .byte $09,$0e,$13,$14,$12,$15,$0d,$05,$0e,$14,$3a ; replace instrument:
                        .byte $ff

replacewithinsttext:    .byte $17,$09,$14,$08,$20,$09,$0e,$13,$14,$12,$15,$0d,$05,$0e,$14,$3a ; replace instrument:
                        .byte $ff

editor_currenthelppage  .byte 0
helppagepointers:
                        .word helppage0,  helppage1,  helppage2,  helppage3
                        .word helppage4,  helppage5,  helppage6,  helppage7
                        .word helppage8,  helppage9,  helppage10, helppage11
                        .word helppage12, helppage13, helppage14, helppage15
                        .word helppage16
EDITOR_HELPPAGESNUM     = (*-helppagepointers)/2

; Keys that are used to enter notes.
; This key set is practically Protracker compatible.
notekeys        .byte KEY_Z, KEY_S, KEY_X, KEY_D, KEY_C, KEY_V, KEY_G, KEY_B, KEY_H, KEY_N, KEY_J, KEY_M
                .byte KEY_Q, KEY_2, KEY_W, KEY_3, KEY_E, KEY_R, KEY_5, KEY_T, KEY_6, KEY_Y, KEY_7, KEY_U, KEY_I, KEY_9, KEY_O, KEY_0, KEY_P
NOTEKEYS_NUM    = *-notekeys

; Default global keys.
global_keys:            .byte KEY_SPACE
                        .word eg_playonoff
                        .byte KEY_F1
                        .word eg_writehelp
                        .byte KEY_F2
                        .word eg_writecontexthelp
                        .byte KEY_F3
                        .word eg_gotoordereditor
                        .byte KEY_F5
                        .word eg_gotopatterneditor
                        .byte KEY_F7
                        .word eg_gotoinsteditor
                        .byte KEY_F4
                        .word eg_configmenu
                        .byte KEY_F6
                        .word eg_specmenu
                        .byte KEY_F8
                        .word eg_diskmenu
                        .byte KEY_COMMA
                        .word eg_instdec
                        .byte KEY_PERIOD
                        .word eg_instinc
                        .byte KEY_AT
                        .word eg_keypatterndec
                        .byte KEY_ASTERISK
                        .word eg_keypatterninc
                        .byte KEY_MINUS
                        .word eg_keyorderdec
                        .byte KEY_EQUALS
                        .word eg_keyorderinc
                        .byte KEY_COMM_1
                        .word eg_mutechannel1
                        .byte KEY_COMM_2
                        .word eg_mutechannel2
                        .byte KEY_COMM_3
                        .word eg_mutechannel3
                        .byte KEY_COMM_4
                        .word eg_muteall
GLOBAL_KEYS_NUM         = *-global_keys

; Default keys specific for orderlist editor.
editorder_keys:         .byte KEY_UP
                        .word eg_keyorderdec
                        .byte KEY_DOWN
                        .word eg_keyorderinc
                        .byte KEY_LEFT
                        .word eo_keyleft
                        .byte KEY_RIGHT
                        .word eo_keyright
                        .byte KEY_SH_A
                        .word eo_keypgup
                        .byte KEY_SH_Z
                        .word eo_keypgdn
                        .byte KEY_SH_Q
                        .word eo_keygotoline00
                        .byte KEY_SH_W
                        .word eo_keygotoline40
                        .byte KEY_SH_E
                        .word eo_keygotoline80
                        .byte KEY_SH_R
                        .word eo_keygotolinec0
                        .byte KEY_INSERT
                        .word eo_insert
                        .byte KEY_DELETE
                        .word eo_delete
EDITORDER_KEYS_NUM      = *-editorder_keys

; Default keys specific for pattern editor.
editpattern_keys:       .byte KEY_UP
                        .word ep_keyup
                        .byte KEY_DOWN
                        .word ep_keydown
                        .byte KEY_LEFT
                        .word ep_keyleft
                        .byte KEY_RIGHT
                        .word ep_keyright
                        .byte KEY_RUNSTOP
                        .word ep_keynextchannel
                        .byte KEY_SH_RUNSTOP
                        .word ep_keyprevchannel
                        .byte KEY_SH_A
                        .word ep_keypgup
                        .byte KEY_SH_Z
                        .word ep_keypgdn
                        .byte KEY_SH_Q
                        .word ep_keygotoline00
                        .byte KEY_SH_W
                        .word ep_keygotoline16
                        .byte KEY_SH_E
                        .word ep_keygotoline32
                        .byte KEY_SH_R
                        .word ep_keygotoline48
                        .byte KEY_COLON
                        .word ep_keytrackdec
                        .byte KEY_SEMICOLON
                        .word ep_keytrackinc
                        .byte KEY_COMM_Y
                        .word ep_edittracknum
                        .byte KEY_UPARROW
                        .word ep_keyclearnote
                        .byte KEY_SLASH
                        .word ep_keyoff
                        .byte KEY_CTRL_1
                        .word ep_octave1
                        .byte KEY_CTRL_2
                        .word ep_octave2
                        .byte KEY_CTRL_3
                        .word ep_octave3
                        .byte KEY_CTRL_4
                        .word ep_octave4
                        .byte KEY_CTRL_5
                        .word ep_octave5
                        .byte KEY_CTRL_6
                        .word ep_octave6
                        .byte KEY_CTRL_7
                        .word ep_octave7
                        .byte KEY_INSERT
                        .word ep_insert
                        .byte KEY_DELETE
                        .word ep_delete
                        .byte KEY_COMM_G
                        .word ep_grabeffect
                        .byte KEY_COMM_F
                        .word ep_dropeffect
                        .byte KEY_COMM_B
                        .word ep_setblockbeg
                        .byte KEY_COMM_E
                        .word ep_setblockend
                        .byte KEY_COMM_T
                        .word ep_selecttrack
                        .byte KEY_COMM_R
                        .word ep_unselectblock
                        .byte KEY_COMM_X
                        .word ep_cutblock
                        .byte KEY_COMM_C
                        .word ep_copyblock
                        .byte KEY_COMM_V
                        .word ep_pasteblock
                        .byte KEY_COMM_M
                        .word ep_mixblock
                        .byte KEY_COMM_Q
                        .word ep_transposeup
                        .byte KEY_COMM_A
                        .word ep_transposedown
                        .byte KEY_LEFTARROW
                        .word ep_editstepincdec
                        .byte KEY_COMM_W
                        .word ep_transposeinc
                        .byte KEY_COMM_S
                        .word ep_transposedec
                        .byte KEY_COMM_D
                        .word ep_edittranspose
                        .byte KEY_COMM_Z
                        .word ep_restoreundobuffer
EDITPATTERN_KEYS_NUM    = *-editpattern_keys

; Default keys specific for instrument editor.
editinstrument_keys:    .byte KEY_UP
                        .word ei_keyup
                        .byte KEY_DOWN
                        .word ei_keydown
                        .byte KEY_LEFT
                        .word ei_keyleft
                        .byte KEY_RIGHT
                        .word ei_keyright
                        .byte KEY_RUNSTOP
                        .word ei_keynextfield
                        .byte KEY_SH_RUNSTOP
                        .word ei_keyprevfield
                        .byte KEY_SH_A
                        .word ei_keypgup
                        .byte KEY_SH_Z
                        .word ei_keypgdn
                        .byte KEY_SH_Q
                        .word ei_keygotoline00
                        .byte KEY_SH_W
                        .word ei_keygotoline40
                        .byte KEY_SH_E
                        .word ei_keygotoline80
                        .byte KEY_SH_R
                        .word ei_keygotolinec0
                        .byte KEY_RETURN
                        .word ei_keygototable
                        .byte KEY_INSERT
                        .word ei_insert
                        .byte KEY_DELETE
                        .word ei_delete
                        .byte KEY_COMM_N
                        .word ei_editname
                        .byte KEY_COMM_C
                        .word ei_copyinst
                        .byte KEY_COMM_V
                        .word ei_pasteinst
EDITINSTRUMENT_KEYS_NUM = *-editinstrument_keys

; Keys for specials menu.
specmenu_keys:          .byte KEY_E
                        .word sm_edittitle
                        .byte KEY_C
                        .word sm_clearsong
                        .byte KEY_I
                        .word sm_clearinstruments
                        .byte KEY_T
                        .word sm_replaceinsttrack
                        .byte KEY_P
                        .word sm_replaceinstpattern
                        .byte KEY_1
                        .word sm_swap12
                        .byte KEY_2
                        .word sm_swap23
                        .byte KEY_3
                        .word sm_swap31
SPECMENU_KEYS_NUM       = *-specmenu_keys

; Keys for config menu.
configmenu_keys:        .byte KEY_UP
                        .word cm_keyup
                        .byte KEY_DOWN
                        .word cm_keydown
                        .byte KEY_RETURN
                        .word cm_putcolors
                        .byte KEY_EXCL
                        .word cm_colorset1
                        .byte KEY_DOUBLEQUOT
                        .word cm_colorset2
                        .byte KEY_HASHMARK
                        .word cm_colorset3
                        .byte KEY_DOLLAR
                        .word cm_colorset4
CONFIGMENU_KEYS_NUM       = *-configmenu_keys

; Keys for disk menu.
diskmenu_keys:          .byte KEY_DOLLAR
                        .word dm_directory
                        .byte KEY_D
                        .word dm_changedevice
                        .byte KEY_L
                        .word dm_loadsong
                        .byte KEY_S
                        .word dm_savesong
                        .byte KEY_R
                        .word dm_readtracks
                        .byte KEY_W
                        .word dm_writetracks
DISKMENU_KEYS_NUM       = *-diskmenu_keys

; Default color sets.
editor_colorsets:
                        .byte 6         ; Panel background.
                        .byte 11        ; Variables background.
                        .byte 15        ; Static text.
                        .byte 1         ; Highlighted text.
                        .byte 0         ; Pattern background.
                        .byte 11        ; Current row highlight.
                        .byte 15        ; Pattern text.
                        .byte 12        ; Pattern text for muted channels.
                        .byte 2         ; Selected block background.
                        .byte 1         ; Sprite cursor color.
EDITOR_COLORSNUM        = *-editor_colorsets

                        .byte 2         ; Panel background.
                        .byte 2         ; Variables background.
                        .byte 7         ; Static text.
                        .byte 1         ; Highlighted text.
                        .byte 9         ; Pattern background.
                        .byte 8         ; Current row highlight.
                        .byte 7         ; Pattern text.
                        .byte 15        ; Pattern text for muted channels.
                        .byte 2         ; Selected block background.
                        .byte 1         ; Sprite cursor color.

                        .byte 6         ; Panel background.
                        .byte 6         ; Variables background.
                        .byte 7         ; Static text.
                        .byte 1         ; Highlighted text.
                        .byte 12        ; Pattern background.
                        .byte 15        ; Current row highlight.
                        .byte 1         ; Pattern text.
                        .byte 0         ; Pattern text for muted channels.
                        .byte 14        ; Selected block background.
                        .byte 1         ; Sprite cursor color.

                        .byte 0         ; Panel background.
                        .byte 0         ; Variables background.
                        .byte 5         ; Static text.
                        .byte 13        ; Highlighted text.
                        .byte 0         ; Pattern background.
                        .byte 11        ; Current row highlight.
                        .byte 5         ; Pattern text.
                        .byte 12        ; Pattern text for muted channels.
                        .byte 11        ; Selected block background.
                        .byte 1         ; Sprite cursor color.

; ======================================
; Editor's read/write data.
; ======================================

; Current colors.
editor_colors:
color_d022:             .byte 0         ; Panel background.
color_d023:             .byte 0         ; Variables background.
color_static:           .byte 0         ; Static text.
color_highlight:        .byte 0         ; Highlighted text.
color_d021:             .byte 0         ; Pattern background.
color_currentrow:       .byte 0         ; Current row highlight.
color_pattern:          .byte 0         ; Pattern text.
color_muted:            .byte 0         ; Pattern text for muted channels.
color_d024:             .byte 0         ; Selected block background.
color_cursor:           .byte 0         ; Sprite cursor color.
; Make row highlight? Nonzero in pattern editor, zero everywhere else.
editor_makerowhighlight: .byte 0

editor_diskdevice:      .byte 0         ; Disk device number (8,9,10,11)
editor_filenamelen:     .byte 0
editor_filename:        .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

editor_isplaying:       .byte 0         ; If non-zero, run player.
editor_whichscreen:     .byte 0         ; Editor screens. See constants EDITOR_*
editor_currentkey:      .byte 0         ; Key returned by getin.
editor_currentraster:   .byte 0         ; Current rastertime used in player.
editor_maxraster:       .byte 0         ; Max rastertime used in player.

; Track numbers currently visible in the pattern editor.
editor_tracks:          .byte 0, 0, 0
editor_tracktransposes: .byte 0, 0, 0

; Block begin/end set flags and begin/end rows in the pattern editor.
editor_blockchannel:    .byte 0         ; Which channel is the block in?
editor_blockflags:      .byte 0
editor_blockbeg:        .byte 0
editor_blockend:        .byte 0

; Cursor location in orderlist editor.
editor_ordercol:        .byte 0         ; valid: $00..$01

; Cursor location in pattern editor.
editor_patternchannel:  .byte 0
; Never change these, since they index into editor_patternfieldxpositions[]
; 0: note
; 1: instrument high nybble
; 2: instrument low nybble
; 3: effect
; 4: effect parameter high nybble
; 5: effect parameter low nybble
editor_patternfield:    .byte 0

; Cursor locations in instrument editor.
editor_instfield:       .byte 0         ; 0: instrument params, 1: wavetable, 2: arpeggiotable
editor_instrow:         .byte 0         ; valid: $00..$0f
editor_instcol:         .byte 0         ; valid: $00..$01
editor_waveindex:       .byte 0
editor_arpindex:        .byte 0

; Cursor location in config menu.
editor_configrow:       .byte 0         ; valid: $00..EDITOR_COLORSNUM-1
editor_configcolors:                    ; Buffer for editing colors.
        REPEAT EDITOR_COLORSNUM
        .byte 0
        REPEND

; Current octave for entering notes.
editor_currentoctave:   .byte 0         ; =octave*12

; Current instrument when entering notes/using instrument editor.
editor_currentinst:     .byte 0

; This is added to the row position when something is entered into a pattern.
editor_editstep:        .byte 0

; Buffer for current row data.
editor_tracknote:       .byte 0
editor_trackinst:       .byte 0
editor_trackeffect:     .byte 0
editor_trackeffectpar:  .byte 0

; Buffer for grab/drop effect.
editor_trackeffectbuf:          .byte 0
editor_trackeffectparbuf:       .byte 0

; Buffer for track undo.
editor_trackundochannel:        .byte 0 ; = channel last modified or $80.
editor_trackundobuffer:
        REPEAT 192
        .byte 0
        REPEND

; Buffer for track cut/copy/paste.
editor_trackcopybufferpos:      .byte 0
editor_trackcopybufferlen:      .byte 0
editor_trackcopybuffer:
        REPEAT 256
        .byte 0
        REPEND

; Buffer for instrument copy/paste.
editor_instcopybuffervalid:     .byte 0
editor_instcopybuffer:          .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
editor_instnamecopybuffer:      .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

; Temp variables for displaying orderlist and pattern.
display_chn:            .byte 0
display_row:            .byte 0
display_row3:           .byte 0
display_x:              .byte 0
display_y:              .byte 0
display_height:         .byte 0
display_index:          .byte 0, 0
display_line:           .byte 0
display_highlightline:  .byte 0
display_indexcolormask: .byte 0
display_datacolormask:  .byte 0

; ======================================
; End of editor.
; ======================================

; ======================================
; Start of editor's player.
; ======================================

        org PLAYER

        jmp player_init
        jmp player_play
        jmp player_stop

; ======================================
player_init:
; ======================================
;  Input: A: start order number
;         chn_muted[]
; ======================================
        pha
        lda #$00
        tax
pi_clear:
        sta playercleardata,x
        inx
        cpx #PLAYERCLEARDATASIZE
        bne pi_clear
        ldy #$80                        ; Force a pattern jump.
        sty forcenewpattern
; Restore order position.
        pla
        sta ordernumber
        sta nextordernumber
; Init speed and speedcounter.
        ldy #6
        sty speed
        dey
        sty speedcounter
; Set global volume to maximum.
        ldy #$0f
        sty globalvolume
        rts

; ======================================
player_play:
; Call this in every frame.
; ======================================
; Play 1 tick of music.
        jmp processtick                 ; !!!! Expand inline.

; ======================================
player_stop:
; ======================================
; Shut up SID.
; ======================================
        lda #$00
        ldx #$18
ps_next:
        sta $d400,x
        dex
        bpl ps_next
        rts

; ======================================
processtick:
; ======================================
; Play 1 tick of music.
; ======================================
; Update the mod3 counter which is used for the arpeggio effect.
        dec mod3counter
        bpl pt_mod3counterdone
        lda #2
        sta mod3counter
pt_mod3counterdone:
; Update speed counter and see if it is time for new trackrow.
        inc speedcounter
        lda speedcounter
        cmp speed
        bne pt_processeffects           ; Do effects only.
; Start new row.
        lda #$00                        ; Reset speed counter.
        sta speedcounter
        lda forcenewpattern             ; Fetching a new pattern is forced at
        bmi pt_newpatternforced         ; start of tune and by order jump and pattern break.
        inc trackrow                    ; Advance one row.
        lda trackrow                    ; Check if we reached the end of pattern.
        cmp #64
        bmi pt_pattern_ok
pt_newpatternforced:
        jsr start_newpattern            ; !!!! Expand inline!

pt_pattern_ok:
; Create offset into track. trackrow3=3*trackrow
        lda trackrow
        asl
        adc trackrow
        sta trackrow3
; Fetch new lines from tracks.
        ldx #$00
pt_fetchnextchn:
        lda chn_trackptrlo,x
        sta player_trackptr
        lda chn_trackptrhi,x
        sta player_trackptr+1
        jsr fetchrow                    ; !!!! Expand inline!
        inx
        cpx #$03
        bne pt_fetchnextchn

; Process running effects for all channels.
pt_processeffects:
        ldx #$00
ptpe_nextchn:
; Suppose there is no vibrato and slide to do.
; Effects and instrument vibrato will override this.
        lda #$00
        sta chn_vibactive,x
; First check if there is an instrument. If there is none,
; there is no instrument wavetable/arpeggio/vibrato to do.
; This is done before effects, so that they can override this.
        lda chn_inst,x
        beq ptpe_noinst
        jsr process_instrument

ptpe_noinst:
; Now process track effects.
        lda chn_effect,x
        beq pt_processeffectsdone
        asl
        tay
        lda effectpointers,y
        sta pt_processeffects0+1
        lda effectpointers+1,y
        sta pt_processeffects0+2
pt_processeffects0:
        jsr $ffff                       ; !!!! Self-modifying.
pt_processeffectsdone:
        lda chn_slideactive,x
        beq ptpe_slideinactive
        lda chn_freqlo,x
        clc
        adc chn_slidefreqlo,x
        sta chn_freqlo,x
        lda chn_freqhi,x
        adc chn_slidefreqhi,x
        sta chn_freqhi,x
ptpe_slideinactive:
        lda chn_vibactive,x
        beq ptpe_vibinactive
        jsr calcvibrato
        lda chn_vibpos,x
        clc
        adc chn_vibspeed,x
        sta chn_vibpos,x
        jmp ptpe_vibdone
ptpe_vibinactive:
        lda chn_freqlo,x
        sta chn_finfreqlo,x
        lda chn_freqhi,x
        sta chn_finfreqhi,x
ptpe_vibdone:
        inx
        cpx #3
        bne ptpe_nextchn

; Do filter cutoff frequency manipulation.
        lda filterspeed
        beq pt_filterdone
        lda filterdir
        beq pt_filterup
; Decrement frequency. If lower limit is reached, reverse direction.
        lda filtercutofflo
        sec
        sbc filterspeed
        sta filtercutofflo
        lda filtercutoffhi
        sbc #0
        cmp filterlimitdown
        bpl pt_filterdownok
        lda #$00
        sta filterdir
        lda filterlimitdown
pt_filterdownok:
        jmp pt_filterupok
; Increment frequency. If upper limit is reached, reverse direction.
pt_filterup:
        lda filtercutofflo
        clc
        adc filterspeed
        sta filtercutofflo
        lda filtercutoffhi
        adc #0
        cmp filterlimitup
        bmi pt_filterupok
        lda #$80
        sta filterdir
        lda filterlimitup
pt_filterupok:
        sta filtercutoffhi
pt_filterdone:

; Dump everything into the SID chip.
        ldx #$00
pt_dump2sid:
        ldy sidregindex,x
        lda chn_plswidthlo,x
        sta $d402,y
        lda chn_plswidthhi,x
        sta $d403,y
        lda chn_finfreqlo,x
        sta $d400,y
        lda chn_finfreqhi,x
        sta $d401,y
        lda chn_muted,x
        beq ptds_channeon
        lda #$00
        sta $d404,y
        sta $d405,y
        sta $d406,y
        beq ptds_channedone
ptds_channeon:
        lda chn_waveform,x
        and chn_gateon,x
        sta $d404,y
        lda chn_ad,x
        sta $d405,y
        lda chn_sr,x
        sta $d406,y
ptds_channedone:
        inx
        cpx #$03
        bne pt_dump2sid
        lda filtercutofflo
        lsr
        lsr
        lsr
        lsr
        sta ptds_filtercutoff0+1
        lda filtercutoffhi
        asl
        asl
        asl
        asl
ptds_filtercutoff0:
        ora #$ff                        ; !!!! Self-modifyig.
        sta $d416
        lda filterinput
        sta $d417
        lda globalvolume
        ora filtermode
        sta $d418
        rts

; ======================================
process_instrument:
; Process instrument arpeggio, vibrato, etc.
; ======================================
;  Input: X: channel number
; Output: !!!! many
;   Uses: player_instptr, player_instptr+1
;         !!!! many
; ======================================
; Process wave table.
        ldy chn_waveidx,x
        lda WAVETABLE,y
        sta chn_waveform,x
; Advance wavetable index.
        iny
        tya
        cmp chn_waveend,x
        bcc pi_waveidxok
        beq pi_waveidxok
        lda chn_waveloop,x
pi_waveidxok:
        sta chn_waveidx,x

; If slide to note is active, do not do arpeggio.
        lda chn_effect,x
        cmp #3
        beq pi_skiparpeggio

; Process arpeggio table.
        ldy chn_arpidx,x
        lda ARPEGGIOTABLE,y
; Arpeggio table entry is < $80 -> add this note to base (relative arpeggio),
; otherwise use entry-$80 (absolute arpeggio)
        bmi pi_arptableabs
        clc
        adc chn_note,x
        adc chn_transpose,x
pi_arptableabs:
        and #$7f
        pha
; Advance arpeggio table index.
        iny
        tya
        cmp chn_arpend,x
        bcc pi_arpidxok
        beq pi_arpidxok
        lda chn_arploop,x
pi_arpidxok:
        sta chn_arpidx,x
        pla
        jsr note2freq                   ; Set frequency for this note.

; Get instrument pointer.
pi_skiparpeggio:
        lda chn_instptrlo,x
        sta player_instptr
        lda chn_instptrhi,x
        sta player_instptr+1
; Process instrument vibrato.
        lda chn_vibdelay,x
        bne pi_vibdelayed
        ldy #INST_VIBDEPTH_SPEED
        lda (player_instptr),y
        and #$f0
        beq pi_vibdone                  ; If depth==0, then no vibrato.
        sta chn_vibdepth,x
        lda (player_instptr),y
        and #$0f
        beq pi_vibdone                  ; If depth==0, then no vibrato.
        sta chn_vibspeed,x
        sta chn_vibactive,x             ; Anything non-zero means true.
        jmp pi_vibdone
pi_vibdelayed:
        dec chn_vibdelay,x
pi_vibdone:

; Do pulse width modulation.
        lda chn_plsspeed,x
        beq pi_pulsedone
        lda chn_plsdir,x
        beq pi_pulseup
; Decrement pulse width. If lower limit is reached, reverse direction.
        lda chn_plswidthlo,x
        sec
        sbc chn_plsspeed,x
        sta chn_plswidthlo,x
        lda chn_plswidthhi,x
        sbc #0
        cmp chn_plslimitdown,x
        bpl pi_pulsedownok
        lda #$00
        sta chn_plsdir,x
        lda chn_plslimitdown,x
pi_pulsedownok:
        sta chn_plswidthhi,x
        rts
; Increment pulse width. If upper limit is reached, reverse direction.
pi_pulseup:
        lda chn_plswidthlo,x
        clc
        adc chn_plsspeed,x
        sta chn_plswidthlo,x
        lda chn_plswidthhi,x
        adc #0
        cmp chn_plslimitup,x
        bmi pi_pulseupok
        lda #$80
        sta chn_plsdir,x
        lda chn_plslimitup,x
pi_pulseupok:
        sta chn_plswidthhi,x
pi_pulsedone:
        rts

; ======================================
effect00:
; ======================================
; Do nothing. This is in fact never called.
; ======================================
        rts

; ======================================
effect01:
; ======================================
; Slide up by param if param<$80, otherwise down by param-$80.
; ======================================
        lda speedcounter
        bne effect01run
; Calculate slide adder.
        lda chn_effectpar,x
        and #$7f
        beq effect01none
; Indicate that slide is active.
        pha
        lda #$80
        sta chn_slideactive,x
        pla
; Multiply parameter by 16 to get slide adder.
        ldy #$00
        sty mul8m
        asl
        rol mul8m
        asl
        rol mul8m
        asl
        rol mul8m
        asl
        rol mul8m
; If sliding goes down, negate adder.
        ldy chn_effectpar,x
        bmi effect01up
        eor #$ff
        clc
        adc #$01
        sta chn_slideaddlo,x
        lda mul8m
        eor #$ff
        adc #$00
        sta chn_slideaddhi,x
        bcc effect01run1                ; short jump
effect01up:
        sta chn_slideaddlo,x
        lda mul8m
        sta chn_slideaddhi,x
        jmp effect01run1
effect01run:
        lda chn_effectpar,x
        beq effect01none
effect01run1:
        lda chn_slidefreqlo,x
        clc
        adc chn_slideaddlo,x
        sta chn_slidefreqlo,x
        lda chn_slidefreqhi,x
        adc chn_slideaddhi,x
        sta chn_slidefreqhi,x
effect01none:
        rts

; ======================================
effect02:
; ======================================
; Set pulse width.
; Parameter is bits 11..4 of pulse width.
; ======================================
        lda chn_effectpar,x
        jmp set_pulsewidth

; ======================================
effect03:
; ======================================
; Slide to note. Damn hack.
; ======================================
        lda speedcounter
        bne effect03run
        lda chn_effectpar,x
        beq effect03none
; Multiply parameter by 16 to get slide adder.
        ldy #$00
        sty mul8m
        asl
        rol mul8m
        asl
        rol mul8m
        asl
        rol mul8m
        asl
        rol mul8m
        sta chn_slideaddlo,x
        lda mul8m
        sta chn_slideaddhi,x
; Decide whether sliding up or down.
        jsr freqcmp
        bcc effect03up
        iny
effect03up:
        tya
        sta chn_slidedir,x
effect03run:
        lda chn_slidedir,x
        beq effect03runup
; Slide down.
        lda chn_freqlo,x
        sec
        sbc chn_slideaddlo,x
        sta chn_freqlo,x
        lda chn_freqhi,x
        sbc chn_slideaddhi,x
        sta chn_freqhi,x
; Check if we have shot past the destination frequency.
        jsr freqcmp
        bcs effect03none                ; Still not reached.
        bcc effect03stop                ; Reached, stop.
; Slide up.
effect03runup:
        lda chn_freqlo,x
        clc
        adc chn_slideaddlo,x
        sta chn_freqlo,x
        lda chn_freqhi,x
        adc chn_slideaddhi,x
        sta chn_freqhi,x
; Check if we have shot past the destination frequency.
        jsr freqcmp
        bcc effect03none                ; Still not reached.
        bcs effect03stop                ; Reached, stop.
effect03stop:
        lda chn_notefreqlo,x
        sta chn_freqlo,x
        lda chn_notefreqhi,x
        sta chn_freqhi,x
effect03none:
        rts

; ======================================
effect04:
; ======================================
; Vibrato.
; High nybble of parameter is depth, low is speed.
; ======================================
        lda speedcounter
        bne effect04run
        lda chn_effectpar,x
        and #$f0
        beq effect04nodepth
        sta chn_effvibdepth,x
effect04nodepth:
        lda chn_effectpar,x
        and #$0f
        beq effect04nospeed
        sta chn_effvibspeed,x
effect04nospeed:
effect04run:
        lda #$80
        sta chn_vibactive,x
        lda chn_effvibdepth,x
        sta chn_vibdepth,x
        lda chn_effvibspeed,x
        sta chn_vibspeed,x
        rts

; ======================================
effect05:
; ======================================
; Set pulse speed.
; ======================================
        lda chn_effectpar,x
        sta chn_plsspeed,x
        rts

; ======================================
effect06:
; ======================================
; Set pulse limits.
; ======================================
        lda chn_effectpar,x
        jsr set_pulselimits
        rts

; ======================================
effect07:
; ======================================
; Set Attack/Decay.
; ======================================
        lda chn_effectpar,x
        sta chn_ad,x
        rts

; ======================================
effect08:
; ======================================
; Set Sustain/Release.
; ======================================
        lda chn_effectpar,x
        sta chn_sr,x
        rts

; ======================================
effect09:
; ======================================
; Set waveform.
; ======================================
        lda chn_effectpar,x
        sta chn_waveform,x
        rts

; ======================================
effect0a:
; ======================================
; Arpeggio. (Protracker-like.)
; ======================================
        lda speedcounter
        bne effect0arun
        lda chn_effectpar,x
        lsr
        lsr
        lsr
        lsr
        sta chn_effarp1,x
        lda chn_effectpar,x
        and #$0f
        sta chn_effarp2,x
effect0arun:
        lda chn_note,x
        clc
        ldy mod3counter
        beq effect0arun2                ; Counter at 0 -> use note 2.
        dey
        bne effect0arun0                ; Counter at 2 -> use base note.
        adc chn_effarp1,x               ; Add note 1.
        bcc effect0arun0                ; Always true, this is a short jump.
effect0arun2:
        adc chn_effarp2,x               ; Add note 2.
effect0arun0:
        jsr note2freq
        rts

; ======================================
effect0b:
; ======================================
; Jump to order.
; ======================================
        lda chn_effectpar,x
        sta nextordernumber
        lda #$80
        sta forcenewpattern
        rts

; ======================================
effect0c:
; ======================================
; Set filter cutoff frequency.
; Parameter is 8 MSBs of frequency.
; ======================================
        lda chn_effectpar,x
        jmp set_filtercutoff

; ======================================
effect0d:
; ======================================
; Pattern break.
; ======================================
        lda chn_effectpar,x
        sta firstrow
        lda #$80
        sta forcenewpattern
        rts

; ======================================
effect0e:
; ======================================
; Filter resonance/input control.
; High nybble is resonance, bit 0,1,2 enable filter for voice 1,2,3.
; ======================================
        lda chn_effectpar,x
        sta filterinput
        rts

; ======================================
effect0f:
; ======================================
; Parameter: $00-$7f: set speed
; Parameter: $80-$8f: set global volume
; Parameter: $90-$9f: set filter mode.
;   bit 0: low pass, bit 2: band pass, bit 3: high pass,
;   bit 3: cut off voice 3's output.
; Parameter: $a0-$af: fine slide down.
; Parameter: $b0-$bf: fine slide up.
; Parameter: $c0-$cf: note cut.
; Parameter: $d0-$df: note delay.                               !!!! TODO
; ======================================
        lda speedcounter
        beq effect0finit
        jmp effect0frun
effect0finit:
        lda chn_effectpar,x
        bpl effect0fspeed
        and #$f0
        tay
        lda chn_effectpar,x
        and #$0f
; Now A is parameter bits 0..3, Y is bits 4..7
        cpy #$80
        beq effect0f80
        cpy #$90
        beq effect0f90
        cpy #$a0
        beq effect0fa0
        cpy #$b0
        beq effect0fb0
        cpy #$c0
        beq effect0fc0
        cpy #$d0
        beq effect0fd0
        rts
; Set speed.
effect0fspeed:
        sta speed
        rts
; Set global volume.
effect0f80:
        sta globalvolume
        rts
; Set filter mode.
effect0f90:
        asl
        asl
        asl
        asl
        sta filtermode
        rts
; Fine slide down.
effect0fa0:
; Multiply parameter by 16 to get slide adder.
        ldy #$00
        sty mul8m
        asl
        rol mul8m
        asl
        rol mul8m
        asl
        rol mul8m
        asl
        rol mul8m
; Negate adder.
        eor #$ff
        tay
        iny
        lda mul8m
        eor #$ff
        adc #$00
        sta mul8m
        tya
        bcc effect0fb0doslide           ; short jump
; Fine slide up.
effect0fb0:
; Multiply parameter by 16 to get slide adder.
        ldy #$00
        sty mul8m
        asl
        rol mul8m
        asl
        rol mul8m
        asl
        rol mul8m
        asl
        rol mul8m
; Add to slidefreq.
effect0fb0doslide:
        adc chn_slidefreqlo,x
        sta chn_slidefreqlo,x
        lda mul8m
        adc chn_slidefreqhi,x
        sta chn_slidefreqhi,x
; Indicate that slide is active.
        lda #$80
        sta chn_slideactive,x
        rts
; Note cut.
effect0fc0:
        cmp speedcounter
        bne effect0fc0done
        lda #$fe                        ; Release gate.
        sta chn_gateon,x
effect0fc0done:
        rts
; Note delay. !!!! TODO
effect0fd0:
        rts
effect0frun:
        lda chn_effectpar,x
        and #$f0
        tay
        lda chn_effectpar,x
        and #$0f
; Now A is parameter bits 0..3, Y is bits 4..7
        cpy #$c0
        beq effect0fc0
        rts

; ======================================
start_newpattern:
; Get order index -> pattern number -> tracks for channels 1,2,3.
; ======================================
;  Input: ordernumber
; Output: chn1track: number of track for channel 1
;         chn2track: number of track for channel 2
;         chn3track: number of track for channel 3
;         trackrow: = previous firstrow
;         firstrow: always 0
;         forcenewpattern: always 0
; ======================================
        ldy nextordernumber             ; Get index in order table.
        sty ordernumber
        inc nextordernumber
; player_patternptr=PATTERNS+6*ORDERLIST[ordernumber]
        ldx #0
        lda ORDERLIST,y            ; Get current pattern.
        asl
        bcc snp_noof1
        inx
        clc
snp_noof1:
        adc ORDERLIST,y
        bcc snp_noof2
        inx
snp_noof2:
        stx player_patternptr+1
        asl
        rol player_patternptr+1
        adc #<PATTERNS
        sta player_patternptr
        lda player_patternptr+1
        adc #>PATTERNS
        sta player_patternptr+1
; Init row positions.
        lda firstrow                    ; Usually 0 or set by pattern break.
        sta trackrow
        lda #$00                        ; Default pattern start is row 0.
        sta firstrow
        sta forcenewpattern             ; Do not force new pattern.
; Fetch transpose values and create track pointers for each channel.
        tax
        tay
        jsr start_newtrack              ; Get track for channel 1
        jsr start_newtrack              ; Get track for channel 2
        jsr start_newtrack              ; Get track for channel 3
        rts

; ======================================
start_newtrack:
; Read track number and calculate 192*track_number+tracks.
; Also read track transpose.
; ======================================
;  Input: player_patternptr, player_patternptr+1: pointer to pattern
;         X: channel number
;         Y: 2*channel number
; Output: chn_trackptrlo, chn_trackptrhi, chn_transpose
; ======================================
        lda (player_patternptr),y              ; Get track number.
        asl
        adc (player_patternptr),y
        sta player_trackptr+1
        lda #$00
        ror player_trackptr+1
        ror
        lsr player_trackptr+1
        ror
        adc #<TRACKS_BASE
        sta chn_trackptrlo,x
        lda player_trackptr+1
        adc #>TRACKS_BASE
        sta chn_trackptrhi,x
        iny
        lda (player_patternptr),y              ; Get transpose.
        sta chn_transpose,x
        inx
        iny
        rts

; ======================================
fetchrow:
; Parse a row of a track, read instrument data, etc.
; ======================================
;  Input: X: channel number
;         trackrow3
;         player_trackptr
; ======================================
; Fetch effect and parameter.
        ldy trackrow3
        iny
        lda (player_trackptr),y
        lsr
        lsr
        lsr
        lsr
        lsr
        sta chn_effect,x
        iny
        lda (player_trackptr),y
        sta chn_effectpar,x
; Fetch effect and instrument.
        dey
        dey
        lda (player_trackptr),y
        lsr
        lsr
        lsr
        lsr
        and #$08
        ora chn_effect,x
        sta chn_effect,x
        iny
        lda (player_trackptr),y
        and #$1f
        bne fr_newinst
        jmp fr_nonewinst
fr_newinst:
        sta chn_inst,x
; Calculate 16*(inst_number-1)+INSTRUMENTS
        sec
        sbc #$01
        ldy #$00
        asl
        asl
        asl
        asl
        bcc fr_noof
        iny
fr_noof:
        clc
        adc #<INSTRUMENTS
        sta chn_instptrlo,x
        sta player_instptr
        tya
        adc #>INSTRUMENTS
        sta chn_instptrhi,x
        sta player_instptr+1
; Copy instrument parameters to channel.
        ldy #INST_AD                    ; Copy AD.
        lda (player_instptr),y
        sta chn_ad,x
        iny                             ; Copy SR.
        lda (player_instptr),y
        sta chn_sr,x
        iny                             ; Wave table start index.
        lda (player_instptr),y
        sta chn_waveidx,x
        iny                             ; Wave table end index.
        lda (player_instptr),y
        sta chn_waveend,x
        iny                             ; Wave table loop index.
        lda (player_instptr),y
        sta chn_waveloop,x
        iny                             ; Arpeggio table start index.
        lda (player_instptr),y
        sta chn_arpidx,x
        iny                             ; Arpeggio table end index.
        lda (player_instptr),y
        sta chn_arpend,x
        iny                             ; Arpeggio table loop index.
        lda (player_instptr),y
        sta chn_arploop,x
        iny                             ; Copy vibrato delay.
        lda (player_instptr),y
        sta chn_vibdelay,x
        iny                             ; Skip vibrato depth/speed.
        iny                             ; Copy pulse width.
        lda (player_instptr),y
        jsr set_pulsewidth
        iny                             ; Copy pulse speed.
        lda (player_instptr),y
        sta chn_plsspeed,x
        iny                             ; Copy pulse limits.
        lda (player_instptr),y
        jsr set_pulselimits
        iny                             ; Copy filter cutoff frequency.
        lda (player_instptr),y
        beq fr_nofilter
        jsr set_filtercutoff
        iny                             ; Cutoff speed.
        lda (player_instptr),y
        sta filterspeed
        iny                             ; Cutoff limits.
        lda (player_instptr),y
        jsr set_filterlimits
fr_nofilter:
; Reset vibrato and pulse width modulation.
        lda #$00
        sta chn_plsdir,x
        sta chn_vibpos,x
fr_nonewinst:
; Fetch note.
        ldy trackrow3
        lda (player_trackptr),y
        and #$7f
        beq fr_nonewnote
        cmp #97
        beq fr_noteoff
; Normal note between C-0 and B-7.
        sta chn_note,x
; If slide to note is active, set chn_notefreq?? only.
        ldy chn_effect,x
        cpy #3
        bne fr_noslidetonote
        clc
        adc chn_transpose,x
        jsr note2notefreq
; Restart wave and arpeggio table and vibrato if there is a new note.
fr_noslidetonote:
        lda chn_instptrlo,x
        sta player_instptr
        lda chn_instptrhi,x
        sta player_instptr+1
        ldy #INST_WAVETABLEINDEX        ; Wave table index start.
        lda (player_instptr),y
        sta chn_waveidx,x
        ldy #INST_ARPTABLEINDEX         ; Arpeggio table index start.
        lda (player_instptr),y
        sta chn_arpidx,x
; Reset slide.
        lda #$00
        sta chn_slidefreqlo,x
        sta chn_slidefreqhi,x
        sta chn_slideactive,x
; Set gate on.
        lda #$ff
        bne fr_setgate                  ; short jump
; Set gate off.
fr_noteoff:
        lda #$fe
fr_setgate:
        sta chn_gateon,x
fr_nonewnote:
        rts

; ======================================
set_pulsewidth:
; ======================================
;  Input: X: channel number
;         A: pulse width bits 4..11
; ======================================
        pha
        asl
        asl
        asl
        asl
        sta chn_plswidthlo,x
        pla
        lsr
        lsr
        lsr
        lsr
        sta chn_plswidthhi,x
        rts

; ======================================
set_pulselimits:
; ======================================
;  Input: X: channel number
;         A: pulse limits. bit 7..4 is lower limit, bit 3..0 is upper limit
;            of 4 MSBs of pulse width.
; ======================================
        pha
        lsr
        lsr
        lsr
        lsr
        sta chn_plslimitdown,x
        pla
        and #$0f
        sta chn_plslimitup,x
        rts

; ======================================
set_filtercutoff:
; ======================================
;  Input: A: 8 MSBs of filter cutoff frequency.
; ======================================
        pha
        asl
        asl
        asl
        asl
        sta filtercutofflo
        pla
        lsr
        lsr
        lsr
        lsr
        sta filtercutoffhi
        rts

; ======================================
set_filterlimits:
; ======================================
;  Input: A: filter cutoff limits. bit 7..4 is lower limit,
;            bit 3..0 is upper limit for 4 MSBs of filter cutoff frequency.
; ======================================
        pha
        lsr
        lsr
        lsr
        lsr
        sta filterlimitdown
        pla
        and #$0f
        sta filterlimitup
        rts

; ======================================
calcvibrato:
; Calc vibrato freq for channel X.
; ======================================
;  Input: X: channel number
; Output: chn_vibadderlo, chn_vibadderhi
; ======================================
; Calc vibrato depth in frequency.
; depthfreq=depth*(freq[(note-1)+2]-freq[note-1])
        jsr calcfreqdifference
; Get vibrato frequency from table using depth.
        lda chn_vibpos,x
        and #$10
        php
        lda chn_vibpos,x
        and #$0f
        plp
        beq cv_vibrato_forward
        eor #$0f
cv_vibrato_forward:
        ora chn_vibdepth,x              ; == depth*16
        tay
        lda vibrato_table,y
        jsr mul16x8
; Divide word in mul8result by 16.
        lsr mul8result+1
        ror mul8result
        lsr mul8result+1
        ror mul8result
        lsr mul8result+1
        ror mul8result
        lsr mul8result+1
        ror mul8result
; mul8result is now the frequency that has to be
; added to or subtracted from the channel frequency.
        lda chn_vibpos,x
        and #$20
        bne cv_vibrato_addfreq
; chn_freq-=mul8result
        lda chn_freqlo,x
        sec
        sbc mul8result
        sta chn_finfreqlo,x
        lda chn_freqhi,x
        sbc mul8result+1
        sta chn_finfreqhi,x
        jmp cv_done
cv_vibrato_addfreq:
; chn_notefreq+=mul8result
        lda chn_freqlo,x
        clc
        adc mul8result
        sta chn_finfreqlo,x
        lda chn_freqhi,x
        adc mul8result+1
        sta chn_finfreqhi,x
cv_done:
        rts

; ======================================
calcfreqdifference:
; ======================================
;  Input: X: channel number
; Output: mul8n, mul8n+1
; ======================================
; Calc frequency difference of 2 halftones around current note.
; freqdiff=(freq[(note-1)+2]-freq[note-1])/16
        lda chn_note,x
        asl
        tay
        iny
        iny
        lda freqtable,y
        sta mul8n
        lda freqtable+1,y
        sta mul8n+1
        lda chn_note,x
        asl
        tay
        dey
        dey
        lda mul8n
        sec
        sbc freqtable,y
        sta mul8n
        lda mul8n+1
        sbc freqtable+1,y
; Divide by 16 so that multiplication will not overflow.
        lsr
        ror mul8n
        lsr
        ror mul8n
        lsr
        ror mul8n
        lsr
        ror mul8n
        sta mul8n+1
        rts

; ======================================
note2notefreq:
; Set freq for note of channel X from freq table.
; Set only chn_notefreq??, used when slide to note is done.
; ======================================
;  Input: X: channel number
;         A: note
; Output: chn_notefreqlo, chn_notefreqhi
; ======================================
        asl
        tay
        dey
        dey
        lda freqtable,y
        sta chn_notefreqlo,x
        lda freqtable+1,y
        sta chn_notefreqhi,x
        rts

; ======================================
note2freq:
; Set freq for note of channel X from freq table.
; ======================================
;  Input: X: channel number
;         A: note
; Output: chn_notefreqlo, chn_notefreqhi, chn_freqlo, chn_freqhi
; ======================================
        asl
        tay
        dey
        dey
        lda freqtable,y
        sta chn_notefreqlo,x
        sta chn_freqlo,x
        lda freqtable+1,y
        sta chn_notefreqhi,x
        sta chn_freqhi,x
        rts

; ======================================
freqcmp:
; See if chn_freq??<chn_notefreq?? for channel X. Used in slide to note.
; ======================================
;  Input: X: channel number
;         chn_freqlo, chn_freqhi: current frequency
;         chn_notefreqlo, chn_notefreqhi: destination frequency
; Output: FLAGS: C=!(chn_freq??<chn_notefreq??)
; ======================================
        lda chn_freqhi,x
        cmp chn_notefreqhi,x
        bne freqcmp_done
        lda chn_freqlo,x
        cmp chn_notefreqlo,x
freqcmp_done:
        rts

; ======================================
mul16x8:
; Multiply 16 bit by 8 bit.
; Clocks: 17 for setup and exit,
; the loop takes 23 clocks for clear bits, 42 for set bits in A,
; which makes the average case (23+42)*4+17=277 clocks. Hm.
; ======================================
;  Input: A (the fever set bits, the faster it goes)
;         mul8n, mul8n+1
; Output: mul8result: low byte
;         mul8result+1: high byte
;   Uses: mul8m
; ======================================
; Perform simple early-out bitwise multiplication.
        sta mul8m
        lda #$00
        sta mul8result
        sta mul8result+1
mul16x8loop:
        lsr mul8m                       ; 5
        beq mul16x8done                 ; 2
        bcc mul16x8noadd                ; 2/3
        lda mul8result                  ; 3
        clc                             ; 2
        adc mul8n                       ; 3
        sta mul8result                  ; 3
        lda mul8result+1                ; 3
        adc mul8n+1                     ; 3
        sta mul8result+1                ; 3
mul16x8noadd:
        asl mul8n                       ; 5
        rol mul8n+1                     ; 5
        bcc mul16x8loop                 ; 3, always true
mul16x8done:
        rts

; Index of SID registers for each channel.
sidregindex     .byte 0, 7, 14

; Pointers to effect procedures.
effectpointers  .word effect00, effect01, effect02, effect03
                .word effect04, effect05, effect06, effect07
                .word effect08, effect09, effect0a, effect0b
                .word effect0c, effect0d, effect0e, effect0f

; Frequencies for all notes.
; Fclk=1 MHz, B-7 would be 66288.
freqtable:
        .word   274,   291,   308,   326,   346,   366,   388,   411,   435,   461,   489,   518   ; octave 0
        .word   549,   581,   616,   652,   691,   732,   776,   822,   871,   923,   978,  1036   ; octave 1
        .word  1097,  1163,  1232,  1305,  1383,  1465,  1552,  1644,  1742,  1845,  1955,  2071   ; octave 2
        .word  2195,  2325,  2463,  2610,  2765,  2930,  3104,  3288,  3484,  3691,  3910,  4143   ; octave 3
        .word  4389,  4650,  4927,  5220,  5530,  5859,  6207,  6577,  6968,  7382,  7821,  8286   ; octave 4
        .word  8779,  9301,  9854, 10440, 11060, 11718, 12415, 13153, 13935, 14764, 15642, 16572   ; octave 5
        .word 17557, 18601, 19708, 20879, 22121, 23436, 24830, 26306, 27871, 29528, 31284, 33144   ; octave 6
        .word 35115, 37203, 39415, 41759, 44242, 46873, 49660, 52613, 55741, 59056, 62567, 65535   ; octave 7

; Fclk=1.0227 MHz (NTSC)
;freqtable:
;        .word   268,   284,   301,   319,   338,   358,   379,   402,   426,   451,   478,   506   ; octave 0
;        .word   536,   568,   602,   638,   676,   716,   759,   804,   852,   902,   956,  1013   ; octave 1
;        .word  1073,  1137,  1204,  1276,  1352,  1432,  1517,  1608,  1703,  1805,  1912,  2026   ; octave 2
;        .word  2146,  2274,  2409,  2552,  2704,  2865,  3035,  3215,  3407,  3609,  3824,  4051   ; octave 3
;        .word  4292,  4547,  4818,  5104,  5407,  5729,  6070,  6431,  6813,  7218,  7647,  8102   ; octave 4
;        .word  8584,  9094,  9635, 10208, 10815, 11458, 12139, 12861, 13626, 14436, 15295, 16204   ; octave 5
;        .word 17168, 18189, 19270, 20416, 21630, 22916, 24279, 25722, 27252, 28872, 30589, 32408   ; octave 6
;        .word 34335, 36377, 38540, 40832, 43260, 45832, 48558, 51445, 54504, 57745, 61179, 64817   ; octave 7

; Fclk=985248 Hz (PAL), B-7 would be 67280.
;freqtable:
;        .word   278,   295,   313,   331,   351,   372,   394,   417,   442,   468,   496,   526   ; octave 0
;        .word   557,   590,   625,   662,   702,   743,   788,   834,   884,   937,   992,  1051   ; octave 1
;        .word  1114,  1180,  1250,  1325,  1403,  1487,  1575,  1669,  1768,  1873,  1985,  2103   ; octave 2
;        .word  2228,  2360,  2500,  2649,  2807,  2973,  3150,  3338,  3536,  3746,  3969,  4205   ; octave 3
;        .word  4455,  4720,  5001,  5298,  5613,  5947,  6300,  6675,  7072,  7493,  7938,  8410   ; octave 4
;        .word  8910,  9440, 10001, 10596, 11226, 11894, 12601, 13350, 14144, 14985, 15876, 16820   ; octave 5
;        .word 17820, 18880, 20003, 21192, 22452, 23787, 25202, 26700, 28288, 29970, 31752, 33640   ; octave 6
;        .word 35641, 37760, 40005, 42384, 44904, 47574, 50403, 53401, 56576, 59940, 63504, 65535   ; octave 7

; from c64prg10.txt
;freqtable       .word   268,   284,   301,   318,   337,   358,   379,   401,   425,   451,   477,   506
;                .word   536,   568,   602,   637,   675,   716,   758,   803,   851,   902,   955,  1012
;                .word  1072,  1136,  1204,  1275,  1351,  1432,  1517,  1607,  1703,  1804,  1911,  2025
;                .word  2145,  2273,  2408,  2551,  2703,  2864,  3034,  3215,  3406,  3608,  3823,  4050
;                .word  4291,  4547,  4817,  5103,  5407,  5728,  6069,  6430,  6812,  7217,  7647,  8101
;                .word  8583,  9094,  9634, 10207, 10814, 11457, 12139, 12860, 13625, 14435, 15294, 16203
;                .word 17167, 18188, 19269, 20415, 21629, 22915, 24278, 25721, 27251, 28871, 30588, 32407
;                .word 34334, 36376, 38539, 40830, 43258, 45830, 48556, 51443, 54502, 57743, 61176, 64814

; 1 table for each possible vibrato depth.
; Each table contains 1/4th of a 64 byte sine wave.
; Total 256 bytes.
vibrato_table:
; Table for vibrato amplitude $00.
        .byte   0,   0,   0,   0,   0,   0,   0,   0
        .byte   0,   0,   0,   0,   0,   0,   0,   0
; Table for vibrato amplitude $10.
        .byte   0,   2,   3,   5,   6,   8,   9,  10
        .byte  11,  12,  13,  14,  15,  15,  16,  16
; Table for vibrato amplitude $20.
        .byte   0,   3,   6,   9,  12,  15,  18,  20
        .byte  23,  25,  27,  28,  30,  31,  31,  32
; Table for vibrato amplitude $30.
        .byte   0,   5,   9,  14,  18,  23,  27,  30
        .byte  34,  37,  40,  42,  44,  46,  47,  48
; Table for vibrato amplitude $40.
        .byte   0,   6,  12,  19,  24,  30,  36,  41
        .byte  45,  49,  53,  56,  59,  61,  63,  64
; Table for vibrato amplitude $50.
        .byte   0,   8,  16,  23,  31,  38,  44,  51
        .byte  57,  62,  67,  71,  74,  77,  78,  80
; Table for vibrato amplitude $60.
        .byte   0,   9,  19,  28,  37,  45,  53,  61
        .byte  68,  74,  80,  85,  89,  92,  94,  96
; Table for vibrato amplitude $70.
        .byte   0,  11,  22,  33,  43,  53,  62,  71
        .byte  79,  87,  93,  99, 103, 107, 110, 111
; Table for vibrato amplitude $80.
        .byte   0,  13,  25,  37,  49,  60,  71,  81
        .byte  91,  99, 106, 113, 118, 122, 126, 127
; Table for vibrato amplitude $90.
        .byte   0,  14,  28,  42,  55,  68,  80,  91
        .byte 102, 111, 120, 127, 133, 138, 141, 143
; Table for vibrato amplitude $a0.
        .byte   0,  16,  31,  46,  61,  75,  89, 102
        .byte 113, 124, 133, 141, 148, 153, 157, 159
; Table for vibrato amplitude $b0.
        .byte   0,  17,  34,  51,  67,  83,  98, 112
        .byte 124, 136, 146, 155, 163, 168, 173, 175
; Table for vibrato amplitude $c0.
        .byte   0,  19,  37,  56,  73,  91, 107, 122
        .byte 136, 148, 160, 169, 177, 184, 188, 191
; Table for vibrato amplitude $d0.
        .byte   0,  20,  41,  60,  80,  98, 116, 132
        .byte 147, 161, 173, 183, 192, 199, 204, 207
; Table for vibrato amplitude $e0.
        .byte   0,  22,  44,  65,  86, 106, 124, 142
        .byte 158, 173, 186, 198, 207, 214, 220, 223
; Table for vibrato amplitude $f0.
        .byte   0,  24,  47,  70,  92, 113, 133, 152
        .byte 170, 186, 200, 212, 222, 230, 235, 239
; Table for vibrato amplitude $ff.
;        .byte   0,  25,  50,  74,  98, 120, 142, 162
;        .byte 180, 197, 212, 225, 236, 244, 250, 254

; Channel data.
chn_muted:      .byte 0,0,0     ; Nonzero means channel is muted.
playercleardata:                ; Variables from here are cleared when playing starts.
chn_gateon:     .byte 0,0,0     ; Bit 0 is SID gate bit, bit 7-1 always set.
chn_vibactive:  .byte 0,0,0     ; Nonzero mean do vibrato.
chn_slideactive:.byte 0,0,0     ; Nonzero mean do slide.
chn_slidedir:   .byte 0,0,0     ; Slide direction in slide to note. 0 means up.
chn_plsdir:     .byte 0,0,0     ; Nonzero means decrement pulse width.
chn_transpose:  .byte 0,0,0     ; Track transpose read from pattern.
chn_track:      .byte 0,0,0     ; Track index. Unused outside editor.
chn_trackptrlo: .byte 0,0,0     ; Track pointer low byte.
chn_trackptrhi: .byte 0,0,0     ; Track pointer high byte.
chn_note:       .byte 0,0,0     ; Note read from track.
chn_inst:       .byte 0,0,0     ; Instrument read from track.
chn_effect:     .byte 0,0,0     ; Effect read from track.
chn_effectpar:  .byte 0,0,0     ; Effect parameter read from track.
chn_waveform:   .byte 0,0,0     ; Waveform from wavetable or set waveform effect.
chn_instptrlo:  .byte 0,0,0     ; Pointer to instrument
chn_instptrhi:  .byte 0,0,0     ;   for speedup.
chn_ad:         .byte 0,0,0     ; CFI, modified by set AD effect.
chn_sr:         .byte 0,0,0     ; CFI, modified by set SR effect.
chn_plswidthlo: .byte 0,0,0     ; CFI, modified by set pulse width effect and pulse width modulation.
chn_plswidthhi: .byte 0,0,0     ; CFI, modified by set pulse width effect and pulse width modulation.
chn_plsspeed:   .byte 0,0,0     ; CFI, modified by set pulse speed effect.
chn_plslimitdown:.byte 0,0,0    ; CFI, modified by set pulse limits effect.
chn_plslimitup: .byte 0,0,0     ; CFI, modified by set pulse limits effect.
chn_vibdelay:   .byte 0,0,0     ; CFI, count down to 0 then start instrument vibrato.
chn_vibdepth:   .byte 0,0,0     ; CFI, modified by vibrato effect.
chn_vibspeed:   .byte 0,0,0     ; CFI, modified by vibrato effect.
chn_waveidx:    .byte 0,0,0     ; CFI, wave table index, updated every tick.
chn_waveend:    .byte 0,0,0     ; CFI, wave table end index.
chn_waveloop:   .byte 0,0,0     ; CFI, wave table loop index.
chn_arpidx:     .byte 0,0,0     ; CFI, arpeggio table index, updated every tick.
chn_arpend:     .byte 0,0,0     ; CFI, arpeggio table end index.
chn_arploop:    .byte 0,0,0     ; CFI, arpeggio table loop index.
chn_notefreqlo: .byte 0,0,0     ; Frequency for note read from pattern.
chn_notefreqhi: .byte 0,0,0     ;
chn_freqlo:     .byte 0,0,0     ; Frequency after (arpeggio) and slide.
chn_freqhi:     .byte 0,0,0     ;   arpeggio is not added when slide to note.
chn_finfreqlo:  .byte 0,0,0     ; Final frequency after (arpeggio) and slide
chn_finfreqhi:  .byte 0,0,0     ;   and vibrato.
chn_vibpos:     .byte 0,0,0     ; Vibrato counter.
chn_slideaddlo: .byte 0,0,0     ; Value to add/sub to chn_slidefreq??.
chn_slideaddhi: .byte 0,0,0     ;   Calculated from note and depth.
chn_slidefreqlo:.byte 0,0,0     ; New note resets this to 0,
chn_slidefreqhi:.byte 0,0,0     ;   slide effect adds/subs chn_slidefreq??.
chn_effvibdepth:.byte 0,0,0     ; Store vibrato effect parameter.
chn_effvibspeed:.byte 0,0,0     ; Store vibrato effect parameter.
chn_effarp1:    .byte 0,0,0     ; Store arpeggio effect parameter.
chn_effarp2:    .byte 0,0,0     ; Store arpeggio effect parameter.

; Global SID parameters.
filterdir:      .byte 0         ; Nonzero means decrement cutoff frequency.
filterspeed:    .byte 0         ; Filter cutoff frequency variation speed.
filterlimitdown:.byte 0         ; Cutoff frequency lower limit.
filterlimitup:  .byte 0         ; Cutoff frequency upper limit.
filtercutofflo: .byte 0         ; Filter cutoff frequency 8 LSBs.
filtercutoffhi: .byte 0         ; Filter cutoff frequency 4 MSBs.
filterinput:    .byte 0         ; Filter input and resonance.
filtermode:     .byte 0         ; Filter mode as in $D418.
globalvolume:   .byte 0         ; Global volume that goes to SID.

; Variables describing the current position and status of the player.
mod3counter     .byte 0         ; Mod 3 counter used by arpeggio effect
speed           .byte 0         ; Speed as set by command F, default: 6
speedcounter    .byte 0         ; Speed counter count from 0 to speed-1
trackrow        .byte 0         ; Index of current trackrow.
trackrow3       .byte 0         ; ==3*trackrow
firstrow        .byte 0         ; First pattern row, usually 0 or set by pattern break.
ordernumber     .byte 0         ; Index into orderlist.
nextordernumber .byte 0         ; Next index into orderlist, set by order jump.
forcenewpattern .byte 0         ; Flag set by order jump and pattern break.

PLAYERCLEARDATASIZE = *-playercleardata

; ======================================
; End of editor's player.
; ======================================

; ======================================
; Font.
; ======================================

        org FONT
        .byte $00,$3c,$66,$6e,$6e,$60,$3c,$00   ; $00
        .byte $00,$3c,$66,$66,$7e,$66,$66,$00   ; $01
        .byte $00,$7c,$66,$7c,$66,$66,$7c,$00   ; $02
        .byte $00,$3c,$66,$60,$60,$66,$3c,$00   ; $03
        .byte $00,$78,$6c,$66,$66,$66,$7c,$00   ; $04
        .byte $00,$7e,$60,$78,$60,$60,$7e,$00   ; $05
        .byte $00,$7e,$60,$60,$78,$60,$60,$00   ; $06
        .byte $00,$3c,$66,$60,$6e,$66,$3c,$00   ; $07
        .byte $00,$66,$66,$7e,$66,$66,$66,$00   ; $08
        .byte $00,$3c,$18,$18,$18,$18,$3c,$00   ; $09
        .byte $00,$1e,$06,$06,$06,$66,$3c,$00   ; $0a
        .byte $00,$66,$6c,$78,$6c,$66,$66,$00   ; $0b
        .byte $00,$60,$60,$60,$60,$60,$7e,$00   ; $0c
        .byte $00,$c6,$ee,$fe,$d6,$c6,$c6,$00   ; $0d
        .byte $00,$7c,$66,$66,$66,$66,$66,$00   ; $0e
        .byte $00,$3c,$66,$66,$66,$66,$3c,$00   ; $0f
        .byte $00,$7c,$66,$66,$7c,$60,$60,$00   ; $10
        .byte $00,$3c,$66,$66,$66,$6c,$36,$00   ; $11
        .byte $00,$7c,$66,$66,$7c,$66,$66,$00   ; $12
        .byte $00,$3e,$60,$3c,$06,$66,$3c,$00   ; $13
        .byte $00,$7e,$18,$18,$18,$18,$18,$00   ; $14
        .byte $00,$66,$66,$66,$66,$66,$3c,$00   ; $15
        .byte $00,$66,$66,$66,$66,$3c,$18,$00   ; $16
        .byte $00,$c6,$c6,$d6,$fe,$ee,$c6,$00   ; $17
        .byte $00,$66,$66,$66,$3c,$66,$66,$00   ; $18
        .byte $00,$66,$66,$3e,$06,$66,$3c,$00   ; $19
        .byte $00,$7e,$66,$0c,$30,$66,$7e,$00   ; $1a
        .byte $00,$3c,$30,$30,$30,$30,$3c,$00   ; $1b
        .byte $00,$3c,$66,$60,$78,$60,$7e,$00   ; $1c
        .byte $00,$3c,$0c,$0c,$0c,$0c,$3c,$00   ; $1d
        .byte $00,$18,$3c,$7e,$18,$18,$18,$00   ; $1e
        .byte $00,$00,$18,$30,$7e,$30,$18,$00   ; $1f
        .byte $00,$00,$00,$00,$00,$00,$00,$00   ; $20
        .byte $00,$18,$18,$18,$18,$00,$18,$00   ; $21
        .byte $00,$36,$36,$6c,$00,$00,$00,$00   ; $22
        .byte $00,$6c,$fe,$6c,$6c,$fe,$6c,$00   ; $23
        .byte $00,$08,$3c,$68,$3c,$16,$3c,$10   ; $24
        .byte $00,$66,$6c,$18,$30,$66,$46,$00   ; $25
        .byte $00,$38,$6c,$38,$6e,$6c,$36,$00   ; $26
        .byte $00,$0c,$0c,$18,$00,$00,$00,$00   ; $27
        .byte $00,$0c,$18,$18,$18,$18,$0c,$00   ; $28
        .byte $00,$30,$18,$18,$18,$18,$30,$00   ; $29
        .byte $00,$00,$6c,$38,$fe,$38,$6c,$00   ; $2a
        .byte $00,$00,$18,$18,$7e,$18,$18,$00   ; $2b
        .byte $00,$00,$00,$00,$00,$18,$18,$30   ; $2c
        .byte $00,$00,$00,$00,$7e,$00,$00,$00   ; $2d
        .byte $00,$00,$00,$00,$00,$18,$18,$00   ; $2e
        .byte $00,$06,$0c,$18,$30,$60,$c0,$00   ; $2f
        .byte $00,$3c,$66,$6e,$76,$66,$3c,$00   ; $30
        .byte $00,$18,$38,$18,$18,$18,$3c,$00   ; $31
        .byte $00,$3c,$66,$06,$3c,$60,$7e,$00   ; $32
        .byte $00,$3c,$66,$0c,$06,$66,$3c,$00   ; $33
        .byte $00,$66,$66,$3e,$06,$06,$06,$00   ; $34
        .byte $00,$7e,$60,$7c,$06,$66,$3c,$00   ; $35
        .byte $00,$3c,$60,$7c,$66,$66,$3c,$00   ; $36
        .byte $00,$7e,$66,$0c,$0c,$18,$18,$00   ; $37
        .byte $00,$3c,$66,$3c,$66,$66,$3c,$00   ; $38
        .byte $00,$3c,$66,$66,$3e,$06,$3c,$00   ; $39
        .byte $00,$00,$18,$00,$00,$18,$00,$00   ; $3a
        .byte $00,$00,$00,$18,$00,$18,$18,$30   ; $3b
        .byte $00,$00,$1c,$30,$60,$30,$1c,$00   ; $3c
        .byte $00,$00,$00,$7e,$00,$7e,$00,$00   ; $3d
        .byte $00,$00,$70,$18,$0c,$18,$70,$00   ; $3e
        .byte $00,$3c,$66,$0c,$18,$00,$18,$00   ; $3f

; ======================================
; Cursor sprite.
; ======================================

        org SPRITE
        .byte $fc,$00,$00               ; row  0        xxxxxx
        .byte $84,$00,$00               ; row  1        x....x
        .byte $84,$00,$00               ; row  2        x....x
        .byte $84,$00,$00               ; row  3        x....x
        .byte $84,$00,$00               ; row  4        x....x
        .byte $84,$00,$00               ; row  5        x....x
        .byte $84,$00,$00               ; row  6        x....x
        .byte $84,$00,$00               ; row  7        x....x
        .byte $84,$00,$00               ; row  8        x....x
        .byte $fc,$00,$00               ; row  9        xxxxxx
        .byte $00,$00,$00               ; row 10
        .byte $00,$00,$00               ; row 11
        .byte $00,$00,$00               ; row 12
        .byte $00,$00,$00               ; row 13
        .byte $00,$00,$00               ; row 14
        .byte $00,$00,$00               ; row 15
        .byte $00,$00,$00               ; row 16
        .byte $00,$00,$00               ; row 17
        .byte $00,$00,$00               ; row 18
        .byte $00,$00,$00               ; row 19
        .byte $00,$00,$00               ; row 20

; ======================================
; Tune data.
; ======================================

        org ORDERLIST

        org SONGTITLE
        REPEAT SONGTITLELEN
        .byte $20
        REPEND

        org PATTERNS

        org INSTRUMENTS

        org INSTRUMENTNAMES
        REPEAT MAX_INSTRUMENTS*INSTRUMENTNAMELEN
        .byte $20
        REPEND

        org WAVETABLE

        org ARPEGGIOTABLE

        org TRACKS_BASE

; ======================================
; Help text (ASCII codes).
; ======================================

        org HELPTEXT

helppage0:
; P0      Welcome to Odin Tracker›
        .byte $d0,$30,$20,$20,$20,$20,$20,$20,$05,$d7,$45,$4c,$43,$4f,$4d,$45,$20,$54,$4f,$20,$cf,$44,$49,$4e,$20,$d4,$52,$41,$43,$4b,$45,$52,$9b,$0d
;
        .byte $0d
; Welcome to Odin Tracker 1.00 for C64
        .byte $d7,$45,$4c,$43,$4f,$4d,$45,$20,$54,$4f,$20,$cf,$44,$49,$4e,$20,$d4,$52,$41,$43,$4b,$45,$52,$20,$31,$2e,$30,$30,$20,$46,$4f,$52,$20,$c3,$36,$34,$0d
; released by Zed on 15 Feb 2000.
        .byte $52,$45,$4c,$45,$41,$53,$45,$44,$20,$42,$59,$20,$da,$45,$44,$20,$4f,$4e,$20,$31,$35,$20,$c6,$45,$42,$20,$32,$30,$30,$30,$2e,$0d
;
        .byte $0d
; Use Up/Down/Left/Right to navigate back
        .byte $d5,$53,$45,$20,$d5,$50,$2f,$c4,$4f,$57,$4e,$2f,$cc,$45,$46,$54,$2f,$d2,$49,$47,$48,$54,$20,$54,$4f,$20,$4e,$41,$56,$49,$47,$41,$54,$45,$20,$42,$41,$43,$4b,$0d
; and forth in the pages. Any other key
        .byte $41,$4e,$44,$20,$46,$4f,$52,$54,$48,$20,$49,$4e,$20,$54,$48,$45,$20,$50,$41,$47,$45,$53,$2e,$20,$c1,$4e,$59,$20,$4f,$54,$48,$45,$52,$20,$4b,$45,$59,$0d
; exits help.
        .byte $45,$58,$49,$54,$53,$20,$48,$45,$4c,$50,$2e,$0d
;
        .byte $0d
; If the editor happens to hang, restart
        .byte $c9,$46,$20,$54,$48,$45,$20,$45,$44,$49,$54,$4f,$52,$20,$48,$41,$50,$50,$45,$4e,$53,$20,$54,$4f,$20,$48,$41,$4e,$47,$2c,$20,$52,$45,$53,$54,$41,$52,$54,$0d
; it with SYS2064 after reset.
        .byte $49,$54,$20,$57,$49,$54,$48,$20,$d3,$d9,$d3,$32,$30,$36,$34,$20,$41,$46,$54,$45,$52,$20,$52,$45,$53,$45,$54,$2e
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage1:
; P1      Song structure›
        .byte $d0,$31,$20,$20,$20,$20,$20,$20,$05,$d3,$4f,$4e,$47,$20,$53,$54,$52,$55,$43,$54,$55,$52,$45,$9b,$0d
;
        .byte $0d
; The song is structed in the good old
        .byte $d4,$48,$45,$20,$53,$4f,$4e,$47,$20,$49,$53,$20,$53,$54,$52,$55,$43,$54,$45,$44,$20,$49,$4e,$20,$54,$48,$45,$20,$47,$4f,$4f,$44,$20,$4f,$4c,$44,$0d
; tracker style. The orderlist found in
        .byte $54,$52,$41,$43,$4b,$45,$52,$20,$53,$54,$59,$4c,$45,$2e,$20,$d4,$48,$45,$20,$4f,$52,$44,$45,$52,$4c,$49,$53,$54,$20,$46,$4f,$55,$4e,$44,$20,$49,$4e,$0d
; the top left corner contains pattern
        .byte $54,$48,$45,$20,$54,$4f,$50,$20,$4c,$45,$46,$54,$20,$43,$4f,$52,$4e,$45,$52,$20,$43,$4f,$4e,$54,$41,$49,$4e,$53,$20,$50,$41,$54,$54,$45,$52,$4e,$0d
; numbers. Each pattern consist of 3
        .byte $4e,$55,$4d,$42,$45,$52,$53,$2e,$20,$c5,$41,$43,$48,$20,$50,$41,$54,$54,$45,$52,$4e,$20,$43,$4f,$4e,$53,$49,$53,$54,$20,$4f,$46,$20,$33,$0d
; track numbers and the transpose values
        .byte $54,$52,$41,$43,$4b,$20,$4e,$55,$4d,$42,$45,$52,$53,$20,$41,$4e,$44,$20,$54,$48,$45,$20,$54,$52,$41,$4e,$53,$50,$4f,$53,$45,$20,$56,$41,$4c,$55,$45,$53,$0d
; for each of them. You can see these
        .byte $46,$4f,$52,$20,$45,$41,$43,$48,$20,$4f,$46,$20,$54,$48,$45,$4d,$2e,$20,$d9,$4f,$55,$20,$43,$41,$4e,$20,$53,$45,$45,$20,$54,$48,$45,$53,$45,$0d
; above the pattern display.
        .byte $41,$42,$4f,$56,$45,$20,$54,$48,$45,$20,$50,$41,$54,$54,$45,$52,$4e,$20,$44,$49,$53,$50,$4c,$41,$59,$2e
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage2:
; P2      Global keys›
        .byte $d0,$32,$20,$20,$20,$20,$20,$20,$05,$c7,$4c,$4f,$42,$41,$4c,$20,$4b,$45,$59,$53,$9b,$0d
;
        .byte $0d
; These keys work in all editor screens.
        .byte $d4,$48,$45,$53,$45,$20,$4b,$45,$59,$53,$20,$57,$4f,$52,$4b,$20,$49,$4e,$20,$41,$4c,$4c,$20,$45,$44,$49,$54,$4f,$52,$20,$53,$43,$52,$45,$45,$4e,$53,$2e,$0d
;
        .byte $0d
; SPACE   stop/stop playing
        .byte $d3,$d0,$c1,$c3,$c5,$20,$20,$20,$53,$54,$4f,$50,$2f,$53,$54,$4f,$50,$20,$50,$4c,$41,$59,$49,$4e,$47,$0d
;    F1   show this help
        .byte $20,$20,$20,$c6,$31,$20,$20,$20,$53,$48,$4f,$57,$20,$54,$48,$49,$53,$20,$48,$45,$4c,$50,$0d
;    F2   context help (well, sort of)
        .byte $20,$20,$20,$c6,$32,$20,$20,$20,$43,$4f,$4e,$54,$45,$58,$54,$20,$48,$45,$4c,$50,$20,$28,$57,$45,$4c,$4c,$2c,$20,$53,$4f,$52,$54,$20,$4f,$46,$29,$0d
;    F3   orderlist editor
        .byte $20,$20,$20,$c6,$33,$20,$20,$20,$4f,$52,$44,$45,$52,$4c,$49,$53,$54,$20,$45,$44,$49,$54,$4f,$52,$0d
;    F5   pattern editor
        .byte $20,$20,$20,$c6,$35,$20,$20,$20,$50,$41,$54,$54,$45,$52,$4e,$20,$45,$44,$49,$54,$4f,$52,$0d
;    F7   instrument editor
        .byte $20,$20,$20,$c6,$37,$20,$20,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$20,$45,$44,$49,$54,$4f,$52,$0d
;    F4   config menu
        .byte $20,$20,$20,$c6,$34,$20,$20,$20,$43,$4f,$4e,$46,$49,$47,$20,$4d,$45,$4e,$55,$0d
;    F6   specials menu
        .byte $20,$20,$20,$c6,$36,$20,$20,$20,$53,$50,$45,$43,$49,$41,$4c,$53,$20,$4d,$45,$4e,$55,$0d
;    F8   disk menu
        .byte $20,$20,$20,$c6,$38,$20,$20,$20,$44,$49,$53,$4b,$20,$4d,$45,$4e,$55,$0d
;   ./,   select instrument
        .byte $20,$20,$2e,$2f,$2c,$20,$20,$20,$53,$45,$4c,$45,$43,$54,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$0d
;   =/-   inc/dec order position
        .byte $20,$20,$3d,$2f,$2d,$20,$20,$20,$49,$4e,$43,$2f,$44,$45,$43,$20,$4f,$52,$44,$45,$52,$20,$50,$4f,$53,$49,$54,$49,$4f,$4e,$0d
;
        .byte $0d
; Up/Down/Left/Right/Inst/Del usually
        .byte $d5,$50,$2f,$c4,$4f,$57,$4e,$2f,$cc,$45,$46,$54,$2f,$d2,$49,$47,$48,$54,$2f,$c9,$4e,$53,$54,$2f,$c4,$45,$4c,$20,$55,$53,$55,$41,$4c,$4c,$59,$0d
; work as expected.
        .byte $57,$4f,$52,$4b,$20,$41,$53,$20,$45,$58,$50,$45,$43,$54,$45,$44,$2e,$0d
; RUNSTOP and SHIFT+RUNSTOP work like
        .byte $d2,$d5,$ce,$d3,$d4,$cf,$d0,$20,$41,$4e,$44,$20,$d3,$c8,$c9,$c6,$d4,$2b,$d2,$d5,$ce,$d3,$d4,$cf,$d0,$20,$57,$4f,$52,$4b,$20,$4c,$49,$4b,$45,$0d
; TAB and SHIFT+TAB on PC.
        .byte $d4,$c1,$c2,$20,$41,$4e,$44,$20,$d3,$c8,$c9,$c6,$d4,$2b,$d4,$c1,$c2,$20,$4f,$4e,$20,$d0,$c3,$2e,$0d
;
        .byte $0d
; Controlling channels
        .byte $c3,$4f,$4e,$54,$52,$4f,$4c,$4c,$49,$4e,$47,$20,$43,$48,$41,$4e,$4e,$45,$4c,$53,$0d
;   C=+1..3   mute/unmute channel 1,2,3
        .byte $20,$20,$c3,$3d,$2b,$31,$2e,$2e,$33,$20,$20,$20,$4d,$55,$54,$45,$2f,$55,$4e,$4d,$55,$54,$45,$20,$43,$48,$41,$4e,$4e,$45,$4c,$20,$31,$2c,$32,$2c,$33,$0d
;      C=+4   mute/unmute all channels
        .byte $20,$20,$20,$20,$20,$c3,$3d,$2b,$34,$20,$20,$20,$4d,$55,$54,$45,$2f,$55,$4e,$4d,$55,$54,$45,$20,$41,$4c,$4c,$20,$43,$48,$41,$4e,$4e,$45,$4c,$53
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage3:
; P3      Instrument editor (press F7)›
        .byte $d0,$33,$20,$20,$20,$20,$20,$20,$05,$c9,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$20,$45,$44,$49,$54,$4f,$52,$20,$28,$50,$52,$45,$53,$53,$20,$c6,$37,$29,$9b,$0d
;
        .byte $0d
; Here are the instrument parameters.
        .byte $c8,$45,$52,$45,$20,$41,$52,$45,$20,$54,$48,$45,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$20,$50,$41,$52,$41,$4d,$45,$54,$45,$52,$53,$2e,$0d
; (MSB means Most Significant Bit,
        .byte $28,$cd,$d3,$c2,$20,$4d,$45,$41,$4e,$53,$20,$cd,$4f,$53,$54,$20,$d3,$49,$47,$4e,$49,$46,$49,$43,$41,$4e,$54,$20,$c2,$49,$54,$2c,$0d
; LSB means Least Significant Bit.)
        .byte $cc,$d3,$c2,$20,$4d,$45,$41,$4e,$53,$20,$cc,$45,$41,$53,$54,$20,$d3,$49,$47,$4e,$49,$46,$49,$43,$41,$4e,$54,$20,$c2,$49,$54,$2e,$29,$0d
;
        .byte $0d
;   0-1. Attack/Decay/Sustain/Release
        .byte $20,$20,$30,$2d,$31,$2e,$20,$c1,$54,$54,$41,$43,$4b,$2f,$c4,$45,$43,$41,$59,$2f,$d3,$55,$53,$54,$41,$49,$4e,$2f,$d2,$45,$4c,$45,$41,$53,$45,$0d
;     2. Wave table start: index of first
        .byte $20,$20,$20,$20,$32,$2e,$20,$d7,$41,$56,$45,$20,$54,$41,$42,$4c,$45,$20,$53,$54,$41,$52,$54,$3a,$20,$49,$4e,$44,$45,$58,$20,$4f,$46,$20,$46,$49,$52,$53,$54,$0d
;        element in wave table.
        .byte $20,$20,$20,$20,$20,$20,$20,$45,$4c,$45,$4d,$45,$4e,$54,$20,$49,$4e,$20,$57,$41,$56,$45,$20,$54,$41,$42,$4c,$45,$2e,$0d
;     3. Wave table end: index of last
        .byte $20,$20,$20,$20,$33,$2e,$20,$d7,$41,$56,$45,$20,$54,$41,$42,$4c,$45,$20,$45,$4e,$44,$3a,$20,$49,$4e,$44,$45,$58,$20,$4f,$46,$20,$4c,$41,$53,$54,$0d
;        element in wave table.
        .byte $20,$20,$20,$20,$20,$20,$20,$45,$4c,$45,$4d,$45,$4e,$54,$20,$49,$4e,$20,$57,$41,$56,$45,$20,$54,$41,$42,$4c,$45,$2e,$0d
;     4. Wave table loop: if the player
        .byte $20,$20,$20,$20,$34,$2e,$20,$d7,$41,$56,$45,$20,$54,$41,$42,$4c,$45,$20,$4c,$4f,$4f,$50,$3a,$20,$49,$46,$20,$54,$48,$45,$20,$50,$4c,$41,$59,$45,$52,$0d
;        has reached the end index, it
        .byte $20,$20,$20,$20,$20,$20,$20,$48,$41,$53,$20,$52,$45,$41,$43,$48,$45,$44,$20,$54,$48,$45,$20,$45,$4e,$44,$20,$49,$4e,$44,$45,$58,$2c,$20,$49,$54,$0d
;        continues from this index. If
        .byte $20,$20,$20,$20,$20,$20,$20,$43,$4f,$4e,$54,$49,$4e,$55,$45,$53,$20,$46,$52,$4f,$4d,$20,$54,$48,$49,$53,$20,$49,$4e,$44,$45,$58,$2e,$20,$c9,$46,$0d
;        you want no loop, set end=loop.
        .byte $20,$20,$20,$20,$20,$20,$20,$59,$4f,$55,$20,$57,$41,$4e,$54,$20,$4e,$4f,$20,$4c,$4f,$4f,$50,$2c,$20,$53,$45,$54,$20,$45,$4e,$44,$3d,$4c,$4f,$4f,$50,$2e,$0d
;   5-7. Arpeggio table start/end/loop:
        .byte $20,$20,$35,$2d,$37,$2e,$20,$c1,$52,$50,$45,$47,$47,$49,$4f,$20,$54,$41,$42,$4c,$45,$20,$53,$54,$41,$52,$54,$2f,$45,$4e,$44,$2f,$4c,$4f,$4f,$50,$3a,$0d
;        All similar to wave table.
        .byte $20,$20,$20,$20,$20,$20,$20,$c1,$4c,$4c,$20,$53,$49,$4d,$49,$4c,$41,$52,$20,$54,$4f,$20,$57,$41,$56,$45,$20,$54,$41,$42,$4c,$45,$2e,$0d
;     8. Vibrato delay: number of ticks
        .byte $20,$20,$20,$20,$38,$2e,$20,$d6,$49,$42,$52,$41,$54,$4f,$20,$44,$45,$4c,$41,$59,$3a,$20,$4e,$55,$4d,$42,$45,$52,$20,$4f,$46,$20,$54,$49,$43,$4b,$53,$0d
;        before starting instrument
        .byte $20,$20,$20,$20,$20,$20,$20,$42,$45,$46,$4f,$52,$45,$20,$53,$54,$41,$52,$54,$49,$4e,$47,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$0d
;        vibrato.
        .byte $20,$20,$20,$20,$20,$20,$20,$56,$49,$42,$52,$41,$54,$4f,$2e,$0d
;     9. Vibrato depth/speed: high nybble
        .byte $20,$20,$20,$20,$39,$2e,$20,$d6,$49,$42,$52,$41,$54,$4f,$20,$44,$45,$50,$54,$48,$2f,$53,$50,$45,$45,$44,$3a,$20,$48,$49,$47,$48,$20,$4e,$59,$42,$42,$4c,$45,$0d
;        is depth, low nybble is speed.
        .byte $20,$20,$20,$20,$20,$20,$20,$49,$53,$20,$44,$45,$50,$54,$48,$2c,$20,$4c,$4f,$57,$20,$4e,$59,$42,$42,$4c,$45,$20,$49,$53,$20,$53,$50,$45,$45,$44,$2e,$0d
;        Larger number means deeper and
        .byte $20,$20,$20,$20,$20,$20,$20,$cc,$41,$52,$47,$45,$52,$20,$4e,$55,$4d,$42,$45,$52,$20,$4d,$45,$41,$4e,$53,$20,$44,$45,$45,$50,$45,$52,$20,$41,$4e,$44,$0d
;        faster vibrato.
        .byte $20,$20,$20,$20,$20,$20,$20,$46,$41,$53,$54,$45,$52,$20,$56,$49,$42,$52,$41,$54,$4f,$2e
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage4:
; P4      Instrument editor (cont)›
        .byte $d0,$34,$20,$20,$20,$20,$20,$20,$05,$c9,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$20,$45,$44,$49,$54,$4f,$52,$20,$28,$43,$4f,$4e,$54,$29,$9b,$0d
;
        .byte $0d
;    10. Pulse width: 8 MSBs of initial
        .byte $20,$20,$20,$31,$30,$2e,$20,$d0,$55,$4c,$53,$45,$20,$57,$49,$44,$54,$48,$3a,$20,$38,$20,$cd,$d3,$c2,$53,$20,$4f,$46,$20,$49,$4e,$49,$54,$49,$41,$4c,$0d
;        pulse width.
        .byte $20,$20,$20,$20,$20,$20,$20,$50,$55,$4c,$53,$45,$20,$57,$49,$44,$54,$48,$2e,$0d
;    11. Pulse speed: value added to the
        .byte $20,$20,$20,$31,$31,$2e,$20,$d0,$55,$4c,$53,$45,$20,$53,$50,$45,$45,$44,$3a,$20,$56,$41,$4c,$55,$45,$20,$41,$44,$44,$45,$44,$20,$54,$4f,$20,$54,$48,$45,$0d
;        8 LSBs of pulse width every
        .byte $20,$20,$20,$20,$20,$20,$20,$38,$20,$cc,$d3,$c2,$53,$20,$4f,$46,$20,$50,$55,$4c,$53,$45,$20,$57,$49,$44,$54,$48,$20,$45,$56,$45,$52,$59,$0d
;        tick.
        .byte $20,$20,$20,$20,$20,$20,$20,$54,$49,$43,$4b,$2e,$0d
;    12. Pulse limits: high nybble is 4
        .byte $20,$20,$20,$31,$32,$2e,$20,$d0,$55,$4c,$53,$45,$20,$4c,$49,$4d,$49,$54,$53,$3a,$20,$48,$49,$47,$48,$20,$4e,$59,$42,$42,$4c,$45,$20,$49,$53,$20,$34,$0d
;        MSBs of lower limit, low nybble
        .byte $20,$20,$20,$20,$20,$20,$20,$cd,$d3,$c2,$53,$20,$4f,$46,$20,$4c,$4f,$57,$45,$52,$20,$4c,$49,$4d,$49,$54,$2c,$20,$4c,$4f,$57,$20,$4e,$59,$42,$42,$4c,$45,$0d
;        is 4 MSBs of upper limit.
        .byte $20,$20,$20,$20,$20,$20,$20,$49,$53,$20,$34,$20,$cd,$d3,$c2,$53,$20,$4f,$46,$20,$55,$50,$50,$45,$52,$20,$4c,$49,$4d,$49,$54,$2e,$0d
; 13-15. Initial filter cutoff frequency,
        .byte $31,$33,$2d,$31,$35,$2e,$20,$c9,$4e,$49,$54,$49,$41,$4c,$20,$46,$49,$4c,$54,$45,$52,$20,$43,$55,$54,$4f,$46,$46,$20,$46,$52,$45,$51,$55,$45,$4e,$43,$59,$2c,$0d
;        filter cutoff speed and limits.
        .byte $20,$20,$20,$20,$20,$20,$20,$46,$49,$4c,$54,$45,$52,$20,$43,$55,$54,$4f,$46,$46,$20,$53,$50,$45,$45,$44,$20,$41,$4e,$44,$20,$4c,$49,$4d,$49,$54,$53,$2e,$0d
;        All similar to pulse paramters.
        .byte $20,$20,$20,$20,$20,$20,$20,$c1,$4c,$4c,$20,$53,$49,$4d,$49,$4c,$41,$52,$20,$54,$4f,$20,$50,$55,$4c,$53,$45,$20,$50,$41,$52,$41,$4d,$54,$45,$52,$53,$2e,$0d
;        Enter zeros to leave filter
        .byte $20,$20,$20,$20,$20,$20,$20,$c5,$4e,$54,$45,$52,$20,$5a,$45,$52,$4f,$53,$20,$54,$4f,$20,$4c,$45,$41,$56,$45,$20,$46,$49,$4c,$54,$45,$52,$0d
;        unchanged.
        .byte $20,$20,$20,$20,$20,$20,$20,$55,$4e,$43,$48,$41,$4e,$47,$45,$44,$2e,$0d
;
        .byte $0d
; Remember that:
        .byte $d2,$45,$4d,$45,$4d,$42,$45,$52,$20,$54,$48,$41,$54,$3a,$0d
;  - instrument 0 cannot be defined.
        .byte $20,$2d,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$20,$30,$20,$43,$41,$4e,$4e,$4f,$54,$20,$42,$45,$20,$44,$45,$46,$49,$4e,$45,$44,$2e,$0d
;  - you enable the filter with track
        .byte $20,$2d,$20,$59,$4f,$55,$20,$45,$4e,$41,$42,$4c,$45,$20,$54,$48,$45,$20,$46,$49,$4c,$54,$45,$52,$20,$57,$49,$54,$48,$20,$54,$52,$41,$43,$4b,$0d
;    effects. See effects F9x, Exx.
        .byte $20,$20,$20,$45,$46,$46,$45,$43,$54,$53,$2e,$20,$d3,$45,$45,$20,$45,$46,$46,$45,$43,$54,$53,$20,$c6,$39,$58,$2c,$20,$c5,$58,$58,$2e,$0d
;  - there is only 1 filter in SID, so
        .byte $20,$2d,$20,$54,$48,$45,$52,$45,$20,$49,$53,$20,$4f,$4e,$4c,$59,$20,$31,$20,$46,$49,$4c,$54,$45,$52,$20,$49,$4e,$20,$d3,$c9,$c4,$2c,$20,$53,$4f,$0d
;    do not play more than 1 sound with
        .byte $20,$20,$20,$44,$4f,$20,$4e,$4f,$54,$20,$50,$4c,$41,$59,$20,$4d,$4f,$52,$45,$20,$54,$48,$41,$4e,$20,$31,$20,$53,$4f,$55,$4e,$44,$20,$57,$49,$54,$48,$0d
;    non-zero filter parameters.
        .byte $20,$20,$20,$4e,$4f,$4e,$2d,$5a,$45,$52,$4f,$20,$46,$49,$4c,$54,$45,$52,$20,$50,$41,$52,$41,$4d,$45,$54,$45,$52,$53,$2e
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage5:
; P5      Instrument editor, keyboard›
        .byte $d0,$35,$20,$20,$20,$20,$20,$20,$05,$c9,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$20,$45,$44,$49,$54,$4f,$52,$2c,$20,$4b,$45,$59,$42,$4f,$41,$52,$44,$9b,$0d
;
        .byte $0d
; Moving around
        .byte $cd,$4f,$56,$49,$4e,$47,$20,$41,$52,$4f,$55,$4e,$44,$0d
;    RUNSTOP   next field
        .byte $20,$20,$20,$d2,$d5,$ce,$d3,$d4,$cf,$d0,$20,$20,$20,$4e,$45,$58,$54,$20,$46,$49,$45,$4c,$44,$0d
;   SHIFT+RS   previous field
        .byte $20,$20,$d3,$c8,$c9,$c6,$d4,$2b,$d2,$d3,$20,$20,$20,$50,$52,$45,$56,$49,$4f,$55,$53,$20,$46,$49,$45,$4c,$44,$0d
;     RETURN   go to wave/arp table
        .byte $20,$20,$20,$20,$d2,$c5,$d4,$d5,$d2,$ce,$20,$20,$20,$47,$4f,$20,$54,$4f,$20,$57,$41,$56,$45,$2f,$41,$52,$50,$20,$54,$41,$42,$4c,$45,$0d
;     COMM+N   edit instrument name
        .byte $20,$20,$20,$20,$c3,$cf,$cd,$cd,$2b,$ce,$20,$20,$20,$45,$44,$49,$54,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$20,$4e,$41,$4d,$45,$0d
;        ./,   select instrument
        .byte $20,$20,$20,$20,$20,$20,$20,$2e,$2f,$2c,$20,$20,$20,$53,$45,$4c,$45,$43,$54,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$0d
;
        .byte $0d
; Navigating in tables
        .byte $ce,$41,$56,$49,$47,$41,$54,$49,$4e,$47,$20,$49,$4e,$20,$54,$41,$42,$4c,$45,$53,$0d
;        INST/DEL   as expected
        .byte $20,$20,$20,$20,$20,$20,$20,$c9,$ce,$d3,$d4,$2f,$c4,$c5,$cc,$20,$20,$20,$41,$53,$20,$45,$58,$50,$45,$43,$54,$45,$44,$0d
;       SHIFT+A,Z   up/down 16 lines
        .byte $20,$20,$20,$20,$20,$20,$d3,$c8,$c9,$c6,$d4,$2b,$c1,$2c,$da,$20,$20,$20,$55,$50,$2f,$44,$4f,$57,$4e,$20,$31,$36,$20,$4c,$49,$4e,$45,$53,$0d
;   SHIFT+Q,W,E,R   go to position $00,
        .byte $20,$20,$d3,$c8,$c9,$c6,$d4,$2b,$d1,$2c,$d7,$2c,$c5,$2c,$d2,$20,$20,$20,$47,$4f,$20,$54,$4f,$20,$50,$4f,$53,$49,$54,$49,$4f,$4e,$20,$24,$30,$30,$2c,$0d
;                   $40,$80,$C0 in table
        .byte $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$24,$34,$30,$2c,$24,$38,$30,$2c,$24,$c3,$30,$20,$49,$4e,$20,$54,$41,$42,$4c,$45,$0d
;
        .byte $0d
; Copying instruments
        .byte $c3,$4f,$50,$59,$49,$4e,$47,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$53,$0d
;   C=+C   copy instrument
        .byte $20,$20,$c3,$3d,$2b,$c3,$20,$20,$20,$43,$4f,$50,$59,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$0d
;   C=+V   paste instrument
        .byte $20,$20,$c3,$3d,$2b,$d6,$20,$20,$20,$50,$41,$53,$54,$45,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage6:
; P6      Pattern editor (press F5)›
        .byte $d0,$36,$20,$20,$20,$20,$20,$20,$05,$d0,$41,$54,$54,$45,$52,$4e,$20,$45,$44,$49,$54,$4f,$52,$20,$28,$50,$52,$45,$53,$53,$20,$c6,$35,$29,$9b,$0d
;
        .byte $0d
; Looks like in any other tracker:
        .byte $cc,$4f,$4f,$4b,$53,$20,$4c,$49,$4b,$45,$20,$49,$4e,$20,$41,$4e,$59,$20,$4f,$54,$48,$45,$52,$20,$54,$52,$41,$43,$4b,$45,$52,$3a,$0d
; note, instrument, effect and effect
        .byte $4e,$4f,$54,$45,$2c,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$2c,$20,$45,$46,$46,$45,$43,$54,$20,$41,$4e,$44,$20,$45,$46,$46,$45,$43,$54,$0d
; parameter for all 3 channels.
        .byte $50,$41,$52,$41,$4d,$45,$54,$45,$52,$20,$46,$4f,$52,$20,$41,$4c,$4c,$20,$33,$20,$43,$48,$41,$4e,$4e,$45,$4c,$53,$2e,$0d
; Don't forget that you edit separate
        .byte $c4,$4f,$4e,$27,$54,$20,$46,$4f,$52,$47,$45,$54,$20,$54,$48,$41,$54,$20,$59,$4f,$55,$20,$45,$44,$49,$54,$20,$53,$45,$50,$41,$52,$41,$54,$45,$0d
; tracks, unlike in MOD. You can see the
        .byte $54,$52,$41,$43,$4b,$53,$2c,$20,$55,$4e,$4c,$49,$4b,$45,$20,$49,$4e,$20,$cd,$cf,$c4,$2e,$20,$d9,$4f,$55,$20,$43,$41,$4e,$20,$53,$45,$45,$20,$54,$48,$45,$0d
; track number and transpose value above
        .byte $54,$52,$41,$43,$4b,$20,$4e,$55,$4d,$42,$45,$52,$20,$41,$4e,$44,$20,$54,$52,$41,$4e,$53,$50,$4f,$53,$45,$20,$56,$41,$4c,$55,$45,$20,$41,$42,$4f,$56,$45,$0d
; the track's data. Transpose $0C is an
        .byte $54,$48,$45,$20,$54,$52,$41,$43,$4b,$27,$53,$20,$44,$41,$54,$41,$2e,$20,$d4,$52,$41,$4e,$53,$50,$4f,$53,$45,$20,$24,$30,$c3,$20,$49,$53,$20,$41,$4e,$0d
; octave up, $8C is an octave down.
        .byte $4f,$43,$54,$41,$56,$45,$20,$55,$50,$2c,$20,$24,$38,$c3,$20,$49,$53,$20,$41,$4e,$20,$4f,$43,$54,$41,$56,$45,$20,$44,$4f,$57,$4e,$2e,$0d
;
        .byte $0d
; I made the pattern editor as feature-
        .byte $c9,$20,$4d,$41,$44,$45,$20,$54,$48,$45,$20,$50,$41,$54,$54,$45,$52,$4e,$20,$45,$44,$49,$54,$4f,$52,$20,$41,$53,$20,$46,$45,$41,$54,$55,$52,$45,$2d,$0d
; packed as I could to help you.
        .byte $50,$41,$43,$4b,$45,$44,$20,$41,$53,$20,$c9,$20,$43,$4f,$55,$4c,$44,$20,$54,$4f,$20,$48,$45,$4c,$50,$20,$59,$4f,$55,$2e,$0d
; It means that there are many key
        .byte $c9,$54,$20,$4d,$45,$41,$4e,$53,$20,$54,$48,$41,$54,$20,$54,$48,$45,$52,$45,$20,$41,$52,$45,$20,$4d,$41,$4e,$59,$20,$4b,$45,$59,$0d
; combinations to remember, but some of
        .byte $43,$4f,$4d,$42,$49,$4e,$41,$54,$49,$4f,$4e,$53,$20,$54,$4f,$20,$52,$45,$4d,$45,$4d,$42,$45,$52,$2c,$20,$42,$55,$54,$20,$53,$4f,$4d,$45,$20,$4f,$46,$0d
; them should be familiar from other
        .byte $54,$48,$45,$4d,$20,$53,$48,$4f,$55,$4c,$44,$20,$42,$45,$20,$46,$41,$4d,$49,$4c,$49,$41,$52,$20,$46,$52,$4f,$4d,$20,$4f,$54,$48,$45,$52,$0d
; programs. For example, _ and SHIFT+_
        .byte $50,$52,$4f,$47,$52,$41,$4d,$53,$2e,$20,$c6,$4f,$52,$20,$45,$58,$41,$4d,$50,$4c,$45,$2c,$20,$5f,$20,$41,$4e,$44,$20,$d3,$c8,$c9,$c6,$d4,$2b,$5f,$0d
; adjust cursor step like in Fast
        .byte $41,$44,$4a,$55,$53,$54,$20,$43,$55,$52,$53,$4f,$52,$20,$53,$54,$45,$50,$20,$4c,$49,$4b,$45,$20,$49,$4e,$20,$c6,$41,$53,$54,$0d
; Tracker and block cut/copy/paste keys
        .byte $d4,$52,$41,$43,$4b,$45,$52,$20,$41,$4e,$44,$20,$42,$4c,$4f,$43,$4b,$20,$43,$55,$54,$2f,$43,$4f,$50,$59,$2f,$50,$41,$53,$54,$45,$20,$4b,$45,$59,$53,$0d
; are familiar from many PC programs.
        .byte $41,$52,$45,$20,$46,$41,$4d,$49,$4c,$49,$41,$52,$20,$46,$52,$4f,$4d,$20,$4d,$41,$4e,$59,$20,$d0,$c3,$20,$50,$52,$4f,$47,$52,$41,$4d,$53,$2e
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage7:
; P7      Pattern editor, effects›
        .byte $d0,$37,$20,$20,$20,$20,$20,$20,$05,$d0,$41,$54,$54,$45,$52,$4e,$20,$45,$44,$49,$54,$4f,$52,$2c,$20,$45,$46,$46,$45,$43,$54,$53,$9b,$0d
;
        .byte $0d
; You can enter these effects in the
        .byte $d9,$4f,$55,$20,$43,$41,$4e,$20,$45,$4e,$54,$45,$52,$20,$54,$48,$45,$53,$45,$20,$45,$46,$46,$45,$43,$54,$53,$20,$49,$4e,$20,$54,$48,$45,$0d
; effect column:
        .byte $45,$46,$46,$45,$43,$54,$20,$43,$4f,$4c,$55,$4d,$4e,$3a,$0d
;
        .byte $0d
; 0  Do nothing.
        .byte $30,$20,$20,$c4,$4f,$20,$4e,$4f,$54,$48,$49,$4e,$47,$2e,$0d
; 1  If parameter<$80 slide down, else
        .byte $31,$20,$20,$c9,$46,$20,$50,$41,$52,$41,$4d,$45,$54,$45,$52,$3c,$24,$38,$30,$20,$53,$4c,$49,$44,$45,$20,$44,$4f,$57,$4e,$2c,$20,$45,$4c,$53,$45,$0d
;    slide up by parameter-$80.
        .byte $20,$20,$20,$53,$4c,$49,$44,$45,$20,$55,$50,$20,$42,$59,$20,$50,$41,$52,$41,$4d,$45,$54,$45,$52,$2d,$24,$38,$30,$2e,$0d
; 2  Set pulse width. Parameter is 8 MSBs
        .byte $32,$20,$20,$d3,$45,$54,$20,$50,$55,$4c,$53,$45,$20,$57,$49,$44,$54,$48,$2e,$20,$d0,$41,$52,$41,$4d,$45,$54,$45,$52,$20,$49,$53,$20,$38,$20,$cd,$d3,$c2,$53,$0d
;    if pulse width.
        .byte $20,$20,$20,$49,$46,$20,$50,$55,$4c,$53,$45,$20,$57,$49,$44,$54,$48,$2e,$0d
; 3  Slide to note.
        .byte $33,$20,$20,$d3,$4c,$49,$44,$45,$20,$54,$4f,$20,$4e,$4f,$54,$45,$2e,$0d
; 4  Vibrato. High nybble is depth, low
        .byte $34,$20,$20,$d6,$49,$42,$52,$41,$54,$4f,$2e,$20,$c8,$49,$47,$48,$20,$4e,$59,$42,$42,$4c,$45,$20,$49,$53,$20,$44,$45,$50,$54,$48,$2c,$20,$4c,$4f,$57,$0d
;    is speed. Temporarily overrides
        .byte $20,$20,$20,$49,$53,$20,$53,$50,$45,$45,$44,$2e,$20,$d4,$45,$4d,$50,$4f,$52,$41,$52,$49,$4c,$59,$20,$4f,$56,$45,$52,$52,$49,$44,$45,$53,$0d
;    instrument vibrato.
        .byte $20,$20,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$20,$56,$49,$42,$52,$41,$54,$4f,$2e,$0d
; 5  Set pulse speed. See instrument
        .byte $35,$20,$20,$d3,$45,$54,$20,$50,$55,$4c,$53,$45,$20,$53,$50,$45,$45,$44,$2e,$20,$d3,$45,$45,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$0d
;    editor.
        .byte $20,$20,$20,$45,$44,$49,$54,$4f,$52,$2e,$0d
; 6  Set pulse limits. See instrument
        .byte $36,$20,$20,$d3,$45,$54,$20,$50,$55,$4c,$53,$45,$20,$4c,$49,$4d,$49,$54,$53,$2e,$20,$d3,$45,$45,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$0d
;    editor.
        .byte $20,$20,$20,$45,$44,$49,$54,$4f,$52,$2e,$0d
; 7  Set Attack/Decay.
        .byte $37,$20,$20,$d3,$45,$54,$20,$c1,$54,$54,$41,$43,$4b,$2f,$c4,$45,$43,$41,$59,$2e,$0d
; 8  Set Sustain/Release.
        .byte $38,$20,$20,$d3,$45,$54,$20,$d3,$55,$53,$54,$41,$49,$4e,$2f,$d2,$45,$4c,$45,$41,$53,$45,$2e
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage8:
; P8      Pattern editor, effects (cont)›
        .byte $d0,$38,$20,$20,$20,$20,$20,$20,$05,$d0,$41,$54,$54,$45,$52,$4e,$20,$45,$44,$49,$54,$4f,$52,$2c,$20,$45,$46,$46,$45,$43,$54,$53,$20,$28,$43,$4f,$4e,$54,$29,$9b,$0d
;
        .byte $0d
; 9  Set waveform. Overrides wave table.
        .byte $39,$20,$20,$d3,$45,$54,$20,$57,$41,$56,$45,$46,$4f,$52,$4d,$2e,$20,$cf,$56,$45,$52,$52,$49,$44,$45,$53,$20,$57,$41,$56,$45,$20,$54,$41,$42,$4c,$45,$2e,$0d
; A  Arpeggio. Parameters like in MOD,
        .byte $c1,$20,$20,$c1,$52,$50,$45,$47,$47,$49,$4f,$2e,$20,$d0,$41,$52,$41,$4d,$45,$54,$45,$52,$53,$20,$4c,$49,$4b,$45,$20,$49,$4e,$20,$cd,$cf,$c4,$2c,$0d
;    Overrides arpeggio table.
        .byte $20,$20,$20,$cf,$56,$45,$52,$52,$49,$44,$45,$53,$20,$41,$52,$50,$45,$47,$47,$49,$4f,$20,$54,$41,$42,$4c,$45,$2e,$0d
; B  Order jump. Just like in MOD.
        .byte $c2,$20,$20,$cf,$52,$44,$45,$52,$20,$4a,$55,$4d,$50,$2e,$20,$ca,$55,$53,$54,$20,$4c,$49,$4b,$45,$20,$49,$4e,$20,$cd,$cf,$c4,$2e,$0d
; C  Set filter cutoff frequency.
        .byte $c3,$20,$20,$d3,$45,$54,$20,$46,$49,$4c,$54,$45,$52,$20,$43,$55,$54,$4f,$46,$46,$20,$46,$52,$45,$51,$55,$45,$4e,$43,$59,$2e,$0d
;    Parameter is 8 MSBs of frequency.
        .byte $20,$20,$20,$d0,$41,$52,$41,$4d,$45,$54,$45,$52,$20,$49,$53,$20,$38,$20,$cd,$d3,$c2,$53,$20,$4f,$46,$20,$46,$52,$45,$51,$55,$45,$4e,$43,$59,$2e,$0d
;    Overrides instrument filter params.
        .byte $20,$20,$20,$cf,$56,$45,$52,$52,$49,$44,$45,$53,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$20,$46,$49,$4c,$54,$45,$52,$20,$50,$41,$52,$41,$4d,$53,$2e,$0d
; D  Pattern break. Parameter is hex,
        .byte $c4,$20,$20,$d0,$41,$54,$54,$45,$52,$4e,$20,$42,$52,$45,$41,$4b,$2e,$20,$d0,$41,$52,$41,$4d,$45,$54,$45,$52,$20,$49,$53,$20,$48,$45,$58,$2c,$0d
;    unlike in MOD.
        .byte $20,$20,$20,$55,$4e,$4c,$49,$4b,$45,$20,$49,$4e,$20,$cd,$cf,$c4,$2e,$0d
; E  Filter resonance/input control.
        .byte $c5,$20,$20,$c6,$49,$4c,$54,$45,$52,$20,$52,$45,$53,$4f,$4e,$41,$4e,$43,$45,$2f,$49,$4e,$50,$55,$54,$20,$43,$4f,$4e,$54,$52,$4f,$4c,$2e,$0d
;    High nybble is resonance, bit 0,1,2
        .byte $20,$20,$20,$c8,$49,$47,$48,$20,$4e,$59,$42,$42,$4c,$45,$20,$49,$53,$20,$52,$45,$53,$4f,$4e,$41,$4e,$43,$45,$2c,$20,$42,$49,$54,$20,$30,$2c,$31,$2c,$32,$0d
;    enable filter for voice 1,2,3.
        .byte $20,$20,$20,$45,$4e,$41,$42,$4c,$45,$20,$46,$49,$4c,$54,$45,$52,$20,$46,$4f,$52,$20,$56,$4f,$49,$43,$45,$20,$31,$2c,$32,$2c,$33,$2e,$0d
; F  If parameter<$80, set speed.
        .byte $c6,$20,$20,$c9,$46,$20,$50,$41,$52,$41,$4d,$45,$54,$45,$52,$3c,$24,$38,$30,$2c,$20,$53,$45,$54,$20,$53,$50,$45,$45,$44,$2e,$0d
;    If parameter=$8x, set global
        .byte $20,$20,$20,$c9,$46,$20,$50,$41,$52,$41,$4d,$45,$54,$45,$52,$3d,$24,$38,$58,$2c,$20,$53,$45,$54,$20,$47,$4c,$4f,$42,$41,$4c,$0d
;    volume to x.
        .byte $20,$20,$20,$56,$4f,$4c,$55,$4d,$45,$20,$54,$4f,$20,$58,$2e,$0d
;    If parameter=$9x, set filter mode.
        .byte $20,$20,$20,$c9,$46,$20,$50,$41,$52,$41,$4d,$45,$54,$45,$52,$3d,$24,$39,$58,$2c,$20,$53,$45,$54,$20,$46,$49,$4c,$54,$45,$52,$20,$4d,$4f,$44,$45,$2e,$0d
;    bit 0: low pass, bit 1: band pass,
        .byte $20,$20,$20,$42,$49,$54,$20,$30,$3a,$20,$4c,$4f,$57,$20,$50,$41,$53,$53,$2c,$20,$42,$49,$54,$20,$31,$3a,$20,$42,$41,$4e,$44,$20,$50,$41,$53,$53,$2c,$0d
;    bit 2: high pass, bit 3: cut off
        .byte $20,$20,$20,$42,$49,$54,$20,$32,$3a,$20,$48,$49,$47,$48,$20,$50,$41,$53,$53,$2c,$20,$42,$49,$54,$20,$33,$3a,$20,$43,$55,$54,$20,$4f,$46,$46,$0d
;    voice 3's output.
        .byte $20,$20,$20,$56,$4f,$49,$43,$45,$20,$33,$27,$53,$20,$4f,$55,$54,$50,$55,$54,$2e,$0d
;    If parameter=$Ax, fine slide down.
        .byte $20,$20,$20,$c9,$46,$20,$50,$41,$52,$41,$4d,$45,$54,$45,$52,$3d,$24,$c1,$58,$2c,$20,$46,$49,$4e,$45,$20,$53,$4c,$49,$44,$45,$20,$44,$4f,$57,$4e,$2e,$0d
;    If parameter=$Bx, fine slide up.
        .byte $20,$20,$20,$c9,$46,$20,$50,$41,$52,$41,$4d,$45,$54,$45,$52,$3d,$24,$c2,$58,$2c,$20,$46,$49,$4e,$45,$20,$53,$4c,$49,$44,$45,$20,$55,$50,$2e,$0d
;    If parameter=$Cx, note cut.
        .byte $20,$20,$20,$c9,$46,$20,$50,$41,$52,$41,$4d,$45,$54,$45,$52,$3d,$24,$c3,$58,$2c,$20,$4e,$4f,$54,$45,$20,$43,$55,$54,$2e
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage9:
; P9      Pattern editor, keyboard›
        .byte $d0,$39,$20,$20,$20,$20,$20,$20,$05,$d0,$41,$54,$54,$45,$52,$4e,$20,$45,$44,$49,$54,$4f,$52,$2c,$20,$4b,$45,$59,$42,$4f,$41,$52,$44,$9b,$0d
;
        .byte $0d
; Moving around
        .byte $cd,$4f,$56,$49,$4e,$47,$20,$41,$52,$4f,$55,$4e,$44,$0d
;   CRSR,INST,DEL   as expected
        .byte $20,$20,$c3,$d2,$d3,$d2,$2c,$c9,$ce,$d3,$d4,$2c,$c4,$c5,$cc,$20,$20,$20,$41,$53,$20,$45,$58,$50,$45,$43,$54,$45,$44,$0d
;       SHIFT+A,Z   up/down 16 lines
        .byte $20,$20,$20,$20,$20,$20,$d3,$c8,$c9,$c6,$d4,$2b,$c1,$2c,$da,$20,$20,$20,$55,$50,$2f,$44,$4f,$57,$4e,$20,$31,$36,$20,$4c,$49,$4e,$45,$53,$0d
;   SHIFT+Q,W,E,R   go to line 0,16,32,48
        .byte $20,$20,$d3,$c8,$c9,$c6,$d4,$2b,$d1,$2c,$d7,$2c,$c5,$2c,$d2,$20,$20,$20,$47,$4f,$20,$54,$4f,$20,$4c,$49,$4e,$45,$20,$30,$2c,$31,$36,$2c,$33,$32,$2c,$34,$38,$0d
;         RUNSTOP   next channel
        .byte $20,$20,$20,$20,$20,$20,$20,$20,$d2,$d5,$ce,$d3,$d4,$cf,$d0,$20,$20,$20,$4e,$45,$58,$54,$20,$43,$48,$41,$4e,$4e,$45,$4c,$0d
;   SHIFT+RUNSTOP   previous channel
        .byte $20,$20,$d3,$c8,$c9,$c6,$d4,$2b,$d2,$d5,$ce,$d3,$d4,$cf,$d0,$20,$20,$20,$50,$52,$45,$56,$49,$4f,$55,$53,$20,$43,$48,$41,$4e,$4e,$45,$4c,$0d
;
        .byte $0d
; Entering notes
        .byte $c5,$4e,$54,$45,$52,$49,$4e,$47,$20,$4e,$4f,$54,$45,$53,$0d
;   CTRL+1..7   select octave
        .byte $20,$20,$c3,$d4,$d2,$cc,$2b,$31,$2e,$2e,$37,$20,$20,$20,$53,$45,$4c,$45,$43,$54,$20,$4f,$43,$54,$41,$56,$45,$0d
;   Z..M,Q..P   enter notes
        .byte $20,$20,$da,$2e,$2e,$cd,$2c,$d1,$2e,$2e,$d0,$20,$20,$20,$45,$4e,$54,$45,$52,$20,$4e,$4f,$54,$45,$53,$0d
;           /   enter note off
        .byte $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$2f,$20,$20,$20,$45,$4e,$54,$45,$52,$20,$4e,$4f,$54,$45,$20,$4f,$46,$46,$0d
;           ^   clear note under cursor
        .byte $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$5e,$20,$20,$20,$43,$4c,$45,$41,$52,$20,$4e,$4f,$54,$45,$20,$55,$4e,$44,$45,$52,$20,$43,$55,$52,$53,$4f,$52,$0d
;         ./,   select instrument
        .byte $20,$20,$20,$20,$20,$20,$20,$20,$2e,$2f,$2c,$20,$20,$20,$53,$45,$4c,$45,$43,$54,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$0d
;
        .byte $0d
; Editing song structure
        .byte $c5,$44,$49,$54,$49,$4e,$47,$20,$53,$4f,$4e,$47,$20,$53,$54,$52,$55,$43,$54,$55,$52,$45,$0d
;    */@   inc/dec pattern for curr order
        .byte $20,$20,$20,$2a,$2f,$40,$20,$20,$20,$49,$4e,$43,$2f,$44,$45,$43,$20,$50,$41,$54,$54,$45,$52,$4e,$20,$46,$4f,$52,$20,$43,$55,$52,$52,$20,$4f,$52,$44,$45,$52,$0d
;    ;/:   inc/dec track for curr channel
        .byte $20,$20,$20,$3b,$2f,$3a,$20,$20,$20,$49,$4e,$43,$2f,$44,$45,$43,$20,$54,$52,$41,$43,$4b,$20,$46,$4f,$52,$20,$43,$55,$52,$52,$20,$43,$48,$41,$4e,$4e,$45,$4c,$0d
;   C=+Y   enter track number for channel
        .byte $20,$20,$c3,$3d,$2b,$d9,$20,$20,$20,$45,$4e,$54,$45,$52,$20,$54,$52,$41,$43,$4b,$20,$4e,$55,$4d,$42,$45,$52,$20,$46,$4f,$52,$20,$43,$48,$41,$4e,$4e,$45,$4c,$0d
;
        .byte $0d
; Editing track transpose
        .byte $c5,$44,$49,$54,$49,$4e,$47,$20,$54,$52,$41,$43,$4b,$20,$54,$52,$41,$4e,$53,$50,$4f,$53,$45,$0d
;   C=+W/C=+S   inc/dec track transpose
        .byte $20,$20,$c3,$3d,$2b,$d7,$2f,$c3,$3d,$2b,$d3,$20,$20,$20,$49,$4e,$43,$2f,$44,$45,$43,$20,$54,$52,$41,$43,$4b,$20,$54,$52,$41,$4e,$53,$50,$4f,$53,$45,$0d
;        C=+D   enter track transpose
        .byte $20,$20,$20,$20,$20,$20,$20,$c3,$3d,$2b,$c4,$20,$20,$20,$45,$4e,$54,$45,$52,$20,$54,$52,$41,$43,$4b,$20,$54,$52,$41,$4e,$53,$50,$4f,$53,$45
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage10:
; P10     Pattern editor, keyboard (cont)›
        .byte $d0,$31,$30,$20,$20,$20,$20,$20,$05,$d0,$41,$54,$54,$45,$52,$4e,$20,$45,$44,$49,$54,$4f,$52,$2c,$20,$4b,$45,$59,$42,$4f,$41,$52,$44,$20,$28,$43,$4f,$4e,$54,$29,$9b,$0d
;
        .byte $0d
; Copying effects
        .byte $c3,$4f,$50,$59,$49,$4e,$47,$20,$45,$46,$46,$45,$43,$54,$53,$0d
;   C=+G   grab effect under cursor
        .byte $20,$20,$c3,$3d,$2b,$c7,$20,$20,$20,$47,$52,$41,$42,$20,$45,$46,$46,$45,$43,$54,$20,$55,$4e,$44,$45,$52,$20,$43,$55,$52,$53,$4f,$52,$0d
;   C=+F   drop the grabbed effect
        .byte $20,$20,$c3,$3d,$2b,$c6,$20,$20,$20,$44,$52,$4f,$50,$20,$54,$48,$45,$20,$47,$52,$41,$42,$42,$45,$44,$20,$45,$46,$46,$45,$43,$54,$0d
;
        .byte $0d
; Selection keys
        .byte $d3,$45,$4c,$45,$43,$54,$49,$4f,$4e,$20,$4b,$45,$59,$53,$0d
;   C=+B   mark block begin
        .byte $20,$20,$c3,$3d,$2b,$c2,$20,$20,$20,$4d,$41,$52,$4b,$20,$42,$4c,$4f,$43,$4b,$20,$42,$45,$47,$49,$4e,$0d
;   C=+E   mark block end
        .byte $20,$20,$c3,$3d,$2b,$c5,$20,$20,$20,$4d,$41,$52,$4b,$20,$42,$4c,$4f,$43,$4b,$20,$45,$4e,$44,$0d
;   C=+T   select whole track
        .byte $20,$20,$c3,$3d,$2b,$d4,$20,$20,$20,$53,$45,$4c,$45,$43,$54,$20,$57,$48,$4f,$4c,$45,$20,$54,$52,$41,$43,$4b,$0d
;   C=+R   remove selection
        .byte $20,$20,$c3,$3d,$2b,$d2,$20,$20,$20,$52,$45,$4d,$4f,$56,$45,$20,$53,$45,$4c,$45,$43,$54,$49,$4f,$4e,$0d
;
        .byte $0d
; Block operations
        .byte $c2,$4c,$4f,$43,$4b,$20,$4f,$50,$45,$52,$41,$54,$49,$4f,$4e,$53,$0d
;   C=+X   cut block
        .byte $20,$20,$c3,$3d,$2b,$d8,$20,$20,$20,$43,$55,$54,$20,$42,$4c,$4f,$43,$4b,$0d
;   C=+C   copy block
        .byte $20,$20,$c3,$3d,$2b,$c3,$20,$20,$20,$43,$4f,$50,$59,$20,$42,$4c,$4f,$43,$4b,$0d
;   C=+V   paste block (overwrite)
        .byte $20,$20,$c3,$3d,$2b,$d6,$20,$20,$20,$50,$41,$53,$54,$45,$20,$42,$4c,$4f,$43,$4b,$20,$28,$4f,$56,$45,$52,$57,$52,$49,$54,$45,$29,$0d
;   C=+M   paste block (mix)
        .byte $20,$20,$c3,$3d,$2b,$cd,$20,$20,$20,$50,$41,$53,$54,$45,$20,$42,$4c,$4f,$43,$4b,$20,$28,$4d,$49,$58,$29,$0d
;   C=+Q   transpose block up
        .byte $20,$20,$c3,$3d,$2b,$d1,$20,$20,$20,$54,$52,$41,$4e,$53,$50,$4f,$53,$45,$20,$42,$4c,$4f,$43,$4b,$20,$55,$50,$0d
;   C=+A   transpose block down
        .byte $20,$20,$c3,$3d,$2b,$c1,$20,$20,$20,$54,$52,$41,$4e,$53,$50,$4f,$53,$45,$20,$42,$4c,$4f,$43,$4b,$20,$44,$4f,$57,$4e,$0d
;
        .byte $0d
; Misc keys
        .byte $cd,$49,$53,$43,$20,$4b,$45,$59,$53,$0d
;   _/SHIFT+_   inc/dec cursor step
        .byte $20,$20,$5f,$2f,$d3,$c8,$c9,$c6,$d4,$2b,$5f,$20,$20,$20,$49,$4e,$43,$2f,$44,$45,$43,$20,$43,$55,$52,$53,$4f,$52,$20,$53,$54,$45,$50,$0d
;        C=+Z   undo last block operation
        .byte $20,$20,$20,$20,$20,$20,$20,$c3,$3d,$2b,$da,$20,$20,$20,$55,$4e,$44,$4f,$20,$4c,$41,$53,$54,$20,$42,$4c,$4f,$43,$4b,$20,$4f,$50,$45,$52,$41,$54,$49,$4f,$4e
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage11:
; P11     Pattern editor, tips›
        .byte $d0,$31,$31,$20,$20,$20,$20,$20,$05,$d0,$41,$54,$54,$45,$52,$4e,$20,$45,$44,$49,$54,$4f,$52,$2c,$20,$54,$49,$50,$53,$9b,$0d
;
        .byte $0d
; If there is no instrument in the track,
        .byte $c9,$46,$20,$54,$48,$45,$52,$45,$20,$49,$53,$20,$4e,$4f,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$20,$49,$4e,$20,$54,$48,$45,$20,$54,$52,$41,$43,$4b,$2c,$0d
; the parameters you set previously with
        .byte $54,$48,$45,$20,$50,$41,$52,$41,$4d,$45,$54,$45,$52,$53,$20,$59,$4f,$55,$20,$53,$45,$54,$20,$50,$52,$45,$56,$49,$4f,$55,$53,$4c,$59,$20,$57,$49,$54,$48,$0d
; track effects will not be re-read from
        .byte $54,$52,$41,$43,$4b,$20,$45,$46,$46,$45,$43,$54,$53,$20,$57,$49,$4c,$4c,$20,$4e,$4f,$54,$20,$42,$45,$20,$52,$45,$2d,$52,$45,$41,$44,$20,$46,$52,$4f,$4d,$0d
; the instrument.
        .byte $54,$48,$45,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$2e,$0d
; When entering notes, select instrument
        .byte $d7,$48,$45,$4e,$20,$45,$4e,$54,$45,$52,$49,$4e,$47,$20,$4e,$4f,$54,$45,$53,$2c,$20,$53,$45,$4c,$45,$43,$54,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$0d
; 0 to avoid entering an instrument
        .byte $30,$20,$54,$4f,$20,$41,$56,$4f,$49,$44,$20,$45,$4e,$54,$45,$52,$49,$4e,$47,$20,$41,$4e,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$0d
; number at the same time.
        .byte $4e,$55,$4d,$42,$45,$52,$20,$41,$54,$20,$54,$48,$45,$20,$53,$41,$4d,$45,$20,$54,$49,$4d,$45,$2e,$0d
;
        .byte $0d
; The Gate bit in SID is released when
        .byte $d4,$48,$45,$20,$c7,$41,$54,$45,$20,$42,$49,$54,$20,$49,$4e,$20,$d3,$c9,$c4,$20,$49,$53,$20,$52,$45,$4c,$45,$41,$53,$45,$44,$20,$57,$48,$45,$4e,$0d
; the wave table element has bit 0 clear,
        .byte $54,$48,$45,$20,$57,$41,$56,$45,$20,$54,$41,$42,$4c,$45,$20,$45,$4c,$45,$4d,$45,$4e,$54,$20,$48,$41,$53,$20,$42,$49,$54,$20,$30,$20,$43,$4c,$45,$41,$52,$2c,$0d
; and also when a note off is found in
        .byte $41,$4e,$44,$20,$41,$4c,$53,$4f,$20,$57,$48,$45,$4e,$20,$41,$20,$4e,$4f,$54,$45,$20,$4f,$46,$46,$20,$49,$53,$20,$46,$4f,$55,$4e,$44,$20,$49,$4e,$0d
; the track.
        .byte $54,$48,$45,$20,$54,$52,$41,$43,$4b,$2e,$0d
; To create long sounds, set bit 0 in
        .byte $d4,$4f,$20,$43,$52,$45,$41,$54,$45,$20,$4c,$4f,$4e,$47,$20,$53,$4f,$55,$4e,$44,$53,$2c,$20,$53,$45,$54,$20,$42,$49,$54,$20,$30,$20,$49,$4e,$0d
; each wave table element for the
        .byte $45,$41,$43,$48,$20,$57,$41,$56,$45,$20,$54,$41,$42,$4c,$45,$20,$45,$4c,$45,$4d,$45,$4e,$54,$20,$46,$4f,$52,$20,$54,$48,$45,$0d
; instrument, and enter a note off into
        .byte $49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$2c,$20,$41,$4e,$44,$20,$45,$4e,$54,$45,$52,$20,$41,$20,$4e,$4f,$54,$45,$20,$4f,$46,$46,$20,$49,$4e,$54,$4f,$0d
; the track when you want to start
        .byte $54,$48,$45,$20,$54,$52,$41,$43,$4b,$20,$57,$48,$45,$4e,$20,$59,$4f,$55,$20,$57,$41,$4e,$54,$20,$54,$4f,$20,$53,$54,$41,$52,$54,$0d
; decaying the note.
        .byte $44,$45,$43,$41,$59,$49,$4e,$47,$20,$54,$48,$45,$20,$4e,$4f,$54,$45,$2e,$0d
;
        .byte $0d
; Vibrato depth 8 always gives an
        .byte $d6,$49,$42,$52,$41,$54,$4f,$20,$44,$45,$50,$54,$48,$20,$38,$20,$41,$4c,$57,$41,$59,$53,$20,$47,$49,$56,$45,$53,$20,$41,$4e,$0d
; amplitude of 1 semitones.
        .byte $41,$4d,$50,$4c,$49,$54,$55,$44,$45,$20,$4f,$46,$20,$31,$20,$53,$45,$4d,$49,$54,$4f,$4e,$45,$53,$2e
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage12:
; P12     Orderlist editor (press F3)›
        .byte $d0,$31,$32,$20,$20,$20,$20,$20,$05,$cf,$52,$44,$45,$52,$4c,$49,$53,$54,$20,$45,$44,$49,$54,$4f,$52,$20,$28,$50,$52,$45,$53,$53,$20,$c6,$33,$29,$9b,$0d
;
        .byte $0d
; You will not use this very often, most
        .byte $d9,$4f,$55,$20,$57,$49,$4c,$4c,$20,$4e,$4f,$54,$20,$55,$53,$45,$20,$54,$48,$49,$53,$20,$56,$45,$52,$59,$20,$4f,$46,$54,$45,$4e,$2c,$20,$4d,$4f,$53,$54,$0d
; editing can be done from the pattern
        .byte $45,$44,$49,$54,$49,$4e,$47,$20,$43,$41,$4e,$20,$42,$45,$20,$44,$4f,$4e,$45,$20,$46,$52,$4f,$4d,$20,$54,$48,$45,$20,$50,$41,$54,$54,$45,$52,$4e,$0d
; editor using =/- for navigating in
        .byte $45,$44,$49,$54,$4f,$52,$20,$55,$53,$49,$4e,$47,$20,$3d,$2f,$2d,$20,$46,$4f,$52,$20,$4e,$41,$56,$49,$47,$41,$54,$49,$4e,$47,$20,$49,$4e,$0d
; orderlist and */@ to change pattern
        .byte $4f,$52,$44,$45,$52,$4c,$49,$53,$54,$20,$41,$4e,$44,$20,$2a,$2f,$40,$20,$54,$4f,$20,$43,$48,$41,$4e,$47,$45,$20,$50,$41,$54,$54,$45,$52,$4e,$0d
; number.
        .byte $4e,$55,$4d,$42,$45,$52,$2e,$0d
;
        .byte $0d
; Moving around
        .byte $cd,$4f,$56,$49,$4e,$47,$20,$41,$52,$4f,$55,$4e,$44,$0d
;   CRSR,INST,DEL   as expected
        .byte $20,$20,$c3,$d2,$d3,$d2,$2c,$c9,$ce,$d3,$d4,$2c,$c4,$c5,$cc,$20,$20,$20,$41,$53,$20,$45,$58,$50,$45,$43,$54,$45,$44,$0d
;       SHIFT+A,Z   up/down 16 lines
        .byte $20,$20,$20,$20,$20,$20,$d3,$c8,$c9,$c6,$d4,$2b,$c1,$2c,$da,$20,$20,$20,$55,$50,$2f,$44,$4f,$57,$4e,$20,$31,$36,$20,$4c,$49,$4e,$45,$53,$0d
;   SHIFT+Q,W,E,R   go to position $00,
        .byte $20,$20,$d3,$c8,$c9,$c6,$d4,$2b,$d1,$2c,$d7,$2c,$c5,$2c,$d2,$20,$20,$20,$47,$4f,$20,$54,$4f,$20,$50,$4f,$53,$49,$54,$49,$4f,$4e,$20,$24,$30,$30,$2c,$0d
;                   $40,$80,$C0
        .byte $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$24,$34,$30,$2c,$24,$38,$30,$2c,$24,$c3,$30,$0d
;
        .byte $0d
; Enter order numbers using 0-9,A-F or
        .byte $c5,$4e,$54,$45,$52,$20,$4f,$52,$44,$45,$52,$20,$4e,$55,$4d,$42,$45,$52,$53,$20,$55,$53,$49,$4e,$47,$20,$30,$2d,$39,$2c,$c1,$2d,$c6,$20,$4f,$52,$0d
; use */@.
        .byte $55,$53,$45,$20,$2a,$2f,$40,$2e
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage13:
; P13     Config menu (press F4)›
        .byte $d0,$31,$33,$20,$20,$20,$20,$20,$05,$c3,$4f,$4e,$46,$49,$47,$20,$4d,$45,$4e,$55,$20,$28,$50,$52,$45,$53,$53,$20,$c6,$34,$29,$9b,$0d
;
        .byte $0d
; Set up your favourite colors.
        .byte $d3,$45,$54,$20,$55,$50,$20,$59,$4f,$55,$52,$20,$46,$41,$56,$4f,$55,$52,$49,$54,$45,$20,$43,$4f,$4c,$4f,$52,$53,$2e,$0d
;
        .byte $0d
;       RETURN  update srceen
        .byte $20,$20,$20,$20,$20,$20,$d2,$c5,$d4,$d5,$d2,$ce,$20,$20,$55,$50,$44,$41,$54,$45,$20,$53,$52,$43,$45,$45,$4e,$0d
;   SHIFT+1..4  load default color sets
        .byte $20,$20,$d3,$c8,$c9,$c6,$d4,$2b,$31,$2e,$2e,$34,$20,$20,$4c,$4f,$41,$44,$20,$44,$45,$46,$41,$55,$4c,$54,$20,$43,$4f,$4c,$4f,$52,$20,$53,$45,$54,$53,$0d
;
        .byte $0d
; Press F4 again to restore current
        .byte $d0,$52,$45,$53,$53,$20,$c6,$34,$20,$41,$47,$41,$49,$4e,$20,$54,$4f,$20,$52,$45,$53,$54,$4f,$52,$45,$20,$43,$55,$52,$52,$45,$4e,$54,$0d
; colors.
        .byte $43,$4f,$4c,$4f,$52,$53,$2e
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage14:
; P14     Specials menu (press F6)›
        .byte $d0,$31,$34,$20,$20,$20,$20,$20,$05,$d3,$50,$45,$43,$49,$41,$4c,$53,$20,$4d,$45,$4e,$55,$20,$28,$50,$52,$45,$53,$53,$20,$c6,$36,$29,$9b,$0d
;
        .byte $0d
; Not much to say, really.
        .byte $ce,$4f,$54,$20,$4d,$55,$43,$48,$20,$54,$4f,$20,$53,$41,$59,$2c,$20,$52,$45,$41,$4c,$4c,$59,$2e,$0d
;
        .byte $0d
; Clear song clears tracks, patterns and
        .byte $c3,$4c,$45,$41,$52,$20,$53,$4f,$4e,$47,$20,$43,$4c,$45,$41,$52,$53,$20,$54,$52,$41,$43,$4b,$53,$2c,$20,$50,$41,$54,$54,$45,$52,$4e,$53,$20,$41,$4e,$44,$0d
; orderlist but keeps instruments.
        .byte $4f,$52,$44,$45,$52,$4c,$49,$53,$54,$20,$42,$55,$54,$20,$4b,$45,$45,$50,$53,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$53,$2e,$0d
;
        .byte $0d
; You can replace instruments in a set
        .byte $d9,$4f,$55,$20,$43,$41,$4e,$20,$52,$45,$50,$4c,$41,$43,$45,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$53,$20,$49,$4e,$20,$41,$20,$53,$45,$54,$0d
; of tracks or all tracks in a set of
        .byte $4f,$46,$20,$54,$52,$41,$43,$4b,$53,$20,$4f,$52,$20,$41,$4c,$4c,$20,$54,$52,$41,$43,$4b,$53,$20,$49,$4e,$20,$41,$20,$53,$45,$54,$20,$4f,$46,$0d
; patterns. Remember that instrument 0
        .byte $50,$41,$54,$54,$45,$52,$4e,$53,$2e,$20,$d2,$45,$4d,$45,$4d,$42,$45,$52,$20,$54,$48,$41,$54,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$20,$30,$0d
; means no instrument.
        .byte $4d,$45,$41,$4e,$53,$20,$4e,$4f,$20,$49,$4e,$53,$54,$52,$55,$4d,$45,$4e,$54,$2e,$0d
;
        .byte $0d
; Swapping tracks also operates in a set
        .byte $d3,$57,$41,$50,$50,$49,$4e,$47,$20,$54,$52,$41,$43,$4b,$53,$20,$41,$4c,$53,$4f,$20,$4f,$50,$45,$52,$41,$54,$45,$53,$20,$49,$4e,$20,$41,$20,$53,$45,$54,$0d
; of patterns you enter.
        .byte $4f,$46,$20,$50,$41,$54,$54,$45,$52,$4e,$53,$20,$59,$4f,$55,$20,$45,$4e,$54,$45,$52,$2e
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage15:
; P15     Disk menu (press F8)›
        .byte $d0,$31,$35,$20,$20,$20,$20,$20,$05,$c4,$49,$53,$4b,$20,$4d,$45,$4e,$55,$20,$28,$50,$52,$45,$53,$53,$20,$c6,$38,$29,$9b,$0d
;
        .byte $0d
; Quite self-explanatory. Save song
        .byte $d1,$55,$49,$54,$45,$20,$53,$45,$4c,$46,$2d,$45,$58,$50,$4c,$41,$4e,$41,$54,$4f,$52,$59,$2e,$20,$d3,$41,$56,$45,$20,$53,$4f,$4e,$47,$0d
; simply dumps memory from $4000 to the
        .byte $53,$49,$4d,$50,$4c,$59,$20,$44,$55,$4d,$50,$53,$20,$4d,$45,$4d,$4f,$52,$59,$20,$46,$52,$4f,$4d,$20,$24,$34,$30,$30,$30,$20,$54,$4f,$20,$54,$48,$45,$0d
; end of the last track used in the song
        .byte $45,$4e,$44,$20,$4f,$46,$20,$54,$48,$45,$20,$4c,$41,$53,$54,$20,$54,$52,$41,$43,$4b,$20,$55,$53,$45,$44,$20,$49,$4e,$20,$54,$48,$45,$20,$53,$4f,$4e,$47,$0d
; with no playroutine.
        .byte $57,$49,$54,$48,$20,$4e,$4f,$20,$50,$4c,$41,$59,$52,$4f,$55,$54,$49,$4e,$45,$2e,$0d
;
        .byte $0d
; Read/write tracks make it possible to
        .byte $d2,$45,$41,$44,$2f,$57,$52,$49,$54,$45,$20,$54,$52,$41,$43,$4b,$53,$20,$4d,$41,$4b,$45,$20,$49,$54,$20,$50,$4f,$53,$53,$49,$42,$4c,$45,$20,$54,$4f,$0d
; remix songs.
        .byte $52,$45,$4d,$49,$58,$20,$53,$4f,$4e,$47,$53,$2e,$0d
;
        .byte $0d
; Hold down SHIFT to pause directory
        .byte $c8,$4f,$4c,$44,$20,$44,$4f,$57,$4e,$20,$d3,$c8,$c9,$c6,$d4,$20,$54,$4f,$20,$50,$41,$55,$53,$45,$20,$44,$49,$52,$45,$43,$54,$4f,$52,$59,$0d
; listing.
        .byte $4c,$49,$53,$54,$49,$4e,$47,$2e
        .byte $00
; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
helppage16:
; P16
        .byte $d0,$31,$36,$0d
;
        .byte $0d
; Well, that's it. You figure out the
        .byte $d7,$45,$4c,$4c,$2c,$20,$54,$48,$41,$54,$27,$53,$20,$49,$54,$2e,$20,$d9,$4f,$55,$20,$46,$49,$47,$55,$52,$45,$20,$4f,$55,$54,$20,$54,$48,$45,$0d
; rest. :)
        .byte $52,$45,$53,$54,$2e,$20,$3a,$29,$0d
;
        .byte $0d
; Suggestions on improvements are
        .byte $d3,$55,$47,$47,$45,$53,$54,$49,$4f,$4e,$53,$20,$4f,$4e,$20,$49,$4d,$50,$52,$4f,$56,$45,$4d,$45,$4e,$54,$53,$20,$41,$52,$45,$0d
; welcome. Just tell me what you want.
        .byte $57,$45,$4c,$43,$4f,$4d,$45,$2e,$20,$ca,$55,$53,$54,$20,$54,$45,$4c,$4c,$20,$4d,$45,$20,$57,$48,$41,$54,$20,$59,$4f,$55,$20,$57,$41,$4e,$54,$2e,$0d
;
        .byte $0d
; If there are requests, I'll code a
        .byte $c9,$46,$20,$54,$48,$45,$52,$45,$20,$41,$52,$45,$20,$52,$45,$51,$55,$45,$53,$54,$53,$2c,$20,$c9,$27,$4c,$4c,$20,$43,$4f,$44,$45,$20,$41,$0d
; packer that will compile the songs into
        .byte $50,$41,$43,$4b,$45,$52,$20,$54,$48,$41,$54,$20,$57,$49,$4c,$4c,$20,$43,$4f,$4d,$50,$49,$4c,$45,$20,$54,$48,$45,$20,$53,$4f,$4e,$47,$53,$20,$49,$4e,$54,$4f,$0d
; a format that is faster to process and
        .byte $41,$20,$46,$4f,$52,$4d,$41,$54,$20,$54,$48,$41,$54,$20,$49,$53,$20,$46,$41,$53,$54,$45,$52,$20,$54,$4f,$20,$50,$52,$4f,$43,$45,$53,$53,$20,$41,$4e,$44,$0d
; includes a playroutine, too.
        .byte $49,$4e,$43,$4c,$55,$44,$45,$53,$20,$41,$20,$50,$4c,$41,$59,$52,$4f,$55,$54,$49,$4e,$45,$2c,$20,$54,$4f,$4f,$2e,$0d
;
        .byte $0d
; It would be nice if someone contributed
        .byte $c9,$54,$20,$57,$4f,$55,$4c,$44,$20,$42,$45,$20,$4e,$49,$43,$45,$20,$49,$46,$20,$53,$4f,$4d,$45,$4f,$4e,$45,$20,$43,$4f,$4e,$54,$52,$49,$42,$55,$54,$45,$44,$0d
; example tunes. Volunteers?
        .byte $45,$58,$41,$4d,$50,$4c,$45,$20,$54,$55,$4e,$45,$53,$2e,$20,$d6,$4f,$4c,$55,$4e,$54,$45,$45,$52,$53,$3f,$0d
;
        .byte $0d
;
        .byte $0d
; mailto:zed@kempelen.inf.bme.hu
        .byte $4d,$41,$49,$4c,$54,$4f,$3a,$5a,$45,$44,$40,$4b,$45,$4d,$50,$45,$4c,$45,$4e,$2e,$49,$4e,$46,$2e,$42,$4d,$45,$2e,$48,$55,$0d
;
        .byte $0d
;
        .byte $0d
;            Wotan mit uns.
        .byte $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$d7,$4f,$54,$41,$4e,$20,$4d,$49,$54,$20,$55,$4e,$53,$2e
        .byte $00
