home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
keyboard
/
fastbf26.arc
/
FASTBF26.ASM
next >
Wrap
Assembly Source File
|
1989-08-14
|
44KB
|
1,648 lines
PAGE ,132
; FASTBUFF.ASM - Keyboard buffer resident utility.
; Version 2.6
; by: David Steiner
; [D.STEINER] on GEnie
; 2035 J Apt. 6
; Lincoln, NE 68510
; (402) 475-0601
; This is a fairly big update from version 1.0. FASTBUFF now
; handles Alt-keypad character entries correctly, plus fixes
; these characters since many IBM clones generate incorrectly.
; The way characters are restored to the system has been updated
; also. We no longer hook the Keyboard I/O interrupt, instead we
; continuously keep the BIOS buffer full by filling it every time
; the system timer clicks (Interrupt 1C).
; This was done as an allowance for programs that bypass the BIOS
; when doing character I/O. It has the added benefit of allowing
; FASTBUFF to be active when installed after other utilities that
; hook into interrupt 16.
; The only software compatibility problems that should arise will
; be with other programs that hook the Keystroke interrupt and attempt
; to insert characters directly into the BIOS keyboard buffer.
; "Key-fake" utilities are an example of such programs. If you
; need to use such a program, it is best to deactivate FASTBUFF first.
;v2.6 Toad Hall, 14 Aug 89
; - Bug: When setting many many cmdline switches (example:
; FASTBUFF /V0 /B50 /D9 /S
; program reports an error with the last switch/parameter.
; Checking out cmdline parsing.
; Thanks to Keith Petersen's son for reporting this one!
; - Bug in cmdline parsing .. we were not correctly AsciiZing the last parm
; (skipped an essential loop!).
; - Bug in SetDelay: checking for illegal high value, the 'jz' branch
; should've been 'jnz' .. dumb!
; - Removing old 'v2.5' comments.
;v2.5 Toad Hall tweak, 1 May 89
; - Removing old "v2.n" comments.
; - Changed default screen blanking to 6 minutes.
; - Using one beep for ACK or on, two beeps for off.
; Rewrote the beep code because of an irritating click and delay
; on my system. Sound is now 8253 timer-driven.
; Frequency is arbitrary.
; Could be this will break any programs doing simultaneous music/sound
; (or other timer-driven activities) and keyboard activity ..
; but then we don't beep THAT often!
;v2.4 Toad Hall Debug, Tweak, 2 Apr 89
; - Added new command line parameter processing.
; - Fixed possible bug in '/v' video blanking delay.
; - Tightened up, debugged GetNum procedure for cmdline numeric values.
; - Adding explanatory error msgs for faulty command line parameters.
; - Left the rest alone for now.
; - Per Keith Petersen's suggestion, adding forced screen blanking
; ([5][End]).
; - Also added a screen blanking toggle ([5][HOME].
; Overrides the commandline '/V' or '-V0' (whichever) (if any).
; - Tightened up Command key processing a little (sharing common code).
; - Added a real short beep to acknowledge [5] commands.
; - Single short beep means "ON", extra-long beep means "OFF".
; - Considering a pop-up window with function-key or cursor-key
; variable settings (rather than the somewhat cryptic [5][whatever]
; combos we're using now). That would add a lot of code, though,
; plus storage space for user's screen, etc. Maybe later.
; - No problem with switching from Int 1CH to Int 8 .. I was tempted
; to do the same myself, but wanted to keep this "well-behaved".
; - Removed code commented out by v2.2, v2.3.
;v2.3 Dave, March 15, 1989
; - Changed timer interrupt vector from 1C to 8.
; This fixes problems with programs that incorrectly hook the
; software timer interrupt (1C). IBM BASIC is one such program.
;v2.2 Toad Hall Tweak, 2 Mar 89
; - Keith Petersen detected DSZ serial port errors when FASTBUFF was
; installed and DSZ was running at VERY high (19200 baud) rates.
; Suspect we're grabbing interrupts too often or too long.
; Tweaking to reduce CLI periods and ANY other delays.
; - Removed a bunch of v2.0 code I'd commented out in v2.1
; (since v2.1 seems to be working all right).
;v2.1 Toad Hall Tweak, 23 Feb 89
; - General tightening
; - Much more use of AX vs. other regs
; - Described a Page40 segment to permit faster BIOS variable
; addressing.
; - Now using DS: instead of ES: to address BIOS variables.
; - FATAL error when new interrupt svcs call or jump to the old
; interrupt vectors! Author didn't address the saved vector
; (oldint9, etc.) as CS:.
; - Tightened up installation procedure:
; - Uses runtime variables rather than installation variables.
; - If error, terminates with proper Svc 4CH, Int 21H.
; - Now freeing environment variable before going TSR
; - Rounding up TSR program/data size to nearest paragraph
; before going TSR.
; - Reduced .COM file about 1Kb. Runtime memory requirements:
; - v2.0 1504 bytes, 2 blocks
; - v2.1 1296 bytes, 1 block
; .. not too shabby.
; Kudos to the author .. nice logic.
; David Kirschbaum
; Toad Hall
; kirsch@braggvax.ARPA
;------ EQUATES
CR equ 0DH
LF equ 0AH
TIMER equ 40H ;timer chip
PORT_B equ 61H ;8253 port B
HIFREQ equ 300 ;high freq for on
LOFREQ EQU 900 ;200 ;low freq for off
ERRFREQ equ 350 ;keyboard overrun beep
;------ BIOS buffer & otherstuff equates
Page40 segment at 40H
org 10H
biosequipflags dw ? ;410H BIOS equipment word address
org 17H
;------ BIOS shift status byte 1 & masks for action key combinations
biosshflags db ? ;417H First BIOS shift status byte
db ?
CLRMASK equ 05H ; Clear buffer key mask
biosaltbuff db ? ;419H Storage for Alt-keypad entries
bioshead dw ? ;41AH BIOS keyboard buffer head
biostail dw ? ;41CH "" tail
biosbuffer db ? ;41EH BIOS keyboard buffer start
org 3EH
biosendbuff label byte ;43EH end
org 65H
bioscrtmodeset db ? ;465H
biospalette db ? ;466H
EQUIPMASK equ 0010H
PALETTEMASK equ 0FH
org 71H
;------ BIOS break detection byte
biosbreak db ? ;471H Bit 7 of this byte indicates
BREAKMASK equ 80H ; ctrl-break was pressed
Page40 ENDS
KBINPORT equ 60H ; Keyboard data port
;KBCTRLPORT equ 61H ; Keyboard control port
ALTSHIFT equ 38H ; ALT key scan code
;------ FASTBUFF control key scan codes
FBCTRLKEY equ 4CH ; Scan code for "5" on keypad
FBONKEY equ 52H ; Code for INS key
FBOFFKEY equ 53H ; Code for DEL key
FBFAST equ 4EH ; Code for keypad "+"
FASTREP equ 2 ; Chars per click for fast rate
FBSLOW equ 4AH ; Code for keypad "-"
SLOWREP equ 1 ; Chars per click for slow rate
FBENDKEY equ 4FH ; Code for END key
FBHOMEKEY equ 47H ; Code for HOME key
;------ Masks for altering bits in our status byte.
FBMASK equ 01H ; Masks for FASTBUFF toggle
REPMASK equ 02H ; On when we need to repeat a character
SCRMASK equ 04H ; On while screen is active
CTRLMASK equ 08H ; On when [5] is being held down
OLDINT9MASK equ 10H ; On when processing char with old int9
VIDMASK equ 20H ; Video blanking active?
BLANKMASK equ 40H ; Forced video blanking?
CSEG segment public para 'CODE'
ASSUME CS:CSEG, DS:CSEG
org 2CH
env_adr dw ? ;environment segment
org 80H
nchar db ? ;PSP cmdline char count
params db ? ;PSP cmdline chars
org 100H
Start:
jmp Initialize
;------ Old vector storage area
oldint9 dw 0,0
oldint10 dw 0,0
oldint8 dw 0,0
;--------------------------------------------------------------------------
;Resident data area
;--------------------------------------------------------------------------
lastchar dw 0FFFFH ; Last character typed, undef at start
repcount db ? ; # ticks till next repeat
repchars db 2 ; Default # chars to repeat
; per click
; Keith Petersen suggested increasing this start delay a little
; (because of repeated Return keys, fast cursor keys, etc.).
; Changing from the original 5 to 7 ticks delay.
startdelay db 7 ; Delay before repeating new char
; 6-minute screen blanking timeout default.
BLANKTIME EQU (6 * 1092) ;6 minutes * 1092 ticks per min
scrcount dw BLANKTIME ; Time left till screen blanked
screendelay dw BLANKTIME ; "Constant" for refreshing
; scrcount
switches db SCRMASK AND VIDMASK ; FASTBUFF off, screen active,
; default screen blanking on
;---------------------------- Int 09 --------------------------------------
;Keystroke Interrupt
; Note that we always allow control to pass on to the old interrupt
; 9 handler. We do, however, ignore the characters returned if one
; of FASTBUFF's four command key combinations were detected.
;--------------------------------------------------------------------------
; We're not concerned about minimizing interrupt delays here.
; If user is typing, he's obviously not using high-speed serial input.
NewInt9 proc far
sti
push ax
push bx
push cx
push dx
push DS
;------ This first section is code always executed by FASTBUFF
and CS:switches,NOT REPMASK ; Turn off repeat switch
mov ax,40H
mov DS,ax
ASSUME DS:Page40
and biosbreak,NOT BREAKMASK ;turn off break bit
; See if the LAST key was a [5][End] to force screen blanking.
; If so, don't fiddle with screen blanking
; We'll leave the forced blanking bit alone until
; we're SURE we're out of control mode (e.g., regular key,
; and not just the [5] release).
test CS:switches,BLANKMASK ;Did we just force blanking?
jnz SkipRestore ;yep, leave blank screen alone
mov ax,CS:screendelay ; Reset screen blank counter
mov CS:scrcount,ax
test CS:switches,SCRMASK ;Is screen active?
jnz SkipRestore ;yep, video is NOT blanked
call RestoreScreen ;was inactive (blanked),turn it back on
SkipRestore:
in al,KBINPORT ; Get scan code from keyboard
test CS:switches,CTRLMASK ; Check if currently in control mode
jnz In_Control ;yep
jmp NotControl ;nope
In_Control:
;------ FASTBUFF control key check when keypad #5 is depressed
cmp al,FBCTRLKEY+80H ; Check if control key was released
jne NotReleased ;nope
push ax ;save AX
and CS:switches,NOT CTRLMASK ; Turn off control mode
jmp CallOld9 ;(will pop AX)
NotReleased:
;2.4 Adding screen blanking with [5] End combo
; Check this first so we don't clear the BLANKMASK bit prematurely.
cmp al,FBENDKEY+80H ;just released [END] key?
; jz Reset1 ;yep, do nothing
jz Reset ;yep, do nothing
cmp al,FBENDKEY ;[5] END?
jnz K00 ;nope
call Check_Delay ;insure we have refresh rate
mov CS:scrcount,1 ;set up so NEXT clock tick
;it'll blank the screen
mov al,VIDMASK + BLANKMASK ;turn video blanking
;and forced blanking on
jmp short On_ClearAll ;common code
; Screen blank toggle
K00:
and CS:switches,NOT BLANKMASK ;NOW turn the forced
;blanking bit off
cmp al,FBHOMEKEY ;[5] HOME?
jnz K0 ;nope
call Check_Delay ;insure we have refresh rate
;(AX = old or default rate)
mov CS:scrcount,ax ;reset blanking counter
mov al,CS:switches ;get old switches
and al,VIDMASK ;mask for video blanking bit
jmp short Off_ClearAll ;toggle it with an XOR
K0:
cmp al,FBONKEY ;[5] INS?
jne K1 ;nope
mov al,FBMASK ;prepare to turn on FASTBUFF
jmp short On_ClearAll
K1:
cmp al,FBOFFKEY ;[5] DEL?
jne K2 ;nope
mov al,FBMASK ; Turn off FASTBUFF
Off_ClearAll:
xor CS:switches,al ;turn off whatever
mov ax,LOFREQ ;low for off
jmp short ClearAll ;finish up
On_ClearAll:
or CS:switches,al ;mask on whatever
mov ax,HIFREQ ;hi for on
ClearAll:
call ErrBeep ;beep low or high
call Clear_All ;Clear both keyboard buffers
jmp short Reset ;and skip to reset
K2:
test CS:switches,FBMASK ; FASTBUFF on?
jz NotPressed ;nope
cmp al,FBFAST ;[5] +?
jne K3 ;nope
mov ax,HIFREQ ;high beep for fast
call ErrBeep
mov al,FASTREP ;set fast repeat rate
jmp short SetRep ;do it (1 beep)
K3:
cmp al,FBSLOW ;[5] -?
jne NotPressed ;nope, none of our control keys
mov ax,LOFREQ ;low beep for slow
call ErrBeep
mov al,SLOWREP ;set slow repeat rate
SetRep:
mov CS:repchars,al ;set to fast or slow repeat rate
Reset:
;Allow old interrupt handler to see the keystroke entered,
;but ignore any output character.
cli
mov ax,biostail
pushf
call dword ptr CS:oldint9
mov biostail,ax
sti
jmp Int9Done
;------ Not currently in control mode so check for other possibilities
NotControl:
and CS:switches,NOT BLANKMASK ;NOW turn the forced
;blanking bit off
cmp al,FBCTRLKEY ; Check if our control key pressed
jne NotPressed
or CS:switches,CTRLMASK ; Turn on control mode
NotPressed:
;------ Finally we will check to see if FASTBUFF is active
test CS:switches,FBMASK ; FASTBUFF on?
jz Off1 ;nope
cmp al,37H ; If Shift-PrtSc is pressed then just
jne On1 ; call old handler and exit
test biosshflags,3
jz On1
Off1:
pushf ; Not on so call old int9 and exit
call dword ptr CS:oldint9
jmp Int9Done
On1:
cmp al,ALTSHIFT+80H ; Check if Alt key released
jne NotAlt
mov ah,biosaltbuff ; If so save alt-keypad character
push ax ;save AX for after CallOld9
jmp short CallOld9
NotAlt:
push ax ;(CallOld9 will pop)
mov al,biosshflags
and al,0FH
cmp al,CLRMASK ; Clear key combination pressed?
jne CallOld9 ;nope
call Clear_All ;clear out buffers
;------ Let the old BIOS handler determine what the ASCII or extended
; character code should be.
CallOld9:
or CS:switches,OLDINT9MASK ; Set bit so we don't update BIOS buff
mov bx,biostail ; Make old int 9 do the dirty work
pushf
call dword ptr CS:oldint9
mov dx,[bx] ; Store new char in DX
mov cx,biostail
mov biostail,bx
and CS:switches,NOT OLDINT9MASK
test biosbreak,BREAKMASK
jz CheckNew
cli
mov bioshead,bx
mov bx,CS:head ; Break detected, clear our buffers
mov word ptr CS:[bx],0 ; and output dummy character
call IncBuff
mov CS:tail,bx
sti
pop ax ;discard saved AX
jmp short Int9Done
CheckNew:
pop ax ;saved AX
cmp bx,cx
jne NewChar
mov CS:lastchar,0FFFFH ; Set non-typeable char as lastchar
jmp short Int9Done
NewChar:
cmp al,ALTSHIFT+80H ; Check if Alt key released
jne PutIn
mov dl,ah ; Fix entered character with keypad
xor dh,dh ; Scan code = 0
PutIn:
call NewInChar
Int9Done:
pop DS
pop dx
pop cx
pop bx
pop ax
iret
NewInt9 endp
;------ NewInChar - Take char from BIOS buffer and insert it into ours,
; then set repeat switch accordingly.
; DS contains BIOSDSEG
; DX contains character that was return by old int 9
;Called 1 time
NewInChar proc near
push si
cmp dx,CS:lastchar
je InDone ; Ignore character if it was from STD
; repeat function
mov CS:lastchar,dx
mov al,CS:startdelay ; Set start delay for repeat function
mov CS:repcount,al
mov bx,CS:tail
mov si,bx ; Save address where char will go
call IncBuff ;bump/wrap bx
cmp bx,CS:head ;overrun?
jne StorChar ;nope
mov ax,ERRFREQ ;error frequency
call ErrBeep ;beep
jmp short InDone ;don't store
StorChar:
cli
mov CS:[si],dx ; Store character
mov CS:tail,bx ; Store new tail value
sti
InDone:
or dh,dh ; Scan code=0 if entered on keypad
jne SwitchOn
test biosshflags,02H ; Allow chars entered via the
jz ReallyDone ; keypad to repeat if the user is
SwitchOn: ; holding down the Left Shift key.
or CS:switches,REPMASK
ReallyDone:
pop si
ret
NewInChar endp
; Clear_All .. NewInt9 subroutine.
; Effectively flushes keyboard buffers
; Called 2 times
Clear_All proc near
ASSUME DS:Page40
cli
mov ax,biostail ; Clear out buffers
mov bioshead,ax
mov ax,CS:tail
mov CS:head,ax
sti
ret
Clear_All endp
; Common subroutine for NewInt9
; Insures we have a screendelay refresh rate
; when toggling or otherwise fiddling screen blanking.
; Called 2 times.
; Returns AX = original screendelay or default
Check_Delay proc near
mov ax,CS:screendelay ;get refresh rate
or ax,ax ;0 (because of '-V0' cmd)?
jnz CD_Exit ;nope, got refresh rate
mov ax,BLANKTIME ;was 0, use default
mov CS:screendelay,ax ;force default refresh rate
CD_Exit:
ret
Check_Delay endp
;Beep routines
;Toad Hall:
; My system was producing an irritating click (in addition to the
; desired beep) when using the "direct speaker drive" code.
; Recoded to use the 8259 timer-driven tone.
;Enter with desired frequency in AX.
;ErrBeep calls Prepare_Timer before we do any beeps.
;Since we don't know what the outside world is doing to the timer,
;we reset it every time we're gonna beep.
;(Probably unnecessary .. but better safe than sorry).
;Hope no one else is trying to use the timer!
Prepare_Timer proc near
;Destroys AX
push ax ;save the freq
mov al,182 ;10110110B ;Sel tim 2,lsb,msb,binary
out TIMER+3,al ;get timer ready
pop ax ;desired frequency
out TIMER+2,al ;load low-order byte
mov al,ah
out TIMER+2,al ;and hi-order byte
ret
Prepare_Timer endp
ErrBeep proc near
;Destroys AX,CX
call Prepare_Timer ;set up timer frequency
in al,PORT_B ;Get current port status
mov ah,al ;and save it
or al,3 ;set bits 0 and 1 on
out PORT_B,al
xor cx,cx ;around 500 ms for a 4.47MHz system
ErrBLup:
loop ErrBLup
mov al,ah ;orig port setting
out PORT_B,al ;better be sound off!
ret
ErrBeep endp
;---------------------------- Int 10 --------------------------------------
;Video I/O
;--------------------------------------------------------------------------
; We ARE concerned about minimizing interrupt delays here.
NewInt10 proc far
sti
cmp ah,0FAH ; Return value to indicate FASTBUFF
jne Vid ; is already resident
mov ax,CS ;Let's return our segment in ES
mov ES,ax ;(for future reaching out...)
mov ax,0FAH ;return value in AL
iret
Vid:
push ax
mov ax,CS:screendelay ; Reset screen blanking counter
mov CS:scrcount,ax
test CS:switches,SCRMASK ; Check if video is currently blanked
jnz SkipRest ;nope, it's active
push DS ;Set up DS for RestoreScreen
mov ax,40H
mov DS,ax
ASSUME DS:Page40
push cx ;save
push dx ;(RestoreScreen doesn't)
call RestoreScreen ;turn it back on
pop dx
pop cx
pop DS ;restore DS
ASSUME DS: NOTHING
SkipRest:
pop ax
jmp dword ptr CS:oldint10 ; Pass control on
; to normal Video I/O
NewInt10 endp
;------------------------------- Int 8 ------------------------------------
;Hardware Timer Tick (changed from int 1CH in v2.3)
;--------------------------------------------------------------------------
; VERY likely place for interrupt delays.
; Optimizing to the max for speed.
NewInt8 proc far
pushf
call dword ptr CS:oldint8 ; IMPORTANT! Call old hardware
; interrupt first to avoid possible
; hardware timing problems (such as
; possible when formatting a disk).
sti
push ax
mov al,CS:switches ;faster testing in AL
test al,FBMASK ;FASTBUFF on?
jz Timer_NoAct ;nope
; Let's do some testing right here to see if we have ANY work to do.
; I know .. the testing is redundant .. but we may save the PUSHes
; and POPs (especially if VIDMASK is turned off).
test al,OLDINT9MASK ;currently using BIOS buff?
jz Timer_GotWork ;nope, got work
test al,REPMASK ;repeat switch on?
jz Chk_Video ;nope
cmp CS:repcount,1 ;upcoming decrement zeroes it?
jz Timer_GotWork ;yep, it'll be 0 .. got work
Chk_Video: ;Test video blanking last
test al,VIDMASK ;video blanking on?
jz Timer_NoAct ;nope, no work at all
test al,SCRMASK ;screen already blanked?
jz Timer_NoAct ;yep, no work at all
; Let's make sure a (scrcount=0) doesn't bite us, ne?
cmp CS:scrcount,1 ;upcoming decrement zeroes it
;or we're already <= 0?
ja Timer_NoAct ;nope, nothing to do, exit
Timer_GotWork:
;BlankScreen destroys AX,BX,CX,DX,ES
;Let's save all our regs NOW rather than later AND in BlankScreen.
push bx
push cx
push dx
push di
push si
push DS
mov bx,40H
mov DS,bx
ASSUME DS:Page40
test al,VIDMASK ;video blanking on?
jz DontBlank ;nope
test al,SCRMASK ;Is screen active?
jz DontBlank ;nope, already blanked
; Let's be sure a (scrcount=0) doesn't bite us, ne?
sub CS:scrcount,1 ;decr blanking counter
ja DontBlank ;> 0, not time
; Doesn't matter if scrcount is below 0, since the first keyboard
; interrupt or Int 10H video I/O will reset scrcount to a proper value.
call BlankScreen ;go blank the screen
mov al,CS:switches ;refresh AL with switches
DontBlank:
test al,OLDINT9MASK ; If currently using BIOS buff
jnz SkipUpdate ; don't try to update it.
call UpdateBIOS ; Insert characters into BIOS buffer
mov al,CS:switches ;refresh AL with switches
SkipUpdate: ; from our buffer.
test al,REPMASK ;repeat switch on?
jz NoRep ;nope
dec CS:repcount ;decrement repeat counter
jnz NoRep ;didn't zero
call RepKey ; Otherwise repeat the key.
;(done with AL)
NoRep:
pop DS
pop si
pop di
pop dx
pop cx
pop bx
Timer_NoAct:
pop ax
iret
NewInt8 endp
;------ UpdateBIOS - Put characters from our buffer into the BIOS buffer
; Note: don't fill BIOS buffer completely, leave space for
; at least one character. This is required to allow
; the int9 handler to use this spot as its input buffer.
;Called 1 time by NewInt8
UpdateBIOS proc near
ASSUME DS:Page40
mov dx,biostail ; Pointer to the BIOS buffer tail
mov cx,dx ;CX = BIOS tail ptr
call BIOSIncBuff ;ZF set if BIOS buffer full
;DX = BIOS tail + 2
je Udone ;BIOS buffer full
; NOW we have a reason to load BX.
; While we're at it .. we have one register left (SI).
; Let's use it as a constant (2) for bumping BX, DX.
mov bx,CS:head ; Pointer to our buffer head
mov si,2 ;handy constant
Uloop:
cmp bx,CS:tail ; Check if we have any characters
je Udone ; left to insert
mov ax,CS:[bx] ;get char before bumping BX
add bx,si ;bump BX 2
cmp bx,CS:endbuff
jne ULOk
mov bx,offset buffer
ULOk:
;Make sure BIOS has room for at least two characters
;before inserting another.
mov di,cx ;last DX BIOS tail ptr (before +2)
mov cx,dx ;BIOS tail + 2
add dx,si ;bump DX BIOS tail buffer ptr
cmp dx,offset biosendbuff ;hit end?
jne ULBok ;not yet
mov dx,offset biosbuffer ;yep, back to start
ULBok:
cmp dx,bioshead ;hit BIOS head ptr?
je Udone ;yep, BIOS buffer full
;If character was inserted, then update the head and tail pointers.
mov CS:head,bx ;our buffer head ptr
mov biostail,cx ;BIOS tail ptr (DX before the +2)
mov [di],ax ;stuff char at last DX
jmp Uloop
Udone:
ret
UpdateBIOS endp
;------ RepKey - repeat key only if the program is ready to accept more
;Called 1 time by NewInt8
RepKey proc near
ASSUME DS:Page40
mov CS:repcount,1 ; Set fast repeat rate
; Recoded for speed, tightness
mov ax,biostail
cmp ax,bioshead ; Anti-skid braking check
jne Rdone
mov di,ax ;DS:DI points to BIOS buffer
mov ax,CS:lastchar ;store repeat char in AX
xor ch,ch
mov cl,CS:repchars ; Set CX for # times to repeat char
mov bx,offset biosendbuff ;handy constant for testing
mov si,2 ;handy constant for bumping DI
cld ;insure fwd
RepLoop:
mov [di],ax ;stuff
add di,si ;bump DI 2
cmp di,bx ;hit buffer end?
jne RepOk ;not yet
mov di,offset biosbuffer ;yep, back to start
RepOk:
loop RepLoop
mov biostail,di ; Update tail pointer
Rdone:
ret
RepKey endp
;------ IncBuff - Increment a FASTBUFF buffer pointer stored in BX
IncBuff proc near
add bx,2
cmp bx,CS:endbuff
jne Ok
mov bx,offset buffer
Ok: ret
IncBuff endp
;------ BIOSIncBuff - Same thing, but for BIOS buffer pointer in DX
BIOSIncBuff proc near
ASSUME DS:Page40
add dx,2
cmp dx,offset biosendbuff
jne Bok
mov dx,offset biosbuffer
Bok:
cmp dx,bioshead ; Make sure BIOS buffer not full
;(common test every call)
ret
BIOSIncBuff endp
;--------------------------------------------------------------------------
;Video Blanking / Restoring procedures
;--------------------------------------------------------------------------
;------BlankScreen - Turn off video display
;Called 1 time by NewInt8
;Regs already saved, OK to destroy everything.
;Reducing CLIs to absolute minimum time off
BlankScreen proc near
ASSUME DS:Page40 ;already done
mov ax,2B21H
call SetVideo ;AL reset to 21H
test biosequipflags,EQUIPMASK
jnz JJ2 ;yep, AL=21H
mov al,bioscrtmodeset
and al,0F7H
mov bioscrtmodeset,al
JJ2: mov dx,03D8H
cli
out dx,al
mov al,biospalette
test al,PALETTEMASK
jz BlankDone
and al,0F0H
inc dx ;now 03D9H
out dx,al
BlankDone:
sti
and CS:switches,NOT SCRMASK ; Clear screen active flag
call ErrBeep ;Let user know we did it
ret
BlankScreen endp
;------ RestoreScreen - Turn video display back on
;Called 2 times (by NewInt9 and NewInt10)
; Reducing CLIs to absolute minimum time off
RestoreScreen proc near
ASSUME DS:Page40
mov ax,0B29H
call SetVideo ;AL reset to 29H
test biosequipflags,EQUIPMASK
jnz J2 ;yep, AL=29H
mov al,bioscrtmodeset
or al,08H
mov bioscrtmodeset,al
J2: mov dx,03D8H
cli
out dx,al
mov al,biospalette
test al,PALETTEMASK
jz RestoreDone
inc dx ;now 03D9H
out dx,al
RestoreDone:
sti
or CS:switches,SCRMASK ; Reset screen active flag
ret
RestoreScreen endp
;------ SetVideo - set video according to contents of CX
; Destroys DX and AX registers also
;Called 2 times (by BlankScreen and RestoreScreen)
;Common to output AL to DX first.
;Reducing CLIs to absolute minimum time off
SetVideo proc near
mov dx,03B8H ;common to 2 calls
push ax ;save the 2BH (blank)
; or 0BH (restore) in AH
cli
out dx,al ;output 21H (blank)
; or 29H (restore)
mov dx,03B4H
mov al,0AH
out dx,al
inc dx ;03B5H
pop ax ;AH=2BH (blank) or 0BH (restore)
xchg al,ah ;prepare AL,
;AH= orig 21H or 29H for return
out dx,al ;output 2BH or 0BH
mov al,0BH
out dx,al
inc dx ;03B6H
inc al ;from 0BH to 0CH
out dx,al
sti
mov al,ah ;AL = orig 21H or 29H
ret
SetVideo endp
;------ This portion must remain at the end, since the location of the
; end of the buffer indicates how large the resident portion is.
head dw buffer ; FASTBUFF buffer head and
tail dw buffer ; tail pointers
endbuff dw 86 ; Buffer size (100, after
; subtracting additional
; 14 chars in BIOS buff)
; Will end up holding pointer
; last buffer byte
buffer dw ? ; New keyboard buffer.
;---------------------- Initialize vectors --------------------------------
;
; After successful initialization process release memory from
; here on down, terminate and stay resident.
;--------------------------------------------------------------------------
Initialize:
ASSUME CS:CSEG,DS:CSEG
call Check_Args ;check PSP cmdline arguments
;(may die)
mov ah,0FAH ; Check if FASTBUFF is already
int 10H ; installed
ASSUME ES:NOTHING ;if resident, ES=resident CS
cmp ax,0FAH
jne InstallOk ;Nope, continue
;Toad Hall:
; We REALLY should have a snazzy way to reach down
; into memory (where a resident FASTBUFF is installed)
; and change ITS runtime parameters!
; However .. with all the interrupt-driven stuff ..
; I'm almost afraid to touch the resident program!
; However .. I added to the Svc 0FAH, Int 10H routine (in the
; TSR code) returning ES=resident CS ..
; so we have a pointer to the resident code if we want it!
mov dx,offset error ; Print error message
Msg_Term: ;jmp here if DX has a fatal
;error msg
mov ah,09H ;display msg
int 21H
Terminate:
mov ax,4C01H ;terminate, ERRORLEVEL 1
int 21H
InstallOk:
mov dx,offset instalmsg ; Print installation message
mov ah,09H
int 21H
;Toad Hall:
; Since we're releasing environment, utilities like MAPMEM
; no longer can find/display our program name.
; Moving a likely identifier into our PSP command line
; (since _Args is done with it).
; I know .. we lose peeking at the args, but that's ok.
mov ax,CS
mov ES,ax ;insure
ASSUME ES:CSEG
mov si,dx ;-> instalmsg
mov di,offset nchar ;-> cmdline char counter
mov ax,INSTALMSGLEN ;nr of chars we'll be moving
dec ax ;remove the '$'
dec ax ;and the LF
mov cx,ax ;nr chars to move
mov ah,' ' ;AL=length byte,AH=space
stosw ;stuff length byte, space
rep movsb ;copy msg into PSP cmdline
; Runtime variables already have defaults.
; Commandline processing may have changed them
; to new values.
; Previous fiddling of video blanking bit now moved
; down into video blank delay subroutine.
;Determine buffer size in bytes and find endbuff address
mov ax,endbuff ;requested or default buffer size
shl ax,1 ;*2 for words
add ax,offset buffer+2 ;+ buffer start + 2
mov endbuff,ax ;save as endbuff pointer
; Try to free our environment block's memory
; as part of preparing to go TSR.
; If we fail at this, let's quit without messing with vectors.
mov ES,env_adr ;PSP environment seg
ASSUME ES:NOTHING ;a reminder
mov ah,49H ;free allocated memory
int 21H
jnb Env_Freed ;freed ok, continue
mov dx,offset envmsg ;'Failed to free memory'
jmp Msg_Term ;display msg, terminate
Env_Freed:
mov ax,40H
mov ES,ax
ASSUME ES:Page40
mov ax,ES:biostail ; Clear BIOS keyboard buffer
mov ES:bioshead,ax
mov ax,3509H ;save old Int 9 vector
int 21H
mov oldint9,bx
mov oldint9[2],ES
mov dx,offset NewInt9
mov ax,2509H ;set new one
int 21H
mov ax,3510H ;save old Int 10H vector
int 21H
mov oldint10,bx
mov oldint10[2],ES
mov dx,offset NewInt10
mov ax,2510H ;set new one
int 21H
mov ax,3508H ;save old Int 8H vector
int 21H
mov oldint8,bx
mov oldint8[2],ES
mov dx,offset NewInt8
mov ax,2508H ;set new one
int 21H
; Go TSR properly.
mov ax,endbuff ;pointer to buffer
; (and resident data) end
add ax,15 ;round up to a para
mov cl,4
shr ax,cl ;convert to paragraphs
mov dx,ax ;DX needs it
mov ax,3100H ;Advanced TSR, code 0
or switches,FBMASK ; Turn FASTBUFF on (last thing)
int 21H ;go TSR
ASSUME DS:CSEG,ES:CSEG
;------ CheckPSP - Check command line parameters
; Note: this procedure may destroy any registers except DS
Comment ~
Toad Hall:
Something's been SERIOUSLY wrong with this entire procedure.
(like locking up with '/v0').
Replacing with an entirely new args procedure (snarfed from
Kegel's KEGELUNX.ARC package and severely hacked for this
specific application).
Comment ends ~
; New command line argument procedures
; An array of argv pointers point to separate parameters
; maintained in the PSP command line buffer.
; (This only works if we aren't opening/closing files!)
; See dynamic variables argc and argv at code end.
; Thanks to Kegel and his Unix-like utils in KEGELUNX.ARC
; Although we only provide for 7 cmdline switch types,
; the user might enter more, enter duplicates, whatever.
; Allowing for a full dozen. (What the heck .. it's only
; dynamic variables anyway ... !)
MAXPARMS equ 12 ;max cmdline args permitted
_Args proc near
mov di,offset argv ;first argv pointer
mov cx,MAXPARMS ;max parms allowed
xor ax,ax
rep stosw ;point all args at null
mov bx, 2 ; argc = 0 (will sub 2 from bx at end)
mov si,offset nchar ;cmdline char count
lodsb ;snarf, bump SI ptr
mov cl,al ;into CX for counter
xor ch,ch
jcxz A_Done ; no arg chars -> we're done.
mov di,si
;Uppercase the entire command line.
;While we're at it, replace all spaces with 0's
;(to INSURE each argv is AsciiZed).
push cx ;save cmdline char count
mov dx,2000H + 'a' ;handy constant
A_UpperLup:
mov al,[di] ;snarf PSP cmdline char
cmp al,dh ;space?
jnz A_NotSpace ;nope, more checking
xor al,al ;replace with a 0
jmp short A_Stuff
A_NotSpace:
cmp al,dl ;'a' ;lower case?
jb A_Stuff ;nope
sub al,dh ;20H ;uppercase it
A_Stuff:
stosb
loop A_UpperLup
xor al,al ;replace CR with a 0 v2.6
stosb ; v2.6
pop cx ;restore cmdline counter
;v2.6 If there's a '?' anywhere on the cmdline,
; skip directly to help and terminate.
mov di,si ;back to first char of cmdline v2.6
mov dx,cx ;save cmdline ctr again v2.6
mov al,'?' ;scan for question mark v2.6
repne scasb
mov cx,dx ;restore cmdline ctr v2.6
jz A_Help ;we found one! help, terminate v2.6
; Big loop- find arguments...
mov dx,'-/' ;DL='-', DH='/'
A_ParmL:
lodsb ; al = [si++]
cmp al,dl ;'-' ;look for a switch
jz A_GotOne ;yep
cmp al,dh ;'/' ;this kind?
jnz A_Relup ;nope, gobble until we find one v2.6
A_GotOne:
mov byte ptr [si-1],0 ;stuff a 0 on the switch
mov word ptr argv[bx],si ; save pointer to this string
add bx,2 ; argc++
cmp bx, MAXPARMS*2
jae A_Done ;done
A_Relup: ; v2.6
jcxz A_Done ;stop any CX wraparound to 0FFFFH! v2.6
loop A_ParmL ;more chars to go v2.6
A_Done:
; All done finding parms; now share argc with caller.
mov ax,bx
sub ax, 2 ;adjust for argv[0]
;(traditionally program name)
shr ax, 1
mov word ptr argc,ax ;save argc
ret
A_Help: jmp GetHelp ;help, terminate
_Args endp
;---- _Shift: --------------------------------------------
; Shifts %2 to %1, %3 to %2, etc. Leaves %0 alone.
; Works by shuffling argv[*].
; Destroys BX v2.6
_Shift proc near
cld
mov si, offset argv[4]
mov di, offset argv[2]
mov bx,offset argc ;nr of args remaining v2.6
mov cx,[bx] ;argc ;move this many args v2.6
rep movsw
dec word ptr [bx] ;argc ;decr arg counter v2.6
ret
_Shift endp
; Check_Args - We've parsed our command line, and all (if any)
; args are in argv[argc] array.
; Check them out and respond accordingly.
Check_Args proc near
cld ;insure fwd
call _Args ;process command line
or ax,ax ;got any args at all?
jz CA_X ;nope, nothing to do v2.6
CA_1: mov si,word ptr argv[2] ;first argument
or si,si ;null arg?
jz CA_X ;yep, all done
lodsb ;snarf arg's first char
;AH=0
mov di,offset parmtable
mov cx,LPARMTABLE
repne scasb
jne H1 ;no match, display help and die
sub di,offset parmtable+1 ; Calculate jump address
;(the +1 adjusts for the last scasb)
shl di,1 ;*2 for addresses
call word ptr jumptable[di] ;jmp to procedure,
;Notice we do NOT shift the argv array until after our subroutines
;have had a chance to process the argv string.
;The current argv is at argv[argc].
call _Shift ;move the argv array down one
jmp CA_1 ;and loop for them all
H1: jmp GetHelp ;Invalid parm found.
;Display help, terminate
CA_X: ret ;done or nothing to do v2.6
Check_Args endp
;------ SetBuff - Set new buffer size
SetBuff proc near
call GetNum ;get buffer value after '/B'
;(may die)
mov dx,offset toobigmsg ;'/B value too large!'
or ah,ah ;bogus if > 255
jnz SetBuff_Bad ;invalid size
mov dx,offset toosmallmsg ;'/B value too small!'
sub ax,14 ; We get 14 characters from BIOS buff
jb SetBuff_Bad ;Went negative! Gotta be bogus
cmp al,11 ; Make sure we have room in our buffer
jb SetBuff_Bad ;invalid size
mov endbuff,ax ;update runtime variable
ret
SetBuff_Bad:
mov ah,9 ;display error msg
int 21H
jmp Bad_Num ;bad number error routine
;error msg, terminate
SetBuff endp
;------ SetFast - Set startup repeat rate to fast (/F)
SetFast proc near
mov repchars,2 ;update runtime variable
ret
SetFast endp
;------ SetSlow - Set startup repeat rate to slow (/S)
SetSlow proc near
mov repchars,1 ;update runtime variable
ret
SetSlow endp
;------ SetDelay - Set delay before a character starts repeating
SetDelay proc near
call GetNum ;get delay number after '/D'
;(may die)
or al,al ;bogus if 0
jz SetDelay_Bad ;illegal value
or ah,ah ;bogus if >255
jnz SetDelay_Bad ;illegal value v2.9
mov startdelay,al ;update runtime variable
ret
SetDelay_Bad:
jmp Bad_Num ;bad number handler
;error msg, terminate
SetDelay endp
;------ SetVid - Set amount of time before video is blanked
SetVid proc near
call GetNum ;get video blank value after '/V'
;(may die)
or ax,ax ;0 or no value means no blanking
jz SetVid0 ;0, no delays
xor dx,dx ;clear for the MUL
mov cx,1092
mul cx ; 1092 clicks per minute
or dx,dx ;we get a carry?
;(e.g., result is bigger
; than a word)
jnz SetVid_Bad ;bogus! Help, die
SetVid0:
mov screendelay,ax ;update runtime variable
mov scrcount,ax ;(both of them)
; While we have the screendelay value handy,
; let's mask switches to turn video blanking on/off
or ax,ax ;any screen delay set?
mov al,VIDMASK ;assume video blanking ON
jnz SetVid_On ;yep
not al ;flip to turn bit off
and switches,al ;turn video blanking OFF
ret
SetVid_On:
or switches,al ;turn video blanking ON
ret
SetVid_Bad:
jmp Bad_Num ;bad number routine
SetVid endp
;------ GetHelp - Ouput help screen and set "don't install" flag (hflag)
; Now jumps directly to Msg_Term (display message in DX, terminate).
GetHelp proc near
mov dx,offset help ; Print help screen
jmp Msg_Term ;display help screen
;and terminate (no install)
GetHelp endp
;------ GetNum - Calculate a number from the parameter list
; SI -> the char after the initial argument char.
; Returns the number in AX
; Heavily rewritten.
; New code works with argv[argc] array member (an AsciiZ string).
; Added checks for bogus values (e.g., larger than a word).
; If the arg is just the char (e.g., no number),
; GetNum returns AX=0.
; This means something like '/v' is just like '/v0'.
GetNum proc near
xor bh,bh ;clear msb (just once)
xor ax,ax
mov di,10 ;use DI for a constant multiplier
mov cx,'90' ;CL='0', CH='9' for fast testing
;Accumulate number until we hit a non-numeric character
NumLoop:
mov bl,[si]
cmp bl,cl ;'0' ;(Since this is an AsciiZ string,
jb NumDone ;the basic algorithm works fine)
cmp bl,ch ;'9'
ja NumDone
xor dx,dx ;insure .hi is clear
mul di ;10
or dx,dx ;result larger than a word?
jnz Bad_Num ;yep, illegal, help and die
sub bl,cl ;'0'
add ax,bx
inc si
jmp NumLoop
NumDone:
ret
Bad_Num:
mov dx,offset badnummsg ;'Illegal number in parameter: '
mov ah,9 ;display msg
int 21H
mov si,word ptr argc ;current argv counter
shl si,1 ;*2 for words
mov si,word ptr argv[si] ;argv[argc] pointer
call Pr_AsciiZ ;display the whole bogus argv
;(It's an AsciiZ string)
mov dx,offset gethelpmsg ;CR/LF,'Enter FASTBUFF -? for help'
jmp Msg_Term ;display, die
GetNum endp
; AsciiZ string displayer
; Resisting temptation to use BIOS or other undocumented fast char
; displays and sticking with DOS functions .. sigh ..
; Enter with DS:SI -> AsciiZ string
Pr_AsciiZ proc near
push dx ;be neat
Pr_Z_Lup:
lodsb ;snarf char
or al,al ;terminating 0?
jz Pr_Z_X ;yep, done
mov dl,al ;char to display
mov ah,2 ;display char
int 21H
jmp short Pr_Z_Lup
Pr_Z_X:
pop dx ;restore
ret
Pr_AsciiZ endp
;--------------------------------------------------------------------------
;Initialization data area
;--------------------------------------------------------------------------
parmtable db 'BFSDVH?' ;legal parameter chars
LPARMTABLE equ $-parmtable
even
jumptable dw SetBuff,SetFast,SetSlow,SetDelay,SetVid
dw GetHelp,GetHelp
instalmsg db 'FASTBUFF v2.6TH keyboard enhancer installed.',CR,LF,'$'
INSTALMSGLEN equ $ - instalmsg
even
help label byte
db '┌──────────────────────────────────────────────────────────────────────────────┐'
db '│ ┌───────────────┐ │'
db '│ David Steiner │ FASTBUFF v2.6 │ March 1989 │'
db '│ [Toad Hall Tweak] └───────────────┘ August 1989 │'
db '│ ╒══════════════════════════════════════════════════════╕ │'
db '│ │ Run-time Control Keys │ │'
db '│ ├──────────────────────────────────────────────────────┤ │'
db '│ │ Ctrl + Right Shift : Clear the keyboard buffer │ │'
db '│ │ [5] + Del key : Turns FASTBUFF off │ │'
db '│ │ [5] + Ins key : Turns FASTBUFF back on │ │'
db '│ │ [5] + Plus key : Selects fast repeat rate │ │'
db '│ │ [5] + Minus key : Selects slow repeat rate │ │'
db '│ │ [5] + Home key : Toggle screen blanking │ │'
db '│ │ [5] + End key : Force screen blanking │ │'
db '│ ╒════════╧══════════════════════════════════════════════════════╧════════╕ │'
db '│ │ Command Line Parameters : Min Max Default │ │'
db '│ ├────────────────────────────────────────────────────────────────────────┤ │'
db '│ │ /Bn : Sets the buffer size to n characters 25 269 100 │ │'
db '│ │ /Dn : Set start delay to n timer clicks 1 255 7 │ │'
db '│ │ /Vn : Set video blank delay to n minutes (0=OFF) 1 60 3 │ │'
db '│ │ /F : Startup repeat speed = fast - - ON │ │'
db '│ │ /S : Startup repeat speed = slow - - OFF │ │'
db '│ ╘════════════════════════════════════════════════════════════════════════╛ │'
db '└──────────────────────────────────────────────────────────────────────────────┘'
db '$'
envmsg db 'Error: Failed to free environment memory',CR,LF,'$'
toobigmsg db '/B value too large!',CR,LF,'$'
toosmallmsg db '/B value too small!',CR,LF,'$'
badnummsg db 'Illegal number in parameter: $'
error db 'Error: FASTBUFF already installed.'
gethelpmsg db CR,LF,'Enter "FASTBUFF -?" for help.',CR,LF,'$'
;_Args data area
; Pointers to up to MAXPARMS parameter strings are held here.
;(All the argv strings are still in the PSP cmdline.)
even
argc equ $ ;really just a byte
;but stuffed as a word
argv equ argc + 2 ;dw MAXPARMS dup (?)
CSEG ENDS
END Start
Old beep code
;------ ErrBeep - Keyboard full error beep, from ROM BIOS listing
;Called 1 time
; Adding short beep to acknowledge [5] commands.
; Destroys AX,BX,CX
ShortBeep proc near
mov ah,30H SHR 1 ;Cycles for 1/32 second tone
jmp short Beep1 ;skip
ErrBeep:
mov ah,30H ;Cycles for 1/16 second tone
Beep1:
mov bx,48H ;handy constant
in al,61H ; Get keyboard ctrl information
mov bl,48H ;handy constant
mov bh,al ;save kbd ctrl info
xor ch,ch ;insure counter MSB clear
BeepCycle:
and al,0FCH ; Turn off timer gate & spkr data
out 61H,al ; Output to control
mov cl,bl ;half cycle time for tone
L1: loop L1 ; Speaker off
or al,2 ; Turn on speaker bit
out 61H,al ; Output to control
mov cl,bl ;set up count
L2: loop L2 ; another half cycle
dec ah ; Total time count
jnz BeepCycle ; Do another cycle
mov al,bh ;recover control
out 61H,al ; Output the control
ret
ShortBeep endp