; Author: Nicolae Dumitrache - Feb2019

USB0_PORT               equ 20h                         ;
USB1_PORT               equ 40h                         ;
BUFSIZE                 equ 16                          ; circular buffer size (must be 2^n)
TYP_DELAY               equ 250                         ; ms
TYP_RATE                equ 30                          ; ms


                        org 0                           ;
                        ld sp,0                         ;
                        ei                              ;
                        jp start                        ;
                        nop                             ;
                        jp intproc                      ; int 8

start:
; ------------ main loop - cooperative multitasking
loop1:                  ld hl,dev0                      ;
                        halt                            ;
                        call action                     ; takes max 15h uFrames with no prints
                        ld hl,dev1                      ;
                        halt                            ;
                        call action                     ; takes max 15h uFrames with no prints
                        jr loop1                        ;

; -------------------- send USB packet at HL, B size. Add synchro field
sends:                  ; HL=packet address, B=size, must be called with enough time before the end of frame;
                        call waitbusy                   ;
                        ld a, 80h                       ; synchro
                        call outdata                    ;
send1:                  ld a,(hl)                       ;
                        call outdata                    ;
                        inc hl                          ;
                        djnz send1                      ;
                        ld a,80h                        ;
                        call outcmd                     ; send
                        inc a                           ; NZ
                        ret                             ;

sendRequest:            ; HL=request, B=send size, DE=ack buffer (2 bytes).;
                        push bc                         ;
                        ld b,3                          ;
                        call sends                      ;
                        pop bc                          ;
                        call sends                      ;
                        ld b,2                          ;
                        call receive                    ; get ACK, max 16 usb bits after "send" command
                        ret                             ;

; ---------- receive USB packet, returns ZF if timeout, A=PID, DE=end buffer-2 (forget last 2 bytes)
receive:                ; B=size, DE=buffer, must be called with enough time before the end of frame;
                        push de                         ; save start buffer to get PID at the end
                        call waitbusy                   ;
                        in a,(0)                        ; get uFrame counter (one count=8USB bits)
                        add 3                           ; max waiting time is 16..24 USB bits
                        ld c,a                          ;
                        ld a,b                          ;
                        add a                           ;
                        add a                           ;
                        add b                           ;
                        rra                             ;
                        or a                            ;
                        rr a                            ;
                        add 2                           ;
                        add c                           ;
                        ld h,a                          ; estimate timeout, as bits*5/4 + 2

receive1:               in a,(0)                        ;
                        cp c                            ;
                        jr z,recv_timeout               ; ZF=timeout, NC
                        call incmd                      ;
                        and 2                           ; 2=read queue empty
                        jr nz,receive1                  ; no data
                        ld c,h                          ; set new timeout
                        call indata                     ;
                        ld (de),a                       ;
                        inc de                          ;
                        djnz receive1                   ;
                        dec de                          ; forget last 2 bytes received (PID or CRC)
                        dec de                          ;
                        inc c                           ; NZ,NC
recv_timeout:
                        pop hl                          ; start buffer
                        inc hl                          ;
                        ld a,(hl)                       ;
                        ret                             ;


; -------------------- receive B bytes from endpoint in HL, wait first for a new frame. Put data at DE.
; Returns ZF on timeout, CF on no data
recv_endpoint_recbuf:   ld de,recbuf                    ;
recv_endpoint:          push hl                         ;
                        push bc                         ;
                        ld b,3                          ;
                        call sends                      ;
                        ld b,2                          ;
                        call receive                    ; receive the first 2 bytes of the data packet
                        pop bc                          ;
                        pop hl                          ;
                        ret z                           ; timeout
                        and a,3                         ;
                        cp a,3                          ;
                        ret c                           ; NZ, CF - not a data packet, probably NAK or STALL, try again next frame
                        call receive                    ; receive the rest of the data packet
                        ret z                           ; timeout
                        ld hl,ack                       ; max 10us accepted by the device before ACK (~15 lo speed bits)
                        ld b,1                          ;
                        jr sends                        ; send ack


; --------------------- whait while USB busy
waitbusy:               call incmd                      ;
                        and 4                           ;
                        jr nz,waitbusy                  ;
                        ret                             ;


