Category : Utilities for DOS and Windows Machines
Archive   : AUTOKEY.ZIP
Filename : AUTOKEY.ASM

Output of file : AUTOKEY.ASM contained in archive : AUTOKEY.ZIP
comment |
This program reads a file of keystrokes into a buffer and uses them
to replace keyboard input until the buffer is empty.

Written for MASM 5.1 (should compile with earlier versions)
LINK AUTOKEY; (ignore stack segment warning)

CR equ 0dh ;Definitions for
LF equ 0ah ; the program's use
TAB equ 09h

code_seg segment
assume cs:code_seg, ds:code_seg, es:code_seg

org 100h ;.COM file format

start: jmp begin

; Data used by the
; memory-resident portion of
; the program.

kbd_head equ 1ah
kbd_tail equ 1ch
kbd_start equ 80h
kbd_end equ 82h

int8_gate db ?

old_08 dd ?
old_09 dd ?
save_ss dw ?
save_sp dw ?
dw 50h dup (?)
our_stack equ $-2

buf_ptr dw offset keybuf
buf_count dw 0

BUF_SIZE equ 500 ;Change to suit your needs
keybuf db BUF_SIZE dup (?)

; Keyboard Interrupt 09 hex
; While keys are being sent
; to the buffer from Autokey,
; disable the physical keyboard
; by simply ignoring all keystrokes

new_int09 proc far
test cs:buf_count,-1 ;Are we sending keystrokes?
jz orig_int9 ;No -- go
push ax ;Else save AX
in al,60h ;Read the keystroke
in al,61h ;Get Port B value
or al,80h ;Set high bit
out 61h,al ;Send it out
and al,7fh ;Clear high bit
out 61h,al ;We said we received it
mov al,20h ;Signal end of
out 20h,al ; hardware interrupt
pop ax ;Recover original AX
iret ;Key is swallowed
jmp dword ptr cs:[old_09] ;Use original code for Int 9
new_int09 endp

; Timer Interrupt
; This is where we stuff the
; keyboard buffer

new_int08 proc far
pushf ;Simulate an interrupt call
call dword ptr cs:[old_08] ;Hand off to previous code
test cs:buf_count,-1 ;Anything to stuff
jz new8_out ;No -- leave immediately
test cs:int8_gate,-1 ;Okay to execute?
jz stuff_keys
iret ;Return to interrupted program

; Set the gate flag.
; switch stacks, and save
; necessary registers.
mov cs:int8_gate,-1 ;Don't allow recursive calls
push ax ;Save caller's AX
mov cs:[save_ss],ss ;Save caller's stack
mov cs:[save_sp],sp
cli ;Interrupts off to switch stack
mov ax,cs ;Get our segment
mov ss,ax ; in SS
mov sp,offset our_stack ;Move to our stack
and sp,0fffeh ;Make sure it's even
sti ;Interrupts on now
push ds ;Save caller's segments
push es
mov ds,ax ;Point to our segment
push di ;Now save registers we need
push si
push bx
mov ax,40h ;Segment for BIOS buffer
mov es,ax ;ES ==> BIOS segment
mov di,es:[kbd_head] ;Get head of buffer
mov bx,es:[kbd_tail] ; and the tail
cmp di,bx ;Is buffer empty?
jne end_stuff ;No -- forget it
mov si,[buf_ptr] ;DS:SI ==> head of our buffer
lodsw ;Get next word
mov [buf_ptr],si ;Save new pointer
dec [buf_count] ;Count this keystroke
or ax,ax ;Is keystroke null?
jz end_stuff ;Yes -- don't stuff it
stosw ;Else put it in buffer
inc bx ;Move tail pointer
inc bx
cmp bx,es:[kbd_end] ;Past the end?
jl dont_wrap ;No -- go
mov bx,es:[kbd_start] ;Get start of key buffer
mov es:[kbd_tail],bx ;Save new value
pop bx ;Clear our stack
pop si
pop di
pop es
pop ds
cli ;Interrupts off to
mov ss,cs:[save_ss] ; switch stacks
mov sp,cs:[save_sp]
pop ax ;All done
mov cs:int8_gate,0 ;Let next call in
jmp new8_out ;And leave
new_int08 endp

res_code equ $ - new_int08

res_size equ $-start

