; =============================================================================
;
;                           Litos8 keyboard driver
;
; =============================================================================

; -----------------------------------------------------------------------------
; Exported user functions (42):
;	SetLang - set language
;	GetLang - get current language
;	TestChar - test if some character is ready
;	GetChar - get character without waiting
;	GetCharWait - get character with waiting
;	TestKey - test if some key is ready
;	GetKey - get key without waiting
;	GetKeyWait - get key with waiting
;	KeyIsModi - check if key is modifier
;	TestKeyModi - test if some key is ready (incl. modifiers Ctrl,Alt...)
;	GetKeyModi - get key without waiting (including modifiers Ctrl, Alt...)
;	GetKeyModiWait - get key with waiting (including modifiers Ctrl,Alt...)
;	KeyGetModi - get keyboard modifiers
;	KeySetModi - set keyboard modifiers (and LEDs)
;	KeyFlush - flush keyboard buffer
;	IsScrollLock - check Scroll Lock state
;	IsNumLock - check Num Lock state
;	IsCapsLock - check Caps Lock state
;	IsInsert - check Insert state
;	IsPause - check Pause state
;	KeySetRate - set keyboard rate
;	ReturnKey - return key into keyboard buffer ring (as first key)
;	SetKey - write key into keyboard buffer ring (as last key)
;	GetKeyMap - get bitmap of pressed keys
;	KeyPressed - test if key is pressed
;	CtrlPressed - test if left or right Ctrl is pressed
;	LCtrlPressed - test if left Ctrl is pressed
;	RCtrlPressed - test if right Ctrl is pressed
;	AltPressed - test if left or right Alt is pressed
;	LAltPressed - test if left Alt is pressed
;	RAltPressed - test if right Alt is pressed
;	ShiftPressed - test if left or right Shift is pressed
;	LShiftPressed - test if left Shift is pressed
;	RShiftPressed - test if right Shift is pressed
;	ScanFromKey - get base scan code from key code
;	ExtFromKey - get extended scan code from key code
;	ModiFromKey - get modifiers from key code
;	ScanModiFromKey - get extended scan code with modifiers from key code
;	CharFromKey - get character code from key code
;	SetTimer2Gate - set Timer 2 gate
;	GetTimer2Out - get Timer 2 output
;	SetSpeakerGate - set speaker gate
; -----------------------------------------------------------------------------

KEY_IRQ		EQU	1		; IRQ for keyboard

KEYDATA		EQU	60h		; data port, read scan code of key,
					;		bit 7: release key
					;	If PB7=1: read switches
					; write commands to keyboard:
					;	0edh: set LEDs, follows:
					;		B0: Scroll Lock is ON
					;		B1: Num Lock is ON
					;		B2: Caps Lock is ON
					;	0eeh: send echo 0eeh back
					;	0f3h: set key rate, follows:
					;		B0..4: rate
					;		   0->30 ... 1fh->2.0
					;		B5,B6: delay
					;		   0->250ms ... 3->1sec
					;		B7: must be 0
					;	0f4H: enable keyboard
					;	0f5h: disable keyboard
					;	0f6h: set default params,
					;		continue tests
					;	0feh: repeat last data
					;	0ffh: keyboard reset

KEYCTRLB	EQU	61h		; control port B, read and write:
					;	B0: timer 2 gate 1=ON, 0=OFF
					;	B1: speaker gate 1=ON, 0=OFF
					;	B2: 0=read spare key (port C)
					;	    1=read r/w memory size
					;	B3: LED 1=ON, 0=OFF
					;	B4: mem.par.check 1=OFF,0=ON
					;	B5: I/O check 1=OFF,0=ON
					;	B6: keyb.clock 1=enab,0=dis
					;	B7: 1=clear keyb, read SW1

KEYCTRL		EQU	64h		; control port, write:
					; 	20h: send controller's command
					;		byte to 60h
					;	60h: next byte written to port
					;		60h sets contr. command
					;	0adh: disable keyboard
					;	0aeh: enable keyboard
					; status port, read:
					;	B0: 0=can read data from 60h 
					;	B1: 0=can write to port 64h

; ------------- macro - check if key is pressed (sets NZ = key is pressed)
; Clears CF (-> NC).

%macro		KEYISPRESSED 1
		test	byte [KeyMap + (%1 >> 3)],(1 << (%1 & 7))
%endmacro

		CODEINIT_SECTION

; -----------------------------------------------------------------------------
;    INIT: Initialize keyboard driver (display should be initialized first)
; -----------------------------------------------------------------------------
; INPUT:	AL = required language (LANG_EN, LANG_CZ)
; -----------------------------------------------------------------------------

; ------------- set keyboard modifiers (and update keyboard layout)

KeyInit:	mov	byte [KeyLayoutSel],255 ; invalid keyboard layout
		mov	byte [Language],255	; invalid language
		mov	byte [KeyModi],KEY_MODI_NUM ; Num Lock is ON
		call	SetLang		; set language

; ------------- set keyboard rate (to maximum)

		xor	ax,ax		; AX <- 0
		call	KeySetRate	; set keyboard rate

; ------------- install INT 09h interrupt handler

		mov	ax,Int09	; AX <- handler address
		mov	cl,9		; CL <- number of Int 09h
		call	SysSetInt	; set interrupt handler
		ret


		CODE_SECTION

; -----------------------------------------------------------------------------
;                      Reinitialize keyboard driver
; -----------------------------------------------------------------------------

KeyReinit:	push	ax		; push AX
		push	cx		; push CX

		mov	ax,Int09	; AX <- handler address
		mov	cl,9		; CL <- number of Int 09h
		call	SysSetInt	; set interrupt handler

		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                           Set Timer 2 gate
; -----------------------------------------------------------------------------
; INPUT:	AL = Timer 2 gate 1=enable, 0=disable
; NOTES:	It temporaly disables interrupts.
;		It takes aprox. 3 us.
; -----------------------------------------------------------------------------

; ------------- push registers

SetTimer2Gate:	pushf			; push flags
		push	ax		; push AX
		cli			; disable interrupts

; ------------- set Timer 2 gate

		mov	ah,al		; AH <- required flag
		in	al,KEYCTRLB	; AL <- get keyboard controller B
		and	al,~B0		; disable old flag
		or	al,ah		; AL <- set new flag
		out	KEYCTRLB,al	; set speaker gate

; ------------- pop registers

		pop	ax		; pop AX
		popf			; pop flags
		ret

; -----------------------------------------------------------------------------
;                           Get Timer 2 output
; -----------------------------------------------------------------------------
; OUTPUT:	AL = Timer 2 output (0 and CY, 1 and NC)
;		CY = output is 0 (AL = 0)
; NOTES:	Output from Timer 2 must be enabled using SetTimer2Gate.
;		It takes aprox. 1.5 us.
; -----------------------------------------------------------------------------

