#undef SLOWCLOCK   ; Defined if we are running slow
#undef NOPRINT     ; Defined to turn off printing

; library.asm must be first!
#include "library.asm"

#include "stdio.asm"
#include "string.asm"

PIC_COMMAND     EQU     08000H
NUMKEY          EQU     08001H
PICBYTE         EQU     08003H
SHIFT_STATUS    EQU     08004H
CTRL_STATUS     EQU     08005H
ALT_STATUS      EQU     08006H
UPKEY_FLAG      EQU     08007H
CURRENT_CMD     EQU     08008H
CURRENT_ADDR    EQU     08009H ; two bytes
CURRENT_LEN     EQU     0800BH
CURRENT_PTR     EQU     0800CH ; point into CURRENT_CMD
SHOULD_RUN      EQU     0800DH
KEYBUF          EQU     0A000H
INPUT_BUF       EQU     0C000H
OUTPUT_BUF      EQU     0C040H

LSHIFT_KEY      EQU     012H
RSHIFT_KEY      EQU     059H
CTRL_KEY        EQU     014H
ALT_KEY         EQU     011H
UP_KEY          EQU     0F0H
EXT_KEY         EQU     0E0H
EXT2_KEY        EQU     0E1H

PIC_NON_CMD     EQU     000H
PIC_SER_CMD     EQU     001H
PIC_KBD_CMD     EQU     002H
PIC_TIM_CMD     EQU     003H

CMD_SEND        EQU     001H
CMD_RUN         EQU     002H
CMD_MAX         EQU     003H    ; one past max value of a command


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

;
; Memory test program
;

MAIN
        LD      HL, INTROSTR
        CALL    LCDPRINT

;
; Setup the 8259
;

        LD      A, 016H    ; F = 1, S = 1
        OUT     (I8259A), A

        LD      A, 03FH    ; Table is at 03F00H
        OUT     (I8259B), A

        LD      A, 0F0H    ; Enable lower 4 IRQs
        OUT     (I8259B), A

;
; Initialize our variables
;

        LD      A, 0
        LD      (PIC_COMMAND), A
        LD      (NUMKEY), A
        LD      (SHIFT_STATUS), A
        LD      (CTRL_STATUS), A
        LD      (ALT_STATUS), A
        LD      (UPKEY_FLAG), A
        LD      (CURRENT_PTR), A
        LD      (SHOULD_RUN), A

;
; Read a byte from the pic in case it triggered
; an interrupt before we were ready (keyboard
; reset).  This resets the PIC interrupt line.
;
; The command read routine throws away commands it does not understand,
; so it is likely we will get the keyboard init status byte here but the
; command read routine will chuck it and we will catch up on the next byte.
;

        IN      A, (PIC)

;
; Setup interrupt mode 0
;

        IM      0
        EI

;
; Go, go, go!
;

PRINTAGAIN
        LD      HL, PLEASESTR
        CALL    LCDPRINT

        LD      HL, INPUT_BUF
        CALL    GETS

        LD      HL, HELLOSTR
        LD      IX, OUTPUT_BUF
        CALL    STRCPY

        LD      HL, INPUT_BUF
        CALL    STRCPY

        LD      (IX), PAUSE
        INC     IX
        LD      (IX), 0

        LD      HL, OUTPUT_BUF
        CALL    LCDPRINT

        JP      PRINTAGAIN

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

ISR0    ; PIC interrupt

        ;
        ; The PIC sends a command byte followed by an
        ; optional data byte.
        ;
        ;   Command byte: PIC_SER_CMD
        ;   Data byte: serial byte in ASCII
        ;
        ;   Command byte: PIC_KBD_CMD
        ;   Data byte: keyboard byte as scan code
        ;
        ;   Command byte: PIC_TIM_CMD
        ;   Data byte: none
        ;
        ; Current command is stored in PIC_COMMAND, which is
        ; equal to PIC_NON_CMD if we are waiting for a command.
        ;

        PUSH    AF

        IN      A, (PIC)
        LD      (PICBYTE), A