; Installation code
; and file parsing.
; Everything from here to the end
; is discarded when the TSR is installed

; Transient Data

temp_buf db BUF_SIZE * 2 dup (0)

hello db CR,"AUTOKEY Automatic Keyboard Entry",CR,LF,"$"
bad_ver db "This program requires DOS 2.0 or later",CR,LF,"$"
no_file db "Usage: AUTOKEY keyfile",CR,LF,"$"
no_open db "Cannot open keystroke file",CR,LF,"$"
bad_read db "Error reading keystroke file",CR,LF,"$"
too_large db "Keystroke file is too large",CR,LF,"$"
installed db "Program and keystrokes installed in memory",CR,LF,"$"
reloaded db "Keystroke buffer reloaded",CR,LF,"$"

first_byte db ? ;For 1st of 2-byte code

code_table dw "cA",1e01h ;Control keys
dw "cB",3002h
dw "cC",2e03h
dw "cD",2004h
dw "cE",1205h
dw "cF",2106h
dw "cG",2207h
dw "cH",2308h
dw "cI",1709h
dw "cJ",240ah
dw "cK",250bh
dw "cL",260ch
dw "cM",320dh
dw "cN",310eh
dw "cO",180fh
dw "cP",1910h
dw "cQ",1011h
dw "cR",1312h
dw "cS",1f13h
dw "cT",1414h
dw "cU",1615h
dw "cV",2f16h
dw "cW",1117h
dw "cX",2d18h
dw "cY",1519h
dw "cZ",2c1ah

dw "aA",1e00h ;Alt keys
dw "aB",3000h
dw "aC",2e00h
dw "aD",2000h
dw "aE",1200h
dw "aF",2100h
dw "aG",2200h
dw "aH",2300h
dw "aI",1700h
dw "aJ",2400h
dw "aK",2500h
dw "aL",2600h
dw "aM",3200h
dw "aN",3100h
dw "aO",1800h
dw "aP",1900h
dw "aQ",1000h
dw "aR",1300h
dw "aS",1f00h
dw "aT",1400h
dw "aU",1600h
dw "aV",2f00h
dw "aW",1100h
dw "aX",2d00h
dw "aY",1500h
dw "aZ",2c00h

dw "F1",3b00h ;Function keys
dw "F2",3c00h
dw "F3",3d00h
dw "F4",3e00h
dw "F5",3f00h
dw "F6",4000h
dw "F7",4100h
dw "F8",4200h
dw "F9",4300h
dw "Ft",4400h
dw "Fe",8500h
dw "Fv",8600h

dw "s1",5400h ;Shift Function keys
dw "s2",5500h
dw "s3",5600h
dw "s4",5700h
dw "s5",5800h
dw "s6",5900h
dw "s7",5a00h
dw "s8",5b00h
dw "s9",5c00h
dw "st",5d00h
dw "se",8700h
dw "sv",8800h

dw "c1",5e00h ;Control Function keys
dw "c2",5f00h
dw "c3",6000h
dw "c4",6100h
dw "c5",6200h
dw "c6",6300h
dw "c7",6400h
dw "c8",6500h
dw "c9",6600h
dw "ct",6700h
dw "ce",8900h
dw "cv",8a00h

dw "a1",6800h ;Alt function keys
dw "a2",6900h
dw "a3",6a00h
dw "a4",6b00h
dw "a5",6c00h
dw "a6",6d00h
dw "a7",6e00h
dw "a8",6f00h
dw "a9",7000h
dw "at",7100h
dw "ae",8b00h
dw "av",8c00h

dw "a!",7800h ;Alt-numbers
dw "a@",7900h
dw "a#",7a00h
dw "a$",7b00h
dw "a%",7c00h
dw "a^",7d00h
dw "a&",7e00h
dw "a*",7f00h
dw "a(",8000h
dw "a)",8100h

dw "t1",9800h ;Tandy 1000 F11 & F12
dw "t2",9900h
dw "!1",0a200h ; (shifted)
dw "!2",0a300h
dw "^1",0ac00h ; (control)
dw "^2",0ad00h
dw "~1",0b600h ; (alt)
dw "~2",0b700h

