%import textio
%zeropage basicsafe
%option no_sysinit

; The documentation for custom PS2 key handlers can be found here:
; https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2002%20-%20Editor.md#custom-keyboard-scancode-handler

main {

    sub start() {

        txt.print("ps2 custom key handler test - press keys! esc to quit!\n")

        sys.set_irqd()
        uword old_keyhdl = cx16.KEYHDL
        cx16.KEYHDL = &keyboard_scancode_handler
        sys.clear_irqd()

        bool escape_pressed
        while not escape_pressed {
            handle_keyboard_event()
        }
        sys.set_irqd()
        cx16.KEYHDL = old_keyhdl
        sys.clear_irqd()
    }

    ; Keyboard handler communication variables.
    ; these need to be in block scope instead of in a subroutine,
    ; so that they won't get overwritten with initialization values every time.
    ; The assembly keyboard handler will set these, prog8 will read them.
    bool @shared keyhdl_event       ; is there a keyboard event to handle?
    ubyte @shared keyhdl_prefix
    ubyte @shared keyhdl_scancode
    ubyte @shared keyhdl_updown

    sub handle_keyboard_event() {
        ; Potentially handle keyboard event.
        ; Note that we do this from the program's main loop instead of
        ; the actual keyboard handler routine itself.
        ; The reason for this is documented below in the handler assembly routine.
        if not keyhdl_event
            return
        keyhdl_event = false
        txt.print_ubhex(keyhdl_prefix, true)
        txt.chrout(':')
        txt.print_ubhex(keyhdl_scancode, true)
        txt.spc()
        if keyhdl_updown
            txt.chrout('u')
        else
            txt.chrout('d')
        txt.nl()
        if keyhdl_prefix==0 and keyhdl_scancode==119 and keyhdl_updown {
            ; escape was pressed! exit back to basic
            main.start.escape_pressed = true
        }
    }

    asmsub keyboard_scancode_handler() {

        ; NOTE that the keyboard handler is an asm subroutine.
        ; Unfortunately is it not possible to use prog8 code or calls here,
        ; because the X register gets overwritten here (to store the prefix byte)
        ; and prog8 uses the X register internally (for the evaluation stack).
        ; So it is unsafe to call prog8 code from here because the evaluation stack pointer
        ; will be invalid which produces undefined results.
        ; So, instead, we store the various keyboard event bytes and signal
        ; the main prog8 program that a keyboard event has occurred.
        ; It then processes it independently from the assembly code here.
        ;
        ; Unfortunately this also means you cannot decide from that prog8 code
        ; if the keyboard press should be consumed/ignored or put into the keyboard queue
        ; (this is controlled by returning 0 or 1 in register A here)
        ;
        ; see:
        ; https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2002%20-%20Editor.md#custom-keyboard-scancode-handler

        %asm {{
            php
            pha
            phx
            stz  keyhdl_updown
            bcc  +
            inc  keyhdl_updown
+           stx  keyhdl_prefix
            sta  keyhdl_scancode
            lda  #1
            sta  keyhdl_event
            ; we can do additional stuff here and decide if we want to
            ; consume the key event or not (A=0 or A!=0)
            plx
            pla
            lda #0     ;By setting A=0 we will remove this key event for now
            tax
            plp
            rts
        }}
    }
}