; ------------------------ interrupt ----------------------
intproc:
                        push af                         ;
                        ld a,(dev0)                     ;
                        cp 73h                          ;
                        jr c, noEOP0                    ; device not ready
                        in a,(21h)                      ;
                        and 8+4                         ; 8 = connected, 4=busy
                        cp 8                            ; connected, not busy
                        jr nz, noEOP0                   ; not connected or busy, skip EOP
                        ld a,80h                        ;
                        out (21h),a                     ; EOP command
noEOP0:                 ld a,(dev1)                     ;
                        cp 73h                          ;
                        jr c, noEOP1                    ; device not ready
                        in a,(41h)                      ;
                        and 8+4                         ; 8 = connected, 4=busy
                        cp 8                            ; connected, not busy
                        jr nz, noEOP1                   ; not connected or busy, skip EOP
                        ld a,80h                        ;
                        out (41h),a                     ; EOP command
noEOP1:                 push bc                         ;
                        push de                         ;
                        push hl                         ;
                        call mouse_int1ms               ;
                        call kb_int1ms                  ;
                        pop hl                          ;
                        pop de                          ;
                        pop bc                          ;
                        pop af                          ;
                        ei                              ;
                        ret                             ;



; ------------------------ Action coroutine  ------------------------
STATE_connect:          call incmd                      ; connect
                        and 8+4                         ; 8 = connected, 4=busy
                        sub 4                           ;
                        jp z, actionend                 ; not connected, busy, stay
                        ccf                             ;
                        jr c, actionend                 ; connected, next state
                        ld a,60h                        ; USB connect
                        jp state61                      ; next state

STATE_displ_conn:       ld de,set_report_data+4         ;
                        ld a,(de)                       ;
                        or 80h                          ;
                        ld (de),a                       ;
                        jp STATE_next                   ; next state

action:                 // HL=device data               ;
                        db 0ddh, 2                      ; INDEX 2
                        ld a,(hl)                       ; ld a,(hl+2) port address
                        ld (indata+1),a                 ;
                        ld (outdata+1),a                ; patch in/out port address
                        inc a                           ;
                        ld (incmd+1),a                  ;
                        ld (outcmd+1),a                 ;
                        ld a,(hl)                       ; state in a
                        push hl                         ;
                        or a                            ;
                        jr z, STATE_displ_disc          ; display "disconnected"
                        dec a                           ;
                        jr z, STATE_connect             ; connect
                        sub a,101                       ;
                        jr c, STATE_next                ; wait 100ms
                        jr nz, action_ge4               ;
                        call incmd                      ;
                        and 8                           ;
                        ld a,1                          ;
                        jr z, action_jmp                ; disconnected, jump to state1
                        jr STATE_displ_conn             ; display "connected"
action_ge4:             ld c,a                          ;
                        call incmd                      ;
                        and 8                           ;
                        jr z, action_jmp                ; disconnected, jump to state0
                        ld a,c                          ;
                        dec a                           ;
                        jr z, STATE_reset_usb           ; reset USB dev
                        sub a,11                        ;
                        jr c,STATE_next                 ; wait 10ms
                        jr z,STATE_idle                 ; idle
                        sub a,41                        ;
                        jr c,STATE_next                 ; wait 40ms
                        jr z,STATE_set_addr1            ; set address
                        dec a                           ;
                        jr z,STATE_in_addr0_endp0_0B    ; receive response (null packet)
                        sub a,4                         ;
                        jr c, STATE_next                ; wait 3ms for the device to change its address
                        jr z,STATE_set_cfg1             ; set configuration 1
                        dec a                           ;
                        jr z,STATE_in_addr1_endp0_0B    ; get null packet
                        dec a                           ;
                        jp z,STATE_get_conf_desc        ; get configuration descriptor (34 bytes)
                        sub a,5                         ;
                        jp c,STATE_in_addr1_endp0_8B    ; receive 4 byte packets
                        jp z,STATE_in_addr1_endp0_2B    ; plus one 2byte packet
                        dec a                           ;
                        jr z,STATE_send_null_pkt        ;
                        dec a                           ;
                        jp z,STATE_poll                 ; polling
                        dec a                           ;
                        jp z,STATE_setreport_data       ;
                        dec a                           ;
                        jp z,STATE_get_null_pkt         ;
                        dec a                           ;
                        jp z,STATE_back_to_poll         ;

actionend0:             or a                            ; CF=0, stay
actionend:              pop hl                          ;
                        ret nc                          ;
                        inc (hl)                        ; next state
                        ret                             ;
action_error:           ld a,0ffh                       ;
action_jmp:             pop hl                          ;
                        ld (hl),a                       ;
                        ret                             ;

STATE_displ_disc:
STATE_next:             scf                             ; next
                        jr actionend                    ;

STATE_reset_usb:        xor a                           ; reset USB device
                        call outcmd                     ; reset command
empty:                  call indata                     ; empty data fifo
                        call incmd                      ;
                        and 2                           ; check data in queue
                        jr z,empty                      ;
                        jr STATE_next                   ; next state

STATE_idle:             ld a,20h                        ; idle
state61:                call outcmd                     ;
                        jr STATE_next                   ; next

STATE_set_addr1:        ld hl,setaddr_setup             ; set address
state71:                ld b,11                         ;
sendreq1:               ld de,recbuf                    ; ACK buffer
                        call sendRequest                ;
                        jr z,action_error               ; timeout
                        sub 0d2h                        ;
                        or a                            ; CF=0
                        jr nz,actionend                 ; no ACK received, stay (CF=0)
                        jr STATE_next                   ;

STATE_in_addr0_endp0_0B:ld hl,in_addr0_endp0            ; receive (null packet)
state81:                ld b,2                          ;
                        call recv_endpoint_recbuf       ; returns CF=0 if ok
                        jr z,action_error               ; timeout
                        ccf                             ;
                        jr actionend                    ;

STATE_set_cfg1:         ld hl,set_conf1                 ; set configuration 1
                        jr state71                      ;

STATE_in_addr1_endp0_0B:ld hl,in_addr1_endp0            ; receive null packet
                        jr state81                      ;

STATE_get_conf_desc:    ld hl,get_conf_desc             ;
                        jr state71                      ;

STATE_send_null_pkt:    ld hl,out_null                  ;
                        ld b,3                          ;
                        jr sendreq1                     ;

STATE_in_addr1_endp0_8B:ld b,10                         ;
state83:                push af                         ;
                        ld hl,in_addr1_endp0            ;
                        call recv_endpoint_recbuf       ; returns CF=0 if ok
                        jr z,action_error               ; timeout
                        ccf                             ;
                        pop bc                          ;
                        jp nc, actionend                ; no data yet, stay
                        pop hl                          ;
                        push hl                         ; restore dev struct
                        ld a,b                          ;
                        add 3                           ;
                        jr z,cfg_pkt2                   ; 2nd packet, 7th byte should be 03 (HID)
                        dec a                           ; 3th packet, first byte should be 01 for KB or 02 for mouse
                        jr z,cfg_pkt3                   ;
                        dec a                           ;
                        jr z,cfg_pkt4                   ; 4th packet, last byte is report size
                        jr STATE_next                   ;

STATE_in_addr1_endp0_2B:ld b,4                          ;
                        jr state83                      ;

cfg_pkt2:               ld a,(recbuf+6)                 ;
                        sub 3                           ; is HID
                        sub 1                           ;
                        sbc a,a                         ; if HID, A = 0ffh
                        jr cfg_pkt22                    ;
cfg_pkt3:               ld a,(recbuf)                   ;
                        cp 3                            ;
                        jr c,$+3                        ;
                        xor a                           ;
                        noflow db 0ddh,3                ; and (hl+3)
                        and (hl)                        ;
cfg_pkt22:              noflow db 0ddh,3                ;
                        ld (hl),a                       ; ld (hl+3),a
                        jr cfg_pkt23                    ;
cfg_pkt4:               ld a,(recbuf+7)                 ; get packet size from endpoint descriptor
                        noflow db 0ddh,4                ; ld (hl+4),a
                        ld (hl),a                       ;
cfg_pkt23:              jp STATE_next                   ;


STATE_poll:             db 0ddh, 3                      ;
                        ld a,(hl)                       ; ld a,(hl+3)
                        sub a,1                         ;
                        jr c,poll1                      ; no mouse or kb
                        ld d,a                          ; A=0 for kb, 1 for mouse
                        jr nz,poll_cont                 ; mouse, no leds
                        ld a,(set_report_data+4)        ;
                        add a                           ;
                        jr nc,poll_cont                 ; no LEDs to set

                        rrca                            ;
                        ld (set_report_data+4),a        ;
                        add a                           ;
                        add low led_crc                 ;
                        ld l,a                          ;
                        ld h,high led_crc               ;
                        jr nc,$+3                       ;
                        inc h                           ;
                        ld a,(hl)                       ;
                        inc hl                          ;
                        ld h,(hl)                       ;
                        ld l,a                          ;
                        ld (set_report_data+5),hl       ; update setreport crc
                        ld hl,set_report                ;
                        jp state71                      ; set report

poll_cont:              noflow db 0ddh, 4               ; ld b,(hl+4)
                        ld b,(hl)                       ; polling mouse or kb, B=interrupt packet size
                        push hl                         ;
                        ld hl,kb_buffer                 ;
                        jr z,$+5                        ;
                        ld hl,mouse_buffer              ;
                        call getfree_buffer             ; check for min 3 bytes free in ps2 buffer
                        pop hl                          ;
                        cp 3                            ; max mouse packet size=3
                        jr c,poll1                      ; less than 3 bytes free in PS2 queue, don't poll USB
                        push de                         ; D=0kb, 1mouse
                        inc b                           ;
                        inc b                           ; packet size+2 (CRC)
                        push hl                         ;
                        ld hl,in_addr1_endp1            ;
                        call recv_endpoint_recbuf       ;
                        pop hl                          ;
                        jr nz, notimeout                ;
                        noflow db 0ddh, 4               ; ld a,(hl+4)
                        ld a,(hl)                       ;
                        sub b                           ;
                        jr z,notimeout                  ; nothing was received,error
                        noflow db 0ddh, 4               ; ld (hl+4),a
                        ld (hl),a                       ; smaller packet received, adjust new packet size
notimeout:              pop bc                          ;
                        jp z, action_error              ; timeout
                        jr c, poll1                     ; no data
                        dec b                           ;
                        push af                         ;
                        call nz,process_kb              ;
                        pop af                          ;
                        call z,process_mouse            ;
poll1:                  jp actionend0                   ;

STATE_setreport_data:   ld hl,set_report_data           ;
                        ld b,4                          ;
                        jp sendreq1                     ;

STATE_get_null_pkt:     ld hl,in_addr1_endp0            ;
                        jp state81                      ;

STATE_back_to_poll:     ld a,(hl)                       ;
                        sub 3                           ;
                        jp action_jmp                   ;


; --------------------- process mouse packets -----------------------
process_mouse:          ; uFrame = 0eh                  ;
                        ld hl,recbuf                    ;
                        ld a,(hl)                       ;
                        and 7                           ;
                        or 8                            ;
                        ld c,a                          ;
                        inc hl                          ;
                        ld b,(hl)                       ;
                        inc hl                          ;
                        xor a                           ;
                        sub a,(hl)                      ; negate Y displacement
                        ld d,a                          ;
                        rla                             ;
                        ld a,b                          ;
                        rra                             ;
                        rra                             ;
                        rra                             ;
                        and 30h                         ;
                        or c                            ; PS2 packet - A=first byte, B=second byte, D = third byte

                        ld hl,mouse_buffer              ;
                        call push_buffer                ;
                        ld a,b                          ;
                        call push_buffer                ;
                        ld a,d                          ;
                        jp push_buffer                  ;

; -------------------- mouse interrupt ----------------
mouse_int1ms:
                        ld hl,mouse_buffer              ;
                        in a,(1)                        ;
                        rrca                            ;
                        jr nc,nocmd                     ;
                        xor a                           ; clear buffer
                        ld (hl),a                       ;
                        inc hl                          ;
                        ld (hl),a                       ;
                        dec hl                          ;
                        ld a,1                          ;
                        out (1),a                       ;
                        xor a                           ;
                        out (1),a                       ; flush data

                        ld a,0fah                       ; ack
                        call push_buffer                ;
                        in a,(2)                        ; read cmd
                        cp 0ffh                         ;
                        jr nz,nocmd                     ;

                        ld a,0aah                       ;
                        call push_buffer                ; BAT code 0AAh
                        xor a                           ;
                        call push_buffer                ; mouse ID 00h

nocmd:                  in a,(1)                        ;
                        rrca                            ;
                        rrca                            ;
                        ret c                           ; busy
                        call pop_buffer                 ;
                        ret c                           ; empty
                        out (2),a                       ;
                        ret                             ;

; --------------------- process kb packets -----------------------
process_kb:             ; uFrame = 12h                  ;
                        ld hl,recbuf+1                  ; convert flags to USB codes
                        noflow db 0ddh, 7               ;
                        ld (hl),0                       ; ld (hl+7),0 - guard
pkb1:                   inc hl                          ;
                        ld a,(hl)                       ;
                        dec a                           ;
                        cp 3                            ;
                        ret c                           ; error packet, ignore codes 1, 2, 3
                        inc a                           ;
                        jr nz,pkb1                      ; find the recbuf end
                        ld a,(recbuf)                   ;
                        ld b,0e8h                       ;
pkb2:                   dec b                           ;
                        add a                           ;
                        jr nc,$+4                       ;
                        ld (hl),b                       ;
                        inc hl                          ;
                        jr nz, pkb2                     ;
                        ld (hl),0ffh                    ; buffer end, all flags are converted

                        ld hl,kb_pressed                ;
                        call getfree_buffer             ;
                        cpl                             ;
                        add BUFSIZE                     ;
                        jr z,newpressed                 ; kb_pressed is empty
                        ld b,a                          ; B = no of elements in kb_pressed
nextitem:               call pop_buffer                 ;
                        ld de,recbuf+2                  ;
                        ex de,hl                        ;
pkb4:                   ld c,(hl)                       ;
                        inc c                           ;
                        jr z,notfound                   ; not pressed anymore, create ps2 break code
                        cp (hl)                         ;
                        jr z,found                      ;
                        inc hl                          ;
                        jr pkb4                         ;

notfound:               ld c,a                          ;
                        ld hl,kb_buffer                 ;
                        call getfree_buffer             ; check buffer space
                        cp 3                            ;
                        ret c                           ; no free space, exit
                        ld a,(typematic_usb_code)       ;
                        sub c                           ;
                        jr nz, notyprelease             ;
                        ld (typematic_usb_code),a       ; remove typematic
notyprelease:           ld a,c                          ;
                        call releaseps2                 ; push PS2 release code sequence
                        ex de,hl                        ;
                        jr pkbcont                      ;

found:                  ld (hl),0                       ; erase key from USB report
                        ex de,hl                        ;
                        call push_buffer                ; put key back in queue
pkbcont:                djnz nextitem                   ;

newpressed:             ld de,recbuf+1                  ; add new pressed keys to kb_pressed
pkb5:                   ld hl,kb_buffer                 ;
                        call getfree_buffer             ;
                        cp 3                            ;
                        ret c                           ; no space
pkb6:                   inc de                          ;
                        ld a,(de)                       ;
                        inc a                           ;
                        ret z                           ; end kb data
                        dec a                           ;
                        jr z,pkb6                       ;
                        ld b,a                          ;
                        call pushps2                    ; push PS2 press code sequence
                        ld a,b                          ;
                        ld hl,kb_pressed                ;
                        ld (typematic_usb_code),a       ;
                        call push_buffer                ;
                        xor a                           ;
                        ld (typematic_delay),a          ;
                        jr pkb5                         ;

pushps2:                ; A=USB code, HL=kb_pressed buffer;
                        call atops2                     ;
                        ret z                           ;
pushps_pb:              jp push_buffer                  ;

releaseps2:             ; A=USB code, HL=kb_pressed buffer;
                        call atops2                     ; convert A to PS2 code
                        ret z                           ;
                        ld c,a                          ;
                        ld a,(scancodeset+1)            ;
                        cp high ps2set1tbl              ;
                        jr z,sc1                        ; code set 1
                        ld a,0f0h                       ;
                        call push_buffer                ;
                        ld a,c                          ;
                        jr pushps_pb                    ;
sc1:                    ld a,c                          ;
                        or 80h                          ; put ps2 break code
                        jr pushps_pb                    ;

; ---------------- USB to PS2 conversion - returns code in A
atops2:                 push hl                         ;
                        add low ps2set1tbl              ;
                        ld l,a                          ;
scancodeset:            ld h,high ps2set2tbl            ; modified by PS2 command 0Fh
                        jr nc,$+3                       ;
                        inc h                           ;
                        ld a,(hl)                       ;
                        pop hl                          ;
                        add a                           ;
                        ret z                           ;
                        ld c,a                          ;
                        ld a,0e0h                       ;
                        call c,push_buffer              ;
                        ld a,c                          ;
                        rrca                            ;
                        or a
                        ret                             ; ZF=0

; -------------------- kb interrupt ----------------
kb_int1ms:
                        ld hl,kb_buffer                 ;
                        in a,(1)                        ;
                        rrca                            ;
                        rrca                            ;
                        rrca                            ;
                        jr nc,kb_nocmd1                 ;

                        xor a                           ; clear buffers
                        ld (hl),a                       ;
                        inc hl                          ;
                        ld (hl),a                       ;
                        dec hl                          ;
                        ld a,2                          ;
                        out (1),a                       ;
                        xor a                           ;
                        out (1),a                       ; flush kb data
                        ld (typematic_usb_code),a       ; remove typematic

param:                  ld a,0fah                       ; ack
                        call push_buffer                ;

                        ld a,(kb_last_cmd)              ;
                        cp 0edh                         ;
                        jr nz,noed_param                ;

                        in a,(8)                        ; cmd ED param
                        rra                             ;
                        jr nc,$+4                       ;
                        or 4                            ;
                        and 7                           ;
                        or 80h                          ;
                        ld (set_report_data+4),a        ;
                        jr kb_nocmd                     ;

noed_param:             cp 0f0h                         ;
                        jr nz,nof0_param                ;

                        in a,(8)                        ; cmd F0 param
                        or a                            ;
                        jr nz,no0                       ;
                        ld a,(scancodeset+1)            ;
                        add a,256-high ps2set2tbl       ;
                        ld a,0                          ;
                        adc a,1                         ;
                        jr pushbyte                     ; send current scancode set
no0:                    dec a                           ;
                        ld a,high ps2set1tbl            ;
                        jr z,$+4                        ;
                        ld a,high ps2set2tbl            ;
                        ld (scancodeset+1),a            ;
                        jr kb_nocmd                     ;

nof0_param:             in a,(8)                        ; read cmd
                        ld (kb_last_cmd),a              ;
                        cp 0edh                         ;
                        jr z,kb_nocmd1                  ; cmd ED wait for parameter
                        cp 0f0h                         ;
                        jr z,kb_nocmd1                  ; cmd F0 wait for parameter
                        cp 0ffh                         ;
                        jr nz,nocmd_ff                  ;

                        ld a,80h                        ; cmd FF
                        ld (set_report_data+4),a        ; clear leds
                        ld a,high ps2set2tbl            ; set default scancode set2
                        ld (scancodeset+1),a            ;
                        ld a,0aah                       ;
                        jr pushbyte                     ; BAT code 0AAh

nocmd_ff:               cp 0f2h                         ;
                        jr nz,kb_nocmd                  ;

                        ld a,0abh                       ; cmd F2
                        call push_buffer                ;
                        ld a,083h                       ;
pushbyte:               call push_buffer                ;

kb_nocmd:               xor a                           ;
                        ld (kb_last_cmd),a              ;
kb_nocmd1:              in a,(1)                        ;
                        rrca                            ;
                        rrca                            ;
                        rrca                            ;
                        rrca                            ;
                        ret c                           ; busy
                        call pop_buffer                 ;
                        jr c, nokeys                    ; empty
                        out (8),a                       ;
; process typematic
nokeys:                 ld a,(typematic_usb_code)       ;
                        or a                            ;
                        ret z                           ; no typematic key
                        ld b,a                          ;
                        ld a,(typematic_delay)          ;
                        inc a                           ;
                        cp TYP_DELAY                    ;
                        jr c,typready                   ;
                        sub TYP_RATE                    ;
typready:               ld (typematic_delay),a          ;
                        ret c                           ;
                        call getfree_buffer             ;
                        cp 3                            ;
                        ret c                           ; no space
                        ld a,b                          ;
                        jp pushps2                      ;

; ----- these procedures are patched with the port address taken from the dev structure DEV[2]
incmd:                  in a,(USB0_PORT+1)              ;
                        ret                             ;
outcmd:                 out (USB0_PORT+1),a             ;
                        ret                             ;
indata:                 in a,(USB0_PORT)                ;
                        ret                             ;
outdata:                out (USB0_PORT),a               ;
                        ret                             ;


; ------------- push circular buffer ------------------
push_buffer:            ; HL=circular buffer, A=data. Do not check if buffer full. Keeps HL, BC, DE, (not A);
                        push de                         ;
                        inc hl                          ;
                        ld e,(hl)                       ;
                        ld d,0                          ;
                        ex de,hl                        ;
                        add hl,de                       ;
                        inc hl                          ;
                        ld (hl),a                       ;
                        ex de,hl                        ;
                        ld a,(hl)                       ;
                        inc a                           ;
                        and BUFSIZE-1                   ; CF=0
                        ld (hl),a                       ; increment write pos
                        dec hl                          ;
                        pop de                          ;
                        ret                             ;

; ------------- pop circular buffer ------------------
pop_buffer:             ; HL=circular buffer. Returns A=result, CF on buffer empty, keeps HL, BC DE;
                        ld a,(hl)                       ;
                        inc hl                          ;
                        cp (hl)                         ;
                        dec hl                          ;
                        scf                             ;
                        ret z                           ; nothing in buffer
                        push de                         ;
                        ld d,0                          ;
                        ld e,a                          ;
                        inc a                           ;
                        and BUFSIZE-1                   ; CF=0
                        ld (hl),a                       ;
                        ex de,hl                        ;
                        add hl,de                       ;
                        inc hl                          ;
                        inc hl                          ;
                        ld a,(hl)                       ;
                        ex de,hl                        ;
                        pop de                          ;
                        ret                             ;

; ------------- get free size from circular buffer ------------------
getfree_buffer:         ; HL=circular buffer. Returns A=free bytes;
                        ld a,(hl)                       ;
                        inc hl                          ;
                        sub (hl)                        ;
                        dec hl                          ;
                        add BUFSIZE-1                   ;
                        and BUFSIZE-1                   ;
                        ret                             ;

; ----------------------- Data
setaddr_setup:          db 02dh,00h,10h                 ; Out token
                        db 0c3h,00h,05h,01h,00h,00h,00h,00h,00h,0ebh,25h ; Out Data0 for SetAddress 1
in_addr0_endp0:         db 69h,00h,10h                  ;
in_addr1_endp0:         db 69h,01h,0e8h                 ;
in_addr1_endp1:         db 69h,81h,58h                  ;
get_conf_desc:          db 2dh,01h,0e8h                 ; Out token
                        db 0c3h,80h,06h,00h,02h,00h,00h,22h,00h,0b0h,0f4h ; requests 34bytes
ack:                    db 0d2h                         ;
set_conf1:              db 2dh,01h,0e8h                 ; Out token
                        db 0c3h,00h,09h,01h,00h,00h,00h,00h,00h,27h,25h;
out_null:               db 0e1h,01h,0e8h                ;
                        db 4bh,00h,00h                  ;
set_report:             db 2dh,01h,0e8h                 ;
                        db 0c3h,21h,09h,00h,02h,00h,00h,01h,00h,9dh,70h;
set_report_data:        db 0e1h,01h,0e8h                ;
                        db 4bh,00h,40h,0bfh             ;
led_crc:                db 40h,0bfh, 81h,7fh, 0c1h,7eh, 0h,0beh, 41h,7ch, 80h,0bch, 0c0h,0bdh, 1h,7dh;

dev0:
                        db 0                            ; state
                        db 0                            ; index
                        db USB0_PORT                    ; port
                        db 0                            ; 0=no dev, 1=kb, 2=mouse
                        db 0                            ; report packet size from endpoint descriptor
dev1:
                        db 0                            ; state
                        db 1                            ; index
                        db USB1_PORT                    ; port
                        db 0                            ; 0=no dev, 1=kb, 2=mouse
                        db 0                            ; report packet size from endpoint descriptor

mouse_buffer:           db 0                            ; read pos
                        db 0                            ; write pos
                        loop BUFSIZE                    ;
                          db 0                          ; circular queue
                          lend                          ;

kb_buffer:              db 0                            ; read pos
                        db 0                            ; write pos
                        loop BUFSIZE                    ;
                          db 0                          ;
                          lend                          ; circular queue

typematic_usb_code:     db 0                            ;
typematic_delay:        db 0                            ;
kb_last_cmd             db 0                            ;

kb_pressed:             db 0                            ; pressed keys buffer, USB codes
                        db 0                            ;
                        loop BUFSIZE                    ;
                          db 0                          ;
                          lend                          ;

ps2set1tbl:             db 0h,0,0,0,01eh,030h,2eh,20h,12h,21h,22h,23h,17h,24h,25h,26h;
                        db 32h,31h,18h,19h,10h,13h,1fh,14h,16h,2fh,11h,2dh,15h,2ch,2,3;
                        db 4,5,6,7,8,9,0ah,0bh,1ch,1,0eh,0fh,39h,0ch,0dh,1ah;
                        db 1bh,2bh,2bh,27h,28h,29h,33h,34h,35h,3ah,3bh,3ch,3dh,3eh,3fh,40h;
                        db 41h,42h,43h,44h,57h,58h,0b7h,46h,0c6h,0d2h,0c7h,0c9h,0d3h,0cfh,0d1h,0cdh;
                        db 0cbh,0d0h,0c8h,45h,0b5h,37h,4ah,4eh,9ch,4fh,50h,51h,4bh,4ch,4dh,47h;
                        db 48h,49h,52h,53h,56h,0ddh,0deh,59h,64h,65h,66h,67h,68h,69h,6ah,6bh;
                        db 6ch,6dh,6eh,76h,0,0,0,0,0,0,0,0,0,0,0,0;
                        db 0,0,0,0,0,7eh,0,73h,70h,7dh,79h,7bh,5ch,0,0,0;
                        db 0,0,78h,77h,76h,0,0,0,0,0,0,0,0,0,0,0;
                        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;
                        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;
                        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;
                        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;
                        db 1dh,2ah,38h,0dbh,9dh,36h,0b8h,0dch,0,0,0,0,0,0,0,0;
                        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;

ps2set2tbl:             db 0,0,0,0,1ch,32h,21h,23h,24h,2bh,34h,33h,43h,3bh,42h,4bh;
                        db 3ah,31h,44h,4dh,15h,2dh,1bh,2ch,3ch,2ah,1dh,22h,35h,1ah,16h,1eh;
                        db 26h,25h,2eh,36h,3dh,3eh,46h,45h,5ah,76h,66h,0dh,29h,4eh,55h,54h;
                        db 5bh,5dh,5dh,4ch,52h,0eh,41h,49h,4ah,58h,05h,06h,04h,0ch,03h,0bh;
                        db 83h,0ah,01h,09h,78h,07h,0fch,7eh,0feh,0f0h,0ech,0fdh,0f1h,0e9h,0fah,0f4h;
                        db 0ebh,0f2h,0f5h,77h,0cah,7ch,7bh,79h,0dah,69h,72h,7ah,6bh,73h,74h,6ch;
                        db 75h,7dh,70h,71h,61h,0afh,0b7h,0fh,08h,10h,18h,20h,28h,30h,38h,40h;
                        db 48h,50h,57h,5fh,0,0,0,0,0,0,0,0,0,0,0,0;
                        db 0,0,0,0,0,6dh,0,51h,13h,6ah,64h,67h,27h,0,0,0;
                        db 0,0,63h,62h,5fh,0,0,0,0,0,0,0,0,0,0,0;
                        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;
                        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;
                        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;
                        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;
                        db 14h,12h,11h,9fh,94h,59h,91h,0a7h,0,0,0,0,0,0,0,0;
                        db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;

recbuf:                 loop 16                         ;
                        db 0                            ;
                        lend                            ;
end2:


output_bin            "firmware.bin",$0,end2            ;;