dw "HM",4700h ;Home
dw "UA",4800h ;Up-arrow
dw "PU",4900h ;Page Up
dw "LA",4b00h ;Left Arrow
dw "RA",4d00h ;Right Arrow
dw "EN",4f00h ;End
dw "DA",5000h ;Down Arrow
dw "PD",5100h ;Page down

dw "IN",5200h ;Insert
dw "DL",5300h ;Delete
dw "CR",1c0dh ;Return/Enter
dw "Tb",0f09h ;Tab
dw "BK",0e08h ;Backspace
dw "sb",0f00h ;Shift-Tab
dw "ES",011bh ;Escape

dw "P+",4e2bh ;Pad Plus
dw "P-",4a2dh ;Pad Minus

dw "ch",7700h ;Ctrl-home
dw "cu",8400h ;Ctrl-up
dw "cl",7300h ;Ctrl-left
dw "cr",7400h ;Ctrl-right
dw "ce",7500h ;Ctrl-end
dw "cd",7600h ;Ctrl-down

dw "NL",0000h ;Null

code_len equ $-code_table ;Length of the table

begin: lea dx,hello ;DS:DX ==> opening message
mov ah,9 ;Print the message
int 21h
mov ah,30h ;Get DOS version
int 21h
cmp al,2 ;At least version 2?
jae version_ok ;Yes -- go
lea dx,bad_ver ;Else report error & leave
jmp error
mov ax,3508h ;Get location of Int 08h
int 21h
mov word ptr [old_08],bx ;Save the address
mov word ptr [old_08+2],es ; in ES:BX
mov ax,3509h ;Get location of Int 09h
int 21h
mov word ptr [old_09],bx ;Save the address
mov word ptr [old_09+2],es ; in ES:BX
lea di,new_int09 ;Get code start address in DI
mov si,di ;Copy to SI
mov cx,res_code ;Bytes to compare
repz cmpsb ;Are we already installed?
jz read_file ;Yes -- go
push ds ;Else move ES
pop es ; to this segment
mov es:buf_count,0 ;Turn off any output
lea dx,no_file ;Ready for possible error
mov si,80h ;DS:SI ==> command tail in PSP
lodsb ;Get length of tail
or al,al ;Anything there?
jnz read_1 ;Yes -- go
jmp error ;Else report error & leave
lodsb ;Get a character
cmp al,' ' ;Is it a space?
je read_1 ;Yes -- skip it
cmp al,TAB ;Is it a tab?
je read_1 ;Yes -- skip it
mov di,si ;Save this position
dec di ;DI ==> beginning of file name
lodsb ;Get next character
cmp al,' ' ;At end of file name?
jg read_2 ;No -- loop back
dec si ;Yes -- move back to terminator
mov byte ptr [si],0 ;Change terminator to NULL
mov ax,di ;Get offset of beginning
sub ax,si ;Check name length
jnz read_3 ;Go if name exists
jmp error ;Else report error & leave
mov dx,di ;DS:DX ==> beginning of name
mov ax,3d00h ;Open file for reading
int 21h ;Try to open file
jnc read_4 ;Go if file was opened
lea dx,no_open ;Else report failure
jmp error ; and leave
mov bx,ax ;File handle in BX
lea dx,temp_buf ;DS:DX ==> receiving buffer
mov cx,BUF_SIZE * 2 ;Maximum bytes to read
mov ah,3fh ;Read from file
int 21h
jnc read_5 ;No error -- continue
lea dx,bad_read ;Else report error
jmp error ; and leave
push ax ;Save number of bytes read
mov ah,3eh ;Close the file
int 21h
pop bx ;BX = number of bytes read
cmp bx,cx ;Did we get the whole file?
jl read_6 ;Yes -- go
lea dx,too_large ;Else report error
jmp error ; and leave
add bx,offset temp_buf ;BX ==> end of input stream
mov byte ptr [bx],0 ;Terminate buffer
lea si,temp_buf ;DS:SI ==> temporary buffer
lea di,keybuf ;ES:DI ==> key buffer
mov cx,0 ;CX will count bytes
mov dl,0 ;DL = in-quotes flag
lodsb ;Get byte from input buffer
cmp al,0 ;End of buffer?
jz read_9 ;Yes -- go
cmp al," " ;Less than a space?
jl read_7 ;Yes -- ignore it
call translate ;Else translate to scan code
jz read_8 ;Go if nothing to save
stosw ;Else put in key buffer
inc cx ;Count the key
inc cx ; twice
cmp cx,BUF_SIZE * 2 ;Buffer full?
jl read_7 ;No -- get some more
lea dx,too_large ;Else report error
jmp error ; and leave
shr cx,1 ;Divide byte count by 2
lea ax,keybuf ;Get start of buffer
mov es:[buf_ptr],ax ;Save start address
mov es:[buf_count],cx ; and keystroke count
mov ax,es ;Get buffer segment
mov bx,ds ;And local segment
cmp ax,bx ;Are they the same?
jne exit ;No -- buffer already installed

lea dx,installed ;Report success
mov ah,09h ;Print a string
int 21h
mov ax,40h ;BIOS data segment
mov es,ax ;Use ES to reach BIOS data
mov ax,es:[kbd_start] ;Is value initialized?
or ax,ax ;Test the value
jnz kbd_ok ;Go if already set
mov ax,1eh ;Else set standard keyboard
mov es:[kbd_start],ax ; buffer in BIOS area
mov es:[kbd_head],ax ;And empty keyboard
mov es:[kbd_tail],ax
mov ax,3eh ;Normal keyboard end
mov es:[kbd_end],ax ;Store that address, also
lea dx,new_int08 ;DS:DX ==> new Int 08 routine
mov ax,2508h ;Hook it into Int8 vector
int 21h
lea dx,new_int09 ;DS:DX ==> new Int 09 routine
mov ax,2509h ;Hook it into Int9 vector
int 21h
mov ax,res_size ;Get size to keep
add ax,10fh ;Add PSP and round up
mov cl,4 ;Convert to paragraphs
shr ax,cl ;AX = paragraphs to keep
mov dx,ax ;Size in DX
mov ax,3100h ;Keep resident program
int 21h ;Back to DOS

exit: lea dx,reloaded ;Report using old buffer
mov ah,9 ;Print the string
int 21h
mov al,0 ;Return errorlevel = 0
exit1: mov ah,4ch ;Exit to DOS
int 21h

error: mov ah,9 ;Print error message
int 21h
mov al,0ffh ;Return errorlevel = 255
jmp exit1 ;Back to DOS

;Translate from the ASCII/human
; format into the appropriate
; ASCII/scancode format for Int 16h
;Receives character from input in AL,
;Returns scan code in AX, ZeroFlag = NZ to save code
; or AX = 0, ZeroFlag = Z to not save the code
translate proc near
or dl,dl ;In quotes now?
jz no_quote ;No -- go
cmp al,dl ;Ending quotes?
jz end_quote ;Yes -- go
cbw ;Else save character in AL
or ax,ax ;Reset Z-flag

mov dl,0 ;Turn off quotes
xor ax,ax ;Clear AX & set Z-flag

cmp al,'"' ;Beginning quote?
jz start_quote ;Yes -- go
cmp al,"'" ;Beginning single quote?
jz start_quote ;Yes -- go
test first_byte,-1 ;2nd byte of 2-byte command
jnz byte_2 ;Yes -- go
cmp al," " ;White space?
jbe end_quote ;Yes -- skip it
mov first_byte,al ;Else begin 2-byte command
xor ax,ax ;Set AX = 0 & Z-flag
mov dl,al ;Save this quote mark
xor ax,ax ;AX = 0 & Z-flag set
byte_2: ;2nd byte of 2-byte command
mov ah,first_byte ;AX = 2-byte code
mov first_byte,0 ;Erase 1st byte
push cx ;Save our registers
push di
push es
push ds ;Bring ES to this segment
pop es
lea di,code_table ;ES:DI ==> table of codes
mov cx,code_len ;Get length of table to search
shr cx,1 ;Divide by 2
repne scasw ;Scan table for match
jcxz no_match ;Go if no match
mov ax,es:[di] ;Else get scan code
pop es ;Recover registers
pop di
pop cx
push ax ;Save code
or ax,1 ;ZeroFlag = NZ
pop ax ;Recover code

pop es ;Recover registers
pop di
pop cx
xor ax,ax ;Set Z-flag
translate endp

code_seg ends
end start

  3 Responses to “Category : Utilities for DOS and Windows Machines
Archive   : AUTOKEY.ZIP
Filename : AUTOKEY.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: