;***************************
; >-< G Soft  Creations >-<
;---------------------------
;   AT Keyboard Routines
;***************************

;----------------
; RAM Usage:
;----------------
; Keyboard vars
.equ    KB_OK,          0x45
.equ    KB_DATA,        0x46
.equ    KB_STATS,       0x47
.equ    KB_SCAN,        0x48
.equ    KB_TEMP,        0x49    ; For the da*n pause key

;------------------
; Defines
;------------------
;XXXXXXXXXXXXXXXXXXXXXXXX KeyBoard XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
.flag   KEYB_DATA,      P1.5    ; AT Keyboard data  (change this for your hardware)
.flag   KEYB_CLOCK,     P3.2    ; AT Keyboard Clock (change this for your hardware)
; This 2 pins should be connected to the keyboard crresponded pins with an 4K7 Pullup.
; The keyboard also needs GND and +5V. 

.equ    KB_LSHIFT,      0x12
.equ    KB_RSHIFT,      0x59
.equ    KB_CTRL,        0x14
.equ    KB_ALT,         0x11
.equ    KB_CAPS,        0x58
.equ    KB_SCROLL,      0x7E
.equ    KB_NUML,        0x77
.equ    KB_TAB,         0x0D
.equ    KB_REL,         0xF0
.equ    KB_EXT,         0xE0
.equ    KB_PAUSE,       0xE1

; For the stauts flags (KB_STATS)
.equ    CAPS,           0x01
.equ    NUML,           0x02
.equ    SCROLL,         0x04
.equ    SHIFT,          0x08
.equ    ACK,            0x10
.equ    CTRL,           0x20
.equ    ALT,            0x40
.equ    RELEASE,        0x80
; For the stauts flags 2 (KB_OK)
.equ    PAUSED,         0x40
.equ    EXTENDED,       0x80
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

;---------------------
; Keyboard Routines:
;---------------------
;*************************************
; WaitKB: Wait for keypress
;*************************************
WaitKB:
        acall   Check_Keyb
        jnc     WaitKB
        ret
;*************************************
; InitKeyb: c=1 if ACK OK
;*************************************
CheckACK:
        mov     a, KB_STATS
        mov     c, acc.4
        clr     acc.4
        mov     KB_STATS, a
        ret
;*************************************
; InitKeyb:
;*************************************
InitKeyb:
        mov     KB_TEMP, #0
        mov     KB_OK, #0
        mov     r1, #0xFF
        acall   Write_Keyb
        acall   Check_Keyb
        acall   CheckACK
        jnc     InitKeyb

        mov     r1, #0xF4       ; Enable
        acall   Write_Keyb
        acall   Check_Keyb
        acall   CheckACK
        jnc     KeybErr

        mov     r1, #0xF3       ; Set Typematic
        acall   Write_Keyb
        acall   Check_Keyb
        acall   CheckACK
        jnc     KeybErr
        mov     r1, #0x00       ; Typematic = 250 ms / 30 cps
        acall   Write_Keyb
        acall   Check_Keyb
        acall   CheckACK
        jnc     KeybErr

        mov     KB_OK, #1
        mov     KB_STATS, #2    ; Num Lock ON

;*************************************
; Keyb_Leds: Set KB_STATS as leds
;*************************************
Keyb_Leds:
        mov     r1, #0xED       ; Set Leds
        acall   Write_Keyb
        acall   Check_Keyb
        acall   CheckACK
        jnc     KeybErr
        mov     r1, KB_STATS
        acall   Write_Keyb
        acall   Check_Keyb
        acall   CheckACK
KeybErr:
        ret

;*************************************
; Zero2One: Wait for 0 to 1 on kb
; clock line, read the kb data line
; and shift right the bit to acc.7
;*************************************
Zero2One:
        jnb     KEYB_CLOCK, *
        jb      KEYB_CLOCK, *
        mov     c, KEYB_DATA
        rrc     a
        ret

;*************************************
; Check_Keyb: Check to see if any key
; are pressed or release, returns
; ASCII codes on KB_DATA, or 1 for
; special keys, 2 for same special
; with shift. Return also the scan
; code on KB_SCAN.
; Special Keys are basicaly all non
; printable keys. See the table below
; all 1 and 2 returned are special keys
;*************************************
Check_Keyb:
        setb    KEYB_DATA
        setb    KEYB_CLOCK              ; CLOCK & DATA high = Idle Pos
        mov     r0, #50                 