GetTimer2Out:	in	al,KEYCTRLB	; AL <- get keyboard controller B
		rol	al,1		; B5 -> B6
		rol	al,1		; B5 -> B7
		rol	al,1		; B5 -> B0
		and	al,B0		; AL <- Timer 2 output
		cmp	al,1		; set CY if AL = 0
		ret

; -----------------------------------------------------------------------------
;                           Set speaker gate
; -----------------------------------------------------------------------------
; INPUT:	AL = speaker gate 1=enable, 0=disable
; NOTES:	It temporaly disables interrupts.
;		It takes aprox. 3 us.
; -----------------------------------------------------------------------------

; ------------- push registers

SetSpeakerGate:	pushf			; push flags
		push	ax		; push AX
		cli			; disable interrupts

; ------------- set speaker gate

		shl	al,1		; AL <- flag to B1 position
		mov	ah,al		; AH <- required flag
		in	al,KEYCTRLB	; AL <- get keyboard controller B
		and	al,~B1		; disable old flag
		or	al,ah		; AL <- set new flag
		out	KEYCTRLB,al	; set speaker gate

; ------------- pop registers

		pop	ax		; pop AX
		popf			; pop flags
		ret

; -----------------------------------------------------------------------------
;                          Set language
; -----------------------------------------------------------------------------
; INPUT:	AL = new language (LANG_EN, LANG_CS)
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

; ------------- push registers

SetLang:	pushf			; push flags
		push	ax		; push AX
		cli			; disable interrupts

; ------------- check if language is changed

		cmp	al,[Language]	; language changed?
		je	short SetLang9	; language not changed
		mov	[Language],al	; store new language

; ------------- set Scroll Lock 

		mov	ah,[KeyModi]	; AH <- current modifiers
		and	ah,~B0		; AH <- clear Scroll Lock flag
		and	al,B0		; AL <- new Scroll Lock flag
		or	al,ah		; AL <- new modifiers
		mov	[KeyModi],al	; set new modifiers
		call	UpdateKeyModi	; update modifiers

; ------------- update fonts

		call	UpdateTextFont	; update fonts

; ------------- pop registers

SetLang9:	pop	ax		; pop Ax
		popf			; pop flags (maybe enable interrupts)
		ret

; -----------------------------------------------------------------------------
;                          Get current language
; -----------------------------------------------------------------------------
; OUTPUT:	AL = current language (LANG_EN, LANG_CS)
; -----------------------------------------------------------------------------

GetLang:	mov	al,[Language]	; AL <- current language
		ret

; -----------------------------------------------------------------------------
;                     Set keyboard modifiers (and LEDs)
; -----------------------------------------------------------------------------
; INPUT:	AL = keyboard modifiers (KEY_MODI_SCROLL, ...)
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

KeySetModi:	mov	[KeyModi],al	; set keyboard modifiers
		call	UpdateKeyModi	; update modifiers
		push	ax		; push AX
		and	al,B0		; AL <- Scroll Lock flag
		call	SetLang		; set language
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                       Update LEDs of modifiers
; -----------------------------------------------------------------------------
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

; ------------- check, if LEDs need to be changed

UpdateKeyModi:	push	ax		; push AX
		mov	al,[KeyModi]	; AL <- keyboard modifiers
		and	al,B0+B1+B2	; mask LEDs
		cmp	al,[KeyLED]	; change?
		je	short UpdateKeyModi4 ; no change needed

; ------------- disable interupts
		
		pushf			; push flags
		cli			; disable interrupts

; ------------- send LED command to keyboard

		mov	al,0edh		; AL <- command
		call	KeySendData	; send command

		mov	al,[KeyModi]	; AL <- keyboard modifiers
		and	al,B0+B1+B2	; mask LEDs
		mov	[KeyLED],al	; save new state
		call	KeySendData	; send LED state

		mov	al,0f4h		; AL <- command to re-enable keyboard
		call	KeySendData	; send enable command

; ------------- pop registers

		popf			; pop flags (maybe enable interrupts)
UpdateKeyModi4:	pop	ax		; pop AX

; ------------- update keyboard layout

; === UpdateKeyLayout must follow !

; -----------------------------------------------------------------------------
;             Update current keyboard layout (after changing Scroll Lock)
; -----------------------------------------------------------------------------

; ------------- push registers

UpdateKeyLayout:push	ax		; push AX
		push	bx		; push BX

; ------------- prepare required layout

		mov	al,[KeyModi]	; Al <- current modifiers
		and	al,B0		; AL <- Scroll Lock state
		cmp	al,[KeyLayoutSel] ; check current layout set
		je	short UpdateKeyLay8 ; layout is set OK
		mov	[KeyLayoutSel],al ; store new current layout

; ------------- prepare descriptor -> BX

		mov	ah,KEYLAYOUT_size ; AH <- size of keyboard layout
		mul	ah		; AX <- offset of descriptor
		xchg	ax,bx		; BX <- offset of descriptor
		lea	bx,[KeyLayoutTabs+bx] ; BX <- descriptor

; ------------- opdate current structure

		mov	ax,[bx+KEYLAYOUT_LOWER] ; AX <- lower table
		mov	[KeyMapLower],ax ; set lower table
		mov	ax,[bx+KEYLAYOUT_UPPER] ; AX <- upper table
		mov	[KeyMapUpper],ax ; set upper table
		mov	ax,[bx+KEYLAYOUT_CTRL] ; AX <- control table
		mov	[KeyMapCtrl],ax ; set control table
		mov	ax,[bx+KEYLAYOUT_LOWUP] ; AX <- lower/upper table
		mov	[KeyMapLowUp],ax ; set lower/upper table
		mov	al,[bx+KEYLAYOUT_DCH1] ; AL <- dead character 1
		mov	[KeyMapDCh1],al	; set dead character 1
		mov	al,[bx+KEYLAYOUT_DCH2] ; AL <- dead character 2
		mov	[KeyMapDCh2],al	; set dead character 2
		mov	ax,[bx+KEYLAYOUT_DEAD1] ; AX <- dead table 1
		mov	[KeyMapDead1],ax ; set dead table 1
		mov	ax,[bx+KEYLAYOUT_DEAD2] ; AX <- dead table 2
		mov	[KeyMapDead2],ax ; set dead table 2
		mov	ax,[bx+KEYLAYOUT_SPEC] ; AX <- special RAlt table
		mov	[KeyMapSpec],ax	; set special RAlt table

		mov	byte [KeyDeadChar],0 ; destroy dead character
		mov	byte [KeyAltChar],0 ; no Alt char. code

; ------------- pop registers

UpdateKeyLay8:	pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                        Get bitmap of pressed keys
; -----------------------------------------------------------------------------
; OUTPUT:	BX = pointer to 256-bit system key map, 1=key is pressed
; NOTES:	Use extended scan code as index into key bitmap.
; -----------------------------------------------------------------------------

GetKeyMap:	mov	bx,KeyMap	; BX <- pointer to key map
		ret