; START TEST
#if 0
        PUSH    IX
        PUSH    HL
        PUSH    BC

        LD      IX, OUTPUT_BUF
        CALL    PHEXBYTE        ; Hex A into IX

        LD      HL, KEYBUF
        LD      A, (NUMKEY)
        LD      C, A
        LD      B, 0
        ADD     HL, BC          ; HL = pointer into buffer
        INC     A
        LD      (NUMKEY), A

        LD      A, (OUTPUT_BUF) ; Key in A
        LD      (HL), A         ; Stick our letter in buffer

        LD      HL, KEYBUF
        LD      A, (NUMKEY)
        LD      C, A
        LD      B, 0
        ADD     HL, BC          ; HL = pointer into buffer
        INC     A
        LD      (NUMKEY), A

        LD      A, (OUTPUT_BUF+1); Key in A
        LD      (HL), A         ; Stick our letter in buffer

        POP     BC
        POP     HL
        POP     IX

        JP      ISR0_END
#endif
; END TEST

        LD      A, (PIC_COMMAND)
        CP      PIC_NON_CMD     ; Cmd not read yet
        JP      Z, ISR0_IS_COMMAND

        CP      PIC_SER_CMD
        JP      Z, ISR0_IS_SERIAL

        CP      PIC_KBD_CMD
        JP      Z, ISR0_IS_KEYBOARD

        JP      ISR0_RESET      ; Whoa, bad byte

ISR0_IS_COMMAND
        LD      A, (PICBYTE)
        CP      PIC_TIM_CMD     ; special case for timer cause no data
        JP      Z, ISR0_IS_TIMER

        LD      (PIC_COMMAND), A
        JP      ISR0_END

ISR0_IS_TIMER
        ; do something
        JP      ISR0_RESET

ISR0_IS_SERIAL
        CALL    HANDLE_SERIAL
        JP      ISR0_RESET

ISR0_IS_KEYBOARD
        CALL    HANDLE_KEYBOARD
        JP      ISR0_RESET

ISR0_RESET
        LD      A, PIC_NON_CMD
        LD      (PIC_COMMAND), A
        ; FALLTHROUGH

ISR0_END
        LD      A, 020H         ; End of Interrupt
        OUT     (I8259A), A

        POP     AF
        EI
        RET

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

HANDLE_SERIAL  ; trashes A
        PUSH    HL
        PUSH    BC

        ; we get downloaded programs through serial.  handle
        ; that first.  we get one command byte, then two
        ; address bytes, then a length byte.  if the command
        ; is CMD_SEND, then this header is followed by "length"
        ; bytes to stick into "address".  if the command is
        ; CMD_RUN, then the length is zero and SHOULD_RUN is
        ; set to 1, which causes the input routine to jump
        ; to the address in CURRENT_ADDR.

        LD      A, (CURRENT_PTR)
        CP      3
        JP      NZ, GET_CMD_HEADER

        ; just got last byte of header
        LD      A, (CURRENT_CMD)
        CP      CMD_RUN
        JP      NZ, GET_CMD_HEADER

        ; ignore length and run
        LD      A, 1
        LD      (SHOULD_RUN), A
        JP      END_HANDLE_SERIAL

GET_CMD_HEADER
        LD      A, (CURRENT_PTR)
        CP      4
        JP      Z, TRANSFER_BYTE

        ; check if it is not a transfer
        CP      0
        JP      NZ, TRANSFER_HEADER

        LD      A, (PICBYTE)
        CP      CMD_MAX
        JP      P, SIMULATE_KEYBOARD ; >= CMD_MAX

TRANSFER_HEADER
        ; transfer into the header
        LD      HL, CURRENT_CMD
        LD      A, (CURRENT_PTR)
        LD      C, A
        LD      B, 0
        ADD     HL, BC
        LD      A, (PICBYTE)
        LD      (HL), A
        INC     C
        LD      A, C
        LD      (CURRENT_PTR), A

        JP      END_HANDLE_SERIAL

