	TITLE	SWPKEYAT -- Swap IBM-PC/AT Keys
	SUBTTL	History

;	This program intercepts the IBM-PC/AT's keyboard hardware interrupt
; and messes around with the keyboard scan codes, specifically to swap the
; "Esc" and "~" keys.  It then returns to the ROM BIOS for the remainder
; of the keyboard interrupt processing.

;	Note that the only way that I can return to the ROM BIOS for the
; remainder of the keyboard processing is to jump to a nonstandard known
; location in ROM.  This is an unfortunate practice.  The only other
; option I can see is for SWPKEYAT to contain it's own copy of the entire
; keyboard interrupt code.  However, I don't have time to do this yet.

;	Because of the way that SWPKEYAT interacts with the keyboard
; hardware interrupt vector and the BIOS ROM, you must load this program
; BEFORE you load other keyboard processing programs, such as ScrollMate
; and BUF160.  Perhaps with more effort I could devise a cleaner interface.

;	Another problem is that some programs, such as Lugaru Epsilon,
; substitute their own keyboard scan interrupt routines while they are
; active.  In order to easily patch such programs I have defined a special
; interrupt number, INT 3FH, which acts like an IN from the keyboard data
; port with the appropriate scan codes swapped.  (INT 3FH is the last
; "Reserved for DOS" interrupt.  DOS 3.10 doesn't use it, but that's no
; guarantee about future availability.)

;	This version is set up for the IBM-PC/AT ROM BIOS as documented in the
; March 1984 edition of the Technical Reference for the IBM Personal Computer
; AT, keyboard BIOS module dated 01/04/84.  Warning!  Ignore the IBM PC/AT
; Technical Reference section "Key Scan Codes", starting on p. 4-10.  It's
; completely bogus.  Also, ignore the "break code prefix".  Instead, relay on
; the BIOS ROM listing (p. 5-28, p. 5-115 and on) in earlier editions of
; the Technical Reference (like the Aug 1981 edition).  The only new key on
; the AT, Sys Req, has scan code 84 (dec).


; swpkeyat.asm  10 Jul 85  Craig Milo Rogers at USC/ISI
;	Improved some of the comments.
; swpkeyat.asm  27 Jun 85  Craig Milo Rogers at USC/ISI
;	Created this program to swap the ESC key on the IBM-PC/AT with the
; tilde/grave key.

	PAGE
	SUBTTL	Declarations

KB_DATA		EQU	060H		; Keyboard data port.
BREAK_BIT	EQU	    080H	; Break-contact scan modifier.
ESC_SCAN	EQU	    001H	; Scan code for the ESC key.
TILDE_SCAN	EQU	    029H	; Scan code for tilde/grave key.

STATUS_PORT 	EQU	064H		; Keyboard status port.
INPT_BUF_FULL	EQU	    002H	; Input-buffer-full bit.
DIS_KBD		EQU	    0ADH	; Disable-keyboard command.

KB_INUM		EQU	009H		; Keyboard hardware interrupt number.

SCAN_INUM	EQU	03FH		; Keyboard scan request interrupt
					; number.


;	The following two segments are used to interface this code to the
; ROM BIOS.

BIOSDATA SEGMENT AT 40H			; BIOS data area in RAM.

BIOSDATA ENDS


BIOSROM	SEGMENT	AT 0F000H		; Known BIOS locations in ROM.

	ORG	03054H+0ABH-08CH	; Place to reenter the keyboard
KB_INT_1A LABEL FAR			; hardware interrupt.

BIOSROM	ENDS				; End of BIOS ROM segment.



CODESEG	SEGMENT				; The code segment for this module.
	ASSUME	CS:CODESEG		; Always in CS.
					; Configure for a .COM file:  start
					; with the Program Segment Prefix.

	ORG	02CH			; The PSP environment pointer.
ENV_SEG	LABEL	WORD

	ORG	100H			; Start of .COM execution.
BEGIN:	JMP	INIT			; Goto to code which initializes
					; everything.

	PAGE
	SUBTTL	KB_INT -- Keyboard Interrupt

;	Control passes here when a message is ready from the keyboard.

KB_INT	PROC	FAR
	STI				; Enable interrupts.
	PUSH	BP			; Save the registers just like
	PUSH	AX			; the ROM.
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	SI
	PUSH	DI
	PUSH	DS
	PUSH	ES
	CLD				; Forward direction.

	MOV	AX,BIOSDATA		; Establish pointer to BIOS data.
	MOV	DS,AX

					; Disable the keyboard:
	SUB	CX,CX			; Clear counter for max cycles.
	CLI				;;; Disable interrupts.
LOOP1:	IN	AL,STATUS_PORT		;;; Read the keyboard status port.
	TEST	AL,INPT_BUF_FULL	;;; Is the command buffer still full?
	 LOOPNZ	LOOP1			;;;   (yes, continue spinning)
	MOV	AL,DIS_KBD		;;; Get command to disable keyboard.
	OUT	STATUS_PORT,AL		;;; Send it out.
	STI				;;; Reenable interrupts.

	NOP				; Window for interrupts.

					; Wait for the keyboard to pick up
					; the command:
	SUB	CX,CX			; Clear counter for max cycles.
	CLI				;;; Disable interrupts.
LOOP2:	IN	AL,STATUS_PORT		;;; Read the keyboard status port.
	TEST	AL,INPT_BUF_FULL	;;; Is the command buffer still full?
	 LOOPNZ	LOOP2			;;;   (yes, continue spinning)
	STI				;;; Reenable interrupts.

	INT	SCAN_INUM		; Read the scan code.

	JMP	KB_INT_1A		; Go enter the mainline ROM code.

KB_INT	ENDP

	PAGE
	SUBTTL	SCAN_INT -- Get a Keyboard Scan Code

SCAN_INT PROC	FAR			; Get a keyboard scan code.
	STI				; Enable interrupts.

	IN	AL,KB_DATA		; Pick up the waiting scan code.

	PUSH	AX			; Save the full scan.
	AND	AL,NOT BREAK_BIT	; Eliminate the break bit.
	CMP	AL,ESC_SCAN		; Is this an ESC?
	 JE	SAW_ESC			;   (yes)
	CMP	AL,TILDE_SCAN		; Is this a tilde?
	 JE	SAW_TILDE		;   (yes)

	POP	AX			; Restore the full code.
	IRET				; Return to caller.


SAW_ESC:POP	AX			; Restore the full scan code.
	AND	AL,BREAK_BIT		; Retain the break bit.
	OR	AL,TILDE_SCAN		; Remap into a tilde.
	IRET				; Return to the caller.

SAW_TILDE:
	POP	AX			; Restore the full scan code.
	AND	AL,BREAK_BIT		; Retain the break bit.
	OR	AL,ESC_SCAN		; Remap into an escape.
	IRET				; Return to the caller.

SCAN_INT ENDP


	PAGE
	SUBTTL	INIT -- Initialize and Exit-staying-resident

INIT	PROC	NEAR			; Initialize and exit.
	ASSUME	DS:CODESEG		; Entered with CS and DS pointing to
					; the Program Segment Prefix, which
					; the first 100H locations of CODESEG.

					; Grab the keyboard hardware int.:
	MOV	DX,OFFSET KB_INT	; Address of int. routine in DS:DX.
	MOV	AL,KB_INUM		; Keyboard hardware interrupt.
	MOV	AH,025H			; Set interrupt vector function.
	INT	21H			; Call DOS to take over the interrupt.

					; Grab our special keyboard scan int.:
	MOV	DX,OFFSET SCAN_INT	; Address of int. routine in DS:DX.
	MOV	AL,SCAN_INUM		; Keyboard scan input request.
	MOV	AH,025H			; Set interrupt vector function.
	INT	21H			; Call DOS to take over the interrupt.

					; Return our environment block to
					;   free storage:
	MOV	AX,ENV_SEG		; Get our environment block's segment.
	MOV	ES,AX
	MOV	AH,049H			; Free allocated memory.
	INT	21H			; Call DOS.
					; Ignore possible error return.

					; Terminate and stay resident.  Start
					; By calculating how much memory to
					; leave allocated to this program:
	MOV	DX,OFFSET INIT		; Don't need to retain init routine.
	ADD	DX,0FH			; Round up to a paragraph.
	MOV	CL,4
	SHR	DX,CL
	MOV	AL,0			; Successful return.
	MOV	AH,031H			; Terminate process & remain resident.
	INT	21H			; Call DOS.

	XOR	AH,AH			; Emergency exit in case of DOS 1.
	INT	21H

INIT	ENDP

CODESEG	ENDS

	END	BEGIN