; -----------------------------------------------------------------------------
;                        Get keyboard modifiers
; -----------------------------------------------------------------------------
; OUTPUT:	AL = keyboard modifiers (KEY_MODI_SCROLL, ...)
; -----------------------------------------------------------------------------

KeyGetModi:	mov	al,[KeyModi]	; AL <- keyboard modifiers
		ret

; -----------------------------------------------------------------------------
;                        Check Scroll Lock state
; -----------------------------------------------------------------------------
; OUTPUT:	AL = Scroll Lock state (0=OFF and CY, 1=ON and NC)
;		CY = Scroll Lock is OFF (AL = 0)
; -----------------------------------------------------------------------------

IsScrollLock:	mov	al,[KeyModi]	; AL <- keyboard modifiers
		and	al,B0		; AL <- Scroll Lock state
		cmp	al,1		; set CY flag
		ret

; -----------------------------------------------------------------------------
;                        Check Num Lock state
; -----------------------------------------------------------------------------
; OUTPUT:	AL = Num Lock state (0=OFF and CY, 1=ON and NC)
;		CY = Num Lock is OFF (AL = 0)
; -----------------------------------------------------------------------------

IsNumLock:	mov	al,[KeyModi]	; AL <- keyboard modifiers
		shr	al,1		; AL <- rotate flag to B0 position
		and	al,B0		; AL <- Num Lock state
		cmp	al,1		; set CY flag
		ret

; -----------------------------------------------------------------------------
;                        Check Caps Lock state
; -----------------------------------------------------------------------------
; OUTPUT:	AL = Caps Lock state (0=OFF and CY, 1=ON and NC)
;		CY = Caps Lock is OFF (AL = 0)
; -----------------------------------------------------------------------------

IsCapsLock:	mov	al,[KeyModi]	; AL <- keyboard modifiers
		shr	al,1
		shr	al,1		; AL <- rotate flag to B0 position
		and	al,B0		; AL <- Caps Lock state
		cmp	al,1		; set CY flag
		ret

; -----------------------------------------------------------------------------
;                        Check Insert state
; -----------------------------------------------------------------------------
; OUTPUT:	AL = Insert state (0=OFF and CY, 1=ON and NC)
;		CY = Insert is OFF (AL = 0)
; -----------------------------------------------------------------------------

IsInsert:	mov	al,[KeyModi]	; AL <- keyboard modifiers
		shr	al,1
		shr	al,1
		shr	al,1		; AL <- rotate flag to B0 position
		and	al,B0		; AL <- Insert state
		cmp	al,1		; set CY flag
		ret

; -----------------------------------------------------------------------------
;                        Check Pause state
; -----------------------------------------------------------------------------
; OUTPUT:	AL = Pause state (0=OFF and CY, 1=ON and NC)
;		CY = Pause is OFF (AL = 0)
; -----------------------------------------------------------------------------

IsPause:	mov	al,[KeyModi]	; AL <- keyboard modifiers
		shr	al,1
		shr	al,1
		shr	al,1
		shr	al,1		; AL <- rotate flag to B0 position
		and	al,B0		; AL <- Pause state
		cmp	al,1		; set CY flag
		ret

; -----------------------------------------------------------------------------
;                        Set keyboard rate
; -----------------------------------------------------------------------------
; INPUT:	AL = rate coefficient (0..31, 0->30 key/sec ... 31->2 key/sec)
;		AH = initial delay (0..3, 0->250 ms ... 3->1000 ms)
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------
					;	0f3h: set key rate, follows:
					;		B0..4: rate
					;		   0->30 ... 1fh->2.0
					;		B5,B6: delay
					;		   0->250ms ... 3->1sec
					;		B7: must be 0

; ------------- push registers and disable interupts
		
KeySetRate:	pushf			; push flags
		push	ax		; push AX
		cli			; disable interrupts

; ------------- prepare rate data

		ror	ah,1
		ror	ah,1
		ror	ah,1		; AH <- rotate delay to position B5,B6
		or	ah,al		; AH <- add rate

; ------------- send RATE command to keyboard

		mov	al,0f3h		; AL <- command
		call	KeySendData	; send command

		mov	al,ah		; AL <- rate data
		call	KeySendData	; send rate data

		mov	al,0f4h		; AL <- command to re-enable keyboard
		call	KeySendData	; send enable command

; ------------- pop registers

		pop	ax		; pop AX
		popf			; pop flags (maybe enable interrupts)
		ret

; -----------------------------------------------------------------------------
;                     Test if left Ctrl is pressed
; -----------------------------------------------------------------------------
; OUTPUT:	AL = key status, 1=pressed (NC), 0=released (CY)
;		CY = key is not pressed (AL = 0)
; -----------------------------------------------------------------------------

LCtrlPressed:	KEYISPRESSED KEY_LCTRL	; check if left Ctrl is pressed
LCtrlPressed2:	jz	short LCtrlPressed6 ; key is not pressed
LCtrlPressed4:	mov	al,1		; AL <- pressed flag
		ret

LCtrlPressed6:	mov	al,0		; AL <- 0, released flag
		stc			; set released flag
		ret

; -----------------------------------------------------------------------------
;                    Test if left or right Ctrl is pressed
; -----------------------------------------------------------------------------
; OUTPUT:	AL = key status, 1=pressed (NC), 0=released (CY)
;		CY = key is not pressed (AL = 0)
; -----------------------------------------------------------------------------

CtrlPressed:	KEYISPRESSED KEY_LCTRL	; check if left Ctrl is pressed
		jnz	short LCtrlPressed4 ; left Ctrl is pressed

; === RCtrlPressed must follow !

; -----------------------------------------------------------------------------
;                     Test if right Ctrl is pressed
; -----------------------------------------------------------------------------
; OUTPUT:	AL = key status, 1=pressed (NC), 0=released (CY)
;		CY = key is not pressed (AL = 0)
; -----------------------------------------------------------------------------

RCtrlPressed:	KEYISPRESSED KEY_RCTRL	; check if right Ctrl is pressed
		jmp	short LCtrlPressed2

; -----------------------------------------------------------------------------
;                     Test if left Alt is pressed
; -----------------------------------------------------------------------------
; OUTPUT:	AL = key status, 1=pressed (NC), 0=released (CY)
;		CY = key is not pressed (AL = 0)
; -----------------------------------------------------------------------------

LAltPressed:	KEYISPRESSED KEY_LALT	; check if left Alt is pressed
		jmp	short LCtrlPressed2

; -----------------------------------------------------------------------------
;                    Test if left or right Alt is pressed
; -----------------------------------------------------------------------------
; OUTPUT:	AL = key status, 1=pressed (NC), 0=released (CY)
;		CY = key is not pressed (AL = 0)
; -----------------------------------------------------------------------------

AltPressed:	KEYISPRESSED KEY_LALT	; check if left Alt is pressed
		jnz	short LCtrlPressed4 ; left Alt is pressed

; === RAltPressed must follow !

; -----------------------------------------------------------------------------
;                     Test if right Alt is pressed
; -----------------------------------------------------------------------------
; OUTPUT:	AL = key status, 1=pressed (NC), 0=released (CY)
;		CY = key is not pressed (AL = 0)
; -----------------------------------------------------------------------------

RAltPressed:	KEYISPRESSED KEY_RALT	; check if right Alt is pressed
		jmp	short LCtrlPressed2

; -----------------------------------------------------------------------------
;                     Test if left Shift is pressed
; -----------------------------------------------------------------------------
; OUTPUT:	AL = key status, 1=pressed (NC), 0=released (CY)
;		CY = key is not pressed (AL = 0)
; -----------------------------------------------------------------------------

LShiftPressed:	KEYISPRESSED KEY_LSHIFT ; check if left Shift is pressed
		jmp	short LCtrlPressed2

; -----------------------------------------------------------------------------
;                    Test if left or right Shift is pressed
; -----------------------------------------------------------------------------
; OUTPUT:	AL = key status, 1=pressed (NC), 0=released (CY)
;		CY = key is not pressed (AL = 0)
; -----------------------------------------------------------------------------

ShiftPressed:	KEYISPRESSED KEY_LSHIFT	; check if left Shift is pressed
		jnz	short LCtrlPressed4 ; left Shift is pressed

; === RShiftPressed must follow !

; -----------------------------------------------------------------------------
;                     Test if right Shift is pressed
; -----------------------------------------------------------------------------
; OUTPUT:	AL = key status, 1=pressed (NC), 0=released (CY)
;		CY = key is not pressed (AL = 0)
; -----------------------------------------------------------------------------

RShiftPressed:	KEYISPRESSED KEY_RSHIFT	; check if right Shift is pressed
		jmp	short LCtrlPressed2

; -----------------------------------------------------------------------------
;                         Test if key is pressed
; -----------------------------------------------------------------------------
; INPUT:	AL = key extended scan code
; OUTPUT:	AL = key status, 1=pressed (NC), 0=released (CY)
;		CY = key is not pressed (AL = 0)
; -----------------------------------------------------------------------------

; ------------- push registers

KeyPressed:	push	bx		; push BX
		push	cx		; push CX

; ------------- prepare offset in table -> BX

		xor	bx,bx		; BX <- 0
		mov	bl,al		; BL <- key code
		mov	cl,3		; CL <- number of rotations
		shr	bx,cl		; BX /= 8

; ------------- get key state -> AL

		mov	cl,al		; CL <- key code
		and	cl,7		; CL <- lowest 3 bits
		mov	al,[bx+KeyMap]	; AL <- get key state
		shr	al,cl		; AL <- shift bit on position 0
		and	al,B0		; mask bit
		cmp	al,1		; set CY of not pressed

; ------------- pop registers

		pop	cx		; pop CX
		pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                        Get base scan code from key code
; -----------------------------------------------------------------------------
; INPUT:	AX = key code
; OUTPUT:	AL = base scan code (0..7Fh)
; -----------------------------------------------------------------------------

ScanFromKey:	mov	al,ah		; AL <- key HIGH
		and	al,7fh		; AL <- mask scan code
		ret

; -----------------------------------------------------------------------------
;                       Get extened scan code from key code
; -----------------------------------------------------------------------------
; INPUT:	AX = key code
; OUTPUT:	AL = extended scan code (0..0FFh)
; -----------------------------------------------------------------------------

ExtFromKey:	or	ax,ax		; control key ?
		jns	short ScanFromKey ; base ASCII character
		shl	al,1		; CY <- extended flag
		mov	al,ah		; AL <- code HIGH
		rcl	al,1		; AL,B0 <- extended flag
		ror	al,1		; AL <- extended scan code
		ret

; -----------------------------------------------------------------------------
;                        Get modifiers from key code
; -----------------------------------------------------------------------------
; INPUT:	AX = key code
; OUTPUT:	AL = modifiers (0..7), B0=Shift, B1=Ctrl, B2=Alt
; -----------------------------------------------------------------------------

ModiFromKey:	or	ax,ax		; control key?
		jns	short ModiFromKey2 ; not control key
		and	al,KEYFLAG_MODI	; AL <- modifiers B4,B5,B6
		shr	al,1
		shr	al,1
		shr	al,1
		shr	al,1		; AL <- modifiers B0,B1,B2
		ret

ModiFromKey2:	mov	al,0		; AL <- 0, no (unkown) modifiers
		ret

; -----------------------------------------------------------------------------
;              Get extended scan code with modifiers from key code
; -----------------------------------------------------------------------------
; INPUT:	AX = key code
; OUTPUT:	AL = extended scan code (0..0FFh)
;		AH = modifiers (0..7), B0=Shift, B1=Ctrl, B2=Alt
; -----------------------------------------------------------------------------

ScanModiFromKey:or	ax,ax		; control key?
		jns	short ScanModiFrmKey2 ; not control key
		shl	al,1		; CY <- extended flag
		rcl	ah,1		; AH,B0 <- extended flag
		ror	ah,1		; AH <- extended scan code
		rol	al,1
		rol	al,1
		rol	al,1		; AL <- modifiers to B0,B1,B2
		and	al,7		; AL <- mask modifiers
		xchg	al,ah		; AH <- modifiers, AL <- scan code
		ret

ScanModiFrmKey2:mov	al,ah		; AL <- key HIGH
		and	ax,7fh		; AX <- scan code
		ret		

; -----------------------------------------------------------------------------
;                        Get character from key code
; -----------------------------------------------------------------------------
; INPUT:	AX = key code
; OUTPUT:	AL = character (0..0ffh)
; -----------------------------------------------------------------------------

CharFromKey:	or	ax,ax		; control key?
		jns	short CharFromKey2 ; not control key
		push	bx		; push BX
		mov	bx,KeyCharTab	; BX <- character table
		and	al,KEYFLAG_MASK	; AL <- mask table character
		xlat			; AL <- character from table
		pop	bx		; pop BX
CharFromKey2:	ret

; -----------------------------------------------------------------------------
;                        Check if key is modifier
; -----------------------------------------------------------------------------
; INPUT:	AX = key code
; OUTPUT:	NC = key is modifier (Cltr,ALt,Shift,x Lock)
; -----------------------------------------------------------------------------

; ------------- push registers

KeyIsModi:	push	ax		; push AX
		mov	al,ah		; AL <- scan code
		and	al,7fh		; mask base scan code

; ------------- check modifier

		cmp	al,KEY_CTRL	; Left or Right Ctrl
		je	short KeyIsModi4
		cmp	al,KEY_ALT	; Left or Right Alt
		je	short KeyIsModi4
		cmp	al,KEY_LSHIFT	; Left Shift
		je	short KeyIsModi4
		cmp	al,KEY_RSHIFT	; Right Shift
		je	short KeyIsModi4
		cmp	al,KEY_CAPSLOCK	; Caps Lock
		je	short KeyIsModi4
		cmp	al,KEY_NUMLOCK	; Num Lock
		je	short KeyIsModi4
		cmp	al,KEY_SCROLL	; Scroll Lock
		je	short KeyIsModi4
		stc			; set error flag

; ------------- pop registers

KeyIsModi4:	pop	ax		; pop AX		
		ret

; -----------------------------------------------------------------------------
;                         Test if some key is ready
; -----------------------------------------------------------------------------
; OUTPUT:	AX = key code, 0=no key (CY)
;		CY = no key (AX = 0)
; NOTES:	Key is not removed from keyboard buffer.
;		It temporaly disables interrupts.
; -----------------------------------------------------------------------------
; Entry point to function is TestKey !

TestKey0:	call	GetKeyModi	; destroy modifier in buffer
TestKey:	call	TestKeyModi	; test key with modifiers
		jc	short TestKey4	; no key
		call	KeyIsModi	; check if key is modifier
		jnc	short TestKey0	; destroy modifier
		clc			; clear error flag
TestKey4:	ret

; -----------------------------------------------------------------------------
;         Get key without waiting (without modifiers Ctrl, Alt...)
; -----------------------------------------------------------------------------
; OUTPUT:	AX = key code, 0=no key (CY)
;		CY = no key (AX = 0)
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

GetKey:		call	GetKeyModi	; get key with modifiers
		jc	short GetKey4	; no key
		call	KeyIsModi	; check if key is modifier
		jnc	short GetKey	; destroy modifier
		clc			; clear error flag
GetKey4:	ret

; -----------------------------------------------------------------------------
;           Get key with waiting (without modifiers Ctrl, Alt...)
; -----------------------------------------------------------------------------
; OUTPUT:	AX = key code
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

GetKeyWait:	call	GetKey		; get key
		jc	short GetKeyWait ; wait for a key
		ret

; -----------------------------------------------------------------------------
;                     Test if some character is ready
; -----------------------------------------------------------------------------
; OUTPUT:	AL = character, 0=no character (CY)
;		CY = no character (AL = 0)
; NOTES:	Character is not removed from keyboard buffer.
;		It temporaly disables interrupts.
; -----------------------------------------------------------------------------

; ------------- push registers

TestChar:	push	cx		; push CX
		xchg	ax,cx		; CH <- push AH

; ------------- input character -> AL

TestChar2:	call	TestKeyModi	; test key with modifiers
		jc	short TestChar4	; no key
		call	CharFromKey	; get character from key
		or	al,al		; is it valid character?
		jnz	short TestChar4	; it is valid character
		call	GetKeyModi	; destroy key from buffer
		jmp	short TestChar2	; next key

; ------------- pop registers

TestChar4:      mov	ah,ch		; AH <- pop AH
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                      Get character without waiting
; -----------------------------------------------------------------------------
; OUTPUT:	AL = character, 0=no character (CY)
;		CY = no character (AL = 0)
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

; ------------- push registers

GetChar:	push	cx		; push CX
		xchg	ax,cx		; CH <- push AH

; ------------- input character -> AL

GetChar2:	call	GetKeyModi	; get key with modifiers
		jc	short GetChar4	; no key
		call	CharFromKey	; get character from key
		or	al,al		; is it valid character?
		jz	short GetChar2	; invalid character

; ------------- pop registers

GetChar4:	mov	ah,ch		; AH <- pop AH
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                    Get character with waiting
; -----------------------------------------------------------------------------
; OUTPUT:	AL = character (1..0ffh)
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

GetCharWait:	call	GetChar		; get character
		jc	short GetCharWait ; wait for a character
		ret

; -----------------------------------------------------------------------------
;         Test if some key is ready (including modifiers Ctrl, Alt...)
; -----------------------------------------------------------------------------
; OUTPUT:	AX = key code, 0=no key (CY)
;		CY = no key (AX = 0)
; NOTES:	Key is not removed from keyboard buffer.
;		It temporaly disables interrupts.
; -----------------------------------------------------------------------------

; ------------- push registers

TestKeyModi:	pushf			; push flags
		push	bx		; push BX
		cli			; disable interrupts

; ------------- check if there are any key in the buffer

		xor	ax,ax		; AX <- 0, preset invalid key
		mov	bl,[KeyReadInx]	; BX <- read index from buffer
		cmp	bl,[KeyWriteInx] ; check buffer
		je	short GetKeyModi8 ; no data in buffer

; ------------- read key from the buffer

		mov	bh,0		; BH <- 0
		shl	bx,1		; BX <- offset in bufer
		mov	ax,[KeyBuff+bx]	; read key from buffer

; ------------- pop registers

TestKeyModi8:	pop	bx		; pop BX
		popf			; pop flags (maybe enable interrupts)
		cmp	ax,1		; set CY if no key
		ret

; -----------------------------------------------------------------------------
;          Get key without waiting (including modifiers Ctrl, Alt...)
; -----------------------------------------------------------------------------
; OUTPUT:	AX = key code, 0=no key (CY)
;		CY = no key (AX = 0)
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

; ------------- push registers

GetKeyModi:	pushf			; push flags
		push	bx		; push BX
		cli			; disable interrupts

; ------------- check if there are any key in the buffer

		xor	ax,ax		; AX <- 0, preset invalid key
		mov	bl,[KeyReadInx]	; BX <- read index from buffer
		cmp	bl,[KeyWriteInx] ; check buffer
		je	short GetKeyModi8 ; no data in buffer

; ------------- read key from the buffer

		mov	bh,0		; BH <- 0
		shl	bx,1		; BX <- offset in bufer
		mov	ax,[KeyBuff+bx]	; read key from buffer

; ------------- increase read index

		shr	bx,1		; BX <- offset in buffer
		inc	bx		; increase offset in buffer
		and	bl,KEYBUFMASK	; BL <- mask offset
		mov	[KeyReadInx],bl	; set new read index

; ------------- pop registers

GetKeyModi8:	pop	bx		; pop BX
		popf			; pop flags (maybe enable interrupts)
		cmp	ax,1		; set CY if no key
		ret

; -----------------------------------------------------------------------------
;                           Flush keyboard buffer
; -----------------------------------------------------------------------------
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

; ------------- push registers

KeyFlush:	pushf			; push flags
		push	ax		; push AX
		cli			; disable interrupts

; ------------- destroy keys in buffer

		mov	al,[KeyWriteInx] ; AL <- read index
		mov	[KeyReadInx],al ; destroy keys in buffer

; ------------- pop registers

		pop	ax		; pop AX
		popf			; pop flags (maybe enable interrupts)
		ret

; -----------------------------------------------------------------------------
;           Get key with waiting (including modifiers Ctrl, Alt...)
; -----------------------------------------------------------------------------
; OUTPUT:	AX = key code
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

GetKeyModiWait:	call	GetKeyModi	; get key with modifiers
		jc	short GetKeyModiWait ; wait for a key
		ret

; -----------------------------------------------------------------------------
;               Return key into keyboard ring buffer (as first key)
; -----------------------------------------------------------------------------
; INPUT:	AX = key code (should not be 0)
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

; ------------- push registers

ReturnKey:	pushf			; push flags
		push	bx		; push BX
		cli			; disable interrupts

; ------------- shift read index down

		mov	bh,0		; BH <- 0
		mov	bl,[KeyReadInx] ; BL <- read index from buffer
		dec	bx		; decrease read index
		and	bl,KEYBUFMASK	; BL <- mask offset

; ------------- check buffer overrun

		cmp	bl,[KeyWriteInx] ; check write index
		jne	short ReturnKey4 ; no overrun
		dec	byte [KeyReadInx] ; shift write index
		and	byte [KeyReadInx],KEYBUFMASK ; mask write index
ReturnKey4:	mov	[KeyReadInx],bl ; set new read index

; ------------- write key into buffer

		shl	bx,1		; BX <- offset in bufer
		mov	[KeyBuff+bx],ax	; write key into buffer

; ------------- pop registers

		pop	bx		; pop BX
		popf			; pop flags (maybe enable interrupts)
		ret

; -----------------------------------------------------------------------------
;              Write key into keyboard ring buffer (as last key)
; -----------------------------------------------------------------------------
; INPUT:	AX = key code (should not be 0)
; NOTES:	It temporaly disables interrupts.
; -----------------------------------------------------------------------------

; ------------- push registers

SetKey:		pushf			; push flags
		push	bx		; push BX
		cli			; disable interrupts

; ------------- write key into ring buffer

		mov	bh,0		; BH <- 0
		mov	bl,[KeyWriteInx] ; BL <- write index into buffer
		shl	bx,1		; BX <- offset in bufer
		mov	[KeyBuff+bx],ax	; write key into buffer

; ------------- shift write index

		shr	bx,1		; BX <- offset in buffer
		inc	bx		; increase offset in buffer
		and	bl,KEYBUFMASK	; BL <- mask offset

; ------------- check buffer overrun

		cmp	bl,[KeyReadInx]	; check read index
		jne	short SetKey4	; no overrun
		inc	byte [KeyReadInx] ; shift read index
		and	byte [KeyReadInx],KEYBUFMASK ; mask read index
SetKey4:	mov	[KeyWriteInx],bl ; set new write index

; ------------- pop registers

		pop	bx		; pop BX
		popf			; pop flags (maybe enable interrupts)
		ret

; -----------------------------------------------------------------------------
;                      Wait for releasing send buffer
; -----------------------------------------------------------------------------
; NOTES:	Interrupt must be disabled.
; -----------------------------------------------------------------------------

; ------------- push registers

KeySendWait:	push	ax		; push AX
		push	cx		; push CX

; ------------- wait for byte to accept

		mov	cx,10000	; CX <- timeout
KeySendWait2:	in	al,KEYCTRL	; AL <- read controller status
		test	al,B1		; output buffer redy?
		loopnz	KeySendWait2	; wait to release send buffer

; ------------- pop registers

		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                      Send command to keyboard controller
; -----------------------------------------------------------------------------
; INPUT:	AL = byte to send
; NOTES:	Interrupt must be disabled.
; -----------------------------------------------------------------------------

KeySendCmd:	call	KeySendWait	; wait for releasing send buffer
		out	KEYCTRL,al	; send byte to keyboard
		ret

; -----------------------------------------------------------------------------
;                        Send data to keyboard
; -----------------------------------------------------------------------------
; INPUT:	AL = byte to send
; NOTES:	Interrupt must be disabled.
; -----------------------------------------------------------------------------

KeySendData:	call	KeySendWait	; wait for releasing send buffer
		out	KEYDATA,al	; send data to keyboard
		ret

; -----------------------------------------------------------------------------
;                      Interrupt Int 09h handler
; -----------------------------------------------------------------------------

; ------------- push registers

Int09:		push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	si		; push SI
		push	ds		; push DS
		push	es		; push ES

; ------------- initialize segment registers

		push	cs		; push CS
		pop	ds		; DS <- CS
		push	cs		; push CS
		pop	es		; ES <- CS

; ------------- maximum number of keys received at once -> CX

		mov	cx,10		; CX <- max. number of keys
		mov	si,KeyModi	; SI <- keyboard modifiers

; ------------- check if controller has any data

Int092:		in	al,KEYCTRL	; AL <- read keyboard controller status
		test	al,B0		; any data byte pending?
		jnz	short Int0923	; valid data
		jmp	Int099		; no data byte ready

; ------------- read keyboard data -> AL

Int0923:	mov	ah,al		; AH <- save controller status
		in	al,KEYDATA	; AL <- scan code

; ------------- data error

		test	ah,B6+B7	; data error?
		jnz	short Int09Next4 ; data error, get next data

; ------------- ignore no key, echo, acknowledge and error control codes

		or	al,al		; no key?
		jz	short Int09Next4 ; no key
		cmp	al,0eeh		; control code?
		jae	short Int09Next4 ; ignore control codes

; ------------- shift random generator with AX code

		call	RandShift	; shift random generator

; ------------- extended code E1

		cmp	al,0e1h		; extended prefix E1?
		jne	short Int093	; no E1
		mov	byte [KeyPrefix],1 ; set E1 prefix flag
		jmp	short Int09Next4 ; next code

; ------------- prefix E1 already set - ignore this key and change to E0

Int093:		cmp	byte [KeyPrefix],1 ; E1 prefix already set?
		je	short Int094	; E1 prefix, change to E0 prefix

; ------------- extended code E0

		cmp	al,0e0h		; extended prefix E0?
		jne	short Int095	; no E0
Int094:		mov	byte [KeyPrefix],80h ; set E0 prefix flag
Int09Next4:	jmp	short Int09Next2 ; next code

; ------------- prepare key code -> AL, save release flag -> AH

Int095:		mov	ah,al		; AH <- release flag (in B7)
		and	al,7fh		; reset release flag
		or	al,[KeyPrefix]	; add 80h flag
		mov	byte [KeyPrefix],0 ; reset key prefix

; ------------- ignore fake Shifts (auto generated by keyboard)

		cmp	al,KEY_LSHIFT2	; fake Left Shift ?
		je	short Int09Next2 ; ignore fake Left Shift
		cmp	al,KEY_RSHIFT2	; fake Right Shift ?
		je	short Int09Next2 ; ignore fake Right Shift

; ------------- prepare offset in map table -> BX, and mask -> CH

		xor	bx,bx		; BX <- 0
		mov	bl,al		; BL <- key code
		mov	cl,3		; CL <- number of rotations
		shr	bx,cl		; BX /= 8
		lea	bx,[bx+KeyMap]	; BX <- map address
		mov	cl,al		; CL <- key code
		and	cl,7		; CL <- lowest 3 bits
		mov	ch,B0		; CH <- mask
		shl	ch,cl		; CH <- shift mask on position