TRANSFER_BYTE
        ; transfer into RAM
        LD      HL, (CURRENT_ADDR)
        LD      A, (PICBYTE)
        LD      (HL), A
        INC     HL
        LD      (CURRENT_ADDR), HL
        LD      A, (CURRENT_LEN)
        DEC     A
        LD      (CURRENT_LEN), A
        JP      NZ, END_HANDLE_SERIAL

        LD      (CURRENT_PTR), A

        JP      END_HANDLE_SERIAL

SIMULATE_KEYBOARD
        LD      HL, KEYBUF
        LD      A, (NUMKEY)
        LD      C, A
        LD      B, 0
        ADD     HL, BC          ; HL = pointer into buffer
        INC     A
        LD      (NUMKEY), A

        LD      A, (PICBYTE)    ; Key in A
        LD      (HL), A         ; Stick our letter in buffer

END_HANDLE_SERIAL

        POP     BC
        POP     HL
        RET

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

HANDLE_KEYBOARD
        PUSH    HL
        PUSH    BC

        LD      A, (PICBYTE)    ; Get the byte

        CP      UP_KEY          ; Key-up is preceded by UP_KEY
        JP      NZ, NOT_UP_KEY

        LD      A, 1
        LD      (UPKEY_FLAG), A
        JP      END_HANDLE_KEYBOARD

NOT_UP_KEY
        CP      EXT_KEY         ; Extended keys preceded by EXT_KEY
        JP      Z, DID_KEY      ; For now just ignore

        CP      EXT2_KEY        ; Break preceded by EXT2_KEY
        JP      Z, DID_KEY      ; For now just ignore

        BIT     7, A            ; See if high bit is on
        JP      NZ, DID_KEY     ; Ignore if >= 128

        CP      LSHIFT_KEY      ; See if it is a shift key
        JP      Z, IS_SHIFT
        CP      RSHIFT_KEY
        JP      NZ, NOT_SHIFT
IS_SHIFT
        LD      A, (UPKEY_FLAG)
        XOR     1
        LD      (SHIFT_STATUS), A
        JP      DID_KEY

NOT_SHIFT
        CP      CTRL_KEY        ; See if it is a control key
        JP      NZ, NOT_CTRL

        LD      A, (UPKEY_FLAG)
        XOR     1
        LD      (CTRL_STATUS), A
        JP      DID_KEY

NOT_CTRL
        CP      ALT_KEY         ; See if it is an alt key
        JP      NZ, NOT_ALT

        LD      A, (UPKEY_FLAG)
        XOR     1
        LD      (ALT_STATUS), A
        JP      DID_KEY

NOT_ALT
        LD      L, A            ; HL = A
        LD      H, 0

        LD      A, (UPKEY_FLAG) ; Ignore all other key ups
        CP      1
        JP      Z, DID_KEY

        ADD     HL, HL          ; HL *= 4
        ADD     HL, HL

        LD      B, 0
        LD      A, (SHIFT_STATUS)
        LD      C, A
        ADD     HL, BC

        LD      A, (CTRL_STATUS)
        LD      C, A
        ADD     HL, BC
        ADD     HL, BC

        LD      A, (ALT_STATUS)
        LD      C, A
        ADD     HL, BC
        ADD     HL, BC
        ADD     HL, BC

        LD      BC, KEY_XLAT    ; Translation table
        ADD     HL, BC

        LD      A, (HL)         ; Get actual key
        PUSH    AF

        LD      HL, KEYBUF
        LD      A, (NUMKEY)
        LD      C, A
        LD      B, 0
        ADD     HL, BC          ; HL = pointer into buffer
        INC     A
        LD      (NUMKEY), A

        POP     AF              ; Key in A
        LD      (HL), A         ; Stick our ASCII letter in there


DID_KEY
        ; Reset "up" flag
        LD      A, 0
        LD      (UPKEY_FLAG), A

END_HANDLE_KEYBOARD

        POP     BC
        POP     HL
        RET

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