CheckAgain:
        jnb     KEYB_CLOCK, KeyHit
        djnz    r0, CheckAgain          ; check r0 times
        sjmp    KeyEnd
KeyHit:
        jnb     KEYB_DATA, KeyHit2      ; Start bit must be 0
KeyEnd:
        clr     KEYB_CLOCK              ; disable keyb
        clr     c                       ; c=0 = no keypress
        ret
KeyHit2:
        mov     r0, #8          ; 8 bits
        clr     a
KeyHit3:
        acall   Zero2One
        djnz    r0, KeyHit3
        mov     r1, a

        clr     a
        acall   Zero2One        ; Parity bit
        acall   Zero2One        ; Stop bit
        ; acc.7 = stop, acc.6 = parity
        clr     KEYB_CLOCK

        mov     a, KB_TEMP
        jz      NoIgnore        
        dec     KB_TEMP         ; Igonre pause scans
        sjmp    ChkKbEndNC

NoIgnore:
        mov     KB_SCAN, r1

        cjne    r1, #0xFA, NoKbACK
        orl     KB_STATS, #ACK
        sjmp    ChkKbEndNC

NoKbACK:
        cjne    r1, #KB_PAUSE, NoKbPause
        mov     KB_TEMP, #7             ; Ignore next 7 scans
        mov     a, KB_OK
        cpl     acc.6
        mov     KB_OK, a
        sjmp    ChkKbEndNC

NoKbPause:
        cjne    r1, #KB_EXT, NoKbExt
        orl     KB_OK, #EXTENDED
        sjmp    ChkKbEndNC

NoKbExt:
        cjne    r1, #KB_REL, NoRelease
        orl     KB_STATS, #RELEASE
        sjmp    ChkKbEndNC

NoRelease:
; Test Num lock, if pressed toggle led
        cjne    r1, #KB_NUML, NoNumLock
        mov     a, KB_STATS
        jnb     acc.7, ChkKbEndNC
        cpl     acc.1
        clr     acc.7
        mov     KB_STATS, a
        acall   Keyb_Leds
        sjmp    ChkKbEndNC

NoNumLock:
; Test Caps lock, if pressed toggle led
        cjne    r1, #KB_CAPS, NoCapsLock
        mov     a, KB_STATS
        jnb     acc.7, ChkKbEndNC
        cpl     acc.2
        clr     acc.7
        mov     KB_STATS, a
        acall   Keyb_Leds
        sjmp    ChkKbEndNC

NoCapsLock:
; Test Scroll lock, if pressed toggle led
        cjne    r1, #KB_SCROLL, NoScrollLock
        mov     a, KB_STATS
        jnb     acc.7, ChkKbEndNC
        cpl     acc.0
        clr     acc.7
        mov     KB_STATS, a
        acall   Keyb_Leds
ChkKbEndNC:
        clr     c
        ret

NoScrollLock:
; Test L & R shifts, set bit if pressed, clear on release
        cjne    r1, #KB_LSHIFT, NoShift1
ShiftOK:
        mov     a, KB_STATS
        jbc     acc.7, ShiftRel
        setb    acc.3           ; not releasing, so Set SHIFT bit
        sjmp    ShiftEnd
ShiftRel:
        clr     acc.3           ; releasing, so Clear SHIFT bit
ShiftEnd:
        mov     KB_STATS, a
        sjmp    ChkKbEndNC      
NoShift1:
        cjne    r1, #KB_RSHIFT, NoShift
        sjmp    ShiftOK

NoShift:
        cjne    r1, #KB_CTRL, NoCtrl
        mov     a, KB_STATS
        jbc     acc.7, CtrlRel
        setb    acc.5           ; not releasing, so Set CTRL bit
        sjmp    CtrlEnd
CtrlRel:
        clr     acc.5           ; releasing, so Clear SHIFT bit
CtrlEnd:
        mov     KB_STATS, a
        sjmp    ChkKbEndNC      

NoCtrl:
        cjne    r1, #KB_ALT, NoAlt
        mov     a, KB_STATS
        jbc     acc.7, AltRel
        setb    acc.6           ; not releasing, so Set ALT bit
        sjmp    AltEnd
AltRel:
        clr     acc.6           ; releasing, so Clear ALT bit
AltEnd:
        mov     KB_STATS, a
        sjmp    ChkKbEndNC      

NoAlt:
        mov     a, KB_STATS             ; Releasing key test
        jnb     acc.7, NoRel2           
        clr     acc.7                   ; if releasing > clear
        mov     KB_STATS, a             ; rel bit on KB_STATS
        clr     c                       ; and do nothing 
        ret
NoRel2:
        mov     a, KB_OK                ; Extended key test
        jnb     acc.7, KbChars           
        clr     acc.7                   ; if Extended > clear
        mov     KB_OK, a                ; EXT bit on KB_OK
        clr     c                       ; and do nothing 
        ret
KbChars:
        mov     dptr, #KbScanCodes
        mov     a, KB_STATS
        jnb     acc.2, TestShift
        jb      acc.3, KbChkOK
        mov     a, r1
        movc    a, @a+dptr
        mov     r0, a
        subb    a, #97
        jc      KbChkOK
        mov     a, r0
        subb    a, #123
        jnc     KbChkOK
        mov     dptr, #KbScanCodes2     ; if (a to z) & Caps > table 2
        sjmp    KbChkOK
TestShift:
        jnb     acc.3, KbChkOK
        mov     dptr, #KbScanCodes2     ; with shift table 2
KbChkOK:
        mov     a, r1
        movc    a, @a+dptr
        mov     KB_DATA, a
        setb    c
        ret

;*************************************
; Zero2One2: Wait for high to low in
; kb clock line
;*************************************
Zero2One2:
        jnb     KEYB_CLOCK, *
        jb      KEYB_CLOCK, *
        ret
;*************************************
; Write_Keyb: Send r1 to the kb
;*************************************
Write_Keyb:
        mov     r0, #8          ; 8 bits to receive
        clr     KEYB_CLOCK      ; break the Keyboard
        mov	r7, #0x00       ; some delay (safety reasons)
_WKwait:djnz	r7, _WKwait
        clr     KEYB_DATA       ; request to send
        setb    KEYB_CLOCK      ; enable the Keyboard
        acall   Zero2One2       ; Start Bit
        mov     a, r1           ; Data Bits
TxData:
        rrc     a
        mov     KEYB_DATA, c
        acall   Zero2One2
        djnz    r0, TxData

        mov     a, r1           ; calculate parity bit
        mov     c, psw.0        ; this is Even parity
        cpl     c               ; and Keyboard needs Odd parity
        mov     KEYB_DATA, c    ; send parity bit
        acall   Zero2One2

        setb    KEYB_DATA       ; send stop bit
        acall   Zero2One2

        acall   Zero2One2
        mov     c, KEYB_DATA    ; get ACK bit
        clr     KEYB_CLOCK      ; stop the keyboard     
        ret


;*************************************************************************
; Main Routine: Example of kb usage
;*************************************************************************
Start:
        acall   InitKeyb

        mov     a, KB_OK
        jz      KBNotOK

Loop:
        acall   Check_Keyb
        jnc     Loop

        mov     a, KB_SCAN
        cjne    a, #0x71, NoCtrlAltDel  ; Check for extended Del
        mov     a, KB_STATS
        jnb     acc.5, NoCtrlAltDel     ; Check for Ctrl
        jnb     acc.6, NoCtrlAltDel     ; Check for Alt
        ljmp    Start                   ; If Ctrl+Alt+Del Reset

NoCtrlAltDel:
        mov     a, KB_DATA
        cjne    a, #1, ChkEsp2
        ; Here handle Especial Keys
        mov     a, KB_SCAN
        cjne    a, #5, NoF1Key

        ; Do something here for F1

NoF1Key:
        cjne    a, #6, NoF2Key

        ; Do something here for F2

NoF2Key:
        cjne    a, #4, NoF3Key

        ; Do something here for F1

NoF3Key:
        sjmp    Loop
ChkEsp2:
        cjne    a, #2, NoEsp
        ; Here handle Shift + Especial Keys
        mov     a, KB_SCAN
        cjne    a, #5, NoShfF1Key

        ; Do something here for Shift + F1

NoShfF1Key:
        sjmp    Loop
NoEsp:

        ; Rest of prgram

        sjmp    Loop