; ------------- Lock correction - don't flip state on repeat

		or	ah,ah		; press key?
		js	short Int0958	; no
		test	[bx],ch		; already pressed?
		jz	short Int0958	; no

		cmp	al,KEY_SCROLL	; Scroll Lock ?
		jne	short Int0952	; no
		xor	byte [si],KEY_MODI_SCROLL ; pre-flip Scroll Lock flag

Int0952:	cmp	al,KEY_NUMLOCK	; Num Lock ?
		jne	short Int0953	; no
		xor	byte [si],KEY_MODI_NUM ; pre-flip Num Lock flag

Int0953:	cmp	al,KEY_CAPSLOCK	; Caps Lock ?
		jne	short Int0954	; no
		xor	byte [si],KEY_MODI_CAPS ; pre-flip Caps Lock flag

Int0954:	cmp	al,KEY_INSERT	; Insert ?
		jne	short Int0955	; no
		xor	byte [si],KEY_MODI_INSERT ; pre-flip Insert flag

Int0955:	cmp	al,KEY_PAUSE	; Pause ?
		jne	short Int0958	; no
		xor	byte [si],KEY_MODI_PAUSE ; pre-flip Pause flag

; ------------- press key

Int0958:	or	[bx],ch		; set flag to be pressed

; ------------- release key

		or	ah,ah		; release key ?
		jns	short Int096	; release key

; ------------- reset bit in map

		xor	[bx],ch		; clear flag

; ------------- release Alt with numeric key

		cmp	al,KEY_RALT	; Right Alt ?
		je	short Int09582	; yes
		cmp	al,KEY_LALT	; Left Alt ?
		jne	short Int09Next2 ; no
Int09582:	cmp	byte [KeyAltChar],0 ; valid character?
		je	short Int09Next2 ; no
		mov	ah,0
		mov	al,[KeyAltChar]	; AL <- character
		mov	byte [KeyAltChar],0 ; clear register
		jmp	Int0988		; store character

Int09Next2:	jmp	Int09Next	; next key

; ------------- Scroll Lock

Int096:		cmp	al,KEY_SCROLL	; Scroll Lock ?
		jne	short Int0962	; no
		mov	al,[si]		; AL <- modifiers
		xor	al,KEY_MODI_SCROLL ; change Scroll Lock flag
		mov	[si],al		; save new flags
		and	al,B0		; AL <- Scroll Lock flag
		call	SetLang		; set language
		mov	al,KEY_SCROLL	; AL <- Scroll Lock
		jmp	short Int0965	; update LEDs

; ------------- Num Lock

Int0962:	cmp	al,KEY_NUMLOCK	; Num Lock ?
		jne	short Int0963	; no
		xor	byte [si],KEY_MODI_NUM ; change Num Lock flag
		jmp	short Int0964	; update LEDs

; ------------- Caps Lock

Int0963:	cmp	al,KEY_CAPSLOCK	; Caps Lock ?
		jne	short Int0965	; no
		xor	byte [si],KEY_MODI_CAPS ; change Caps Lock flag
Int0964:	call	UpdateKeyModi	; update LEDs

; ------------- Insert

Int0965:	cmp	al,KEY_INSERT	; Insert ?
		jne	short Int0966	; no
		xor	byte [si],KEY_MODI_INSERT ; flip Insert flag

; ------------- Pause

Int0966:	cmp	al,KEY_PAUSE	; Pause ?
		jne	short Int0967	; no
		xor	byte [si],KEY_MODI_PAUSE ; flip Pause flag

; ------------- map Alt modifiers

Int0967:	mov	ah,al		; AH <- key code
		call	AltPressed	; check if Alt is pressed
		jc	short Int09675	; Alt is not pressed

; ------------- RAlt special characters

		mov	al,0		; AL <- 0, Alt flag
		KEYISPRESSED KEY_RALT	; check if right Alt is pressed
		jz	short Int09673	; RAlt is not pressed
		
		mov	bx,[KeyMapSpec]	; BX <- special table
Int09672:	mov	cx,[bx]		; CL <- scan code, CH <- character
		jcxz	Int09673	; end of table
		inc	bx
		inc	bx		; BX <- shift pointer
		cmp	ah,cl		; check scan code
		jne	short Int09672	; next code
		mov	al,ch		; AL <- character

; ------------- Alt + numberic character code

Int09673:	cmp	ah,47h		; first numeric key
		jb	short Int09674	; not numeric key
		cmp	ah,52h		; last numeric key
		ja	short Int09674	; not numeric key
		mov	bx,KeyCharNumTab - 47h ; BX <- values of keys
		xchg	ax,cx		; CH <- push AH
		mov	ah,10		; AH <- multiplier
		mov	al,[KeyAltChar]	; AL <- old code
		mul	ah		; AX <- shift value
		xchg	ax,cx		; CX <- value, AH <- scan code
		mov	al,ah		; AL <- scan code
		xlat			; AL <- key value
		add	al,cl		; AL <- new value
		mov	[KeyAltChar],al	; set new value
		jmp	short Int09Next2 ; destroy character

Int09674:	jmp	Int09837	; Alt is pressed

; ------------- prepare index of key in mapping table -> CH

Int09675:	mov	ch,KEYMAP_LEN	; CH <- remap ext. 1
		cmp	ah,KEYMAP_EXT1	; extended character 1 ?
		je	short Int0968

		mov	ch,KEYMAP_LEN+1	; CH <- remap ext. 2
		cmp	ah,KEYMAP_EXT2	; extended character 2 ?
		je	short Int0968

		mov	ch,KEYMAP_LEN+2	; CH <- remap ext. 3
		cmp	ah,KEYMAP_EXT3	; extended character 3 ?
		je	short Int0968

		mov	ch,0		; CH <- default mapping
		cmp	ah,KEYMAP_LEN	; check maximal code
		jae	short Int0968	; use default mapping
		mov	ch,ah		; CH <- key code

; ------------- map Ctrl modifiers

Int0968:	call	CtrlPressed	; check if Ctrl is pressed
		jc	short Int0969	; Ctrl is not pressed
		mov	al,ch		; AL <- mapping index
		mov	bx,[KeyMapCtrl]	; BX <- mapping table
		xlat			; AL <- remap Ctrl key
		jmp	short Int0982	; code key

; ------------- prepare Num Lock modification -> BL

Int0969:	xor	bx,bx		; BX <- 0
		cmp	ah,47h		; first key
		jb	short Int0972	; not numeric key
		cmp	ah,53h		; last key
		ja	short Int0972	; not numeric key
		mov	bl,[KeyModi]	; BL <- modificators
		shr	bl,1		; BL <- NUm Lock into B0
		and	bl,B0		; BL <- Num Lock B0 flag

; ------------- prepare mapping address -> BX, and default code -> AL

Int0972:	call	ShiftPressed	; check if Shift is pressed
		xor	al,bl		; check Num Lock shift
		mov	bx,[KeyMapLower] ; BX <- lower case
		jz	short Int0973	; Shift it not pressed
		mov	bx,[KeyMapUpper] ; BX <- upper case

; ------------- remap character -> AL

Int0973:	mov	al,ch		; AL <- mapping index
		xlat			; AL <- remap character

; ------------- Caps Lock service

Int0975:	test	byte [KeyModi],KEY_MODI_CAPS ; Caps Lock is ON ?
		jz	short Int0982	; Caps Lock is not ON
		call	UpperLowerCaseNat ; change character upper/lower

; ------------- dead character

Int0982:	or	al,al		; valid character?
		jz	short Int09838	; not valid character
		cmp	[KeyMapDCh1],al	; dead character 1?
		jne	short Int09832	; no
		cmp	al,[KeyDeadChar] ; already set?
		je	short Int09837	; character will be generated
		mov	[KeyDeadChar],al ; set new dead character
		jmp	short Int09Next	; next character

Int09832: 	cmp	[KeyMapDCh2],al	; dead character 2?
		jne	short Int09834	; no
		cmp	al,[KeyDeadChar] ; already set?
		je	short Int09837	; character will be generated
		mov	[KeyDeadChar],al ; set new dead character
		jmp	short Int09Next	; next character

Int09834:	mov	cl,[KeyDeadChar] ; CL <- current dead character
		or	cl,cl		; dead character is set?
		jz	short Int09838	; no dead character
		mov	bx,[KeyMapDead1] ; BX <- table 1
		cmp	cl,[KeyMapDCh1] ; check dead character 1
		je	short Int09835	; use table 1
		mov	bx,[KeyMapDead2] ; BX <- table 2
Int09835:	mov	cx,[bx]		; CL <- ASCII character, CH <- accent
		jcxz	Int09837	; end of table
		inc	bx
		inc	bx		; BX <- increase pointer in table
		cmp	al,cl		; character found?
		jnz	short Int09835	; continue with another character
		mov	al,ch		; AL <- substitute with new character

; ------------- check control character (extended scans must be tabled!)

Int09837:	mov	byte [KeyDeadChar],0 ; destroy dead character
Int09838:	mov	cx,16		; CX <- number of tabled characters
		mov	bx,KeyCharTab-1	; BX <- table of characters
Int0984:	inc	bx		; BX <- new offset in table
		cmp	al,[bx]		; is character in table?
		loopne	Int0984		; find character
		jne	short Int0987	; need no coding

; ------------- code control character AH:AL

		mov	al,15
		sub	al,cl		; AL <- tabled code

Int0985:	mov	ch,ah		; CH <- scan code
		and	ch,80h		; CH <- extended flag
		or	ah,80h		; AH <- set control flag
		or	ch,al		; CH <- add extended flag

		call	AltPressed	; check Alt modifier
		ror	al,1
		ror	al,1		; AL <- flag on B6
		or	ch,al		; CH <- add Alt flag

		call	CtrlPressed	; check Ctrl modifier
		ror	al,1
		ror	al,1
		ror	al,1		; AL <- flag on B5
		or	ch,al		; CH <- add Ctrl flag

		call	ShiftPressed	; check Shift modifier
		ror	al,1
		ror	al,1
		ror	al,1
		ror	al,1		; AL <- flag on B4
		or	al,ch		; AL <- modifiers
		jmp	short Int0988

; ------------- write key into buffer

Int0987:	and	ah,7fh		; clear control flag
Int0988:	call	SetKey		; write key into kyboard ring buffer
		
; ------------- try next key

Int09Next:	dec	cx		; counter
		jz	short Int099	; end
		jmp	Int092		; next key

; ------------- enable interrupt controller

Int099:		in	al,21h		; release interrupt controller 1
		mov	al,60h+KEY_IRQ ; AL <- keyboard IRQ
		out	20h,al		; acknowledge interrupt

; ------------- pop registers

		pop	es		; pop ES
		pop	ds		; pop DS
		pop	si		; pop SI
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		iret

; -----------------------------------------------------------------------------
;                            Constant data
; -----------------------------------------------------------------------------

		CONST_SECTION

; ------------- tabled characters

KeyCharTab:	db	0		; 0: no character
		db	08h		; 1: BS, Ctrl+H, BackSpace
		db	09h		; 2: TAB, Ctrl+I
		db	0Ah		; 3: LF, Ctrl+J
		db	0Dh		; 4: CR, Ctrl+M, Enter
		db	1Bh		; 5: ESC, Ctrl+[
		db	1Ch		; 6: FSEP,Ctrl+"\"
		db	1Dh		; 7: GSEP, Ctrl+]
		db	1Eh		; 8: RSEP, Ctrl+^
		db	1Fh		; 9: USEP, Ctrl+_
		db	20h		; 10: SPACE
		db	2Ah		; 11: "*"
		db	2Bh		; 12: "+"
		db	2Dh		; 13: "-"
		db	2Fh		; 14: "/"
		db	7Fh		; 15: DEL

; ------------- values of numeric keys

KeyCharNumTab:	db	7		; 47h [7 Home]
		db	8		; 48h [8 Up]
		db	9		; 49h [9 PgUp]
		db	0		; 4Ah Gray [-]
		db	4		; 4Bh [4 Left]
		db	5		; 4Ch [5]
		db	6		; 4Dh [6 Right]
		db	0		; 4Eh Gray [+]
		db	1		; 4Fh [1 End]
		db	2		; 50h [2 Down]
		db	3		; 51h [3 PgDn]
		db	0		; 52h [0 Ins]

; -----------------------------------------------------------------------------
;                            Uninitialised data
; -----------------------------------------------------------------------------

		DATA_SECTION

		align	4, resb 1
KeyBuff:	resw	KEYBUFSIZE	; key ring buffer
KeyWriteInx:	resb	1		; write index into buffer
KeyReadInx:	resb	1		; read index from buffer
KeyPrefix:	resb	1		; 80h:prefix flag E0,01h:prefix flag E1
		align	4, resb 1
KeyMap:		resb	KEYMAPSIZE	; keys map (1=key is pressed)
KeyModi:	resb	1		; keyb. modifiers (KEY_MODI_SCROLL,...)
KeyLED:		resb	1		; current LED state
KeyDeadChar:	resb	1		; dead character (0=none)
KeyAltChar:	resb	1		; char. code with Alt+[0]..[9] (0=none)

; ------------- current keyboard layout

Language:	resb	1		; current language (LANG_EN, LANG_CZ)
KeyLayoutSel:	resb	1		; index of current keyboard layout

		align	2, resb 1

KeyLayout:			; --- KEYLAOUT structure
KeyMapLower:	resw	1		; pointer to current lower table
KeyMapUpper:	resw	1		; pointer to current upper table
KeyMapCtrl:	resw	1		; pointer to current control table
KeyMapLowUp:	resw	1		; pointer to current lower/upper table
KeyMapDCh1:	resb	1		; dead character 1 (0=none)
KeyMapDCh2:	resb	1		; dead character 2 (0=none)
KeyMapDead1:	resw	1		; pointer to dead table 1
KeyMapDead2:	resw	1		; pointer to dead table 2
KeyMapSpec:	resw	1		; special RAlt table
