;				PiCBoard
;
;               PC-Keyboard emulator using a PIC16F84 
;     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			2000-01-23
; ITERATION		1.0B
; FILE SAVED AS		PiCBoard.ASM	
; FOR			PIC16F84-10/P		
; 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
;
;
;
;
;***************************************************************************
;
;				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://204.210.50.240/techref/default.asp?url=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 return the keyboard to the normal keymap.
;	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"



		Processor       16F84
		Radix   DEC
		EXPAND

	

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

	INCLUDE <C:\PROGRAM\MPLAB\P16F84.INC>       	; this might need changing !

;***** CONFIGURATION BITS 

    __CONFIG _WDT_ON&_HS_OSC&_CP_OFF&_PWRTE_ON		; _WDT_OFF
    
    __IDLOCS 010Bh	; 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
	BCF	STATUS,RP0
	ENDM

BANK1	MACRO
	BSF	STATUS,RP0
	ENDM

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

PUSH	MACRO
	MOVWF	Saved_w		; Save W register on current bank
	SWAPF	STATUS,W	; Swap status to be saved into W
	BANK0			; Select BANK0
	MOVWF	Saved_Status    ; Save STATUS register on bank 0
	MOVFW	PCLATH
	MOVWF	Saved_Pclath	; Save PCLATH on bank 0
	ENDM

PULL	MACRO
	BANK0			; Select BANK0
	MOVFW	Saved_Pclath
	MOVWF	PCLATH		; Restore PCLATH
	SWAPF	Saved_Status,W
	MOVWF	STATUS		; Restore STATUS register - restores bank
	SWAPF	Saved_w,F
	SWAPF	Saved_w,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
		
		BTFSC	WHICH_PORT,WHICH_PIN	; is the pin high ?
		GOTO 	TOGGLE_PIN10		; yes, clear it
		BSF	WHICH_PORT,WHICH_PIN	; no, so set it										
	    	GOTO   	TOGGLE_END
TOGGLE_PIN10:
		BCF	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:
;       MOVF    SB,W    ;W = B
;       XORWF   SA,F    ;A+ = A ^ B
;       COMF    SB,F    ;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        

	MOVF    DBState,W
        XORWF   DBCnt,F        
	COMF    DBState,F

    	;See if any changes occurred        

	MOVF    NewSample,W        
	XORWF   DebouncedSample,W

    	;Reset the counter if no change has occurred        

	ANDWF   DBState,F
        ANDWF   DBCnt,F    	;Determine the counter's state
        MOVF    DBState,W        
	IORWF   DBCnt,W

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

	ANDWF   DebouncedSample,F
        XORLW   0xff    	;Re-write the bits that haven't changed.
        ANDWF   NewSample,W        
	IORWF   DebouncedSample,F        
	
	ENDM

	

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


;	Reset Vector

	ORG	H'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

        CLRF	PCLATH
	CLRF	INTCON	

	GOTO	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	H'04'

INT
	PUSH			; Save registers and set to BANK 0
				
	
	BTFSS   INTCON,T0IF    	; check if TMR0 interrupt
        GOTO    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	
	BCF	INTCON,T0IF 	; Clear the calling flag !
	
 	BTFSC	_TX_Mode	; check if we are in tx mode
	GOTO	INT_TX		; yep, goto tx mode code..	
	BTFSC	_RX_Mode	; are we in rx mode ?
	GOTO	INT_RX		; yep goto rx mode code
	GOTO	INT_HEART_BEAT  ; nope just goto 'heart beat' mode

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

INT_TX
	MOVLW   H'FA'           ; preset timer with 252 ( 256 - 6  = 250 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 6 + 8us ( int handling code )= 27 us
    	MOVWF   TMR0
 
	BTFSS	clkCount,0	; check if we should toggle clock
	GOTO	INT_DEC_CLOCK	; bit low,decrement and check if we should toggle data instead
	
	DECFSZ	clkCount,F	; decrement and check if we are at zero..
	GOTO	INT_CLOCK 	; not zero then toggle clock line
	
	GOTO	INT_EXIT_TX		

INT_CLOCK
	
	BTFSC	pcCLOCK_out	; check if we are low
	GOTO	INT_CLOCK_HIGH  ; yep set to high
				
	BTFSS	pcCLOCK_in	; check if pc is pulling the clock line low
				; i.e. it wants to abort and send instead..
	GOTO	INT_TX_CHECK_ABORT	; abort this transfer
	BSF	pcCLOCK_out	; ok to set clock low ( pull down )
	GOTO	INTX

INT_CLOCK_HIGH
	BCF	pcCLOCK_out	; set high ( release line )
				;BCF	_ClockHigh	;
	GOTO	INTX

INT_TX_CHECK_ABORT
	GOTO	INT_EXIT_TX

INT_DEC_CLOCK	
	DECF	clkCount,F	; decrement clock counter ( so we toggle next time )
INT_DATA
	BTFSS	bitCount,0	; check bit counter
	GOTO	INT_DATA_IDLE	; no data toggle

	DECFSZ	bitCount,F	; decrement bit counter
	GOTO	INT_DATA_NEXT	; next bit..

INT_NO_BITS
	BSF	bitCount,0	; just in case ( stupid code, not sure its needed, just
				; to make it impossible to overdecrement )***
	BTFSC	_isParity	; are we sending parity ?
	GOTO	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
	
	BSF	_isParity	; set flag data is parity
	
	BTFSS	KeyParity, 0    ; is the last parity bit high? ( odd num of bits )
				; then parity should be high ( free )
	GOTO 	INT_DATA_HIGH_PAR  	; yes
	BSF	pcDATA_out	; no, parity should be 'low' ( pulled down )
	GOTO 	INTX		;

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


INT_DATA_END	
	BTFSS	_isStopBit	; is the stopbit sent ?
	GOTO	INT_DATA_STOPB	; nope then set stopbit flag

	BCF	pcDATA_out	; parity bit sent, always release data line ( stop bit )		
	GOTO	INTX

INT_DATA_STOPB
	BSF	_isStopBit	; set the stopbit flag
	GOTO	INTX		; and exit

INT_DATA_IDLE
	DECF	bitCount,F	; decrement bit counter
	GOTO 	INTX		; no toggle of data line

INT_DATA_NEXT
	BTFSS 	CurrKey,0	; is the last bit of the key_buffer high?
	GOTO 	INT_DATA_LOW	; no, pull data low
	BCF  	pcDATA_out	; yes, release data line 
	INCF    KeyParity,F	; increment parity bit
	GOTO	INT_DATA_ROTATE	; rotate data 
	
INT_DATA_LOW	; last bit is low
	BSF	pcDATA_out	; set the bit

INT_DATA_ROTATE
	RRF	CurrKey,F	; rotate right by 1 bit	
	GOTO	INTX

INT_EXIT_TX
	; setup the timer so we accomplish an delay after an tx seq
	
	BCF	_TX_Mode	; clear tx mode flag
	BCF	pcCLOCK_out	; release clock line
	BCF	pcDATA_out	; and data line
;
	MOVLW   H'64'           ; start timer with 100 ( 256-100 = 156 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 156 = (roufly) 0.5 mS
                                
        MOVWF   TMR0

	GOTO	INTX


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

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

INT_RX
	MOVLW   H'FA'           ; preset timer with 252 ( 256 - 6  = 250 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 6 + 8us ( int handling code )= 27 us
    	MOVWF   TMR0


	BTFSS	clkCount,0	; check if we should toggle clock
	GOTO	INT_RX_DEC_CLOCK	; bit low,decrement and check if we should read data instead
	
	DECFSZ	clkCount,F	; decrement and check if we are at zero..
	GOTO	INT_RX_CLOCK 	; not zero then toggle clock line
	
	BCF	pcCLOCK_out	; release the clock line we are done..
	BCF	_RX_Mode	; clear rx mode bit ( go over to heart beat mode )
	GOTO	INT_EXIT_RX			

INT_RX_CLOCK

	BTFSC	pcCLOCK_out	; check if we are low
	GOTO	INT_RX_CLOCK_HIGH 	; yep set to high ( release line )
	
	BTFSC	_isStartBit	; check if this is the first bit ( start )
	GOTO	INT_RX_START	; clear start bit and continue
	
	BTFSC	_isParity	; check if this is the parity bit ( or parity has been received )
	GOTO	INT_RX_PAR	; yep check parity

	GOTO	INT_RX_BIT	; ok just a 'normal' bit read it
	
	
INT_RX_PAR			; check parity
	BTFSC	_doRXAck	; check the handshake flag
	GOTO	INT_RX_HNDSHK	; start handshake check

	BTFSS	pcDATA_in	; is the input high ?
	GOTO	INT_RX_PAR_HIGH	; yep
	BTFSC	KeyParity,0	; is the parity '0' ( should be )
	GOTO	INT_RX_PAR_ERR	; nope parity error
	GOTO	INT_RX_ACK	; parity ok next should be ack ( we take data line low )

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

INT_RX_PAR_ERR
	BSF	_KeyError	; set error flag

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

INT_RX_HNDSHK
	BTFSS	_RXEnd		; if we are done dont take data low
	BSF	pcCLOCK_out	; ok to set clock low ( pull down )
	
	BTFSC	_RXAckDone	; chek if hand shake ( ack ) is done ?
	BSF	_RXEnd		; ok we are now done just make one more clock pulse

	GOTO	INTX		; exit	
		

INT_RX_CLOCK_HIGH

	BCF	pcCLOCK_out	; set high ( release line )
	BTFSS	_RXAckDone	; are we done.. ?
	GOTO	INTX
	BTFSS	_RXDone		; finished ?
	GOTO	INTX

	BCF	_RX_Mode	; and clear rx flag..
	GOTO	INT_EXIT_RX	; bye bye baby

INT_RX_DEC_CLOCK	

	DECF	clkCount,F	; decrement clock counter ( so we toggle next time )
	BTFSS	_doRXAck	; check if we are waiting for handshake
	GOTO	INTX
	

 	BTFSC	pcCLOCK_out     ; check if the clock is low ( pulled down )
	GOTO	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.. ???

 	
	BTFSC	_RXEnd		; are we done ?
	GOTO	INT_RX_END

	; handshake check if data line is free ( high )
	BTFSS	pcDATA_in	; is data line free ?
	GOTO	INTX		; nope

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

INT_RX_END
	BCF	pcDATA_out	; release data line
	BSF	_RXDone		; we are now done
	GOTO	INTX

INT_RX_START
	BCF	_isStartBit	; clear start bit flag
	BSF	pcCLOCK_out	; ok to set clock low ( pull down )
	GOTO	INTX
INT_RX_BIT
	BCF	CurrKey,7
	BTFSS	pcDATA_in	; is bit high
	GOTO	INT_RX_NEXT	; nope , it's a '0'
	BSF	CurrKey,7	; set highest bit to 1
	INCF	KeyParity,F	; increase parity bit counter
	
INT_RX_NEXT
	BSF	pcCLOCK_out	; ok to set clock low ( pull down )

	DECFSZ	bitCount,F	; decrement data bit counter	
	GOTO	INT_RX_NEXT_OK

	BSF	bitCount,0	; just in case ( so we cannot overdecrement )
	BSF	_isParity	; next bit is parity
	GOTO	INTX

INT_RX_NEXT_OK
	CLRC			; clear carry, so it doesnt affect receving byte
	RRF 	CurrKey,F	; rotate to make room for next bit
	GOTO	INTX		; and exit

INT_EXIT_RX	

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

	MOVLW   H'C4'           ; preset timer with 196 ( 256 - 60  = 196 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 60  + 8 ( int handling code )= 200 us
				; 
    	MOVWF   TMR0		; this delay seems to be needed ( handshake ? )
	
	; check if this is an data byte ( rate/delay led status etc )
	
	MOVF	CommandData,F	; reload into itself ( affect zero flag )

	BTFSS	STATUS,Z	; check zero flag
	GOTO	INT_STORE_DATA	; byte contains data ( rate/delay etc )
	
	CALL	CHECK_RX_KEY	; no data, handle recevied command 
	GOTO	INTX

INT_STORE_DATA
	; store data byte in 'currkey', 
	; first reply with 'ack'
	
	MOVLW	H'FA'		; keyboard ack
	CALL	ADD_KEY		;

	BTFSS	_IsLedStatus	; is it led status byte ?
	GOTO	INT_STORE_RATE  ; nope check next
	
INT_STORE_NUM
	; byte in 'currkey' is led status byte, store it
	MOVF	CurrKey,W	; get byte
	MOVWF	KbLedStatus	; and store it
	BTFSC	_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

	GOTO	INT_STORE_EXIT	; store it in local ram copy and exit

INT_STORE_RATE

	BTFSS	_IsRateDelay	; is it rate/delay byte ?
	GOTO	INT_STORE_EXIT  ; nope then send ack end exit
	; byte in 'currkey' is rate/delay byte, store it
	MOVF	CurrKey,W	; get byte
	MOVWF	KbRateDelay	; and store it
	
INT_STORE_EXIT
	
	CLRF	CommandData	; clear data byte flags
	GOTO	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

	MOVLW   H'64'           ; start timer with 100 ( 256-100 = 156 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 156 = (roufly) 0.5 mS
                                
        MOVWF   TMR0


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
		
	BTFSS	pcDATA_in	; is the data line high ( free )..
	GOTO	INT_CHECK_RX	; Nope it's pulled down, check if rx is requested
		
	BTFSC	pcCLOCK_in	; Is the clk line low  ( pulled down ) ?		
	GOTO	INT_CHECK_BUFF  ; Nope, so check if we have any keys to send

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

		
						
INT_CHECK_RX   		; pc ( probably ) wants to send something..
		
	BTFSS	pcCLOCK_in	; wait until clock is released before we go into receving mode..
	GOTO	INT_RX_IDLE	; nope still low

	; clock now high test if we are set to start an rx seq. 
	BTFSS	_RxCanStart     ; have we set the flag ?
	GOTO	INT_WAIT_RX	; nope then set it  	
			
	BTFSC	pcDATA_in	; make sure that data still is low
	GOTO	INT_ABORT_RX	; nope abort rx req, might been a 'glitch'

	; initiate the rx seq.

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

	BSF	_RX_Mode      	; set rx mode flag..
	BSF	_isStartBit	; set that next sampling is start bit
	
	; preset bit and clock counters 

	MOVLW	H'2F'		; = 47 dec, will toggle clock output every even number until zero
	MOVWF	clkCount	; preset clock pulse counter

	MOVLW	H'08'		; = 8 dec, number of bits to read
				; then parity bit will be set instead

	MOVWF	bitCount	; 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'     

	MOVLW   H'C4'           ; preset timer with 196 ( 256 - 60  = 196 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 60  + 8us ( int handling code )= 200 us
				; 
    	MOVWF   TMR0
	
	GOTO	INTX		; exit, the next int will start an rx seq

INT_WAIT_RX:
	BSF	_RxCanStart	; set flag so we start rx next int ( 0.5 ms )
INT_RX_IDLE
	; reload clock so we check more often
	MOVLW   H'F0'           ; start timer with 240 ( 256-16  = 240 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 16 + 8/10us ( int handling code )= (about) 60 us
    	MOVWF   TMR0

	GOTO	INTX		;

INT_ABORT_RX
	BCF	_RxCanStart	; clear flag ( forces a 'new' rx start delay )
	GOTO	INT_IDLE	;
  		 									

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

        MOVF 	KeyBufferTail,W ; get end of buffer to 'w'
        SUBWF 	KeyBufferHead,W ; subtract start of buffer if head - tail = zero, no byte
        BTFSC   STATUS, Z       ; zero flag = no byte to send         
        GOTO	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
	MOVF	CurrKey,W
	MOVWF	LastKey		; store last sent key

	; setup our tx/rx vars 

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

	BSF	_TX_Mode      	; set tx mode flag..
	
	; preset bit and clock counters

	MOVLW	H'2B'		; = 43 dec, will toggle clock out put every even number until zero
	MOVWF	clkCount	; preset clock pulse counter

	MOVLW	H'12'		; = 18 dec, will shift data out every even number until zero
				; then parity bit will be set instead
	MOVWF	bitCount	; preset bit counter

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

	BSF	pcDATA_out	; start bit, always 'low' ( we pull down )
     
	MOVLW   H'FA'           ; start timer with 252 ( 256-6  = 250 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 6 + 8 ( int handling code )= 27 us
        MOVWF   TMR0
	
	GOTO	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

	DECF	Divisor_10ms,F  ; Count 0.5ms down to give 10 milli second tick
	BNZ	INTX		; Exit if divider not zeroed
	MOVLW	.20
	MOVWF	Divisor_10ms     ; Preset the divide by 20

	;+++
	; 10 ms tick here
	


	; Divide the 10 ms tick to give 100 ms second tick
INT_10MS
	DECF	Divisor_100ms,F	; Count 10ms down to give 100 milli second tick
	BNZ	INTX		; Exit if divider not zeroed
	MOVLW	.10
	MOVWF	Divisor_100ms	; 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.

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

	BTFSS	_WaitNumLock	; are we waiting for pc numlock reply ?
	GOTO	INT_NUMLOCK_CHECK ; yep then do repeat check instead
	
	DECFSZ	Temp_Var,F	;
	GOTO	INT_REPEAT_CHECK

	CALL	RELEASE_NUMLOCK

INT_NUMLOCK_CHECK

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

	; nope numlock is off, is our wanted state also off ?
	BTFSS	_NumLock	; is wanted state off ?
	GOTO	INT_REPEAT_CHECK ; yep continue   

	CALL	PRESS_NUMLOCK	; nope then send numlock press/release code

	GOTO	INT_REPEAT_CHECK
			

INT_NUMLOCK_ON
	BTFSC	_NumLock	; is wanted state also 'on' ?
	GOTO	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 )
	BTFSS	_startRepeat	; start repeating a key ? ( delay !!! )
	GOTO	INT_CHECK_KEY	; nope, then check if key should be repeated
	DECF	RepeatTimer,F	;
	BNZ	INT_500MS	; not zero yet, check timer instead 

	BCF	_startRepeat	; stop repeat timer ( delay is accomplished )
	BSF	_doRepeat	; and enable 'key' is still down check
	MOVLW	.02		; start repeat send timer
	MOVWF	Divisor_Repeat  ;

	GOTO	INT_500MS	; do next timer check

INT_CHECK_KEY
	BTFSS	_doRepeat	; key should be repeated ?
	GOTO	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', 

	BTFSS	STATUS,C	; check carry
	BCF	_doRepeat	; clear repeat bit, stop repeating the key 

	BTFSS	_doRepeat	; still pressed ?
	GOTO	INT_500MS	; nope

	DECF	Divisor_Repeat,F  ; should we send the key ?
	BNZ	INT_500MS	; nope

	MOVLW	DELAY_RATE	; reload timer with key rate delay
	;MOVLW	.02		; restart timer
	MOVWF	Divisor_Repeat  ;
	
	BSF	_doSendKey	; set flag to send key, NOTE the actual sending ( putting into send buffer )
				; is done inside mainloop.
 	
	
INT_500MS
	
	DECF	Divisor_500ms,F	; Count 100ms down to give 500 milli second tick
	BNZ	INTX		; Exit if divider not zeroed
	MOVLW	.05
	MOVWF	Divisor_500ms	; Preset the divide by 5

        ;+++
 	; 500 ms tick here


INT_500_NEXT
	
	TOGGLE_PIN O_led_KEYCOMM_ok	; toggle the disco light ;-) 

	BTFSS	_DoExitAltKeymap	; is the alt keymap toggle key pressed the second time ?
					; if so skip timeout test and exit
	BTFSS	_InAltKeymap	; are we in altkeymap ?
	GOTO	INTX		; nope 
	
	; we are in altkeymap, decrement the lastkeytime
	; and check if we are at zero then we exit 
	; the altkeymap.

	DECF	LastKeyTime,F	; decrease time
	BNZ	INTX		; exit, timer has not expired
	; timer expired, get out of altkey map
	BSF	_ExitAltKeymap	;



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

INTX
	;BCF	INTCON,T0IF 	; Clear the calling flag

	PULL			; Restore registers
	RETFIE
	
; **************** end interrupt routine **************


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

PRESS_NUMLOCK:	 
		MOVLW	H'77' 		; numlock key scancode, make
		CALL	ADD_KEY
		MOVLW	H'06' 		; 6 x 100 ms = 600 ms ( release delay )
		MOVWF	Temp_Var	;
		BSF	_WaitNumLock    ; we are waitin for numlock status reply from pc
		RETURN

RELEASE_NUMLOCK:
		MOVLW	BREAK		; break prefix
		CALL	ADD_KEY	
		MOVLW	H'77' 		; numlock key scancode 
		CALL	ADD_KEY
		BCF	_WaitNumLock
		RETURN
	
; ***********************************************************************
;
;  CHECK_RX_KEY - handles the received commands from pc
;
CHECK_RX_KEY	
	; check the key in 'currkey' ( command from pc )

CHECK_ED
	MOVF	CurrKey,W	; move key buffer into W register
	SUBLW	H'ED'		; subtract value in W with 0xED
	BTFSS   STATUS, Z	; check if the zero bit is set
	GOTO	CHECK_EE	; the result of the subtraction was not zero check next
	; ok 'ED'=set status leds ( in next byte ) received
	BSF	_IsLedStatus	; set bit that next incoming byte is kb led staus 
	GOTO	CHECK_SEND_ACK	; send ack

CHECK_EE	
	MOVF	CurrKey,W	; move key buffer into W register
	SUBLW	H'EE'		; subtract value in W with 0xEE
	BTFSS   STATUS, Z	; check if the zero bit is set
	GOTO	CHECK_F0	; the result of the subtraction was not zero check next
	; ok 'EE'= echo command received
	GOTO	CHECK_SEND_EE	; send echo

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

CHECK_F3	
	MOVF	CurrKey,W	; move key buffer into W register
	SUBLW	H'F3'		; subtract value in W with 0xF3
	BTFSS   STATUS, Z	; check if the zero bit is set
	GOTO	CHECK_FE
;	GOTO	CHECK_F4	; the result of the subtraction was not zero check next
	; ok 'F3'= set repeat rate ( in next commming byte ) received
	BSF	_IsRateDelay	; next incomming byte is rate/delay info
	GOTO	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	
;	MOVF	CurrKey,W	; move key buffer into W register
;	SUBLW	H'F4'		; subtract value in W with 0xF4
;	BTFSS   STATUS, Z	; check if the zero bit is set
;	GOTO	CHECK_F5	; the result of the subtraction was not zero check next
	; ok 'F4'= keyboard enable received
;	GOTO	CHECK_SEND_ACK	; send ack
;CHECK_F5	
;	MOVF	CurrKey,W	; move key buffer into W register
;	SUBLW	H'F5'		; subtract value in W with 0xF5
;	BTFSS   STATUS, Z	; check if the zero bit is set
;	GOTO	CHECK_FE	; the result of the subtraction was not zero check next
	; ok 'F5'= keyboard disable received
;	GOTO	CHECK_SEND_ACK	; send ack
CHECK_FE	
	MOVF	CurrKey,W	; move key buffer into W register
	SUBLW	H'FE'		; subtract value in W with 0xFE
	BTFSS   STATUS, Z	; check if the zero bit is set
	GOTO	CHECK_FF	; the result of the subtraction was not zero check next
	; ok 'FE'= resend last sent byte
	MOVF	LastKey,W	; get last key
	CALL	ADD_KEY		; and put it on the que
	GOTO	CHECK_DONE

CHECK_FF
	MOVF	CurrKey,W	; move key buffer into W register
	SUBLW	H'FF'		; subtract value in W with 0xFF
	BTFSS   STATUS, Z	; check if the zero bit is set
	GOTO	CHECK_ERROR	; the result of the subtraction was not zero, unknown command
	; ok 'FF'= reset keyboard received
	
	GOTO	CHECK_SEND_AA	; send 'AA' power on self test passed

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

CHECK_SEND_ID
	MOVLW	H'FA'		; keyboard ack
	CALL	ADD_KEY		;

	MOVLW	H'AB'		; keyboard id first byte, always 0xAB
	CALL	ADD_KEY		;

	MOVLW	H'83'		; keyboard id second byte, always 0x83
	CALL	ADD_KEY		;

	GOTO	CHECK_DONE

CHECK_SEND_ACK

	MOVLW	H'FA'		; keyboard ack
	CALL	ADD_KEY		;
	GOTO	CHECK_DONE

CHECK_SEND_AA
	MOVLW	H'FA'		; keyboard ack
	CALL	ADD_KEY		; 

	MOVLW	H'AA'		; keyboard post passed
	CALL	ADD_KEY		;
	GOTO	CHECK_DONE

CHECK_SEND_EE
	MOVLW	H'EE'		; keyboard echo
	CALL	ADD_KEY		;

CHECK_DONE
	RETLW	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 !!!!!!! 
	BCF	INTCON,GIE	; disable global interrupts..
	BTFSC	INTCON,GIE	; check that is really was disabled
	GOTO	ADD_STOP_INT	; nope try again
ADD_KEY				; inside interuppt we call this instead ( as we dont need to disable int :-) )
	MOVWF	BufTemp		; store key temporary
	MOVF    KeyBufferHead,W ; move buffer head out of FSR temporarily
        MOVWF   Temp            ; store in temp  
        MOVF    KeyBufferTail,W ; set FSR to buffer tail
        MOVWF   FSR		; set indirect file pointer
        MOVF    BufTemp,W       ; set W to new scancode to send
        MOVWF   INDF            ; and put it in the buffer
        MOVF    Temp,W		; get the head pointer back
        MOVWF   KeyBufferHead	;

        INCF    KeyBufferTail,W ; get the end of the buffer
        SUBLW   KbBufferMax     ; check if at buffer max
        INCF    KeyBufferTail,W ; (reload value to w - doesn't affect C) 
        BTFSS   STATUS, C       ; if so (negative result)
        MOVLW   KbBufferMin     ; set to buffer min ( wrap around )

        MOVWF   KeyBufferTail	
        SUBWF   KeyBufferHead,W ; see if we have any room ( head and tail have meet ) 
        BTFSC   STATUS, Z       ; if so (Z set)
        CALL    INC_KEY_HEAD    ; dump oldest byte

				; finally turn on interrupts again
        MOVLW   b'10100000'     ; enable global & TMR0 interrupts
        MOVWF   INTCON

	RETURN


; ***********************************************************************
;
;  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	
	  	MOVF    INDF, W         ;put the byte to send into key buffer
                MOVWF   CurrKey
		RETURN			; 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    INCF    KeyBufferHead,W ; set to next byte in buffer
                SUBLW   KbBufferMax     ; check if at buffer max
                INCF    KeyBufferHead,W ; (reload value to w - doesn't affect C) 
                BTFSS   STATUS, C       ; if so (negative result)
                MOVLW   KbBufferMin     ; set to buffer min ( wrap around )
                MOVWF   KeyBufferHead   ; and store ( in FSR )
                RETURN			; 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
	
	MOVF	LastMakeOffset,W	; get offset 
	ANDLW	H'18'		; mask out column bits  
				; lastmake offset has the following bits:
				; '000yyxxx' where 'yy' is column no
				; and 'xxx' is key num, 
	BTFSC	STATUS,Z	; zero = column 1 & 2
	GOTO	CHECK_COL_12	; it is in column 1

	MOVWF	repTemp		; save it temporary
	SUBLW	H'08'		; subtract value in W with 0x08 ( columns 3 & 4 )
	BTFSC   STATUS, Z	; check if the zero bit is set
	GOTO	CHECK_COL_34	; it is in column 3 & 4

	MOVF	repTemp,W	; get the column bits back
	SUBLW	H'10'		; subtract value in W with 0x10 ( columns 5 & 6 )
	BTFSC   STATUS, Z	; check if the zero bit is set
	GOTO	CHECK_COL_56	; it is in column 5 & 6
	
CHECK_COL_78
	MOVF	kbColumn78_Old,W ; get bit map ( key status ) for keys in column 7 & 8
	MOVWF	repKeyMap	; and store it
	GOTO	CHECK_KEY	; and continue to check bit

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

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

CHECK_COL_12
	MOVF	kbColumn12_Old,W ; get bit map ( key status ) for keys in column 1 & 2
	MOVWF	repKeyMap	; 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 )

	MOVF	LastMakeOffset,W ; get key offset again
	ANDLW	H'07' 		; mask out column bits
	SUBLW   H'02'		; check if its bit num 2 ( the enter 'alt keymap' key )

	BTFSS   STATUS, Z	; check if the zero bit is set	GOTO	CHECK_KEY	; nope than another key was the last
				; skip altkeymap enable

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

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

	; enable altkeymap if key is still pressed
	
	BTFSC	repKeyMap,2	; test bit 2 ( should be key 'F7' )
	GOTO	CHECK_ENABLE_ALT ; ok enable altkeymap ( if we are not already in altkeymap )
	GOTO	CHECK_KEY	; nope another key in column 1&2 continue check 

CHECK_ENABLE_ALT
	BTFSC	_AltKeymap	; are we already in altkeymap ?
	GOTO	CHECK_KEY	; yep then just continue

	; We are just entering/enabling the alt. keymap

	BSF	_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 
	
	;MOVLW	BREAK	; send break prefix
	;CALL	ADD_KEY
	;MOVLW	H'83'   ; and scancode for the enter alt keymap
	;CALL	ADD_KEY
	;MOVLW	H'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
	
	;BCF	_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.
	
	;BTFSC	_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
	MOVF	LastMakeOffset,W ; get offset again
	ANDLW	H'07'		; mask out key number ( lowest 3 bits )
	
 	BTFSC	STATUS,Z	; bit num zero ?
	GOTO	CHECK_KEY_DONE	; yep lowest bit, check and return
	
   	MOVWF	repTemp		; and store it
CHECK_KEY_LOOP
	RRF	repKeyMap,F	; rotate one step to right
	DECFSZ	repTemp,F		; decrement bit counter
	GOTO	CHECK_KEY_LOOP	; loop again

CHECK_KEY_DONE
	; ok the key to test should now be the lowest bit in repKeyMap
	CLRC			; clear carry
	BTFSC	repKeyMap,0	; check bit 0
	BSF	STATUS,C	; ok key is pressed set carry
	RETURN			; 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
		MOVLW	H'F0'		; wait 255 cycles
		MOVWF	kbTemp		; this var is 'safe' to be used in side mainloop
		MOVLW	H'03'
		MOVWF	kbState
DELAY_LOOP
		DECFSZ	kbTemp,F	; decrement
		GOTO	$-1		;
		
		MOVLW	H'F0'
		MOVWF	kbTemp
		DECFSZ	kbState,F
		GOTO	DELAY_LOOP

		RETURN


;---------------------------------------------------------------------------
;
;		Initialisation
;
;---------------------------------------------------------------------------
        
        
INIT:  

	;+++
	;	Set up the ports

		; PORT A

	BANK1
        MOVLW	b'00000110'		; Set port data directions RA1,RA2 inputs RA0,RA3,RA4 outputs
	MOVWF	TRISA			; PC keyboard connections


		; PORT B

		; Used for our own 3x8 matrix keyboard
	
        BANK1
        MOVLW	b'11111000'		; Set port data directions RB4-RB7 inputs rest outputs
	MOVWF	TRISB


	;	Clear all registers on bank 0 ( memory )
	
	BANK0
	MOVLW	H'0C'
	MOVWF	FSR
	
INITMEM
	CLRF	0		; Clear a register pointed to be FSR
	INCF	FSR,F
	CLRWDT			; clear watchdog
	MOVLW	H'50'		; Test if at top of memory
	SUBWF	FSR,W
	BNZ	INITMEM		; Loop until all cleared

	;+++ 	
	;	Initiate the keybuffer pointers

INIT_BUFF:

	MOVLW   KbBufferMin	; get adress of first buffer byte
        MOVWF	KeyBufferHead	; store in FSR
        MOVWF	KeyBufferTail	; and set last byte to the same ( no bytes in buffer )

	;+++
	;	Preset the timer dividers

	MOVLW	.20
	MOVWF	Divisor_10ms
	MOVLW	.10
	MOVWF	Divisor_100ms
	MOVLW	.05
	MOVWF	Divisor_500ms

	;+++
	;	Set up Timer 0.

	;	Set up TMR0 to generate a 0.5ms tick
	;	Pre scale of /8, post scale of /1
	
	BANK1
        
	MOVLW   b'00000010'     ; Initialisation of TMR0 prescale 8 '010'
                                ; weak pullup enabled by latch values.

        MOVWF   OPTION_REG      ; load option reg with prescale of 8
                
        BANK0

	MOVLW   H'64'           ; start timer with 100 ( 256-100 = 156 )
                                ; timetick = 0.4uS x 8 ( prescale ) x 156 = (roufly) 0.5 mS
                                
        MOVWF   TMR0
        

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

MAIN:    
		BSF	pcDATA_in
		BSF	pcCLOCK_in
		BCF	pcDATA_out
		BCF	pcCLOCK_out
		CLRF	PORTB

		MOVLW	H'08'		; preset the column counter
		MOVWF	kbColumnCnt	;
		
 		BSF	_NumLock	; default state is numlock = on 
		BSF	_IsFirstLedStatus ; we have not yet recevied led status byte.

	        MOVLW   b'10100000'     ; enable global & TMR0 interrupts
	        MOVWF   INTCON

		CLRWDT			; clear watchdog
		BTFSS	O_led_KEYCOMM_ok
		GOTO    $-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.

		CLRWDT			; clear watchdog
		BTFSC	O_led_KEYCOMM_ok
		GOTO    $-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.


		MOVLW	H'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 :-)
		CLRWDT			; 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

		CLRF	kbColumnVal
		
		; get column counter / adress out
		MOVF	kbColumnCnt,W  		
		
		MOVWF	PORTB		; set the columns adress to the 74HCT4051
					; i.e. make column low
		IFNDEF	DEBUG
		CALL	DELAY_1ms	; wait 1 ms let pins stabilize
		ENDIF

		COMF	PORTB,W		; read back the pin values ( complement i.e. key pressed = '1' )
		ANDLW   b'11110000'	; mask out unused pins
		
		MOVWF	kbColumnVal	; store the pin values

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

		INCF	kbColumnCnt,F     ; 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
		MOVF	kbColumnCnt,W  		
		
		MOVWF	PORTB		; set the columns adress to the 74HCT4051
					; i.e. make column low

		IFNDEF	DEBUG
		CALL	DELAY_1ms	; wait 1 ms
		ENDIF

		COMF	PORTB,W		; read back the pin values ( complement i.e. key pressed = '1' )
		ANDLW   b'11110000'	; mask out unused pins
		
		ADDWF	kbColumnVal,F	; and store pin values

		INCF	kbColumnCnt,F   

		; reset column counter check
		; i.e. we are 'only' using adress 0 - 7 
		MOVF	kbColumnCnt,W
		SUBLW	H'08'		; subtract value in W with 0x08
		BTFSS   STATUS, Z	; check if the zero bit is set
		GOTO	MAIN_CHECK_DEBOUNCE ; nope continue

		CLRF	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:
		BTFSS	I_jmp_NoRepeat	; check if repeat code is enabled ?
		GOTO	MAIN_CHECK_REPEAT ; yep check key repeating
		
		; keyrepeat disabled then do check on exit of altkeymap instead

		BTFSS	_ExitAltKeymap	; we want to exit altkeymap ?
		GOTO	MAIN_LOOP	; nope

		
		; check that ALL keys are released
		; before exiting the alt keymap
		MOVF	kbColumn78_Old,F ; reload column 78 to itself ( affect zero flag )
		BTFSS	STATUS,Z	; check if zero ?
		GOTO	MAIN_LOOP	; key/s still down in column 78

		MOVF	kbColumn56_Old,F ; reload column 56 to itself ( affect zero flag )
		BTFSS	STATUS,Z	; check if zero ?
		GOTO	MAIN_LOOP	; key/s still down in column 56

		MOVF	kbColumn34_Old,F ; reload column 34 to itself ( affect zero flag )
		BTFSS	STATUS,Z	; check if zero ?
		GOTO	MAIN_LOOP	; key/s still down in column 34

		MOVF	kbColumn12_Old,F ; reload column 12 to itself ( affect zero flag )
		BTFSS	STATUS,Z	; check if zero ?
		GOTO	MAIN_LOOP	; key/s still down in column 12
	
		; all keys released !! 
		BCF	_AltKeymap	; exit altkeymap
		BCF	_ExitAltKeymap	; exit release check
		BCF	_InAltKeymap    ; clear flag for second keypress check
		BCF	_DoExitAltKeymap ;
		GOTO	MAIN_LOOP	


MAIN_CHECK_REPEAT
		BTFSS	_doSendKey	; if we should send a repeated key
		GOTO	MAIN_LOOP	; nope continue

		; send the key in RepeatedKey but first check if its an extended key
		BTFSS 	_RepeatIsExt	; is it extended ?
		GOTO	MAIN_SEND_REPEAT ; nope just send scan code
		
		; last key pressed was extended send extended prefix
		MOVLW	EXTENDED	; get extended code
		CALL	ADD_KEY_BUFFER	; and put it into the buffer
		
MAIN_SEND_REPEAT:
		MOVF	RepeatKey,W	; get key code for the last pressed key
		CALL	ADD_KEY_BUFFER	; and put it into the buffer
		BCF	_doSendKey	; and clear the flag, it will be set again 
					; inside int handler if key still is pressed	
		
		GOTO	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:

	MOVF	Offset,W	; get current offset
	MOVWF	TempOffset	; 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, 


	CLRC			; clear carry so it dont affect byte rotation
	RLF	Offset,F	; first rotate
	RLF	Offset,F	; 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'

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

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

	BCF	_isExtended     ; clear extended flag

	MOVLW   LOW LOOKUP_KEY	; get low bit of table adress
	ADDWF	Offset,F	; 8 bit add
	MOVLW	HIGH LOOKUP_KEY	; get high 5 bits
	BTFSC	STATUS,C	; is page boundary crossed ?
	ADDLW	1		; yep, then inc high adress
	MOVWF	PCLATH		; load high adress in latch
	MOVF	Offset,W	; load computed offset in w
	CLRC		        ; 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

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

	BTFSS	STATUS,C	; check carry flag
	GOTO	KB_CHK_BREAK	; nope then check make/break status

	BSF	_isExtended	; set extended flag
	MOVLW	EXTENDED	; 
	CALL	ADD_KEY_BUFFER	; get extended code and put in in the buffer

KB_CHK_BREAK:
	
	; check if it's make or break
	BTFSS	_isBreak	; check if its pressed or released ? 
	GOTO	KB_DO_MAKE_ONLY	; send make code 

	BCF	_isBreak	; clear bit for next key

	; break code, key is released
	MOVLW	BREAK		; get break code
	CALL	ADD_KEY_BUFFER	; and put into buffer
	GOTO	KB_DO_MAKE	; and send key code also

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

	BTFSC 	_isExtended     ; is it extended ?
	BSF	_RepeatIsExt	; set the flag 
	
	; save this key in 'last' pressed, to be used in key repeat code

	MOVF	TempOffset,W	; get saved offset
	MOVWF	LastMakeOffset  ; and store it 

	; if keyrepat = enabled, alternative mapping = disabled
	BTFSS	I_jmp_NoRepeat	; check if repeat code is enabled ?
	GOTO	KB_REP_NOR	; yep set normal delay ( 800 ms )
	
	; else keyrepat = disabled, alternative mapping = enabled
	MOVLW	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.
							
	GOTO	KB_REP_SET	; and set it
KB_REP_NOR:	
	
	MOVLW	DELAY_REPEAT	; reload 'normal' repeat delay ( 800 ms )
			
KB_REP_SET:
	MOVWF	RepeatTimer	; and (re)start the timer for key repeat
	MOVF	kbScan,W	; get key scan code	
	MOVWF	RepeatKey	; and save it

KB_DO_MAKE:
	; key pressed/released ( i.e. the scancode is sent both on make and break )
	MOVF	kbScan,W	; 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.

	MOVLW	DELAY_EXIT_ALTKEYMAP ; reload the delay for exiting the altkeymap when no
				; key is pressed ( 7.5 sec )
	MOVWF	LastKeyTime	; (re)set lastkey timer ( used to get out of altkeymap )	
	RETURN

; ***********************************************************************
;
;  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) 
	MOVF	kbColumnCnt,F	; reload value into itself ( affect zero flag )
	BTFSC	STATUS,Z	; is it zero ?
	GOTO	KB_DEBOUNCE_78	; debounce columns 7 & 8

	MOVF	kbColumnCnt,W	; move column counter into W register
	SUBLW	H'04'		; subtract value in W with 0x04 ( columns 5 & 6 )
	BTFSC   STATUS, Z	; check if the zero bit is set
	GOTO	KB_DEBOUNCE_34	; debounce columns 3 & 4

	MOVF	kbColumnCnt,W	; move column counter into W register
	SUBLW	H'06'		; subtract value in W with 0x02 ( columns 3 & 4 )
	BTFSC   STATUS, Z	; check if the zero bit is set
	GOTO	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

	MOVF	kbColumn12_New,W	; get debounced sample
	XORWF	kbColumn12_Old,W	; get changed bits
	
	BTFSC	STATUS,Z	; check if zero = no change
	RETURN			; 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'

	MOVWF	kbState		; save change bit/s
	MOVLW	H'07'           ; preset bit counter
	MOVWF	kbBitCnt	; loop though all eight bits.
	BCF	_LastColumn	; clear end seq bit ( set when are done with last bit )

	MOVF	kbColumn12_New,W ; get new sample
	MOVWF	kbTemp		; and store it

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

	CLRC			; clear carry
	RLF	kbState,F	; rotate left, and store back
	BTFSS	STATUS,C 	; check carry '1' = bit was high = change has occured
	GOTO	KB_LOOP_12_SKIP	; nope, no change check next bit ( or exit )
		
	; bit changed
	MOVF	kbBitCnt,W	; get bit counter ( for offset calc. )
	MOVWF	Offset		; store bit num ( for offset )
		
	CLRC			; clear carry
	RLF	kbTemp,F	; rotate left ( next bit )
	BTFSS	STATUS,C 	; check carry '1' = key is down ( i.e. make )
	BSF     _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


	GOTO	KB_LOOP_12_NEXT	

KB_LOOP_12_SKIP
	RLF	kbTemp,F	; rotate so we read next key
	
KB_LOOP_12_NEXT
	BTFSC	_LastColumn	; are we done ?
	GOTO	KB_12_DONE	; yep, save new key bit map and exit

	DECFSZ	kbBitCnt,F	; decrement bit counter
	GOTO	KB_LOOP_12	; bits left
	BSF	_LastColumn	; set bit so we break out after next run
	GOTO	KB_LOOP_12

KB_12_DONE:
	; and update our 'last known' status for the columns
	MOVF	kbColumn12_New,W ; get new status
	MOVWF	kbColumn12_Old	; 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
	;

	BTFSS	_AltKeymap		; is altkeymap enabled ?
	RETURN				; nope return


	BTFSC   _InAltKeymap		; are we in altkeymap ?
	GOTO    KB_12_IN		; yep alt keymap key has been released once
	
	; nope still waiting for first release
	BTFSS   kbColumn12_Old,2	; is key released ? ( first time )
	GOTO	KB_12_ALT		; yep, reset timers and set bit variables

KB_12_IN
	BTFSC	_DoExitAltKeymap	; are we waiting for release ?
	GOTO	KB_12_OUT		; yes

	; the key has been released once test for second press
	BTFSC   kbColumn12_Old,2	; is it still pressed ?
	GOTO	KB_12_ALT2		; yep

	BTFSS	_DoExitAltKeymap        ; are we now waiting for the last ( second ) release ?
	RETURN				; nope

KB_12_OUT
	BTFSS	kbColumn12_Old,2	; check if key still pressed ?
	BSF	_ExitAltKeymap		; nope, then enable exit check that
					; will exit alt keymap as soon as all key are released
	
KB_12_ALT2
	BSF	_DoExitAltKeymap	; check for second release
	RETURN
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

	MOVLW	H'0F'		; x0.5 sec = 7.5 sec
	MOVWF	LastKeyTime	; (re)set lastkey timer ( used to get out of altkeymap automaticly)	

	BSF	_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 !

	;BSF	_NumLock	; and also force numlock to be 'on'
				; as it is set to 'off' when we enter altkeymap
				; we must set it 'back'
	
	RETURN	
		
KB_DEBOUNCE_34:	
	; debounce columns 3 & 4
	DEBOUNCE_BYTE	kbColumnVal,kbColumn34_New,kbColumn34Cnt,kbColumn34State

	MOVF	kbColumn34_New,W	; get debounced sample
	XORWF	kbColumn34_Old,W	; get changed bits
	
	BTFSC	STATUS,Z	; check if zero = no change
	RETURN			; 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'

	MOVWF	kbState		; save change bit/s
	MOVLW	H'07'           ; preset bit counter
	MOVWF	kbBitCnt	; loop though all eight bits.
	BCF	_LastColumn	; clear end seq bit ( set when are done with last bit )

	MOVF	kbColumn34_New,W ; get new sample
	MOVWF	kbTemp		; and store it

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

	CLRC			; clear carry
	RLF	kbState,F	; rotate left, and store back
	BTFSS	STATUS,C 	; check carry '1' = bit was high = change has occured
	GOTO	KB_LOOP_34_SKIP	; nope, no change check next bit ( or exit )
		
	; bit changed
	MOVF	kbBitCnt,W	; get bit counter ( for offset calc. )
	MOVWF	Offset		; store bit num ( for offset )
	
	BSF	Offset,3	; set bit 3 for table read ( column 3 & 4 )

	;BCF	_isBreak	; clear break flag
	CLRC			; clear carry
	RLF	kbTemp,F	; rotate left ( next bit )
	BTFSS	STATUS,C 	; check carry '1' = key is down ( i.e. make )
	BSF     _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


	GOTO	KB_LOOP_34_NEXT

KB_LOOP_34_SKIP
	RLF	kbTemp,F	; rotate so we read next key
	
KB_LOOP_34_NEXT
	BTFSC	_LastColumn	; are we done ?
	GOTO	KB_34_DONE	; yep, save new key bit map and exit

	DECFSZ	kbBitCnt,F	; decrement bit counter
	GOTO	KB_LOOP_34	; bits left
	BSF	_LastColumn	; set bit so we break out after next run
	GOTO	KB_LOOP_34

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


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

	MOVF	kbColumn56_New,W	; get debounced sample
	XORWF	kbColumn56_Old,W	; get changed bits
	
	BTFSC	STATUS,Z	; check if zero = no change
	RETURN			; 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'

	MOVWF	kbState		; save change bit/s
	MOVLW	H'07'           ; preset bit counter
	MOVWF	kbBitCnt	; loop though all eight bits.
	BCF	_LastColumn	; clear end seq bit ( set when are done with last bit )

	MOVF	kbColumn56_New,W ; get new sample
	MOVWF	kbTemp		; and store it

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

	CLRC			; clear carry
	RLF	kbState,F	; rotate left, and store back
	BTFSS	STATUS,C 	; check carry '1' = bit was high = change has occured
	GOTO	KB_LOOP_56_SKIP	; nope, no change check next bit ( or exit )
		
	; bit changed
	MOVF	kbBitCnt,W	; get bit counter ( for offset calc. )
	MOVWF	Offset		; store bit num ( for offset )
	
	BSF	Offset,4	; set bit 4 for table read ( column 5 & 6 )
	
	CLRC			; clear carry
	RLF	kbTemp,F	; rotate left ( next bit )
	BTFSS	STATUS,C 	; check carry '1' = key is down ( i.e. make )
	BSF     _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


	GOTO	KB_LOOP_56_NEXT

KB_LOOP_56_SKIP
	RLF	kbTemp,F	; rotate so we read next key

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

	DECFSZ	kbBitCnt,F	; decrement bit counter
	GOTO	KB_LOOP_56	; bits left
	BSF	_LastColumn	; set bit so we break out after next run
	GOTO	KB_LOOP_56

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

KB_DEBOUNCE_78:	
	; debounce columns 7 & 8
	DEBOUNCE_BYTE	kbColumnVal,kbColumn78_New,kbColumn78Cnt,kbColumn78State
	
	MOVF	kbColumn78_New,W	; get debounced sample
	XORWF	kbColumn78_Old,W	; get changed bits
	
	BTFSC	STATUS,Z	; check if zero = no change
	RETURN			; 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'

	MOVWF	kbState		; save change bit/s
	MOVLW	H'07'           ; preset bit counter
	MOVWF	kbBitCnt	; loop though all eight bits. ( 7-0 )
	BCF	_LastColumn	; clear end seq bit ( set when are done with last bit )

	MOVF	kbColumn78_New,W ; get new sample
	MOVWF	kbTemp		; and store it

KB_LOOP_78	
	CLRF	Offset		; clear offset counter ( for table read )
	CLRC			; clear carry
	RLF	kbState,F	; rotate left, and store back
	BTFSS	STATUS,C 	; check carry '1' = bit was high = change has occured
	GOTO	KB_LOOP_78_SKIP	; nope, no change check next bit ( or exit )
		
	; bit changed
	MOVF	kbBitCnt,W	; get bit counter ( for offset calc. )
	MOVWF	Offset		; store bit num ( for offset )
	
	BSF	Offset,4	; set bit 3,4 for table read ( column 7 & 8 )
	BSF	Offset,3	; 
	
	CLRC			; clear carry
	RLF	kbTemp,F	; rotate left ( next bit )
	BTFSS	STATUS,C 	; check carry '1' = key is down ( i.e. make )
	BSF     _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

	GOTO	KB_LOOP_78_NEXT
	
KB_LOOP_78_SKIP
	RLF	kbTemp,F	; rotate so we read next key
	
KB_LOOP_78_NEXT
	BTFSC	_LastColumn	; are we done ?
	GOTO	KB_78_DONE	; yep, save new key bit map and exit

	DECFSZ	kbBitCnt,F	; decrement bit counter
	GOTO	KB_LOOP_78	; bits left
	BSF	_LastColumn	; set bit so we break out after next run
	GOTO	KB_LOOP_78

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

	
; ***********************************************************************
; 
;  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
		
	MOVWF    PCL	  ; add to program counter  		
; R1 - C1 i.e. key 1
	NOP
	NOP
	NOP	
	RETLW	H'05'	; scan code 'F1'
; R2 - C1 i.e. key 2
	NOP
	NOP
	NOP	
	RETLW	H'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
	RETLW	H'83'		; scan code for F7 ( in 'normal' mode )
; R4 - C1 i.e. key 4
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'76'		; send scancode for 'ESC'  instead
	BSF	STATUS,C 	; set carry ( i.e. extended code )
	RETLW	H'6B'		; scan code 'arrow left' '<-'
; R1 - C2 i.e. key 5
	NOP
	NOP
	NOP	
	RETLW	H'06'	;  scan code 'F2' hex06
; R2 - C2 i.e. key 6
	NOP
	NOP
	NOP	
	RETLW	H'03'	; scan code 'F5'
; R3 - C2 i.e. key 7
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'0D'		; send scancode for 'horizontaltab' HT instead
	BSF	STATUS,C 	; set carry ( i.e. extended code )
	RETLW	H'75'		; scan code 'arrow up' '^'
; R4 - C2 i.e. key 8
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'14'		; send scancode for 'left ctrl' instead
	BSF	STATUS,C 	; set carry ( i.e. extended code )
	RETLW	H'72'		; scan code 'arrow down' 
; R1 - C3 i.e. key 9
	NOP	
	NOP	
	NOP	
	RETLW	H'04'	; scan code 'F3'
; R2 - C3 i.e. key 10
	NOP	
	NOP	
	NOP	
	RETLW	H'0B'	; scan code 'F6'
; R3 - C3 i.e. key 11
	NOP	
	NOP	
	NOP	
	RETLW	H'0A'	; scan code 'F8'
; R4 - C3 i.e. key 12
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'11'		; send scancode for 'left alt' instead
	BSF	STATUS,C ; set carry ( i.e. extended code )
	RETLW	H'74'	; scan code 'arrow right' '->'
; R1 - C4 i.e. key 13
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'6C'		; send scancode for numeric '7' instead
	NOP	
	RETLW	H'3D'	; scan code '7'
; R2 - C4 i.e. key 14
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'6B'		; send scancode for numeric '4' instead
	NOP	
	RETLW	H'25'	; scan code '4'
; R3 - C4 i.e. key 15
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'69'		; send scancode for numeric '1' instead
	NOP	
	RETLW	H'16'	; scan code '1'
; R4 - C4 i.e. key 16
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'7B'		; send scancode for numeric '-' instead
	NOP	
	RETLW	H'4A'	; scan code '-' minus ( swe kbd )
; R1 - C5 i.e. key 17
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'75'		; send scancode for numeric '8' instead
	NOP	
	RETLW	H'3E'	; scan code '8'
; R2 - C5 i.e. key 18
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'73'		; send scancode for numeric '5' instead
	NOP	
	RETLW	H'2E'	; scan code '5'
; R3 - C5 i.e. key 19
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'72'		; send scancode for numeric '2' instead
	NOP	
	RETLW	H'1E'	; scan code '2'
; R4 - C5 i.e. key 20
	BTFSS	_AltKeymap	; check for alternative keymap
	RETLW	H'45'		; scan code '0' ( from keypad ) normal key 
	BSF	STATUS,C 	; set carry ( i.e. extended code ) 
	RETLW	H'1F'		; alt keycode ( windows start menu activate )
; R1 - C6 i.e. key 21
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'7D'		; send scancode for numeric '9' instead
	NOP	
	RETLW	H'46'	; scan code '9'
; R2 - C6 i.e. key 22
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'74'		; send scancode for numeric '6' instead
	NOP	
	RETLW	H'36'	; scan code '6'
; R3 - C6 i.e. key 23
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'7A'		; send scancode for numeric '3' instead
	NOP	
	RETLW	H'26'	; scan code '3'
; R4 - C6 i.e. key 24
	BTFSS	_AltKeymap	; check for alternative keymap
	RETLW	H'49'		; scan code '.' ( swe kbd ) normal key
				; use alternative keymap
	BSF	STATUS,C	; set carry ( i.e. extended code )
	RETLW	H'4A'	; send scancode for numeric '/' instead
; R1 - C7 i.e. key 25
	BTFSC	_AltKeymap	; check for alternative keymap
	RETLW	H'79'		; send scancode for numeric '+' instead
	NOP	
	RETLW	H'4E'	; scan code '+'
; R2 - C7 i.e. key 26
	BTFSS	_AltKeymap	; check for alternative keymap
	RETLW	H'66'		; scan code 'back space' BS, normal key
				; use alternative keymap
	BSF	STATUS,C	; set carry ( i.e. extended code )
	RETLW	H'71'	; send scancode for 'delete' instead
; R3 - C7 i.e. key 27
	BTFSS	_AltKeymap	; check for alternative keymap
	RETLW	H'5A'		; scan code 'enter', normal key
				; use alternative keymap
	BSF	STATUS,C	; set carry ( i.e. extended code )
	RETLW	H'5A'	; send scancode for numeric enter instead ( note ! extended )
; R4 - C7 i.e. key 28
	BTFSS	_AltKeymap	; check for alternative keymap
	RETLW	H'5A'		; scan code 'enter', normal key
				; use alternative keymap
	BSF	STATUS,C	; set carry ( i.e. extended code )
	RETLW	H'5A'	; send scancode for numeric enter instead ( note ! extended )
; R1 - C8 i.e. key 29
	NOP	
	NOP	
	NOP	
	RETLW	H'2C'	; scan code 't'
; R2 - C8 i.e. key 30
	NOP	
	NOP	
	NOP	
	RETLW	H'24'	; scan code 'e'
; R3 - C8 i.e. key 31
	NOP	
	NOP	
	NOP	
	RETLW	H'1B'	; scan code 's'
; R4 - C8 i.e. key 32
	NOP	
	NOP	
	NOP	
	RETLW	H'2C'	; scan code 't'

    END

Comments: