DOS Keyboard Fix TSR
I have an old AST Premium Exec 386SX/20 laptop running DOS. After the mechanical harddisk failed I replaced it with a flashdisk, but after re-assembly some of the keys on the keyboard would no longer work. I traced this to the '.', 'ø', 'å', '+' and '\' keys, all of which happens to be on one row/column on the keyboard matrix. After further troubleshooting I could find no fault with the keyboard or associated cables, so unfortunately the problem seems to be within the keyboard controller which is embedded into a custom chipset.
The lack of '.' and '\' makes it hard to navigate around in DOS, so I started looking into a solution. Since it is nearly impossible to find spare parts like that custom chipset I ended up with a software solution, specifically in the form of a TSR.
This TSR hooks into and intercepts the int 16h calls that are used for keyboard services. By pressing Alt+F1 a scancode representing '.' is returned instead. Also Alt+F2 returns '\' and Alt+F3 returns ':'. This will only work for DOS programs like COMMAND.COM or EDIT that actually use the BIOS services. For most games it probably won't work, but luckily the affected keys are seldom used in games.
The code:
org 0x100
bits 16
cpu 8086
section .text
start:
jmp main
int16_interrupt:
sti ; Allow other interrupts!
mov word [int16_incoming_ax], ax ; Store incoming parameter for later use.
; Call original interrupt handler:
pushf
cli
original_int16:
call original_int16:original_int16 ; Will be overwritten runtime!
sti
; Check if result should be intercepted:
pushf
push bx
push ax
mov word bx, [int16_incoming_ax]
cmp bh, 0x00 ; Check if AH was "Wait for Keypress".
je int16_check_f1
cmp bh, 0x10 ; Check if AH was "Extended Wait for Keypress".
je int16_check_f1
jmp int16_end_pop_ax
int16_check_f1:
cmp ax, 0x6800 ; Alt + F1
jne int16_check_f2
pop ax
mov ax, 0x342E ; '.'
jmp int16_end
int16_check_f2:
cmp ax, 0x6900 ; Alt + F2
jne int16_check_f3
pop ax
mov ax, 0x2B5C ; '\'
jmp int16_end
int16_check_f3:
cmp ax, 0x6A00 ; Alt + F3
jne int16_end_pop_ax
pop ax
mov ax, 0x273A ; ':'
jmp int16_end
int16_end_pop_ax:
pop ax
int16_end:
pop bx
popf
retf 2
int16_incoming_ax:
dw 0
tsr_end: ; TSR end marker.
main:
; NOTE: No protection to prevent TSR from being loaded twice or more!
; Call DOS to get original interrupt handler:
mov al, 0x16
mov ah, 0x35
int 0x21
mov word [original_int16 + 3], es
mov word [original_int16 + 1], bx
; Call DOS to set new interrupt handler:
mov al, 0x16
mov ah, 0x25
; DS is already same as CS, no need to change.
mov dx, int16_interrupt
int 0x21
; Terminate and Stay Resident:
mov dx, tsr_end
shr dx, 1
shr dx, 1
shr dx, 1
shr dx, 1
add dx, 0x11 ; Add 0x1 for remainder and 0x10 for PSP.
mov ax, 0x3100
int 0x21
Assemble it with NASM: nasm fixkeys.asm -fbin -o fixkeys.com