{Ed: This code has been translated to SX but not tested}
;				PiCBoard
;
;               PC-Keyboard emulator using an SX28
;     Scan a 32 key keyboard ( with alternative mapping = 64 keys )
;

;                 COPYRIGHT (c)1999 BY Tony Kübek
; This is kindly donated to the PIC community. It may be used freely,
;     and you are forbidden to deprive others from those rights.
;    Please read the FSF (Free Software Foundation) GNU statement.
;
;*********************************************************************
;
;			E-MAIL	tony.kubek@flintab.se
;
;
; DATE			2002-10-20
; ITERATION		1.0C
; FILE SAVED AS		PiCBoard_SX.ASM
; FOR			SX28
; CLOCK			10.00 MHz RESONATOR ( OSC-HS )
; INSTRUCTION CLOCK	2.50 MHz T= 0.4 us
; SETTINGS		WDT-ON, PWT-ON, CP-OFF
; REVISION HISTORY
;			0.1b -	First beta, mainly for testing
;			1.0b - 	First public beta release
;			1.0c -  Converted and Optimized for the SX by James Newton
;
;
;
;***************************************************************************
;
;				PREFACE ;-)
;
;	This is NOT an tutorial on pc keyboards in general, there are quite
;	a few sites/etc that have that already covered. However i DID find
;	some minor ambiguities regarding the actual protocol used but nothing
;	that warrants me to rewrite an complete pc keyboard FAQ.
;	So for 'general' pc keyboard info ( scancodes/protocol/pin config/etc )
;	here are some useful links:
;
;	http://www.senet.com.au/~cpeacock/
;	http://ourworld.compuserve.com/homepages/steve_lawther/keybinfo.htm
;	http://www.sxlist.com/techref/io/keyboard.htm
;	http://www.arne.si/~mauricio/PIC.HTM
;
;	PLEASE do not complain about code implementation, I know there are parts
;	in which is it 'a bit' messy and hard to follow, and other parts where
;	the code is not optimised. Take it as is. Futhermore I did hesitate
;	to include all of the functionality thats currently in, but decided to
;	keep most of it, this as the major complaint i had with the other available
;	pc-keyboard code was just that - 'is was incomplete'. Also do not
;	be discoraged by the size and complexity of it if you are an beginner,
;	as a matter of fact this is only my SECOND program ever using a pic.
;	I think I managed to give credit were credit was due ( 'borrowed code' ).
;	But the originators of these snippets has nothing to do with this project
;	and are probably totally unaware of this, so please do not contact them
;	if you have problems with 'my' implementation.
;
;	BTW ( shameless plug ) UltraEdit rules ! ( if you dont have it, get it ! )
;	http://www.ultraedit.com ( dont 'forget' the 'wordfile' for PIC, color indenting )
;	Without that I guess this file would be 'messy'.
;
;	Ok with that out of the way here we go:
;
;
;***************************************************************************

;				DESCRIPTION ( short version ;-) )

;	A set of routines which forms a PC keyboard emulator.
;	Routines included are:
;	Interrupt controlled clock ( used for kb comm. and 'heart beat )
;	A 4x8 matrix keyboard scanner ( using an 74HCT4051 3 to 8 analogue multiplexer )
;	Communincation with PC keyboard controller, both send end recive.

; 	PC keyboard routines are using 4 pins (2 inputs & 2 outputs) to control
; 	keyboard's 2 bidirectional OC lines (CLK & DATA). The following
; 	'drawing' conceptually shows how to connect the related pins/lines
;
;	( ASCII art/info shamelessly 'borrowed' from http://www.arne.si/~mauricio/PIC.HTM )
;
;			     vcc	vcc
;			      | 	 |
;			      \ 	-+-
;			      / 2K2	/_\  1N4148
;			      \ 	 |
; pcCLOCK_in -----------------o---o------o---------   kbd CLOCK
;			      |   |	 |
;			 2N2222   |50pF -+-
; pcCLOCK_out ____/\/\/\____|/	 ===	/_\
;		    2K2     |\>   |	 |
;			      |   |	 |
;			     /// ///	///
;
; 	An identical circuit is used for the DATA line.
;	Note: The 2N2222 transitors can be replaced with BC337 ( NPN ) which are cheaper
;	The keyboard matrix routines are using RB4-RB7 as inputs.
;	and RB0-RB2 as output to/from an 3 to 8 multiplexer
;	so that it can read up to 4x8 keys ( 32 ).
;
;	RA4/TOCK1 is an input from an jumper, if low then keyrepeat is disabled and
;	alt-key is enabled instead i.e. instead of repeating a key that has
;	been depressed a certain amount of time, a bit is set that can change the
;	scancode for a key ( of course, all keys can have an alternate scancode ).
;	To exit the alt. keymap press the 'enter alt. keymap' key once again or wait until
;	timeout. ( defined in the code )
;	NOTE !! The so called 'enter alt. keymap' key is hardcoded, i.e the key
;	i've choosen in this 'example' is Column 1 Row 2, if this is to be changed
;	code has to be changed/moved in the debounce and checkkeystate routines.
;	( marked with <-------Alt keymap code-------> )
; 	RB3 is currently used for a flashing diode. ( running )
;	Note my real keyboard ( KeyTronic ) uses a clock rate of 40 us, my implementation
;	uses about 50 us, easily to change.
;

;***************************************************************************

;			DESCRIPTION ( longer version ;-) )
;
;	Pin 	Used for
;	------------------------------------------------------------
;	RA0	Pc keyboard data out ( to pc )
;	RA1	Pc keyboard data in ( from pc )
;	RA2	Pc keyboard clock in
;	RA3	Pc keyboard clock out
;	RA4	Flashing disco led 0.5s duty cycle( could be used for some more meaningful purpose )
;
;	RB0	Least significant bit in column adress to 4051 multiplexer ( own keyboard )
;	RB1	Middle...
;	RB2	Most significant bit -- || --
;	RB3	Input. Disable keyrepeat & enable alternative keymapping ( if = '0',ground)
;		OR if '1' ( free,+V ) enable keyrepeat & disable alternative keymapping
;	RB4	Own keyboard input row 1
;	RB5	--- || --- row 2
;	RB6	--- || --- row 3
;	RB7	--- || --- row 4
;
;	'Basic program structure':
;
;	Init	-	Initialise ports , ram, int, vars
;	Start delay - 	After init the timer int is enabled and the flashing led will
;			start to toggle ( flash ). Before I enter the mainloop
;			( and send any keycodes ) I wait until the led has flashed
;			twice.	This is of course not really needed but I normally
;			like to have some kind of start delay ( I know 1 sec is a bit much :-) )
;	Time Int    -	The timer interrupt (BIG), runs in the background:
;			- 'Normal' rate 0.5 ms, when in rx/tx rate is 27 us
;			 - Performs an rx/tx check, then jumps to rx, tx or 'heart beat' code.
;			 - TX code sends a byte to pc, at a rate of 27us per int.
;			   The int rate is actually double the bit rate, as
;			   a bit is shifted out in the middle of the clock pulse,
;			   I've seen different implementations of this and I think
;			   that the bit is not sampled until clock goes low again BUT
;			   when logging my keyboard ( Keytronic ) this is the way that it
;		   	   does it. When all bits are sent, stopbit/parity is sent.
;			   And the key is removed from the buffer.
;			   After stopbit/parity is sent, a delay is inserted ( 0.5 ms )
;			   before next rx/tx check.
;			 - RX code recevies a byte from the pc, PIC is controlling clock !!
;			   Int rate ( 27 us ) is, again, double bit rate.
;			   Toggles clock and samples the data pin to read a byte from pc.
;			   When reception is finished an 'handshake' takes place.
;			   When a byte has been recevied a routine is called to check
;			   which command and/or data was received. If it was
;			   keyboard rate/delay or led status byte, it is stored in local ram
;			   variables. NOTE: The rate/delay is not actually used for
;			   key repeat as the code is right now ( I use my 'own' fixed rate/delay )
;			   however it is very easy to implement.
;			   After handshake a delay is inserted ( 0.5 ms )
;			   before next rx/tx check.
;			- 'Heart beat' ( idle code 0.5 ms tick ) performs:
;			 - Check clock/data lines to see if pc wants to send something or
;			   if tx is allowed.
;			 - If tx is possible it checks the keybuffer for an available key and
;			   if keys are in buffer then it initiates a tx seq.
;			   and sets the int rate to 27 us.
;			 - If the pc wants to send something, an rx seq. is initiated
;			   ( there is some handshaking involved, during which
;			   the int rate is set to 60 us ) after that, the int rate is
;			   set to 27 us and an rx seq is started.
;			 - Divides some clock counters to achive 10ms,100ms,500ms sections.
;			 - In 100 ms section it performes a numlock status check and
;			   keyrepeat check ( both rate and delay is local in 100 ms ticks,
;			   thats why I dont use the 'real' rate delay )
;			  - If numlock status is not the desired one code is called to
;			    toggle the numlock status.
;			  - If a key has been pressed long enough for repeat, an bit is set
;			    so we can repeat the key ( send the scancode again ) in the main loop.
;			- In 500 ms section the led is toggled on each loop
;			  - Some various alternative keymap checks to get out of
;			    alternative keymap. ( i'll get to that in a bit )
;
;	Main loop	- Outputs an adress to the 4051 multiplexer waits 1 ms
;			  reads the row inputs ( 4 bits/keys ), increments address
;			  and outputs the new adress, waits 1 ms and reads the input
; 			  ( next 4 bits/keys ). Now using the address counter, calls a
;			  debounce/send routine that first debounces the input,
;			  ( four consecutive readings before current state is affected )
;			  and when a key is changed a make/break code is sent ( put in buffer ).
;			  In the next loop the next two columns are read etc. until all
;			  4 column pairs are read.
;			- If keyrepeat is enabled ( see pin conf. above ) the
;			  repeat flag is checked and if '1' the last pressed key scancode
;			  is sent again ( put in buffer ).
;			- If keyrepeat is not enabled( alternative keymap is enabled instead )
;			  then various checks to exit the alternative keymap are performed instead.
;
;	Scancodes for all key are located in a lookup table at the end of this file,
;	each key has four program rows, to make room for extended codes and alt. keymap codes.
;
; 	Explanation of 'alternative' keymap:
;
;	Using this program ( or an heavily modified version of it anyway :-) )
; 	on a computer running Windows posed some small problems; namely:
;	-The keyboard ( mapping ) I used did not have any 'special' key such as
;	 <alt>,<ctrl>,<tab> etc.
;	-In windows, things can go wrong, :-) if a dialog pops up or something similar
;	 there were just no way one could dispose of this with the keymapping i used.
;	- 'Only' 28 keys were implemented ( hardware wise ).
;	In this particular case the keyrepeat was disabled ( due to the nature of the application )
;	Therefore i came up with the solution to use the keyrepeat related routines and vars.
;	To handle a so called 'alternative' keymapping.
;	This means that an key is dedicated to be the alt. keymap toggle key,
;	when pressing this longer than the programmed repeat delay, instead of
;	repeating the key a bit variable is set to use another set of scancodes for
;	the keyboard. This 'alternative' keymap is then enabled even though the
;	alt. keymap toggle key is released, but it also incorporates an timeout
;	that will return to normal keymap if no key is pressed within a certain time ( currently 7 sec )
;	Of course pressing the alt. keymap toggle key again ( while in alt. keymap )
;	will	RET
;	NOTE !! the key choosen for the alt. keymap toggle is hardcoded, so if the scancode
;	for this is changed in the lookup table, changes have to be made in other routines
;	as well. ( namly : CHECK_KEY_STATE, and DEBOUNCE_COLUMN routines )
;	While in alt. keymap each key CAN have an alternative scancode ( see lookup table ).
;	Also by using the numlock bit, one can toggle numlock status 'on the fly' when entering
;	or exiting the alt keymap.
;
;	Some notes about the local keyboard interface ( matrix ):
;	Although the hardware circuit and software allows virtually unlimited
;	simultaneosly pressed keys, the keyboard matrix itself normally poses
;	some limitations on this. If an keymatrix without any protective diodes
;	are used then one would have loops INSIDE the keymatrix itself when
;	multiple keys are pressed in different columns .
;	Look at the ( although horrible ;-) ) ASCII art below(internal weak pullup enabled):
;	0 - Key is free
;	1 - Key is pressed ( connection between hor/ver rows )
;	Three keys pressed adressing ( reading ) left column :
;
;	To pic1	--------0-------0------ ( row 1 )
;	        	|       |
;	To pic2	--------1-------1------ ( row 2 )
;            		|       |
;	To pic3	--------1-------0------ ( row 3 )
;               	|       |
;    	Column(4051)	0V	-	( Current column is set to 0V when adressing )
;
;	This works as intended, we can read a '0' on pic inputs 2,3 which
;	is what we expected. The current ( signal ) follows the route marked with '*':
;	( only the 'signal path' is shown for clarity )
;
;	To pic1	--------0-------0------ ( row 1 )
;	        	|       |
;	To pic2	*********-------1------ ( row 2 )
;            		*       |
;	To pic3	*********-------0------ ( row 3 )
;               	*       |
;    	Column(4051)	0V	-	( Current column is set to 0V when adressing )
;
;	However, when we now read ( address ) the right column instead we
;	do not read what is expected ( same three keys still pressed ):
;
;	To pic1	--------0-------0------ ( row 1 )
;	        	|       |
;	To pic2	*****************------ ( row 2 )
;            		*<-     *
;	To pic3	*********-------*------ ( row 3 )
;               	|       *
;    	Column(4051)	-	0V	( Current read column is set to 0V when adressing )
;
;	As you can see the 'signal' travels in the keymatrix itself ( where the '<-' is )and
;	will cause a 'ghost' signal to be read on the pic. So instead
;	of having an '0' on input 2 only we also can read an '0' on input 3.
;	This is because the two keys in column 1 are interconnected ( when they are pressed ).
;	Keep this in mind if you are planning to support multiple pressed keys.
;
;
;***************************************************************************
;
;	Some suggestions for 'improvements' or alternations
;
;	- Using the jumper 'disable-repeat' as a dedicated key for switching
;	  to alternative keymapping.
;	- Enable repeat in alternative keymapping
;	- Clean up TX/RX code ( a bit messy )
;	- Using the led output ( or jumper input ) as an extra adress line
;	  to the multiplexers ( in this case 2 pcs. 74HCT4051 ) we could have
;	  4x16 keys instead. Would require some heavy modifications though
;	  as there are not much ram/program space left. But if alternative
;  	  keymapping is discarded ( most likely if one has 64 keys ) each
;	  key in the lookup table only needs to be 2 lines instead of 4.
;	  That would 'only' require some modifications to preserv ram.
;	- Using the EERAM for 'macros' or similar ( not used at all now )
;
;***************************************************************************
;
;			LEDGEND
;
;	I tend to use the following when naming vars. etc. :
;	( yes i DO like long names )
;
;	For 'general' purpose pins:
;
;	An input pin is named I_xxx_Name where :
;
;		I_   - This is an input pin ;-)
;		xxx_ - Optional what type of input, jmp=jumper etc.
;		Name - Self explanatory
;
;	An output pin is named O_xxx_Name where:
;
;		O_   - This is an output pin ;-)
;		xxx_ - Optional what type of output, led=LED etc.
;		Name - Self explanatory
;
;	Application(function) specific pins:
;
;	An application(function) specific pin is named xxName where:
;
;		xx   - What/Where, for example pc=To/From pc
;		Name - Self explanatory ( what does it control etc )
;
;	An #define/constant/equ (no pin or ram ) uses all capital letters e.x. #define BREAK 0xF0
;
;	A bit variable will always start with '_'. For example '_IsLedStatus'
;
;	All other ( mostly ramvars. ) are named in various ways.
;
;
;***************************************************************************





	TITLE "PC Keyboard emulator - By Tony K&uuml;bek"



	device SX28
		Radix   DEC
		EXPAND



;***** HARDWARE DEFINITIONS ( processor type include file )

;***** CONFIGURATION BITS


	CODE "010C"	; version 1.0B

;***** CONSTANT DEFINITIONS

	CONSTANT	BREAK = 0xF0		; the break key postfix ( when key is released )
	CONSTANT	EXTENDED = 0xE0 	; the extended key postfix

	; As i dont really use the rate/delay I receive from the pc ( easy to change )
	; this is the current rate/delay times i use:
	CONSTANT	DELAY_ENTER_ALTKEYMAP = 0x1E	; x100 ms , approx 3 seconds ( 30 x 100 ms )
							; how long the 'enter altkeymap' key must
							; be in pressed state before the altkeymap is enabled
	CONSTANT	DELAY_EXIT_ALTKEYMAP = 0x0F	; x0.5 sec , approx 7.5 sec
							; how long before we exit the alt keymap if no key is
							; pressed.
	CONSTANT 	DELAY_REPEAT	= 0x08		; x100 ms, approx 800 ms
							; how long before we START repeating a key
	CONSTANT	DELAY_RATE	= 0x02		; x100 ms, approx 200 ms repeat rate
							; how fast we are repeating a key ( after the delay above )

;***** CONSTANT DEFINITIONS ( pins )

;	For connection with PC keyboard

#define pcCLOCK_in  PORTA,2   ; Input PC keyboard clock
#define pcCLOCK_out PORTA,3   ; Output PC keyboard clock
#define pcDATA_in   PORTA,1   ; Input PC keyboard data
#define pcDATA_out  PORTA,0   ; Output PC Keyboard data


;	For connection (input) with our own keyboard
; Note I actually dont use these (definitions!) in the program, but they might come in handy
; at one time or another ;-) ( I use the pins though.. )

#define kbROW_1   PORTB,7
#define kbROW_2   PORTB,6
#define kbROW_3   PORTB,5
#define kbROW_4   PORTB,4

;	Indications ( output )

#define O_led_KEYCOMM_ok	PORTA,4		; communication seems ok led ( flashing )

;	Disable/enable key repeat input jumper

#define I_jmp_NoRepeat	PORTB,3	; note: internal weak pullup enabled

;	For keybuffer ( using the indirect file selector FSR )

#define KeyBufferHead	FSR


;***** RAM ASSIGNMENT


		CBLOCK	0x0C
			KeyBufferTail	; where the last byte in buffer is..
			clkCount     	; used for clock timing
			Offset		; used for table reads
                        Saved_Pclath   	; Saved registers during interrrupt
			Saved_Status   	; -----
			Saved_w        	; -----
			CurrKey		; current key ( rx or tx )..
			KeyParity	; key parity storage ( inc. for every '1' )
			Divisor_10ms	; for the timer
			Divisor_100ms   ; ditto
			Divisor_500ms   ;
			Divisor_Repeat  ; timer for repeated key sends

			Flags		; various flags
			RepeatFlags     ; flags for repeating a key
			bitCount	; bitcounter for tx/rx
			Comm_Flags	; flags used by both rx and tx routines
			Temp_Var	; temp storage, can be used outside int loop
			TRX_Flags	; flags used by both rx and tx routines
			CommandData     ; bit map when receving data bytes from pc
					; for example led status/ delay / etc
			KbLedStatus     ; to store status led bitmap for keyboard ( pc first sends 'ED' then this byte )
        				;  bit 0=Scroll lock  ( 1=on )
        				;  bit 1=Num lock
        				;  bit 2=Caps lock
        				;  bits 3-7 = unused
			KbRateDelay	; to store repeat delay/rate ( pc first sends 'F3' then this byte )
        				;  bit 0-4 (rate) = '00000' is 30x/sec '11111' is 2x/sec ( i.e. value * 33 ms )
        				;  bit 5-7 (delay)= '00' is 250 ms, '11' is 1000 ms ( ie. value * 250 ms )
					;  bit 7 = unused
			BufTemp		; temp byte for storing scancode to put in buffer
			Temp		; temp byte, used locally in buffer routine
			Temp2		;
			LastKey		; stores the last sent key
			KbBufferMin	; where our keybuffer starts
			Kb1		; used in keybuffer
			Kb2		; used in keybuffer
			Kb3		; used in keybuffer
			Kb4		; used in keybuffer
			Kb5		; used in keybuffer
			Kb6		; used in keybuffer
			Kb7		; used in keybuffer
			Kb8		; used in keybuffer
			Kb9		; used in keybuffer
			Kb10		; used in keybuffer
			KbBufferMax	; end of keybuffer
			TempOffset	; temporary storage for key offset ( make/break )

			LastMakeOffset  ; storage of last pressed key ( offset in table )
			RepeatTimer	; timer to determine how long a key has been pressed
			RepeatKey 	; the key to repeat
			repTemp		; temporary storage in repeat key calc.
			repKeyMap	; bit pattern for the column in which the repeat key is in
					; i.e. a copy of kbColumnXX_Old where 'XX' is the column
			LastKeyTime	; counter when last key was pressed, used to get out of altkeymap
					; after a specific 'timeout'

			kbScan		; scan code for pressed/released key
			kbTemp		; temp storage for key states

			kbState		; which keys that has changed in current columns
			kbBitCnt	; bit counter for key check ( which key/bit )

			kbColumnCnt	; column counter ( loops from 8 to 0 )
					; Used as output to multiplexer/decoder and in debounce routines

			kbColumnVal	; current value of the last 2 columns ( 2x4bits = 8 bits ) input pins ( keys )
					;
					; Note the kbColumnXX_New variables is not really needed
					; used it while making the program ( debugging ;-) ).
					; so if more free ram is needed change code using these to use
					; the current 'input' sample instead. ( kbColumnVal )
			kbColumn12_New  ; New debounced reading for column 1 & 2
			kbColumn12_Old  ; Latest known valid status of column 1 & 2
			kbColumn12Cnt	; Debounce counter for column 1 & 2
			kbColumn12State ; State of debounce for column 1 & 2

			kbColumn34_New  ; New debounced reading for column 3 & 4
			kbColumn34_Old  ; Latest known valid status of column 3 & 4
			kbColumn34Cnt	; Debounce counter for column 3 & 4
			kbColumn34State ; State of debounce for column 3 & 4

			kbColumn56_New  ; New debounced reading for column 5 & 6
			kbColumn56_Old  ; Latest known valid status of column 5 & 6
			kbColumn56Cnt	; Debounce counter for column 5 & 6
			kbColumn56State ; State of debounce for column 5 & 6

			kbColumn78_New  ; New debounced reading for column 7 & 8
			kbColumn78_Old  ; Latest known valid status of column 7 & 8
			kbColumn78Cnt	; Debounce counter for column 7 & 8
			kbColumn78State ; State of debounce for column 7 & 8

		ENDC

;******* END RAM

; *** flags used by both rx and tx routines
#define _isParity	Comm_Flags,0	; bit in rx/tx is parity bit
#define _KeyError	Comm_Flags,1	; set to '1' when an error is detected
#define _isStartBit	Comm_Flags,2	; set to '1' when bit in rx/tx is startbit
#define _isStopBit	Comm_Flags,3	; --- || --- but stopbit
#define _RxCanStart     Comm_Flags,4	; for handshaking, when negotiating an rx seq.


; *** dito used for rx and tx
#define _KeyReceived	TRX_Flags,0	; rx
#define _RX_Mode	TRX_Flags,1	; rx is in progress ( started )
#define _doRXAck	TRX_Flags,2	; do rx handshake
#define _RXAckDone	TRX_Flags,3	; rx handshake is done
#define _RXEnd		TRX_Flags,4	; rx seq is finished
#define _RXDone		TRX_Flags,5	; rx exit bit
#define _KeySent	TRX_Flags,6	; tx key has been succesfully sent
#define _TX_Mode	TRX_Flags,7	; tx is in progress ( started )


; *** flags to determine the meaning of an incomming data is
; i.e. when then pc has sent a 'data follow' instruction
; these bits are set according to the command,
; i.e. the next incomming byte is.....
#define _IsLedStatus    CommandData,0	; the next incoming byte contains kb led status
#define _IsRateDelay    CommandData,1	; the next incoming byte contains kb rate/delay
#define _SkipByte	CommandData,2	; other data not saved

; *** bit meaning in KbLedStatus ( the leds on the keyboard )
; i.e local ram copy of this byte
#define _LedScrollLock	KbLedStatus,0	; '1' led is on
#define _LedNumLock     KbLedStatus,1	;
#define _LedCapsLock	KbLedStatus,2	;
#define _IsFirstLedStatus KbLedStatus,7	; set this to '1' at startup, to know that our local
;					; copy (led status) is not yet syncronised. Used to
;					; 'force' sync. the first time we set the local numlock bit.

; *** flags used for various purposes
#define _Timer		Flags,0 ; used for waiting
;#define _WrongPar	Flags,1	; ?? ( used during dev. to force wrong parity sends, removed )
#define _LastColumn	Flags,2	; for kb scan code
#define _isBreak	Flags,3 ; if '1' the currently handled key is break ( released ), else make ( pressed )
;#define _isFree         Flags,4 ; Not used ! ( temporary during dev. )
#define _AltKeymap      Flags,5 ; is we use alternative keymap ( other scancodes ) see below !
				; enabled ONLY when keyrepeat is disabled ( I_jmp_NoRepeat (RB3) is low )
#define _ExitAltKeymap  Flags,6 ; start checking if we should exit alternative keymap
				; if all keys are 'free' ( not pressed ) we exit altkeymap
#define _InAltKeymap    Flags,7 ; if we are waiting for second press/release to get out of altkeymap


