;***************************************************************************
;                                                                     
;	AT Keyboard Scan Code Debug Interface V1.02
;	===========================================
;
;	written by Peter Luethi, 12.07.2000, Dietikon, Switzerland
;	http://www.electronic-engineering.ch
;	last update: 25.03.2005
;
;	V1.02:	Re-structured entire ISR and RS232 echo sub-routines
;		(18.04.2004)
;	V1.01:	Changed keyboard data pin to PORTA,4 (open-collector).
;		(16.08.2003)
;	V1.00:	Initial release (12.7.2000)
;
;	This code and accompanying files may be distributed freely and
;	modified, provided this header with my name and this notice remain
;	intact. Ownership rights remain with me.
;	You may not sell this software without my approval.
;
;	This software comes with no guarantee or warranty except for my
;	good intentions. By using this code you agree to indemnify me from
;	any liability that might arise from its use.
;
;	
;	SPECIFICATIONS:
;	===============
;	Processor:			Microchip PIC 16F84
;	Clock Frequency:		4.00 MHz XT
;	Throughput:			1 MIPS
;	RS232 Baud Rate:		19200 baud (depends on the module included)
;	Serial Output:			19200 baud, 8 bit, no parity, 1 stopbit
;	Keyboard Routine Features:	Capability of uni-directional
;					communication between microcontroller
;					and keyboard
;	Acquisition Methodology:	Non-preemptive, interrupt-based
;					keyboard scan pattern acquisition
;	Code Size of entire Program:	208 instruction words
;	Required Hardware:		AT Keyboard, MAX 232
;	Required Software:		RS232 terminal (recommended: Excel 97
;					RS232-Debug-Interface)
;
;	DESCRIPTION:
;	============
;	Developed and tested on PIC 16F84.
;	Any key stroke on the keyboard connected to the PIC will send the
;	corresponding key scan code to the computer terminal via the RS232 
;	connection.
;	Verification of keyboard scan patterns and to get the scan code of 
;	unknown keys on non-english or non-german keyboards.
;
;	The keyboard scan code capture and decoding is done by an interrupt
;	service routine. The event, which triggers the interrupt is a falling
;	edge on the keyboard clock line at the KBDclkpin (PORTB,0).
;	The keyboard data (scan code) will be fetched at the KBDdatapin
;	(PORTA,4).
;	There is only RS232 transmission, so only the TXport is connected. 
;	No reception is necessary because there aren't any output devices
;	attached to the microcontroller and PORTB,0 is already used by the
;	keyboard clock line. The configuration of the KBDclkpin (PORTB,0)
;	interrupt is done by the RS232init procedure (although used by the
;	keyboard), because all settings are the same.
;
;
;	IMPORTANT:
;	==========
;	To get all parts of a complete scan pattern, it is recommended to
;	use this code in compliance with the above specifications.
;	Otherwise, consecutive interrupt calls are launched before the
;	termination of the ISR. As a consequence, parts of scan patterns
;	will not be displayed or in worst case, a system crash will happen.
;	Of course, it is possible to use this code with a faster crystal
;	and/or with RS232 modules specified for higher baud rates.
;
;
;	CREDITS:
;	========
;	- Craig Peacock, the author of the excellent page about keyboards
;	  "Interfacing the PC's Keyboard" available at his website:
;	  http://www.beyondlogic.org/keyboard/keybrd.htm
;	- Steve Lawther for inspirations concerning the scan code fetch
;	  routine.
;
;***************************************************************************

;***** EXCLUDE COMPILATION MESSAGES & WARNINGS *****

	ERRORLEVEL -207 	; found label after column 1
	ERRORLEVEL -302 	; register in operand not in bank 0

;***** PROCESSOR DECLARATION & CONFIGURATION *****

	PROCESSOR 16F84
	#include "p16f84.inc"
	
	; embed configuration data within .asm file
	__CONFIG   _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC

;***** MEMORY STRUCTURE *****

	ORG     0x00		; processor reset vector
  	goto    MAIN

	; interrupt service routine at ORG 0x04 declared below

;***** PORT DECLARATION *****

	#define	TXport	PORTA,0x00	; RS232 output port, could be
	#define	TXtris	TRISA,0x00	; any active push/pull port
	#define	KBDdatapin PORTA,0x04	; keyboard data input port
	#define	KBDdatatris TRISA,0x04	; (open-collector pin)
	#define	KBDclkpin PORTB,0x00	; keyboard clock input port (IntB)
	#define	KBDclktris  TRISB,0x00  ; @ INTF interrupt source (RB0)

;***** CONSTANT DECLARATION *****

	CONSTANT BASE = 0x0C	; Base address of user file registers	

;***** REGISTER DECLARATION *****

	TEMP1	set	BASE+d'0'	; universal temporary register
	TEMP2	set	BASE+d'1'
	TEMP3	set	BASE+d'2'
	TEMP4	set	BASE+d'3'

	TXD	equ	BASE+d'4'	; RS232 TX-Data register
	RXD	equ	BASE+d'5'	; RS232 RX-Data register

	; interrupt context save/restore
	W_TEMP	equ	BASE+d'6'	; context register (ISR)
	STATUS_TEMP equ	BASE+d'7'	; context register (ISR)
	PCLATH_TEMP equ	BASE+d'8'	; context register (ISR)
	FSR_TEMP equ	BASE+d'9'	; context register (ISR)

	ISRtmp1	equ	BASE+d'10'	; ISR temporary register
	ISRtmp2	equ	BASE+d'11'	; ISR temporary register
	KBD	equ	BASE+d'12'	; keyboard data register
	
;***** INCLUDE FILES *****

	ORG	0x50
	#include "..\..\m_bank.asm"	; standard macros
	#include "..\..\m_wait.asm"
	;#include "..\..\m_rs096.asm"	; 9600 baud, not recommended
	#include "..\..\m_rs192.asm"	; 19200 baud @ 4 MHz

;***** INTERRUPT SERVICE ROUTINE *****

	ORG	0x04		; interrupt vector location

ISR	;************************
	;*** ISR CONTEXT SAVE ***
	;************************

	bcf	INTCON,GIE	; disable all interrupts
	btfsc	INTCON,GIE	; assure interrupts are disabled
	goto	ISR
	movwf	W_TEMP		; context save: W
	swapf	STATUS,W	; context save: STATUS
	movwf	STATUS_TEMP	; context save
	clrf	STATUS		; bank 0, regardless of current bank
	movfw	PCLATH		; context save: PCLATH
	movwf	PCLATH_TEMP	; context save
	clrf	PCLATH		; page zero, regardless of current page
	bcf	STATUS,IRP	; return to bank 0
	movfw	FSR		; context save: FSR
	movwf	FSR_TEMP	; context save
	;*** context save done ***

	;**************************
	;*** ISR MAIN EXECUTION ***
	;**************************

	;*** Until now, already 16 cycles (16 us) have been passed ***
	btfsc	KBDdatapin	; test start bit of keyboard data input
	goto	KBDabort	; no valid start bit, abort
	movlw	0x08		; 8 data bits to receive / counter
	movwf	ISRtmp1
waitHI	btfss	KBDclkpin	; loop, wait for kbd clk HIGH transition
	goto	waitHI
waitLO	btfsc	KBDclkpin	; loop, wait for kbd clk LO transition
	goto	waitLO
	btfss	KBDdatapin
	goto	KBD_1
	bsf	KBD,0x07	
	goto	KBD_2
KBD_1	bcf	KBD,0x07	
KBD_2	decfsz	ISRtmp1,W	; skip if ISRtmp1 == 1
	rrf	KBD,F		; do this only 7 times
	decfsz	ISRtmp1,F
	goto	waitHI		; loop 8 times
	;*** ignore parity bit, check stop bit: ***
	movlw	0x02		; set counter
	movwf	ISRtmp1
waitHIp	btfss KBDclkpin		; loop, wait for kbd clk HIGH transition
	goto	waitHIp
waitLOp	btfsc KBDclkpin		; loop, wait for kbd clk LO transition
	goto	waitLOp
	decfsz	ISRtmp1,F
	goto	waitHIp
	btfsc	KBDdatapin	; check if stop bit is valid
	goto	KBD_3		; if valid, continue execution of ISR
KBDabort clrf	KBD		; else, abort / invalid data
	goto	ISRend		; terminate execution of ISR

	;*** STALL KEYBOARD TO DECODE DATA *** 
KBD_3	BANK1			; hold keyboard (with kbd clk low):
	bcf	KBDclktris	; set clk line to output
	BANK0
	bcf	KBDclkpin	; set keyboard clk line low (stall)

	movfw	KBD
	SENDw			; send actual pressed keyboard character

	;*** DISABLE STALL *** 
	BANK1
	bsf	KBDclktris	; set clk line back to input (and goes high)
	BANK0			;	(release stall)

	;***********************************
	;*** CLEARING OF INTERRUPT FLAGS ***
	;***********************************

_ISR_RS232error
_ISR_RS232end
	bcf	INTCON,INTF	; clear RB0/INT interrupt flag
	;goto	ISRend		; terminate execution of ISR

	;*****************************************
	;*** ISR TERMINATION (CONTEXT RESTORE) ***
	;*****************************************

ISRend	movfw	FSR_TEMP	; context restore
	movwf	FSR		; context restore
	movfw	PCLATH_TEMP	; context restore
	movwf	PCLATH		; context restore
	swapf	STATUS_TEMP,W	; context restore
	movwf	STATUS		; context restore
	swapf	W_TEMP,F	; context restore
	swapf	W_TEMP,W	; context restore
	RETFIE			; enable global interrupt (INTCON,GIE)

;***** END OF INTERRUPT SERVICE ROUTINE *****


;************** MAIN **************

	ORG	0xB0

MAIN	BANK1
	clrf	OPTION_REG	; PORTB pull-ups enabled
	bsf	KBDclktris
	bsf	KBDdatatris
	BANK0
	RS232init		; RS232 initialization
	WAIT	d'5'

MLoop	movlw	d'39'		; store amount of table items in counter
	movwf	TEMP3

_LOOP	movlw	HIGH WelcomeTable ; get correct page for PCLATH
	movwf	PCLATH		; prepare right page bits for table read
	movfw	TEMP3		; get actual count-down value
	sublw	d'39'		; table offset: w = d'39' - TEMP3
	call	WelcomeTable	; call lookup table
	SENDw
	decfsz	TEMP3,F		; decrement counter
	goto	_LOOP
	WAITX	d'255',d'7'	; wait 17 seconds @ 4 MHz
	goto	MLoop

WelcomeTable
	addwf	PCL,F
	DT	"PIC 16F84 Keyboard Decoder connected" ; create table
	retlw	CR		; Carriage Return
	retlw	LF		; Line Feed
WTableEND retlw	LF		; Line Feed

	IF (high (WelcomeTable) != high (WTableEND))
		ERROR "Welcome table hits page boundary!"
	ENDIF

	END