; last 262 addr of code mem with scan codes tables
.org    0x1EF8
KbScanCodes:
; Keyboard Scancodes
;       ?,  F9, ?,  F5,  F4,  F1,  F2, F12, ?, F10,  F8,  F6,  F4, TAB,  ~
.db     0,  1 , 0,  1 ,  1 ,  1 ,  1 ,  1 , 0,  1 ,  1 ,  1 ,  1 ,  1 , '~'
;       ?, ?,Lalt,Lshf, ?,Lctr,  Q ,  ! , ?, ?, ?,  Z ,  S ,  A ,  W ,  @
.db     0, 0,  0 ,  0 , 0,  0 , 'q', '1', 0, 0, 0, 'z', 's', 'a', 'w', '2'
;       ?, ?,  C ,  X ,  D ,  E ,  $ ,  # , ?, ?, " ",  V ,  F ,  T ,  R
.db     0, 0, 'c', 'x', 'd', 'e', '4', '3', 0, 0, ' ', 'v', 'f', 't', 'r'
;       % , ?, ?,  N ,  B ,  H ,  G ,  Y ,  ^ , ?, ?, ?,  M ,  J ,  U ,  &
.db    '5', 0, 0, 'n', 'b', 'h', 'g', 'y', '6', 0, 0, 0, 'm', 'j', 'u', '7'
;       * , ?, ?,  < ,  K ,  I ,  O ,  ) ,  ( , ?, ?,  > ,  ? ,  L ,  : ,  P
.db    '8', 0, 0, ',', 'k', 'i', 'o', '0', '9', 0, 0, '.', '/', 'l', ';', 'p'
;       _ , ?, ?, ?,  " , ?,  { ,  + , ?, ?,Caps,Rshf,Entr,  } , ?,  | 
.db    '-', 0, 0, 0, ''', 0, '[', '=', 0, 0,  0 ,  0 ,  1 , ']', 0,  92
;       ?, ?, ?, ?, ?, ?, ?, ?,BkSp, ?, ?,  1 , ?,  4 ,  7 , ?, ?, ?,  0
.db     0, 0, 0, 0, 0, 0, 0, 0,  1 , 0, 0, '1', 0, '4', '7', 0, 0, 0, '0'
;       . ,  2 ,  5 ,  6 ,  8 , ESC,Numl, F11,  + ,  3 ,  - ,  * ,  9 ,Scrl
.db    '.', '2', '5', '6', '8',  1 ,  0 ,  1 , '+', '3', '-', '*', '9',  0
;       ?, ?, ?, ?,  F7
.db     0, 0, 0, 0,  1

KbScanCodes2:
; Keyboard Scancodes with shift
;       ?,  F9, ?,  F5,  F4,  F1,  F2, F12, ?, F10,  F8,  F6,  F4, TAB,  ~
.db     0,  2 , 0,  2 ,  2 ,  2 ,  2 ,  2 , 0,  2 ,  2 ,  2 ,  2 ,  2 , '`'
;       ?, ?,Lalt,Lshf, ?,Lctr,  Q ,  ! , ?, ?, ?,  Z ,  S ,  A ,  W ,  @
.db     0, 0,  0 ,  0 , 0,  0 , 'Q', '!', 0, 0, 0, 'Z', 'S', 'A', 'W', '@'
;       ?, ?,  C ,  X ,  D ,  E ,  $ ,  # , ?, ?, " ",  V ,  F ,  T ,  R
.db     0, 0, 'C', 'X', 'D', 'E', '$', '#', 0, 0, ' ', 'V', 'F', 'T', 'R'
;       % , ?, ?,  N ,  B ,  H ,  G ,  Y ,  ^ , ?, ?, ?,  M ,  J ,  U ,  &
.db    '%', 0, 0, 'N', 'B', 'H', 'G', 'Y', '^', 0, 0, 0, 'M', 'J', 'U', '&'
;       * , ?, ?,  < ,  K ,  I ,  O ,  ) ,  ( , ?, ?,  > ,  ? ,  L ,  : ,  P
.db    '*', 0, 0, '<', 'K', 'I', 'O', ')', '(', 0, 0, '>', '?', 'L', ':', 'P'
;       _ , ?, ?, ?,  " , ?,  { ,  + , ?, ?,Caps,Rshf,Entr,  } , ?,  | 
.db    '_', 0, 0, 0, '"', 0, '{', '+', 0, 0,  0 ,  0 ,  2 , '}', 0, '|'
;       ?, ?, ?, ?, ?, ?, ?, ?,BkSp, ?, ?,  1 , ?,  4 ,  7 , ?, ?, ?,  0
.db     0, 0, 0, 0, 0, 0, 0, 0,  2 , 0, 0, '1', 0, '4', '7', 0, 0, 0, '0'
;       . ,  2 ,  5 ,  6 ,  8 , ESC,Numl, F11,  + ,  3 ,  - ,  * ,  9 ,Scrl
.db    '.', '2', '5', '6', '8',  2 ,  0 ,  2 , '+', '3', '-', '*', '9',  0
;       ?, ?, ?, ?,  F7
.db     0, 0, 0, 0,  2