#define _doRepeat	RepeatFlags,0 ; if the last pressed key should be 'repeated'
#define _doSendKey	RepeatFlags,1 ; send the key in RepeatKey to pc
#define _isExtended     RepeatFlags,2 ; temp flag if last key is extended
#define _RepeatIsExt	RepeatFlags,3 ; the key to repeat is extended key
#define _startRepeat    RepeatFlags,4 ; start repeat timer
#define _DoExitAltKeymap RepeatFlags,5 	; bit set when we are getting out of alternative keymap
#define _NumLock	RepeatFlags,6  	; 'mirror' of numlockstatus, by setting/clearing this bit
				       	; numlock status will be changed.
					; I.e. there is no need to 'manually' send break/make code for numlock
					; key, by setting this bit to '1' numlock status will by automaticlly
					; ( inside int handler ) set to 'on'( within 100 ms ). Se code for example.
#define _WaitNumLock	RepeatFlags,7	; bit set when we have sent make/break numlock scancode
					; and waiting for numlock status byte reply.
					; ( to inhibit a new numlock scancode send )


;**************************************************************************
;				Macros
;**************************************************************************


;+++++
;	BANK0/1 selects register bank 0/1.
;	Leave set to BANK0 normally.

BANK0	MACRO
	CLRB	STATUS.RP0
	ENDM

BANK1	MACRO
	SETB	STATUS.RP0
	ENDM

;+++++
;	PUSH/PULL save and restore W, PCLATH and STATUS registers -
;	used on interrupt entry/exit

PUSH	MACRO
	MOV	Saved_w, W	; Save W register on current bank
	MOV	W, <>STATUS	; Swap status to be saved into W
	BANK0			; Select BANK0
	MOV	Saved_Status, W	; Save STATUS register on bank 0
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
;	MOVFW	PCLATH
	MOV	W, PCLATH
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
;	MOVWF	Saved_Pclath	; Save PCLATH on bank 0
	MOV	Saved_Pclath, W	; Save PCLATH on bank 0
	ENDM

PULL	MACRO
	BANK0			; Select BANK0
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
;	MOVFW	Saved_Pclath
	MOV	W, Saved_Pclath
;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
;	MOVWF	PCLATH		; Restore PCLATH
	MOV	PCLATH, W	; Restore PCLATH
	MOV	W, <>Saved_Status
	MOV	STATUS, W	; Restore STATUS register - restores bank
	SWAP	Saved_w
	MOV	W, <>Saved_w	; Restore W register
	ENDM




;+++++
; 	We define a macro that will switch an output pin on or off depending
; 	on its previous state. We must be on bank0 !!
;

TOGGLE_PIN	MACRO WHICH_PORT,WHICH_PIN
		LOCAL TOGGLE_PIN10, TOGGLE_END

	SNB	WHICH_PORT.WHICH_PIN	; is the pin high ?
	JMP	TOGGLE_PIN10	; yes, clear it
	SETB	WHICH_PORT.WHICH_PIN	; no, so set it
	JMP	TOGGLE_END
TOGGLE_PIN10:
	CLRB	WHICH_PORT.WHICH_PIN	; clear the pin
TOGGLE_END:
		ENDM


;*************************************************************
; Credit for routine ( almost unchanged, expect it's now a macro ;-) )
; goes to Scott Dattalo http://www.interstice.com/~sdattalo/technical/software/software.html
; ( debouce routine at http://www.interstice.com/~sdattalo/technical/software/pic/debounce.html )
;
; 	DEBOUNCE_BYTE - 'debounces' 8 bits, when a bit state
;	has been 'active' for 4 consecutive debounce loops
;	it's state is put into the 'debounced' sample ( i.e. output )
;
; The purpose of this routine is to debounce, i.e. digitally low pass filter
; inputs. The algorithm handles upto 8 bits at a time. An input is considered
; filtered if it has not changed states in the last 4 samples.
;
; 2-bit cyclic vertical counters count the 4 samples. As long as there is no
; change, the counters are held in the reset state of 00b. When a change is detected
; between the current sample and the filtered or debounced sample, the counters
; are incremented. The counting sequence is 00,01,10,11,00... When the counters
; roll over from 11b to 00b, the debounced state is updated. If the input changes
; back to the filtered state while the counters are counting, then the counters
; are re-initialized to the reset state and the filtered state is unaffected.
; In other words, a glitch or transient input has been filtered.
;
; Here's the C-psuedo code:
;
;static unsigned clock_A,clock_B,debounced_state
;
;debounce(unsigned new_sample)
;{
;  unsigned delta;
;
;  delta = new_sample ^ debounced_state;   //Find all of the changes;
;  clock_A ^= clock_B;                     //Increment the counters
;  clock_B  = ~clock_B;
;
;  clock_A &= delta;                       //Reset the counters if no changes
;  clock_B &= delta;                       //were detected.
;
;      //Preserve the state of those bits that are being filtered and simultaneously
;      //clear the states of those bits that are already filtered.
;  debounced_state &= (clock_A | clock_B);
;      //Re-write the bits that are already filtered.
;  debounced_state |= (~(clock_A | clock_B) & new_sample);
;}
;
; The 2-bit counters are arranged "vertically". In other words 8 counters
; are formed with 2 bytes such that the corresponding bits in the bytes are
; paired (e.g. MSBit of each byte is paired to form one counter).
; The counting sequence is 0,1,2,3,0,1,... And the state tables and Karnaugh
; maps are:
;
;State Table:     Karnaugh Maps:
;pres  next      B
; SS  SS         0   1
; AB  AB       +---+---+    +---+---+
;--------   A 0|   | 1 |    | 1 |   |
; 00  01       +---+---+    +---+---+
; 01  10      1| 1 |   |    | 1 |   |
; 10  11       +---+---+    +---+---+
; 11  00      A+ = A ^ B     B+ = ~B
;
; Here's the PIC code that implements the counter:
;	MOV	W, SB	;W = B
;	XOR	SA, W	;A+ = A ^ B
;	NOT	SB	;B+ = ~B
;  14 instructions
;  15 cycles
; Inputs:
;   NewSample - The current sample
; Outputs
;   DebouncedSample - The current value (filtered version of NewSample)
;
; VARS used
;   DBCnt,
;   DBState - State variables for the 8 2-bit counters
;

DEBOUNCE_BYTE MACRO NewSample,DebouncedSample,DBCnt,DBState

    	;Increment the vertical counter

	MOV	W, DBState
	XOR	DBCnt, W
	NOT	DBState

    	;See if any changes occurred

	MOV	W, NewSample
	XOR	W, DebouncedSample

    	;Reset the counter if no change has occurred

	AND	DBState, W
	AND	DBCnt, W	;Determine the counter's state
	MOV	W, DBState
	OR	W, DBCnt

    	;Clear all bits that are filtered-or more accurately, save
    	;the state of those that are being filtered

	AND	DebouncedSample, W
	XOR	W, #$ff	;Re-write the bits that haven't changed.
	AND	W, NewSample
	OR	DebouncedSample, W

	ENDM



;**************************************************************************
;			     Program Start
;**************************************************************************


;	Reset Vector

	ORG	$00

	; For the sole purpose of squeezing every last byte of the programming mem
	; I actually use the 3 program positions before the interrupt vector
	; before jumping to the main program. Take note though that
	; ONLY 3 instructions are allowed before the jump to main loop !!

	BANK0

;*** WARNING: PCLATH register bits are in STATUS PAx bits. Or use PAGE/IREAD if possible
;        CLRF	PCLATH
	CLR	PCLATH
	CLR	INTCON

	JMP	INIT

;**************************************************************************
;			     	Interrupt routine
; An humongously big int handler here ;-)
; but the keyboard handling is FULLY in the background, without any intervention
; in the main loop whatsoever. I like the solution anyway.
;
;**************************************************************************


;	Interrupt vector

	ORG	$04

INT
	PUSH			; Save registers and set to BANK 0


	SB	INTCON.T0IF	; check if TMR0 interrupt
	JMP	INTX	; whoops ! 'unknown' int, should be disabled...
				; NOTE ! if an 'unknown' int triggers the int routine
				; the program will loop here for ever ;-) ( as the calling flag is not cleared )



	;+++
	; Timer (TMR0) timeout either heart beat or tx/rx mode
	; In 'heart beat mode' we monitor the clock and data lines
	; at ( roughly )= 0.5 ms interval, we also check the send buffer
	; if there are any keys to send to pc ( if clock/data levels allows us to )
	; In tx/rx mode we are controlling the clock/data line = 27 us tick
	; Note: This software works using both 8Mhz and 10Mhz resonators without modifications
	; however the 'timing' will then of course be 'off'.

INT_CHECK
	CLRB	INTCON.T0IF	; Clear the calling flag !

	SNB	_TX_Mode	; check if we are in tx mode
	JMP	INT_TX	; yep, goto tx mode code..
	SNB	_RX_Mode	; are we in rx mode ?
	JMP	INT_RX	; yep goto rx mode code
	JMP	INT_HEART_BEAT	; nope just goto 'heart beat' mode

;*************** TX code start ***********************************

INT_TX
	MOV	W, #$FA	; preset timer with 252 ( 256 - 6  = 250 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 6 + 8us ( int handling code )= 27 us
	MOV	RTCC, W

	SB	clkCount.0	; check if we should toggle clock
	JMP	INT_DEC_CLOCK	; bit low,decrement and check if we should toggle data instead

	DECSZ	clkCount	; decrement and check if we are at zero..
	JMP	INT_CLOCK	; not zero then toggle clock line

	JMP	INT_EXIT_TX

INT_CLOCK

	SNB	pcCLOCK_out	; check if we are low
	JMP	INT_CLOCK_HIGH	; yep set to high

	SB	pcCLOCK_in	; check if pc is pulling the clock line low
				; i.e. it wants to abort and send instead..
	JMP	INT_TX_CHECK_ABORT	; abort this transfer
	SETB	pcCLOCK_out	; ok to set clock low ( pull down )
	JMP	INTX

INT_CLOCK_HIGH
	CLRB	pcCLOCK_out	; set high ( release line )
;	CLRB	_ClockHigh	;
	JMP	INTX

INT_TX_CHECK_ABORT
	JMP	INT_EXIT_TX

INT_DEC_CLOCK
	DEC	clkCount	; decrement clock counter ( so we toggle next time )
INT_DATA
	SB	bitCount.0	; check bit counter
	JMP	INT_DATA_IDLE	; no data toggle

	DECSZ	bitCount	; decrement bit counter
	JMP	INT_DATA_NEXT	; next bit..

INT_NO_BITS
	SETB	bitCount.0	; just in case ( stupid code, not sure its needed, just
				; to make it impossible to overdecrement )***
	SNB	_isParity	; are we sending parity ?
	JMP	INT_DATA_END	; exit

	; all bits sent
	; delete the last key from the buffer
	CALL	INC_KEY_HEAD	; remove the ( last ) key form the buffer as is was sent ok..

	; all bits sent check parity

	SETB	_isParity	; set flag data is parity

	SB	KeyParity.0	; is the last parity bit high? ( odd num of bits )
				; then parity should be high ( free )
	JMP	INT_DATA_HIGH_PAR	; yes
	SETB	pcDATA_out	; no, parity should be 'low' ( pulled down )
	JMP	INTX	;

INT_DATA_HIGH_PAR:
	CLRB	pcDATA_out	; set parity bit high ( release data line )
	JMP	INTX	; and exit..


INT_DATA_END
	SB	_isStopBit	; is the stopbit sent ?
	JMP	INT_DATA_STOPB	; nope then set stopbit flag

	CLRB	pcDATA_out	; parity bit sent, always release data line ( stop bit )
	JMP	INTX

INT_DATA_STOPB
	SETB	_isStopBit	; set the stopbit flag
	JMP	INTX	; and exit

INT_DATA_IDLE
	DEC	bitCount	; decrement bit counter
	JMP	INTX	; no toggle of data line

INT_DATA_NEXT
	SB	CurrKey.0	; is the last bit of the key_buffer high?
	JMP	INT_DATA_LOW	; no, pull data low
	CLRB	pcDATA_out	; yes, release data line
	INC	KeyParity	; increment parity bit
	JMP	INT_DATA_ROTATE	; rotate data

INT_DATA_LOW	; last bit is low
	SETB	pcDATA_out	; set the bit

INT_DATA_ROTATE
	RR	CurrKey	; rotate right by 1 bit
	JMP	INTX

INT_EXIT_TX
	; setup the timer so we accomplish an delay after an tx seq

	CLRB	_TX_Mode	; clear tx mode flag
	CLRB	pcCLOCK_out	; release clock line
	CLRB	pcDATA_out	; and data line
;
	MOV	W, #$64	; start timer with 100 ( 256-100 = 156 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 156 = (roufly) 0.5 mS

	MOV	RTCC, W

	JMP	INTX


;************** TX code end *****************

;************** RX code start ***************

INT_RX
	MOV	W, #$FA	; preset timer with 252 ( 256 - 6  = 250 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 6 + 8us ( int handling code )= 27 us
	MOV	RTCC, W


	SB	clkCount.0	; check if we should toggle clock
	JMP	INT_RX_DEC_CLOCK	; bit low,decrement and check if we should read data instead

	DECSZ	clkCount	; decrement and check if we are at zero..
	JMP	INT_RX_CLOCK	; not zero then toggle clock line

	CLRB	pcCLOCK_out	; release the clock line we are done..
	CLRB	_RX_Mode	; clear rx mode bit ( go over to heart beat mode )
	JMP	INT_EXIT_RX

INT_RX_CLOCK

	SNB	pcCLOCK_out	; check if we are low
	JMP	INT_RX_CLOCK_HIGH	; yep set to high ( release line )

	SNB	_isStartBit	; check if this is the first bit ( start )
	JMP	INT_RX_START	; clear start bit and continue

	SNB	_isParity	; check if this is the parity bit ( or parity has been received )
	JMP	INT_RX_PAR	; yep check parity

	JMP	INT_RX_BIT	; ok just a 'normal' bit read it


INT_RX_PAR			; check parity
	SNB	_doRXAck	; check the handshake flag
	JMP	INT_RX_HNDSHK	; start handshake check

	SB	pcDATA_in	; is the input high ?
	JMP	INT_RX_PAR_HIGH	; yep
	SNB	KeyParity.0	; is the parity '0' ( should be )
	JMP	INT_RX_PAR_ERR	; nope parity error
	JMP	INT_RX_ACK	; parity ok next should be ack ( we take data line low )

INT_RX_PAR_HIGH
	SB	KeyParity.0	; check that parity bit is '1'
	JMP	INT_RX_PAR_ERR	; nope parity error
	JMP	INT_RX_ACK	; parity ok, next is ack ( we take data line low )

INT_RX_PAR_ERR
	SETB	_KeyError	; set error flag

INT_RX_ACK
	SETB	pcCLOCK_out	; ok to set clock low ( pull down )
	SETB	_doRXAck	; enable ack check
	JMP	INTX

INT_RX_HNDSHK
	SB	_RXEnd	; if we are done dont take data low
	SETB	pcCLOCK_out	; ok to set clock low ( pull down )

	SNB	_RXAckDone	; chek if hand shake ( ack ) is done ?
	SETB	_RXEnd	; ok we are now done just make one more clock pulse

	JMP	INTX	; exit


INT_RX_CLOCK_HIGH

	CLRB	pcCLOCK_out	; set high ( release line )
	SB	_RXAckDone	; are we done.. ?
	JMP	INTX
	SB	_RXDone	; finished ?
	JMP	INTX

	CLRB	_RX_Mode	; and clear rx flag..
	JMP	INT_EXIT_RX	; bye bye baby

INT_RX_DEC_CLOCK

	DEC	clkCount	; decrement clock counter ( so we toggle next time )
	SB	_doRXAck	; check if we are waiting for handshake
	JMP	INTX


	SNB	pcCLOCK_out	; check if the clock is low ( pulled down )
	JMP	INTX	; nope we are pulling down then exit
				; we only take over the data line if
				; the clock is high ( idle )
				; not sure about this though.. ???


	SNB	_RXEnd	; are we done ?
	JMP	INT_RX_END

	; handshake check if data line is free ( high )
	SB	pcDATA_in	; is data line free ?
	JMP	INTX	; nope

	SETB	pcDATA_out	; takeover data line
	SETB	_RXAckDone	; we are done..at next switchover from low-high we exit
	JMP	INTX	;

INT_RX_END
	CLRB	pcDATA_out	; release data line
	SETB	_RXDone	; we are now done
	JMP	INTX

INT_RX_START
	CLRB	_isStartBit	; clear start bit flag
	SETB	pcCLOCK_out	; ok to set clock low ( pull down )
	JMP	INTX
INT_RX_BIT
	CLRB	CurrKey.7
	SB	pcDATA_in	; is bit high
	JMP	INT_RX_NEXT	; nope , it's a '0'
	SETB	CurrKey.7	; set highest bit to 1
	INC	KeyParity	; increase parity bit counter

INT_RX_NEXT
	SETB	pcCLOCK_out	; ok to set clock low ( pull down )

	DECSZ	bitCount	; decrement data bit counter
	JMP	INT_RX_NEXT_OK

	SETB	bitCount.0	; just in case ( so we cannot overdecrement )
	SETB	_isParity	; next bit is parity
	JMP	INTX

INT_RX_NEXT_OK
	CLRB	C	; clear carry, so it doesnt affect receving byte
	RR	CurrKey	; rotate to make room for next bit
	JMP	INTX	; and exit

INT_EXIT_RX

	; handle the recevied key ( if not it is an 'data' byte )

	MOV	W, #$C4	; preset timer with 196 ( 256 - 60  = 196 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 60  + 8 ( int handling code )= 200 us
				;
	MOV	RTCC, W	; this delay seems to be needed ( handshake ? )

	; check if this is an data byte ( rate/delay led status etc )

	TEST	CommandData	; reload into itself ( affect zero flag )

	SB	Z	; check zero flag
	JMP	INT_STORE_DATA	; byte contains data ( rate/delay etc )

	CALL	CHECK_RX_KEY	; no data, handle recevied command
	JMP	INTX

INT_STORE_DATA
	; store data byte in 'currkey',
	; first reply with 'ack'

	MOV	W, #$FA	; keyboard ack
	CALL	ADD_KEY	;

	SB	_IsLedStatus	; is it led status byte ?
	JMP	INT_STORE_RATE	; nope check next

INT_STORE_NUM
	; byte in 'currkey' is led status byte, store it
	MOV	W, CurrKey	; get byte
	MOV	KbLedStatus, W	; and store it
	SNB	_WaitNumLock	; was this something we were waiting for ?
				; i.e. we sent the make scancode for numlock.
	CALL	RELEASE_NUMLOCK	; yep, then send release code for 'soft' numlock

	JMP	INT_STORE_EXIT	; store it in local ram copy and exit

INT_STORE_RATE

	SB	_IsRateDelay	; is it rate/delay byte ?
	JMP	INT_STORE_EXIT	; nope then send ack end exit
	; byte in 'currkey' is rate/delay byte, store it
	MOV	W, CurrKey	; get byte
	MOV	KbRateDelay, W	; and store it

INT_STORE_EXIT

	CLR	CommandData	; clear data byte flags
	JMP	INTX


;******************* 'heart' beat code ( 0.5 ms tick ) *********
; Note: As I 'mess' with the int timer this 'heart beat' is by no means
; an accurate clock. However it can work as a rough estimate for local time.
;
INT_HEART_BEAT

	MOV	W, #$64	; start timer with 100 ( 256-100 = 156 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 156 = (roufly) 0.5 mS

	MOV	RTCC, W


INT_CHECK_CLKDTA:

	; CLOCK DATA   Action
	;-----------+--------
	;   L    L  |  wait ?
	;   L    H  |  wait, buffer keys
	;   H    L  |  start an rx sequence
	;   H    H  |  keyboard can tx

	SB	pcDATA_in	; is the data line high ( free )..
	JMP	INT_CHECK_RX	; Nope it's pulled down, check if rx is requested

	SNB	pcCLOCK_in	; Is the clk line low  ( pulled down ) ?
	JMP	INT_CHECK_BUFF	; Nope, so check if we have any keys to send

	JMP	INT_IDLE	; clock is low , wait and buffer keys( i.e. no rx/tx )



INT_CHECK_RX   		; pc ( probably ) wants to send something..

	SB	pcCLOCK_in	; wait until clock is released before we go into receving mode..
	JMP	INT_RX_IDLE	; nope still low

	; clock now high test if we are set to start an rx seq.
	SB	_RxCanStart	; have we set the flag ?
	JMP	INT_WAIT_RX	; nope then set it

	SNB	pcDATA_in	; make sure that data still is low
	JMP	INT_ABORT_RX	; nope abort rx req, might been a 'glitch'

	; initiate the rx seq.

	CLR	Comm_Flags	; used by both tx/rx routines ( and _RxCanStart bit !! )
	CLR	TRX_Flags	; clear tx/rx flags
	CLR	KeyParity	; clear parity counter

	SETB	_RX_Mode	; set rx mode flag..
	SETB	_isStartBit	; set that next sampling is start bit

	; preset bit and clock counters

	MOV	W, #$2F	; = 47 dec, will toggle clock output every even number until zero
	MOV	clkCount, W	; preset clock pulse counter

	MOV	W, #$08	; = 8 dec, number of bits to read
				; then parity bit will be set instead

	MOV	bitCount, W	; preset bit counter

	; note as we are starting the clock here we allow a longer time before we start
	; the actual 20 us tick, this the first time we wait about 200 us before the first clock 'tick'

	MOV	W, #$C4	; preset timer with 196 ( 256 - 60  = 196 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 60  + 8us ( int handling code )= 200 us
				;
	MOV	RTCC, W

	JMP	INTX	; exit, the next int will start an rx seq

INT_WAIT_RX:
	SETB	_RxCanStart	; set flag so we start rx next int ( 0.5 ms )
INT_RX_IDLE
	; reload clock so we check more often
	MOV	W, #$F0	; start timer with 240 ( 256-16  = 240 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 16 + 8/10us ( int handling code )= (about) 60 us
	MOV	RTCC, W

	JMP	INTX	;

INT_ABORT_RX
	CLRB	_RxCanStart	; clear flag ( forces a 'new' rx start delay )
	JMP	INT_IDLE	;


INT_CHECK_BUFF:
	; check if we have any keys to send to pc

	MOV	W, KeyBufferTail	; get end of buffer to 'w'
	MOV	W, KeyBufferHead-w	; subtract start of buffer if head - tail = zero, no byte
	SNB	Z	; zero flag = no byte to send
	JMP	INT_IDLE	; then do the 'idle' stuff

INT_SEND_KEY
	;key in buffer, get it and initiate an tx seq...
	CALL	GET_KEY_BUFFER	; get the key into CurrKey
	MOV	W, CurrKey
	MOV	LastKey, W	; store last sent key

	; setup our tx/rx vars

	CLR	Comm_Flags	; used by both tx/rx routines ( and _RxCanStart bit !! )
	CLR	TRX_Flags	; clear tx/rx flags
	CLR	KeyParity	; clear parity counter

	SETB	_TX_Mode	; set tx mode flag..

	; preset bit and clock counters

	MOV	W, #$2B	; = 43 dec, will toggle clock out put every even number until zero
	MOV	clkCount, W	; preset clock pulse counter

	MOV	W, #$12	; = 18 dec, will shift data out every even number until zero
				; then parity bit will be set instead
	MOV	bitCount, W	; preset bit counter

	; data now set, initiate the clock to generate a 20 us tick ( rx/tx heart beat )

	SETB	pcDATA_out	; start bit, always 'low' ( we pull down )

	MOV	W, #$FA	; start timer with 252 ( 256-6  = 250 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 6 + 8 ( int handling code )= 27 us
	MOV	RTCC, W

	JMP	INTX	; exit, the next int will start an tx

INT_IDLE:

	; 'Idle' code i.e. no rx or tx possible AND/OR nothing to send

	DEC	Divisor_10ms	; Count 0.5ms down to give 10 milli second tick
	JNZ	INTX		; Exit if divider not zeroed
	MOV	W, #20
	MOV	Divisor_10ms, W	; Preset the divide by 20

	;+++
	; 10 ms tick here



	; Divide the 10 ms tick to give 100 ms second tick
INT_10MS
	DEC	Divisor_100ms	; Count 10ms down to give 100 milli second tick
	JNZ	INTX		; Exit if divider not zeroed
	MOV	W, #10
	MOV	Divisor_100ms, W	; Preset the divide by 10

        ;+++
 	; 100 ms tick here

INT_100MS


	; numlock check !! bit variable _Numlock does not actually set the numlock status directly.
	; However, by setting this bit to '1' we make a test against the current numlock led status
	; ( bit no 1 in variable KbLedStatus ), if that is different from this variable
	; we send a 'numlock' press/release ( to toggle numlock status )
	; so in essence, setting this bit to '1' will force numlock to be 'on' etc.

	SNB	_IsFirstLedStatus	;  if = 1 then we have not yet received numlock status
	JMP	INT_REPEAT_CHECK	; nope, then this is a consecutive byte, store as 'normal'

	SB	_WaitNumLock	; are we waiting for pc numlock reply ?
	JMP	INT_NUMLOCK_CHECK	; yep then do repeat check instead

	DECSZ	Temp_Var	;
	JMP	INT_REPEAT_CHECK

	CALL	RELEASE_NUMLOCK

INT_NUMLOCK_CHECK

	SNB	_LedNumLock	; is the led on ?
	JMP	INT_NUMLOCK_ON	; yep, then test our 'local' numlock state ( wanted numlock state )

	; nope numlock is off, is our wanted state also off ?
	SB	_NumLock	; is wanted state off ?
	JMP	INT_REPEAT_CHECK	; yep continue

	CALL	PRESS_NUMLOCK	; nope then send numlock press/release code

	JMP	INT_REPEAT_CHECK


INT_NUMLOCK_ON
	SNB	_NumLock	; is wanted state also 'on' ?
	JMP	INT_REPEAT_CHECK	; yep

	CALL	PRESS_NUMLOCK	; nope then toggle numlock state

INT_REPEAT_CHECK

	; check if a key should be 'repeated' ( when pressed longer than 500 ms )
	SB	_startRepeat	; start repeating a key ? ( delay !!! )
	JMP	INT_CHECK_KEY	; nope, then check if key should be repeated
	DEC	RepeatTimer	;
JNZ	INT_500MS	; not zero yet, check timer instead

	CLRB	_startRepeat	; stop repeat timer ( delay is accomplished )
	SETB	_doRepeat	; and enable 'key' is still down check
	MOV	W, #02	; start repeat send timer
	MOV	Divisor_Repeat, W	;

	JMP	INT_500MS	; do next timer check

INT_CHECK_KEY
	SB	_doRepeat	; key should be repeated ?
	JMP	INT_500MS	; nope

	; ok key should be repeated, check if it still pressed ?
	CALL	CHECK_KEY_STATE	; uses MakeKeyOffset to calculate which key that was
				; the last pressed, and then check if it's still pressed
				; if still pressed carry = '1',

	SB	C	; check carry
	CLRB	_doRepeat	; clear repeat bit, stop repeating the key

	SB	_doRepeat	; still pressed ?
	JMP	INT_500MS	; nope

	DEC	Divisor_Repeat	; should we send the key ?
;*** WARNING: MPASM macro BNZ is not supported yet. Replace manually.
	BNZ	INT_500MS	; nope

	MOV	W, #DELAY_RATE	; reload timer with key rate delay
;	MOV	W, #02	; restart timer
	MOV	Divisor_Repeat, W	;

	SETB	_doSendKey	; set flag to send key, NOTE the actual sending ( putting into send buffer )
				; is done inside mainloop.


INT_500MS

	DEC	Divisor_500ms	; Count 100ms down to give 500 milli second tick
	JNZ	INTX		; Exit if divider not zeroed
	MOV	W, #05
	MOV	Divisor_500ms, W	; Preset the divide by 5

        ;+++
 	; 500 ms tick here


INT_500_NEXT

	TOGGLE_PIN O_led_KEYCOMM_ok	; toggle the disco light ;-)

	SB	_DoExitAltKeymap	; is the alt keymap toggle key pressed the second time ?
					; if so skip timeout test and exit
	SB	_InAltKeymap	; are we in altkeymap ?
	JMP	INTX	; nope

	; we are in altkeymap, decrement the lastkeytime
	; and check if we are at zero then we exit
	; the altkeymap.

	DEC	LastKeyTime	; decrease time
	JNZ	INTX		; exit, timer has not expired
	; timer expired, get out of altkey map
	SETB	_ExitAltKeymap	;



; ***************** 'heart' beat code end ***************

INTX
;	CLRB	INTCON.T0IF	; Clear the calling flag

	PULL			; Restore registers
	RETI

; **************** end interrupt routine **************


;+++++
; 	Routines that will 'toggle' keyboard numlock status
; 	by sending numlock make/break code
;

PRESS_NUMLOCK:
	MOV	W, #$77	; numlock key scancode, make
	CALL	ADD_KEY
	MOV	W, #$06	; 6 x 100 ms = 600 ms ( release delay )
	MOV	Temp_Var, W	;
	SETB	_WaitNumLock	; we are waitin for numlock status reply from pc
	RET

RELEASE_NUMLOCK:
	MOV	W, #BREAK	; break prefix
	CALL	ADD_KEY
	MOV	W, #$77	; numlock key scancode
	CALL	ADD_KEY
	CLRB	_WaitNumLock
	RET

; ***********************************************************************
;
;  CHECK_RX_KEY - handles the received commands from pc
;
CHECK_RX_KEY
	; check the key in 'currkey' ( command from pc )

CHECK_ED
	MOV	W, CurrKey	; move key buffer into W register
	XOR	W,$ED		; subtract value in W with 0xED
	SB	Z	; check if the zero bit is set
	JMP	CHECK_EE	; the result of the subtraction was not zero check next
	; ok 'ED'=set status leds ( in next byte ) received
	SETB	_IsLedStatus	; set bit that next incoming byte is kb led staus
	JMP	CHECK_SEND_ACK	; send ack

CHECK_EE
	MOV	W, CurrKey	; move key buffer into W register
	XOR	W,$EE		; subtract value in W with 0xEE
	SB	Z	; check if the zero bit is set
	JMP	CHECK_F0	; the result of the subtraction was not zero check next
	; ok 'EE'= echo command received
	JMP	CHECK_SEND_EE	; send echo

CHECK_F0
	MOV	W, CurrKey	; move key buffer into W register
	XOR	W,$F0		; subtract value in W with 0xF0
	SB	Z	; check if the zero bit is set
	JMP	CHECK_F2	; the result of the subtraction was not zero check next
	; ok 'F0'= scan code set ( in next commming byte ) received
	SETB	_SkipByte	; skip next incomming byte ( or dont interpret )
	JMP	CHECK_DONE	; do not send ack !
CHECK_F2
	MOV	W, CurrKey	; move key buffer into W register
	XOR	W,$F2		; subtract value in W with 0xF0
	SB	Z	; check if the zero bit is set
	JMP	CHECK_F3	; the result of the subtraction was not zero check next
	; ok 'F2'= Read ID command responds with 'AB' '83'
	JMP	CHECK_SEND_ID	; send id bytes

CHECK_F3
	MOV	W, CurrKey	; move key buffer into W register
	XOR	W,$F3		; subtract value in W with 0xF3
	SB	Z	; check if the zero bit is set
	JMP	CHECK_FE
;	JMP	CHECK_F4	; the result of the subtraction was not zero check next
	; ok 'F3'= set repeat rate ( in next commming byte ) received
	SETB	_IsRateDelay	; next incomming byte is rate/delay info
	JMP	CHECK_SEND_ACK	; send ack

; **** Note ! removed from test as I 'don't care' ********
; **** i.e. I dont disable or enable the keyboard at any time.

;CHECK_F4
;	MOV	W, CurrKey	; move key buffer into W register
;	XOR	W,$F4		; subtract value in W with 0xF4
;	SB	Z	; check if the zero bit is set
;	JMP	CHECK_F5	; the result of the subtraction was not zero check next
	; ok 'F4'= keyboard enable received
;	JMP	CHECK_SEND_ACK	; send ack
;CHECK_F5
;	MOV	W, CurrKey	; move key buffer into W register
;	XOR	W,$F5		; subtract value in W with 0xF5
;	SB	Z	; check if the zero bit is set
;	JMP	CHECK_FE	; the result of the subtraction was not zero check next
	; ok 'F5'= keyboard disable received
;	JMP	CHECK_SEND_ACK	; send ack
CHECK_FE
	MOV	W, CurrKey	; move key buffer into W register
	XOR	W,$FE		; subtract value in W with 0xFE
	SB	Z	; check if the zero bit is set
	JMP	CHECK_FF	; the result of the subtraction was not zero check next
	; ok 'FE'= resend last sent byte
	MOV	W, LastKey	; get last key
	CALL	ADD_KEY	; and put it on the que
	JMP	CHECK_DONE

CHECK_FF
	MOV	W, CurrKey	; move key buffer into W register
	XOR	W,$FF		; subtract value in W with 0xFF
	SB	Z	; check if the zero bit is set
	JMP	CHECK_ERROR	; the result of the subtraction was not zero, unknown command
	; ok 'FF'= reset keyboard received

	JMP	CHECK_SEND_AA	; send 'AA' power on self test passed

CHECK_ERROR			; unknown command ( or command not interpreted )
	JMP	CHECK_SEND_ACK

CHECK_SEND_ID
	MOV	W, #$FA	; keyboard ack
	CALL	ADD_KEY	;

	MOV	W, #$AB	; keyboard id first byte, always 0xAB
	CALL	ADD_KEY	;

	MOV	W, #$83	; keyboard id second byte, always 0x83
	CALL	ADD_KEY	;

	JMP	CHECK_DONE

CHECK_SEND_ACK

	MOV	W, #$FA	; keyboard ack
	CALL	ADD_KEY	;
	JMP	CHECK_DONE

CHECK_SEND_AA
	MOV	W, #$FA	; keyboard ack
	CALL	ADD_KEY	;

	MOV	W, #$AA	; keyboard post passed
	CALL	ADD_KEY	;
	JMP	CHECK_DONE

CHECK_SEND_EE
	MOV	W, #$EE	; keyboard echo
	CALL	ADD_KEY	;

CHECK_DONE
	RETW	#0	; and we are done

; ***********************************************************************
;  Buffer code ( a bit modified ) from Stewe Lawther
;  http://ourworld.compuserve.com/homepages/steve_lawther/ucindex.htm
;  And of course source of the exellent keyboard viewer. !! ( without which, this
;  project would have been close to impossible ) ( and of course my nifty
;  memory oscilloscope )
;
;  ADD_KEY_BUFFER - (outside int)add the key in CurrKey to our keybuffer que in the first
;		    free position. If there is no more room the oldest byte is
;		    'dumped'.
;  ADD_KEY	  - Same but to be used inside int routine.( just skips int disable code )

ADD_KEY_BUFFER

ADD_STOP_INT			; first stop all interrupts !!!!!!!
	CLRB	INTCON.GIE	; disable global interrupts..
	SNB	INTCON.GIE	; check that is really was disabled
	JMP	ADD_STOP_INT	; nope try again
