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