ISR1
        PUSH    AF

        LD      A, 020H         ; End of Interrupt
        OUT     (I8259A), A

        POP     AF
        EI
        RET

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

ISR2
        PUSH    AF

        LD      A, 020H         ; End of Interrupt
        OUT     (I8259A), A

        POP     AF
        EI
        RET

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

ISR3
        PUSH    AF

        LD      A, 020H         ; End of Interrupt
        OUT     (I8259A), A

        POP     AF
        EI
        RET

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

ISR4
        PUSH    AF

        LD      A, 020H         ; End of Interrupt
        OUT     (I8259A), A

        POP     AF
        EI
        RET

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

ISR5
        PUSH    AF

        LD      A, 020H         ; End of Interrupt
        OUT     (I8259A), A

        POP     AF
        EI
        RET

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

ISR6
        PUSH    AF

        LD      A, 020H         ; End of Interrupt
        OUT     (I8259A), A

        POP     AF
        EI
        RET

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

ISR7
        PUSH    AF

        LD      A, 020H         ; End of Interrupt
        OUT     (I8259A), A

        POP     AF
        EI
        RET

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

INTROSTR        DB      "PIC16C64 test", 10, 0

PLEASESTR       DB      "Your name?", 10, 0
HELLOSTR        DB      "Hello ", 0

KEY_XLAT        ; Normal, shift, ctrl, alt
        DB       '?', '?', '?', '?'
        DB       '9', '9', '9', '9'  ; F9
        DB       '?', '?', '?', '?'
        DB       '5', '5', '5', '5'  ; F5
        DB       '3', '3', '3', '3'  ; F3
        DB       '1', '1', '1', '1'  ; F1
        DB       '2', '2', '2', '2'  ; F2
        DB       '1', '1', '1', '1'  ; F12
        DB       '?', '?', '?', '?'
        DB       '1', '1', '1', '1'  ; F10
        DB       '8', '8', '8', '8'  ; F8
        DB       '6', '6', '6', '6'  ; F6
        DB       '4', '4', '4', '4'  ; F4
        DB         9,   9,   9,   9  ; Tab
        DB       '`', '~', '`', '`'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'  ; Alt
        DB       '?', '?', '?', '?'  ; Left shift
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'  ; Control
        DB       'q', 'Q',  17,  17
        DB       '1', '!', '1', '1'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       'z', 'Z',  26,  26
        DB       's', 'S',  19,  19
        DB       'a', 'A',   1,   1
        DB       'w', 'W',  23,  23
        DB       '2', '@', '2', '2'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       'c', 'C',   3,   3
        DB       'x', 'X',  24,  24
        DB       'd', 'D',   4,   4
        DB       'e', 'E',   5,   5
        DB       '4', '$', '4', '4'
        DB       '3', '#', '3', '3'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       ' ', ' ', ' ', ' '
        DB       'v', 'V',  22,  22
        DB       'f', 'F',   6,   6
        DB       't', 'T',  20,  20
        DB       'r', 'R',  18,  18
        DB       '5', '%', '5', '5'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       'n', 'N',  14,  14
        DB       'b', 'B',   2,   2
        DB       'h', 'H',   8,   8
        DB       'g', 'G',   7,   7
        DB       'y', 'Y',  25,  25
        DB       '6', '^', '6', '6'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       'm', 'M',  13,  13
        DB       'j', 'J',  10,  10
        DB       'u', 'U',  21,  21
        DB       '7', '&', '7', '7'
        DB       '8', '*', '8', '8'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       ',', '<', ',', ','
        DB       'k', 'K',  11,  11
        DB       'i', 'I',   9,   9
        DB       'o', 'O',  15,  15
        DB       '0', ')', '0', '0'
        DB       '9', '(', '9', '9'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '.', '>', '.', '.'
        DB       '/', '?', '/', '/'
        DB       'l', 'L',  12,  12
        DB       ';', ':', ';', ';'
        DB       'p', 'P',  16,  16
        DB       '-', '_', '-', '-'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB        39, '"',  39,  39  ; Single quote
        DB       '?', '?', '?', '?'
        DB       '[', '{', '[', '['
        DB       '=', '+', '=', '='
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'  ; Caps lock
        DB       '?', '?', '?', '?'  ; Right shift
        DB        10,  10,  10,  10  ; Enter
        DB       ']', '}', ']', ']'
        DB       '?', '?', '?', '?'
        DB        92, '|',  92,  92  ; Backslash
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB         8,   8,   8,   8  ; Backspace
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '1', '1', '1', '1'  ; Numeric 1
        DB       '?', '?', '?', '?'
        DB       '4', '4', '4', '4'  ; Numeric 4
        DB       '7', '7', '7', '7'  ; Numeric 7
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '?', '?', '?', '?'
        DB       '0', '0', '0', '0'  ; Numeric 0
        DB       '.', '.', '.', '.'  ; Numeric .
        DB       '2', '2', '2', '2'  ; Numeric 2
        DB       '5', '5', '5', '5'  ; Numeric 5
        DB       '6', '6', '6', '6'  ; Numeric 6
        DB       '8', '8', '8', '8'  ; Numeric 8
        DB        27,  27,  27,  27  ; Escape
        DB       '?', '?', '?', '?'  ; Num lock
        DB       '1', '1', '1', '1'  ; F11
        DB       '+', '+', '+', '+'  ; Numeric +
        DB       '3', '3', '3', '3'  ; Numeric 3
        DB       '-', '-', '-', '-'  ; Numeric -
        DB       '*', '*', '*', '*'  ; Numeric *
        DB       '9', '9', '9', '9'  ; Numeric 9
        DB       '?', '?', '?', '?'  ; Scroll lock
        DB       '?', '?', '?', '?'

        ORG     03F00H
INTTABLE

        ; IRQ0
        JP      ISR0
        DB      0

        ; IRQ1
        JP      ISR1
        DB      0

        ; IRQ2
        JP      ISR2
        DB      0

        ; IRQ3
        JP      ISR3
        DB      0

        ; IRQ4
        JP      ISR4
        DB      0

        ; IRQ5
        JP      ISR5
        DB      0

        ; IRQ6
        JP      ISR6
        DB      0

        ; IRQ7
        JP      ISR7
        DB      0

        ORG     04000H

#if 0

; start tmp code
        ORG     06000H  /* J UMP TO HERE */

#if 0
;this code writes 0-255 in 0x4000 through 0x40ff continuously

        DI
        LD      A, 0
        LD      HL, 04000H
TEST_WRITE_AGAIN
        LD      (HL), A
        INC     A
        INC     L
        JP      TEST_WRITE_AGAIN
#endif


        LD      HL, 07000H
        LD      A, 0
TEST_AGAIN
        PUSH    HL
        LD      (HL), A
        LD      A, (HL)
        LD      IX, OUTPUT_BUF
        CALL    PHEXBYTE
        LD      (IX), PAUSE
        INC     IX
        LD      (IX), 0
        LD      HL, OUTPUT_BUF
        CALL    LCDPRINT

#if 0
        LD      HL, 04000H
        LD      (HL), 0FFH
        LD      A, (HL)
        LD      IX, OUTPUT_BUF
        CALL    PHEXBYTE
        LD      (IX), PAUSE
        INC     IX
        LD      (IX), 0
        LD      HL, OUTPUT_BUF
        CALL    LCDPRINT
#endif
        POP     HL
        INC     HL
        INC     A

        JP      TEST_AGAIN

; end tmp code
#endif

#if 1
#include "image.asm"
#endif

#if 1
        ORG     09000H   /* JUMP TO HERE */
#endif

#if 0
/* fill the screen */
        LD      HL, 16384
        LD      A, 088H

LOOP_AGAIN_YES
        LD      (HL), A
        INC     HL
        JP      LOOP_AGAIN_YES
        DJNZ    LOOP_AGAIN_YES
#endif

#if 0
#include "zcc_out.asm"
#include "libc.asm"
#endif

LOOP_FOREVER
        JP      LOOP_FOREVER

        END