ADD_KEY				; inside interuppt we call this instead ( as we dont need to disable int :-) )
	MOV	BufTemp, W	; store key temporary
	MOV	W, KeyBufferHead	; move buffer head out of FSR temporarily
	MOV	Temp, W	; store in temp
	MOV	W, KeyBufferTail	; set FSR to buffer tail
	MOV	FSR, W	; set indirect file pointer
	MOV	W, BufTemp	; set W to new scancode to send
	MOV	INDF, W	; and put it in the buffer
	MOV	W, Temp	; get the head pointer back
	MOV	KeyBufferHead, W	;

	INC	KeyBufferTail
	MOV	W, #KbBufferMax		; check if at buffer max
	MOV	W, KeyBufferTail-W	
	MOV	W, KeyBufferTail	; (reload value to w - doesn't affect C)
	SB	C	; if so (negative result)
	MOV	W, #KbBufferMin	; set to buffer min ( wrap around )

	MOV	KeyBufferTail, W
	MOV	W, KeyBufferHead-w	; see if we have any room ( head and tail have meet )
	SNB	Z	; if so (Z set)
	CALL	INC_KEY_HEAD	; dump oldest byte

				; finally turn on interrupts again
	MOV	W, #%10100000	; enable global & TMR0 interrupts
	MOV	INTCON, W

	RET


; ***********************************************************************
;
;  GET_KEY_BUFFER - 	Gets a char from the buffer, and puts it into KeyBuffer
;			NOTE: Does not increase buffer pointers ( dump this key ).
;			A	CALL	to INC_KEY_HEAD will do this if the key is sent ok
GET_KEY_BUFFER
	MOV	W, INDF	;put the byte to send into key buffer
	MOV	CurrKey, W
	RET	; and go back, NOTE ! the key is not
					; removed from the buffer until a call
					; to INC_KEY_HEAD is done.

; ***********************************************************************
;
;  INC_KEY_HEAD - 	dump oldest byte in keybuffer, Do not call if byte
;			has not been fetched before ( GET_KEY_BUFFER )
;
INC_KEY_HEAD:
	INC	KeyBufferHead		; set to next byte in buffer
	MOV	W, #KbBufferMax	
	MOV	W, KeyBufferHead-W	; check if at buffer max
	MOV	W, KeyBufferHead	; (reload value to w - doesn't affect C)
	SB	C	; if so (negative result)
	MOV	W, #KbBufferMin	; set to buffer min ( wrap around )
	MOV	KeyBufferHead, W	; and store ( in FSR )
	RET	; go back



; ***********************************************************************
;
;  CHECK_KEY_STATE - Check if the last pressed key is still pressed
;		     Returns with carry = '1' if still pressed
;		     else carry = '0' ( or error )
;

CHECK_KEY_STATE:
	; uses LastMakeOffset to calculate which key to test

	MOV	W, LastMakeOffset	; get offset
	AND	W, #$18	; mask out column bits
				; lastmake offset has the following bits:
				; '000yyxxx' where 'yy' is column no
				; and 'xxx' is key num,
	SNB	Z	; zero = column 1 & 2
	JMP	CHECK_COL_12	; it is in column 1

	MOV	repTemp, W	; save it temporary
	XOR	W,$08		; subtract value in W with 0x08 ( columns 3 & 4 )
	SNB	Z	; check if the zero bit is set
	JMP	CHECK_COL_34	; it is in column 3 & 4

	MOV	W, repTemp	; get the column bits back
	XOR	W,$10		; subtract value in W with 0x10 ( columns 5 & 6 )
	SNB	Z	; check if the zero bit is set
	JMP	CHECK_COL_56	; it is in column 5 & 6

CHECK_COL_78
	MOV	W, kbColumn78_Old	; get bit map ( key status ) for keys in column 7 & 8
	MOV	repKeyMap, W	; and store it
	JMP	CHECK_KEY	; and continue to check bit

CHECK_COL_56
	MOV	W, kbColumn56_Old	; get bit map ( key status ) for keys in column 5 & 6
	MOV	repKeyMap, W	; and store it
	JMP	CHECK_KEY	; and continue to check bit

CHECK_COL_34
	MOV	W, kbColumn34_Old	; get bit map ( key status ) for keys in column 3 & 4
	MOV	repKeyMap, W	; and store it
	JMP	CHECK_KEY	; and continue to check bit

CHECK_COL_12
	MOV	W, kbColumn12_Old	; get bit map ( key status ) for keys in column 1 & 2
	MOV	repKeyMap, W	; and store it

;<-------Alt keymap code------->

	;Checks ONLY column 1&2 bitmap ( keymap ) ( as it is now )
	; this code has to be moved/changed if another column is choosen as the alt. keymap toggle key

	; alternative keymap handling if key r3 c1 is pressed ( bit 2 in column 1&2 )
	; then enable alternative keymap ( only if keyrepeat is disabled )

	; check if this was the last key pressed

	; check bit representing the alt. keymap key ( i've choosen key 2 )

	MOV	W, LastMakeOffset	; get key offset again
	AND	W, #$07	; mask out column bits
	XOR   W,$02		; check if its bit num 2 ( the enter 'alt keymap' key )

	SB	Z	; check if the zero bit is set	
	JMP	CHECK_KEY	; nope than another key was the last
				; skip altkeymap enable

	; the altkeymap key was the last pressed !
	; is key repeat disabled ?

	SB	I_jmp_NoRepeat	; check if repeat code is enabled ?
	JMP	CHECK_KEY	; yep, then skip altkeymap enable test

	; enable altkeymap if key is still pressed

	SNB	repKeyMap.2	; test bit 2 ( should be key 'F7' )
	JMP	CHECK_ENABLE_ALT	; ok enable altkeymap ( if we are not already in altkeymap )
	JMP	CHECK_KEY	; nope another key in column 1&2 continue check

CHECK_ENABLE_ALT
	SNB	_AltKeymap	; are we already in altkeymap ?
	JMP	CHECK_KEY	; yep then just continue

	; We are just entering/enabling the alt. keymap

	SETB	_AltKeymap	; enable alternative keymap

	; Example of using an 'advanced' alt keymap handling
	; not enabled, to avoid intial confusion.

	; I.E This snippet would only be called once when we
	; are just entering(enabling) the alternative keymapping !

	; This example code will 'soft' release the current altkeymap key ( it is in pressed state ! )
	; ( i.e send release code for the enter alt. keymap key 'F7' )
	; send the make scancode for left <alt> key instead.
	; and force numlock to be off.

	; Do not use if you dont understand the implifications !

	; Also note that the scancodes are hardcoded here !
	; ( i.e do not use the lookup table definition of the key/s )

	; ***** start snippet

;	MOV	W, #BREAK	; send break prefix
;	CALL	ADD_KEY
;	MOV	W, #$83	; and scancode for the enter alt keymap
;	CALL	ADD_KEY
;	MOV	W, #$11	; send make code for the left <alt> key
;	CALL	ADD_KEY

	; example of forcing the numlock status to a particular state
	; the numlockstatus will change ( be checked ) inside the int routine
	; See also at the end of KB_DEBOUNCE_12 where the numlock status
	; will be restored when we release the key

;	CLRB	_NumLock	; 'force' numlock to be off

	; This bit MUST also be checked as we do not know if we have recevied
	; first numlock status byte yet ( pc does not send numlock/led status
	; after intial poweron, if not one of the numlock/capslock/scrolllock are pressed )
	; i.e. if you connect this keyboard to a 'running' pc, the numlock status
	; will be unknown.However if connected before poweron, it will be updated
	; as numlock status is sent during pre-boot seq.

;	SNB	_IsFirstLedStatus	; have we recevied numlock status yet ?
;	CALL	PRESS_NUMLOCK

	; ***** end snippet

CHECK_KEY
	; 'normal' key down check
	; column for pressed key is now in repKeyMap
	MOV	W, LastMakeOffset	; get offset again
	AND	W, #$07	; mask out key number ( lowest 3 bits )

	SNB	Z	; bit num zero ?
	JMP	CHECK_KEY_DONE	; yep lowest bit, check and return

	MOV	repTemp, W	; and store it
CHECK_KEY_LOOP
	RR	repKeyMap	; rotate one step to right
	DECSZ	repTemp	; decrement bit counter
	JMP	CHECK_KEY_LOOP	; loop again

CHECK_KEY_DONE
	; ok the key to test should now be the lowest bit in repKeyMap
	CLRB	C	; clear carry
	SNB	repKeyMap.0	; check bit 0
	SETB	C	; ok key is pressed set carry
	RET	; and we are done..

; ***********************************************************************
;
;  DELAY_1ms - 	Delay routine ! used when scanning our own keyboard
; 		Delay is between output of adress to 4051 and reading of inputs
; 		Increase to have a slower 'scan' rate or decrease to have a higher scan rate.
;

DELAY_1ms
	MOV	W, #$F0	; wait 255 cycles
	MOV	kbTemp, W	; this var is 'safe' to be used in side mainloop
	MOV	W, #$03
	MOV	kbState, W
DELAY_LOOP
	DECSZ	kbTemp	; decrement
	JMP	$-1	;

	MOV	W, #$F0
	MOV	kbTemp, W
	DECSZ	kbState
	JMP	DELAY_LOOP

	RET


;---------------------------------------------------------------------------
;
;		Initialisation
;
;---------------------------------------------------------------------------


INIT:

	;+++
	;	Set up the ports

		; PORT A
	MODE	$0F
	BANK1
	MOV	W, #%00000110	; Set port data directions RA1,RA2 inputs RA0,RA3,RA4 outputs
	MOV	RA!,W			; PC keyboard connections
	

		; PORT B

		; Used for our own 3x8 matrix keyboard

        BANK1
	MOV	W, #%11111000	; Set port data directions RB4-RB7 inputs rest outputs
	MOV	RB!, W


	;	Clear all registers on bank 0 ( memory )

	BANK0
	MOV	W, #$0C
	MOV	FSR, W

INITMEM
	CLR	0	; Clear a register pointed to be FSR
	INC	FSR
	CLR	!WDT	; clear watchdog
	MOV	W, #$50	; Test if at top of memory
	MOV	W, FSR-w
	JNZ	INITMEM		; Loop until all cleared

	;+++
	;	Initiate the keybuffer pointers

INIT_BUFF:

	MOV	W, #KbBufferMin	; get adress of first buffer byte
	MOV	KeyBufferHead, W	; store in FSR
	MOV	KeyBufferTail, W	; and set last byte to the same ( no bytes in buffer )

	;+++
	;	Preset the timer dividers

	MOV	W, #20
	MOV	Divisor_10ms, W
	MOV	W, #10
	MOV	Divisor_100ms, W
	MOV	W, #05
	MOV	Divisor_500ms, W

	;+++
	;	Set up Timer 0.

	;	Set up TMR0 to generate a 0.5ms tick
	;	Pre scale of /8, post scale of /1

	BANK1

	MOV	W, #%00000010	; Initialisation of TMR0 prescale 8 '010'
                                ; weak pullup enabled by latch values.

	MOV	!OPTION, W	; load option reg with prescale of 8

        BANK0

	MOV	W, #$64	; start timer with 100 ( 256-100 = 156 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 156 = (roufly) 0.5 mS

	MOV	RTCC, W



;---------------------------------------------------------------------------
;
;		the main 'program' loop ( starts really at MAIN_LOOP )
;
;---------------------------------------------------------------------------

MAIN:
	SETB	pcDATA_in
	SETB	pcCLOCK_in
	CLRB	pcDATA_out
	CLRB	pcCLOCK_out
	CLR	RB

	MOV	W, #$08	; preset the column counter
	MOV	kbColumnCnt, W	;

	SETB	_NumLock	; default state is numlock = on
	SETB	_IsFirstLedStatus	; we have not yet recevied led status byte.

	MOV	W, #%10100000	; enable global & TMR0 interrupts
	MOV	INTCON, W

	CLR	!WDT	; clear watchdog
	SB	O_led_KEYCOMM_ok
	JMP	$-2	; make an 0.5 second delay here
					; i.e. the led will come on when 0.5 seconds has passed
					; set inside the timer int.

	CLR	!WDT	; clear watchdog
	SNB	O_led_KEYCOMM_ok
	JMP	$-2	; make an additional 0.5 second delay here
					; i.e. the led will be dark when 0.5 seconds has passed
					; set inside the timer int.


	MOV	W, #$AA	; post passed :-), always 0xAA
	CALL	ADD_KEY_BUFFER

		; now go into infinite loop, the pc kb interface runs in the background ( as an int )
		; where we continuously monitor the pcCLOCK/DATA_in lines


MAIN_LOOP:
		; check whatever :-)
	CLR	!WDT	; clear watchdog

MAIN_CHECK_COL_1:
		; scan our own keyboard, first four bits

		; address and read column, read as complement so key pressed = '1'
		; since we pull down when key is pressed ( weak pullup enabled )
		; ( i.e. pressed key has level 0V ) to make it more 'logical' to work with

	CLR	kbColumnVal

		; get column counter / adress out
	MOV	W, kbColumnCnt

	MOV	RB, W	; set the columns adress to the 74HCT4051
					; i.e. make column low
		IFNDEF	DEBUG
	CALL	DELAY_1ms	; wait 1 ms let pins stabilize
		ENDIF

	MOV	W, /RB	; read back the pin values ( complement i.e. key pressed = '1' )
	AND	W, #%11110000	; mask out unused pins

	MOV	kbColumnVal, W	; store the pin values

	SWAP	kbColumnVal	; swap nibbles ( low<->high ) to make room for next column

	INC	kbColumnCnt	; inc column adress
MAIN_CHECK_COL_2:
		; read next four bits
		; put out adress and read next column, read as complement so key pressed = '1'
		; this as we pull down when key is pressed ( weak pullup enabled )

		; get column counter / adress out
	MOV	W, kbColumnCnt

	MOV	RB, W	; set the columns adress to the 74HCT4051
					; i.e. make column low

		IFNDEF	DEBUG
	CALL	DELAY_1ms	; wait 1 ms
		ENDIF

	MOV	W, /RB	; read back the pin values ( complement i.e. key pressed = '1' )
	AND	W, #%11110000	; mask out unused pins

	ADD	kbColumnVal, W	; and store pin values

	INC	kbColumnCnt

		; reset column counter check
		; i.e. we are 'only' using adress 0 - 7
	MOV	W, kbColumnCnt
	XOR	W,$08		; subtract value in W with 0x08
	SB	Z	; check if the zero bit is set
	JMP	MAIN_CHECK_DEBOUNCE	; nope continue

	CLR	kbColumnCnt	; reset counter/adress

MAIN_CHECK_DEBOUNCE:
	CALL	KB_DEBOUNCE	; do debouncing on the current values and send make/break
					; for any key that has changed
					; NOTE uses the current column adress to determine which
					; columns to debounce!
MAIN_REPEAT:
	SB	I_jmp_NoRepeat	; check if repeat code is enabled ?
	JMP	MAIN_CHECK_REPEAT	; yep check key repeating

		; keyrepeat disabled then do check on exit of altkeymap instead

	SB	_ExitAltKeymap	; we want to exit altkeymap ?
	JMP	MAIN_LOOP	; nope


		; check that ALL keys are released
		; before exiting the alt keymap
	TEST	kbColumn78_Old	; reload column 78 to itself ( affect zero flag )
	SB	Z	; check if zero ?
	JMP	MAIN_LOOP	; key/s still down in column 78

	TEST	kbColumn56_Old	; reload column 56 to itself ( affect zero flag )
	SB	Z	; check if zero ?
	JMP	MAIN_LOOP	; key/s still down in column 56

	TEST	kbColumn34_Old	; reload column 34 to itself ( affect zero flag )
	SB	Z	; check if zero ?
	JMP	MAIN_LOOP	; key/s still down in column 34

	TEST	kbColumn12_Old	; reload column 12 to itself ( affect zero flag )
	SB	Z	; check if zero ?
	JMP	MAIN_LOOP	; key/s still down in column 12

		; all keys released !!
	CLRB	_AltKeymap	; exit altkeymap
	CLRB	_ExitAltKeymap	; exit release check
	CLRB	_InAltKeymap	; clear flag for second keypress check
	CLRB	_DoExitAltKeymap	;
	JMP	MAIN_LOOP


MAIN_CHECK_REPEAT
	SB	_doSendKey	; if we should send a repeated key
	JMP	MAIN_LOOP	; nope continue

		; send the key in RepeatedKey but first check if its an extended key
	SB	_RepeatIsExt	; is it extended ?
	JMP	MAIN_SEND_REPEAT	; nope just send scan code

		; last key pressed was extended send extended prefix
	MOV	W, #EXTENDED	; get extended code
	CALL	ADD_KEY_BUFFER	; and put it into the buffer

MAIN_SEND_REPEAT:
	MOV	W, RepeatKey	; get key code for the last pressed key
	CALL	ADD_KEY_BUFFER	; and put it into the buffer
	CLRB	_doSendKey	; and clear the flag, it will be set again
					; inside int handler if key still is pressed

	JMP	MAIN_LOOP	; and return


; ***********************************************************************
;
;  KB_SEND_KEY - uses the Offset stored in var 'Offset' to fetch a key from our
;		 key lookup table.
;	  	 then checks the bit var _isBreak to see if make or break codes should be sent
;		 It then puts the code/s into the key buffer ( for sending later, in int routine )

KB_SEND_KEY:

	MOV	W, Offset	; get current offset
	MOV	TempOffset, W	; save it ( to be used in key repeat code, is its 'make' )
				; temp offset has the following bits:
				; '000yyxxx' where 'yy' is column offset
				; and 'xxx' is key num,


	CLRB	C	; clear carry so it dont affect byte rotation
	RL	Offset	; first rotate
	RL	Offset	; second rotate

				; offset no have the following bits:
				; '0yyxxx01' where 'yy' is column offset
				; and 'xxx' is key num,
				; as each key in table has 4 bytes of 'space'

	INC	Offset	; add one, for the 'movwf pcl' at the start of the table

	CLRB	Offset.7	; clear to bit, just in case so we dont
				; 'overflow' the table, should not be needed !

	CLRB	_isExtended	; clear extended flag

	MOV	W, #LOW LOOKUP_KEY	; get low bit of table adress
	ADD	Offset, W	; 8 bit add
	PAGE	LOOKUP_KEY	; get high 5 bits
	SNB	C	; is page boundary crossed ?
	PAGE	LOOKUP_KEY+$200
	MOV	W, Offset	; load computed offset in w
	CLRB	C	; clear carry ( default= key is not extended )
				; if key is extended then carry is set in jumptable lookup_key

	CALL	LOOKUP_KEY	; get key scan code/s for this key
				; key scan code/s are saved in
				; W - scancode, should go into kbScan
				; carry set - extend code
				; carry clear - not extended code

	MOV	kbScan, W	; store scancode
	; if carry is set then key is extended so first send extended code
	; before any make or break code

	SB	C	; check carry flag
	JMP	KB_CHK_BREAK	; nope then check make/break status

	SETB	_isExtended	; set extended flag
	MOV	W, #EXTENDED	;
	CALL	ADD_KEY_BUFFER	; get extended code and put in in the buffer

KB_CHK_BREAK:

	; check if it's make or break
	SB	_isBreak	; check if its pressed or released ?
	JMP	KB_DO_MAKE_ONLY	; send make code

	CLRB	_isBreak	; clear bit for next key

	; break code, key is released
	MOV	W, #BREAK	; get break code
	CALL	ADD_KEY_BUFFER	; and put into buffer
	JMP	KB_DO_MAKE	; and send key code also

	; key is pressed !
KB_DO_MAKE_ONLY:
	CLRB	_doSendKey	; stop repeat sending
	CLRB	_doRepeat	; and bit for repeat key send
	SETB	_startRepeat	; and set flag for start key repeat check
	CLRB	_RepeatIsExt	; clear repeat key extended flag ( just in case )

	SNB	_isExtended	; is it extended ?
	SETB	_RepeatIsExt	; set the flag

	; save this key in 'last' pressed, to be used in key repeat code

	MOV	W, TempOffset	; get saved offset
	MOV	LastMakeOffset, W	; and store it

	; if keyrepat = enabled, alternative mapping = disabled
	SB	I_jmp_NoRepeat	; check if repeat code is enabled ?
	JMP	KB_REP_NOR	; yep set normal delay ( 800 ms )

	; else keyrepat = disabled, alternative mapping = enabled
	MOV	W, #DELAY_ENTER_ALTKEYMAP	;reload delay before entering the altkeymap ( 3 sec )
				; i.e how long the enter altkeymap key must be pressed before
				; we enable altkey keymap codes.

	JMP	KB_REP_SET	; and set it
KB_REP_NOR:

	MOV	W, #DELAY_REPEAT	; reload 'normal' repeat delay ( 800 ms )

KB_REP_SET:
	MOV	RepeatTimer, W	; and (re)start the timer for key repeat
	MOV	W, kbScan	; get key scan code
	MOV	RepeatKey, W	; and save it

KB_DO_MAKE:
	; key pressed/released ( i.e. the scancode is sent both on make and break )
	MOV	W, kbScan	; get scan code into w
	CALL	ADD_KEY_BUFFER	; and add to send buffer


	; reset the 'get out of alt. keymap timer for each keypress
	; note don't care if we are 'in' alt. keymap. Reset this timer anyway
	; as the code for checking if we are currently in alt. key map
	; would be as long as it takes to reset the timer.

	MOV	W, #DELAY_EXIT_ALTKEYMAP	; reload the delay for exiting the altkeymap when no
				; key is pressed ( 7.5 sec )
	MOV	LastKeyTime, W	; (re)set lastkey timer ( used to get out of altkeymap )
	RET

; ***********************************************************************
;
;  KB_DEBOUNCE - debounces two column readings from our keyboard
;		 If a bit 'state' has been 'stable' for 4 consecutive debounces
;	  	 the 'new' byte is updated with the new state
;		 'normal' loop time ( no tx/rx/key press ) is about 2-3 ms
;		 so from 'key' down until 'new' is updated it takes about 8-10 ms
;		 ( as we are scanning columns two by two, the whole keyboard needs
;		 4 loops to be fully updated, then 4 debounce samples for each 'pair' )

KB_DEBOUNCE:
	; debounce current column(s)
	TEST	kbColumnCnt	; reload value into itself ( affect zero flag )
	SNB	Z	; is it zero ?
	JMP	KB_DEBOUNCE_78	; debounce columns 7 & 8

	MOV	W, kbColumnCnt	; move column counter into W register
;*** WARNING: Manual replacement required for "SUBLW k" instruction (w = k - w). Check if previous instruction is a skip instruction. 
	SUBLW	H'04'		; subtract value in W with 0x04 ( columns 5 & 6 )
	SNB	Z	; check if the zero bit is set
	JMP	KB_DEBOUNCE_34	; debounce columns 3 & 4

	MOV	W, kbColumnCnt	; move column counter into W register
;*** WARNING: Manual replacement required for "SUBLW k" instruction (w = k - w). Check if previous instruction is a skip instruction. 
	SUBLW	H'06'		; subtract value in W with 0x02 ( columns 3 & 4 )
	SNB	Z	; check if the zero bit is set
	JMP	KB_DEBOUNCE_56	; ok column 1 & 2 debounce

	; all above tests 'failed'
	; columns to debouce are 1 & 2

KB_DEBOUNCE_12:
	; debounce columns 1 & 2
	DEBOUNCE_BYTE	kbColumnVal,kbColumn12_New,kbColumn12Cnt,kbColumn12State

	MOV	W, kbColumn12_New	; get debounced sample
	XOR	W, kbColumn12_Old	; get changed bits

	SNB	Z	; check if zero = no change
	RET	; no change. return

	; key/s has been changed, w contains which key/s that has been changed in column 7 & 8
	; Note ! Not the actual state of the key, only 'change has occured' = '1'

	MOV	kbState, W	; save change bit/s
	MOV	W, #$07	; preset bit counter
	MOV	kbBitCnt, W	; loop though all eight bits.
	CLRB	_LastColumn	; clear end seq bit ( set when are done with last bit )

	MOV	W, kbColumn12_New	; get new sample
	MOV	kbTemp, W	; and store it

KB_LOOP_12
	CLR	Offset	; clear offset counter ( for table read )

	CLRB	C	; clear carry
	RL	kbState	; rotate left, and store back
	SB	C	; check carry '1' = bit was high = change has occured
	JMP	KB_LOOP_12_SKIP	; nope, no change check next bit ( or exit )

	; bit changed
	MOV	W, kbBitCnt	; get bit counter ( for offset calc. )
	MOV	Offset, W	; store bit num ( for offset )

	CLRB	C	; clear carry
	RL	kbTemp	; rotate left ( next bit )
	SB	C	; check carry '1' = key is down ( i.e. make )
	SETB	_isBreak	; c = '0' = send break code, i.e. key is released

	CALL	KB_SEND_KEY	; send key code/s make/break uses
				; Offset, and _isBreak vars


	JMP	KB_LOOP_12_NEXT

KB_LOOP_12_SKIP
	RL	kbTemp	; rotate so we read next key

KB_LOOP_12_NEXT
	SNB	_LastColumn	; are we done ?
	JMP	KB_12_DONE	; yep, save new key bit map and exit

	DECSZ	kbBitCnt	; decrement bit counter
	JMP	KB_LOOP_12	; bits left
	SETB	_LastColumn	; set bit so we break out after next run
	JMP	KB_LOOP_12

KB_12_DONE:
	; and update our 'last known' status for the columns
	MOV	W, kbColumn12_New	; get new status
	MOV	kbColumn12_Old, W	; and store it..

;<-------Alt keymap code------->

	; ***** alternative keymap handling
	; The alternative keymap is enabled by pressing key r4 c1 ( i.e. bit 3 in column 12 )
	; Here, we enable a check to turn off alternative keymap if
	; that key and all others are released ( bit is cleared ).
	; ( else no (alternative)break codes would be sent for those keys that are still pressed )
	; NOTE: _Altkeymap is set inside int routine when checking
	; keyrepeat so there is a 'variable' delay before the altkeymap is active
	;

	SB	_AltKeymap	; is altkeymap enabled ?
	RET	; nope return


	SNB	_InAltKeymap	; are we in altkeymap ?
	JMP	KB_12_IN	; yep alt keymap key has been released once

	; nope still waiting for first release
	SB	kbColumn12_Old.2	; is key released ? ( first time )
	JMP	KB_12_ALT	; yep, reset timers and set bit variables

KB_12_IN
	SNB	_DoExitAltKeymap	; are we waiting for release ?
	JMP	KB_12_OUT	; yes

	; the key has been released once test for second press
	SNB	kbColumn12_Old.2	; is it still pressed ?
	JMP	KB_12_ALT2	; yep

	SB	_DoExitAltKeymap	; are we now waiting for the last ( second ) release ?
	RET	; nope

KB_12_OUT
	SB	kbColumn12_Old.2	; check if key still pressed ?
	SETB	_ExitAltKeymap	; nope, then enable exit check that
					; will exit alt keymap as soon as all key are released

KB_12_ALT2
	SETB	_DoExitAltKeymap	; check for second release
	RET
KB_12_ALT
	; first release of the enter alt keymap key
	; reset 'get out' timer and set bit variables to enable check
	; for second press/release

	MOV	W, #$0F	; x0.5 sec = 7.5 sec
	MOV	LastKeyTime, W	; (re)set lastkey timer ( used to get out of altkeymap automaticly)

	SETB	_InAltKeymap	; yep the first time, then set flag that we are now
				; waiting for a second press/release to exit alt key map
				; all keys are released before exiting altkeymap

	;***** Example snippet(one line) to be paired with code in CHECK_KEY_STATE where I
	; forced numlock status to be off while enetering the alt keymap
	; but have not yet released the alt keymap toggle key.
	; this code will be called at the first release of this key. Used
	; to restore numlock status.
	; As said before, do not use if implifications are not known !

;	SETB	_NumLock	; and also force numlock to be 'on'
				; as it is set to 'off' when we enter altkeymap
				; we must set it 'back'

	RET

KB_DEBOUNCE_34:
	; debounce columns 3 & 4
	DEBOUNCE_BYTE	kbColumnVal,kbColumn34_New,kbColumn34Cnt,kbColumn34State

	MOV	W, kbColumn34_New	; get debounced sample
	XOR	W, kbColumn34_Old	; get changed bits

	SNB	Z	; check if zero = no change
	RET	; no change. return

	; key/s has been changed, w contains which key/s that has been changed in column 7 & 8
	; Note ! Not the actual state of the key, only 'change has occured' = '1'

	MOV	kbState, W	; save change bit/s
	MOV	W, #$07	; preset bit counter
	MOV	kbBitCnt, W	; loop though all eight bits.
	CLRB	_LastColumn	; clear end seq bit ( set when are done with last bit )

	MOV	W, kbColumn34_New	; get new sample
	MOV	kbTemp, W	; and store it

KB_LOOP_34
	CLR	Offset	; clear offset counter ( for table read )

	CLRB	C	; clear carry
	RL	kbState	; rotate left, and store back
	SB	C	; check carry '1' = bit was high = change has occured
	JMP	KB_LOOP_34_SKIP	; nope, no change check next bit ( or exit )

	; bit changed
	MOV	W, kbBitCnt	; get bit counter ( for offset calc. )
	MOV	Offset, W	; store bit num ( for offset )

	SETB	Offset.3	; set bit 3 for table read ( column 3 & 4 )

;	CLRB	_isBreak	; clear break flag
	CLRB	C	; clear carry
	RL	kbTemp	; rotate left ( next bit )
	SB	C	; check carry '1' = key is down ( i.e. make )
	SETB	_isBreak	; c = '0' = send break code, i.e. key is released

	CALL	KB_SEND_KEY	; send key code/s make/break uses
				; Offset, and _isBreak vars


	JMP	KB_LOOP_34_NEXT

KB_LOOP_34_SKIP
	RL	kbTemp	; rotate so we read next key

KB_LOOP_34_NEXT
	SNB	_LastColumn	; are we done ?
	JMP	KB_34_DONE	; yep, save new key bit map and exit

	DECSZ	kbBitCnt	; decrement bit counter
	JMP	KB_LOOP_34	; bits left
	SETB	_LastColumn	; set bit so we break out after next run
	JMP	KB_LOOP_34

KB_34_DONE:
	; and update our 'last known' status for the columns
	MOV	W, kbColumn34_New	; get new status
	MOV	kbColumn34_Old, W	; and store it..
	RET


KB_DEBOUNCE_56:
	; debounce columns 5 & 6
	DEBOUNCE_BYTE	kbColumnVal,kbColumn56_New,kbColumn56Cnt,kbColumn56State

	MOV	W, kbColumn56_New	; get debounced sample
	XOR	W, kbColumn56_Old	; get changed bits

	SNB	Z	; check if zero = no change
	RET	; no change. return

	; key/s has been changed, w contains which key/s that has been changed in column 7 & 8
	; Note ! Not the actual state of the key, only that 'a change has occured' = '1'

	MOV	kbState, W	; save change bit/s
	MOV	W, #$07	; preset bit counter
	MOV	kbBitCnt, W	; loop though all eight bits.
	CLRB	_LastColumn	; clear end seq bit ( set when are done with last bit )

	MOV	W, kbColumn56_New	; get new sample
	MOV	kbTemp, W	; and store it

KB_LOOP_56
	CLR	Offset	; clear offset counter ( for table read )

	CLRB	C	; clear carry
	RL	kbState	; rotate left, and store back
	SB	C	; check carry '1' = bit was high = change has occured
	JMP	KB_LOOP_56_SKIP	; nope, no change check next bit ( or exit )

	; bit changed
	MOV	W, kbBitCnt	; get bit counter ( for offset calc. )
	MOV	Offset, W	; store bit num ( for offset )

	SETB	Offset.4	; set bit 4 for table read ( column 5 & 6 )

	CLRB	C	; clear carry
	RL	kbTemp	; rotate left ( next bit )
	SB	C	; check carry '1' = key is down ( i.e. make )
	SETB	_isBreak	; c = '0' = send break code, i.e. key is released

	CALL	KB_SEND_KEY	; send key code/s make/break uses
				; Offset, and _isBreak vars


	JMP	KB_LOOP_56_NEXT

KB_LOOP_56_SKIP
	RL	kbTemp	; rotate so we read next key

KB_LOOP_56_NEXT
	SNB	_LastColumn	; are we done ?
	JMP	KB_56_DONE	; yep, save new key bit map and exit

	DECSZ	kbBitCnt	; decrement bit counter
	JMP	KB_LOOP_56	; bits left
	SETB	_LastColumn	; set bit so we break out after next run
	JMP	KB_LOOP_56

KB_56_DONE:
	; and update our 'last known' status for the columns
	MOV	W, kbColumn56_New	; get new status
	MOV	kbColumn56_Old, W	; and store it..
	RET

KB_DEBOUNCE_78:
	; debounce columns 7 & 8
	DEBOUNCE_BYTE	kbColumnVal,kbColumn78_New,kbColumn78Cnt,kbColumn78State

	MOV	W, kbColumn78_New	; get debounced sample
	XOR	W, kbColumn78_Old	; get changed bits

	SNB	Z	; check if zero = no change
	RET	; no change. return

	; key/s has been changed, w contains which key/s that has been changed in column 7 & 8
	; Note ! Not the actual state of the key, only 'change has occured' = '1'

	MOV	kbState, W	; save change bit/s
	MOV	W, #$07	; preset bit counter
	MOV	kbBitCnt, W	; loop though all eight bits. ( 7-0 )
	CLRB	_LastColumn	; clear end seq bit ( set when are done with last bit )

	MOV	W, kbColumn78_New	; get new sample
	MOV	kbTemp, W	; and store it

KB_LOOP_78
	CLR	Offset	; clear offset counter ( for table read )
	CLRB	C	; clear carry
	RL	kbState	; rotate left, and store back
	SB	C	; check carry '1' = bit was high = change has occured
	JMP	KB_LOOP_78_SKIP	; nope, no change check next bit ( or exit )

	; bit changed
	MOV	W, kbBitCnt	; get bit counter ( for offset calc. )
	MOV	Offset, W	; store bit num ( for offset )

	SETB	Offset.4	; set bit 3,4 for table read ( column 7 & 8 )
	SETB	Offset.3	;

	CLRB	C	; clear carry
	RL	kbTemp	; rotate left ( next bit )
	SB	C	; check carry '1' = key is down ( i.e. make )
	SETB	_isBreak	; c = '0' = send break code, i.e. key is released


	CALL	KB_SEND_KEY	; send key code/s make/break uses
				; Offset, and _isBreak vars

	JMP	KB_LOOP_78_NEXT

KB_LOOP_78_SKIP
	RL	kbTemp	; rotate so we read next key

KB_LOOP_78_NEXT
	SNB	_LastColumn	; are we done ?
	JMP	KB_78_DONE	; yep, save new key bit map and exit

	DECSZ	kbBitCnt	; decrement bit counter
	JMP	KB_LOOP_78	; bits left
	SETB	_LastColumn	; set bit so we break out after next run
	JMP	KB_LOOP_78

KB_78_DONE:
	; and update our 'last known' status for the columns
	MOV	W, kbColumn78_New	; get new status
	MOV	kbColumn78_Old, W	; and store it..
	RET


; ***********************************************************************
;
;  LOOKUP_KEY -  lookup table for key scancodes.
;		 Returns a scancode in w
;		 Sets carry if key is extended
;		 NOTE: If key R3 C1 has been pressed longer than keyrepeat delay
;		 AND keyrepeat is disabled ( jumper on pin RB3 ) THEN the
; 		 bit _AltKeymap is set and we can return an alternative scancode in W
;

LOOKUP_KEY	; lookup table for the keys ( 32 keys x 4 lines + 1 = 129 lines )
		; keys are labelled Rx - Cy where x = row number and y = column number
		; handles a 4 row x 8 column keyboard = 32 keys

	MOV	PC, W	; add to program counter
; R1 - C1 i.e. key 1
	NOP
	NOP
	NOP
	RETW	#$05	; scan code 'F1'
; R2 - C1 i.e. key 2
	NOP
	NOP
	NOP
	RETW	#$0C	; scan code 'F4'
; R3 - C1 i.e. key 3
	; The famous alternative keymap toggle key !!!  ;-)
	; It is adviced that this key does not use an alt scancode
	; makes things cleaner and simplified.
	; IF USED though, remember that a 'soft' release code must be sent
	; for this key when entering the altkeymap !( + 'soft' make code for the alternative key )
	; This as the key is pressed when entering altkeymap
	; which makes the bit _Altkeymap be set, and hence when released
	; the release code for this 'normal' key will never be sent
	; instead the release code for the alternative key will be sent.
	; To 'fix' this put the release code at the end of CHECK_KEY_STATE ( where the
	; alt keymap bit is set ).i.e. break prefix+scancode for normal key.
	NOP
	NOP
	NOP
	RETW	#$83	; scan code for F7 ( in 'normal' mode )
; R4 - C1 i.e. key 4
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$76	; send scancode for 'ESC'  instead
	SETB	C	; set carry ( i.e. extended code )
	RETW	#$6B	; scan code 'arrow left' '<-'
; R1 - C2 i.e. key 5
	NOP
	NOP
	NOP
	RETW	#$06	;  scan code 'F2' hex06
; R2 - C2 i.e. key 6
	NOP
	NOP
	NOP
	RETW	#$03	; scan code 'F5'
; R3 - C2 i.e. key 7
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$0D	; send scancode for 'horizontaltab' HT instead
	SETB	C	; set carry ( i.e. extended code )
	RETW	#$75	; scan code 'arrow up' '^'
; R4 - C2 i.e. key 8
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$14	; send scancode for 'left ctrl' instead
	SETB	C	; set carry ( i.e. extended code )
	RETW	#$72	; scan code 'arrow down'
; R1 - C3 i.e. key 9
	NOP
	NOP
	NOP
	RETW	#$04	; scan code 'F3'
; R2 - C3 i.e. key 10
	NOP
	NOP
	NOP
	RETW	#$0B	; scan code 'F6'
; R3 - C3 i.e. key 11
	NOP
	NOP
	NOP
	RETW	#$0A	; scan code 'F8'
; R4 - C3 i.e. key 12
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$11	; send scancode for 'left alt' instead
	SETB	C	; set carry ( i.e. extended code )
	RETW	#$74	; scan code 'arrow right' '->'
; R1 - C4 i.e. key 13
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$6C	; send scancode for numeric '7' instead
	NOP
	RETW	#$3D	; scan code '7'
; R2 - C4 i.e. key 14
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$6B	; send scancode for numeric '4' instead
	NOP
	RETW	#$25	; scan code '4'
; R3 - C4 i.e. key 15
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$69	; send scancode for numeric '1' instead
	NOP
	RETW	#$16	; scan code '1'
; R4 - C4 i.e. key 16
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$7B	; send scancode for numeric '-' instead
	NOP
	RETW	#$4A	; scan code '-' minus ( swe kbd )
; R1 - C5 i.e. key 17
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$75	; send scancode for numeric '8' instead
	NOP
	RETW	#$3E	; scan code '8'
; R2 - C5 i.e. key 18
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$73	; send scancode for numeric '5' instead
	NOP
	RETW	#$2E	; scan code '5'
; R3 - C5 i.e. key 19
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$72	; send scancode for numeric '2' instead
	NOP
	RETW	#$1E	; scan code '2'
; R4 - C5 i.e. key 20
	SB	_AltKeymap	; check for alternative keymap
	RETW	#$45	; scan code '0' ( from keypad ) normal key
	SETB	C	; set carry ( i.e. extended code )
	RETW	#$1F	; alt keycode ( windows start menu activate )
; R1 - C6 i.e. key 21
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$7D	; send scancode for numeric '9' instead
	NOP
	RETW	#$46	; scan code '9'
; R2 - C6 i.e. key 22
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$74	; send scancode for numeric '6' instead
	NOP
	RETW	#$36	; scan code '6'
; R3 - C6 i.e. key 23
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$7A	; send scancode for numeric '3' instead
	NOP
	RETW	#$26	; scan code '3'
; R4 - C6 i.e. key 24
	SB	_AltKeymap	; check for alternative keymap
	RETW	#$49	; scan code '.' ( swe kbd ) normal key
				; use alternative keymap
	SETB	C	; set carry ( i.e. extended code )
	RETW	#$4A	; send scancode for numeric '/' instead
; R1 - C7 i.e. key 25
	SNB	_AltKeymap	; check for alternative keymap
	RETW	#$79	; send scancode for numeric '+' instead
	NOP
	RETW	#$4E	; scan code '+'
; R2 - C7 i.e. key 26
	SB	_AltKeymap	; check for alternative keymap
	RETW	#$66	; scan code 'back space' BS, normal key
				; use alternative keymap
	SETB	C	; set carry ( i.e. extended code )
	RETW	#$71	; send scancode for 'delete' instead
; R3 - C7 i.e. key 27
	SB	_AltKeymap	; check for alternative keymap
	RETW	#$5A	; scan code 'enter', normal key
				; use alternative keymap
	SETB	C	; set carry ( i.e. extended code )
	RETW	#$5A	; send scancode for numeric enter instead ( note ! extended )
; R4 - C7 i.e. key 28
	SB	_AltKeymap	; check for alternative keymap
	RETW	#$5A	; scan code 'enter', normal key
				; use alternative keymap
	SETB	C	; set carry ( i.e. extended code )
	RETW	#$5A	; send scancode for numeric enter instead ( note ! extended )
; R1 - C8 i.e. key 29
	NOP
	NOP
	NOP
	RETW	#$2C	; scan code 't'
; R2 - C8 i.e. key 30
	NOP
	NOP
	NOP
	RETW	#$24	; scan code 'e'
; R3 - C8 i.e. key 31
	NOP
	NOP
	NOP
	RETW	#$1B	; scan code 's'
; R4 - C8 i.e. key 32
	NOP
	NOP
	NOP
	RETW	#$2C	; scan code 't'

    END