;************************************************************************
; *
; Copyright (C) 1991 by Trace Center (just kidding) *
; *
; HANDICAP.ASM *
; *
;************************************************************************
;----------------------------------------------------------------------------
; HANDICAP.ASM
;
; FilterKeys = Adjustment of typematic rate, key acceptance, debounce time and recovery times
; StickyKeys = One finger typing feature
; MouseKeys = Complete mouse function from the numeric keypad
; ToggleKeys = Audible feedback of indicator light states of keyboard
;
; The way this is done is to chain together the routines that implement
; these features. The routine below, keybd_int, actually reads the keyboard
; port and then builds up the extended scan code which consists of the
; "hidden" byte and then the scan byte. It then passes it in AX to the first
; routine in line (FilterKeys). Each successive routine then continues to
; pass it on or inhibits it. Note, that if the scan code is an "e0", "e1", or
; a response to a keyboard command, (>"f0"), then this routine passes the
; code to the original keyboard int. while also saving it to use as a scan code
; identification as the real scan code is later passed along between the routines.
; There may be a few exceptions to this, like if a control break is being trapped.
;
; The routine keybd_int does all the hardware management, unless one of the
; routines (namely the BIOS in the computer ROM ) resets the hardware, in which case
; keybd_int does not. The variable fbios_called_direct is used to flag this condition.
print MACRO string
mov dx,OFFSET string
mov ah,9
int 21h
ENDM
INCLUDE keyboard.inc
IFDEF BUG
PUBLIC portid,portout
EXTRN HexCharsOut:PROC
ENDIF; BUG
EXTRN _end:ABS
EXTRN flatch:byte ; in Stickeys.asm
EXTRN shift_flg:byte
EXTRN FilterKeys:NEAR ; in FilterKeys.asm
EXTRN FilterKeys_timer:PROC
EXTRN FilterKeys_dialog:PROC
EXTRN fshift_click:byte
EXTRN MouseKeys_timer:PROC ; in MouseKeys.asm
EXTRN InjectMouse:PROC
EXTRN button_click:PROC
;; EXTRN Put_Mouse_Data:PROC
EXTRN fMoving:byte
EXTRN Mouse_Status:word
EXTRN Delta_X:word
EXTRN Delta_Y:word
EXTRN Last_Direction:byte
EXTRN Status:byte
EXTRN mouse_data:word
EXTRN mouse_data_head:word
EXTRN mouse_data_tail:word
EXTRN mouse_data_end:word
EXTRN fbutton_up:byte
EXTRN fbutton_down:byte
EXTRN MouseKeys_dialog:PROC
EXTRN Button_Status:byte
EXTRN mouse_cnt:byte
EXTRN fnum_lock_was_off:byte
EXTRN fserial_stop:byte
EXTRN ToggleKeys_timer:PROC ; in ToggleKeys.asm
EXTRN ToggleKeys_dialog:PROC
EXTRN TimeOut_Reset:PROC ; in TimeOut.asm
EXTRN TimeOut_dialog:PROC
EXTRN last_address:word
EXTRN key_cnt:byte ; in StickeyKeys.asm
EXTRN StickeyKeys_dialog:PROC
EXTRN fkeys_injected:byte
EXTRN set_shift_states:PROC
; EXTRN _serialKeysInit:PROC ; in SerialKeys
EXTRN _kickStartSerialKeys:PROC
; EXTRN _initCommPort:PROC
; EXTRN _serialKeysStartupInit:PROC
EXTRN _forcedInt9Flag:byte
EXTRN _injectByte:byte
EXTRN _skWindowCompatible:byte
EXTRN _serialKeysEnableFar:FAR
EXTRN _serialKeysDisableFar:FAR
EXTRN fFilterKeysOn:byte ; from Param.asm
EXTRN fMouseKeysOn:byte
EXTRN fcomp_dialog:byte
EXTRN fcomp_dialog_id:byte
EXTRN fDialog_Filter_off:byte
EXTRN fDialog_Stickeys_off:byte
EXTRN fDialog_Mouse_off:byte
EXTRN fDialog_Toggle_off:byte
EXTRN fDialog_TimeOut_off:byte
EXTRN fDialog_Action:byte
EXTRN fmkeys_override:byte
EXTRN fspace_saver:byte
EXTRN fhearing_on:byte
EXTRN fvideo_flash:byte
EXTRN fcomputer_not_found:byte
EXTRN fSticKeysOn:byte
EXTRN fclick_on:byte
EXTRN _serialKeysOn:byte
EXTRN fslow_baud_mouse:byte
EXTRN _comp_id:byte
EXTRN _combase:word
EXTRN fmouse_driver:byte
EXTRN _finject_keys:byte
EXTRN _vector:byte
EXTRN comp_flag:byte
EXTRN ExtendedSeg:word
EXTRN _fmouse_id:byte
EXTRN comp_flag:byte
EXTRN btn_1:byte
EXTRN btn_2:byte
EXTRN Current_Button:byte
EXTRN fmouse_driver:byte
EXTRN fvideo_type:byte
EXTRN fserial_keys_loaded:byte
PUBLIC beep_low ; in Handicap.asm
PUBLIC pass_to_computer
PUBLIC beep_high
PUBLIC click
PUBLIC no_beep
PUBLIC beep_turn_on
PUBLIC beep_turn_off
PUBLIC InjectKeys
PUBLIC Get_Mouse_Data
PUBLIC Put_Key_Data
PUBLIC shift_tbl
PUBLIC shift_tbl_len
PUBLIC real_states
PUBLIC prev_real_states
PUBLIC current_shift
PUBLIC fpause_being_sent
PUBLIC fmouse_button
PUBLIC fwindows_st_re
PUBLIC fsilent_click
PUBLIC key_data_tail
PUBLIC key_data_head
PUBLIC key_data
PUBLIC key_data_end
PUBLIC faccess_sound
PUBLIC fswitching_video
PUBLIC fbios_called_timing
PUBLIC Enable
PUBLIC fsecond_cue
PUBLIC fkey_not_passed
PUBLIC ftimer_1C_active
PUBLIC fserial_key_recieved
PUBLIC fmousetrapping
; PUBLIC fwindows_enh ; DEBUGGING
;----------------------------------------------------------------------------
_TEXT segment word public 'CODE'
assume CS:_TEXT
assume DS:_TEXT
assume ES:_TEXT
assume SS:NOTHING
;;PUBLIC timer_Stack_top
PUBLIC kybd_Stack_top
; even
;timer_sp_save dw 0
;timer_ss_save dw 0
; db 'tstacke'
; even
;timer_Stack dw 150 dup(0)
;timer_Stack_top dw $-2
; db 'tstacks'
even
kybd_sp_save dw 0
kybd_ss_save dw 0
db 'kstacke'
even
kybd_Stack dw 75 dup(0)
kybd_Stack_top dw $-2
db 'kstacks'
;; ORG 100h
;;
;;begin:
;;
;; jmp _enable
;----------------------------------------------------------------------------
; R E S I D E N T D A T A A R E A
;
; The below area defines the resident data area.
;
;----------------------------------------------------------------------------
fserial_key_recieved db false ; flag used to tell my code modules that they are processing a serial key input
fkey_not_passed db false ; flag used to signal that a key didn't pass yet in int 9 w/stickeys on
ftimer_1C_active db false ; flag to signal me when I am in a timer 1C interrupt
fint9_active_temp db false ; flag used by int 9 to prevent double pushing of stack
;;fint1C_active_temp db false ; flag used by int 1C to prevent double pushing of stack
fdonnot_hook_address db false ; flag used by int 33 routine to hook or unhook address's
fslow_mouse db false ; flag used in slow serial mice to tell timer to write every other clock tic
fvideo_flash_cnt db 0 ; reset counter at start
fvideo_flash_on db false ; reset flag at start
video_cnst db 4 ; 4 for wait between toggling screen
shift_tbl label byte
dw RAlt,RCtrl,Alt,Ctrl,LShift,RShift
shift_tbl_len equ 6
ctrl_break_buf label byte
db 0e0h,046h,0e0h,0c6h
ctrl_break_buf_len equ 4
ctrl_break_buf_cnt DB false ; counter into buffer when keys are being Injected
prev_real_states DB 0 ; previous state of real shift keys
real_states DB 0 ; current state of real shift keys
current_shift DB 0 ; holds flag of current shift if there is one
video_state DB 0 ; hold current video state for comparison checking
old_char_1 DB 0 ; hold the char, which gets over written by hearing impaired symbol
old_char_2 DB 0 ; hold the char, which gets over written by hearing impaired symbol
old_char_1_attr db 0 ; hold attribute of char 1
old_char_2_attr db 0 ; hold attribute of char 2
fsecond_cue db 0 ; holds second character if needed for visual cue
old_row DB 0 ; temp storage of cursor row position if hearing flag set
old_col DB 0 ; temp storage of cursor column position if hearing flag set
old_cursor_1 DB 0 ; temp save of cursor type
old_cursor_2 DB 0 ; temp save of cursor type
txt_or_grph DB 0 ; temp save of current video state, text or graphics mode
video_count DB false ; counter used if hearing flag set
faccess_sound DB false ; flag which will cause video output for AccesDos beeps
; since some of them are too short in length to be trapped by the timer int.
fswitching_video DB false ; flag to inform ADOS that video is switching when true
fint9_by_timer DB false ; flag to our int15 routine that timer inititated an int9h
; and that we should get scan code out of the key_data_buffer
floop_back db false ; flag to let me know if I need to grab byte/replace byte
; to prevent keyboard int from getting between timer injected codes
count_down_ticks label word ; counter used during tone setup
no_tone DW 014h ; soft tone sound
low_tone DW 06c0h ; 500 hz FilterKeys tones
high_tone DW 0120h ; 2.0 khz
click_tone DW 06c0h ; hz ??????
turn_on_start_beep DW 0360h ; 1.0 khz
turn_off_start_beep DW 0120h ; 2.0 khz
on_off_change DW 6 ; for 100 steps
fbios_called_direct DB false ; flag for if BIOS routine called
fbios_called_timing DB false ; flag for when timer function calls BIOS routine
fpause_being_sent DB false ; flag to tell MouseKeys that a real PAUSE key is passing, so don't
; turn off on the "45h" part of it
fmouse_or_kybd DB false ; flag to tell us if the int. routine called the keybd or mouse int. vector
frehook_mouse DB false ; flag to during int33h
fmouse_button DB false ; flag to tell int 33 or timer when mousekeys has a mouse button down for polled mice
fFake_mouse DB false ; flag to tell sub int33h, when mask has been set for Int. driven mice
fsilent_click DB false ; flag to call click but silently
; so we should convert to control break on computers which support it
fmousetrapping DB false ; if this flag is false, we allow MouseKeys to hook int 33h (added 4/92)
fctrl_break_on DB false ; same as above,used in Handicap.asm to eat keystrokes
fsysreq DB false ; flag to tell us the alternate key is down and a print screen key was pressed
; so we should convert to system request on computers which support it
; keyboard buffer
fscroll_changed DB false ; flag to tell us we detected shift+numlock on space saver keyboard
fvideo_flash_save DB false ; temp storage flag for fvideo_flash when Windows takes over
fhearing_on_save DB false ; "" "" ""
call_mask DW 0 ; mask sent by applications to mouse driver
;------------------------------------------------------------------------------------------------------------------------------
fwindows DB false ; flag to tell us if we started program inside of or under windows
; anytime we exit Windows, this TSR program will have to terminate if started
; after Windows was already running.
fwindows_st_re DB false ; flag to tell us which mode of Windows we are running in
fwindows_enh DB false ; flag to tell us which mode of Windows we are running in
;------------------------------------------------------------------------------------------------------------------------------
fwindows_after DB false ; flag to tell us that Windows was loaded after ADOS was already started
fwindows_after_st DB false ; flag that says Windows started after ADOS in standard mode
fwindows_after_re DB false ; flag that says Windows started after ADOS in real mode
fwinfows_after_enh DB false ; flag that says Windows started after ADOS in enhanced mode
fDOS_box DB false ; flag to Windows and serial keys that Windows is either DOS boxing
; int2f w/ax=1680 =true first fime, or alt/ctrl+esc from DSO box back to Windows
; and fDOS_box=false
;-------------------------------------------------------------------------------------------------------------------------------
falt_esc DB false ; flag to tell us that an alt+esc make was sent
falt_esc_send DB false ; flag that alt+esc break was sent and we can now safely inject Windows Alt+esc
fctrl_esc DB false ; flag to tell us that an ctrl+esc make was sent
fctrl_esc_send DB false ; flag that ctrl+esc break was sent and we can now safely inject Windows Ctrl+esc
mouse_x_temp DB false ; byte used to keep semi track of mouse x coordinate, to tell if it moved
; during int. 33 polling
mouse_y_temp DB false ; byte used to keep semi track of mouse y coordinate, to tell if it moved
; during int. 33 polling
hidden_code DB 0 ; holds either E0 or E1 or 00
key_data_tail DW ? ; pointer into key_data
key_data_head DW ? ; pointer into key_data
key_data DW 20 DUP (?) ; array of (20 words) for circular buffer of keyboard scan code data
key_data_end label word
IFDEF BUG
portid DB 0
portout DB 0
fserial_debugging DB 0
PUBLIC old_row,old_col
PUBLIC fserial_debugging
; Message data area for the various routines used during initialization
mesg41 DB "Program started while running Windows in Enhanced Mode", 13, 10, "$"
mesg42 DB "Program started while running Windows in Real/Standard Mode", 13, 10, "$"
mesg45 DB "Your version of DOS (pre 3.0) does not support all Access DOS features", 13, 10, "$"
mesg46 DB "Your computer is running DOS version 3.0 or higher", 13, 10, "$"
ENDIF; BUG
mesg22 DB "Computer was not identifiable and will be treated as a PC/XT/AT with an 84 key keyboard.", 13, 10, "$"
mesg22A DB "Please restart AccesDOS menu, type access, to change this selection if your ", 13, 10, "$"
mesg22B DB "computer is NOT a PC/XT/AT with an 84 key keyboard.", 13, 10, "$"
mesg43 DB "Windows loading aborted, DO NOT run Enhanced Mode after AccessDOS is started", 13, 10, "$"
mesg44 DB "Unload Access DOS or run Windows in Standard Mode (win /s) ", 13, 10, "$"
mesg60 DB "Please wait, AccessDOS is loading...", 13, 10, "$"
; store these old vector variables here such that they always get addressed correcly
old_1C_int label word ; holds far call vector to previous timer int
old_1C_int_off DW 0 ; holds old timer routine offset address
old_1C_int_seg DW 0 ; holds old timer routine segment address
old_kybd_int label word ; holds far call vector to previous keyboard int
old_kybd_int_off DW 0
old_kybd_int_seg DW 0
old_2f_int label word ; holds far call vector to previous int 2f service routine
old_2f_int_off DW 0 ; we watch this int solely to know if windows is loaded after
old_2f_int_seg DW 0 ; ADOS has been started
old_33_int label word ; holds far call vector to previous int 33 service routine
old_33_int_off DW 0
old_33_int_seg DW 0
old_ps2mou_int label word ; holds far call vector to previous int
old_ps2mou_int_off DW 0
old_ps2mou_int_seg DW 0
;testpattern db 'markstart'
sub_int33_ptr label word
sub_int33_ptr_off DW 0 ; holds far call vector to an address if an application trys
sub_int33_ptr_seg DW 0 ; holds far call vector to an address if an application trys
;sub_int33_ptr_2 label word
;sub_int33_ptr_off_2 DW 0 ; holds far call vector to an address if an application trys
;sub_int33_ptr_seg_2 DW 0 ; holds far call vector to an address if an application trys
;
;sub_int33_ptr_3 label word
;sub_int33_ptr_off_3 DW 0 ; holds far call vector to an address if an application trys
;sub_int33_ptr_seg_3 DW 0 ; holds far call vector to an address if an application trys
;---------------------------------------------------------------------------
; ship_it
;
; Routine to disable and enable the keyboard. Works for IBM AT, NAT, 50,60 70,80
; according to Tech Ref. manual. Send what is in al register to status port of
; keyboard. We only need to use this routine for int 9 computers of AT class
;
; expects al register to contain code to send to the keyboard control chip
ship_it proc
push cx
push ax
xor cx,cx ; zero cx register
ship_it_5:
in al,status_port ; read keyboard status port
test al,inpt_buf_full ; wait till we are allowed to write to port
loopnz ship_it_5
pop ax ; char to send is in al register
out status_port,al ; may have gotten an enable or disable code for keyboard in "al" register
pop cx ; clean up stack
ret
ship_it endp
;----------------------------------------------------------------------------
; pass_to_computer
;
; This routine passes the scan code to the original keyboard device driver when
; called on a computer using keyboard interrupt 9h in byte form in al.
;
; In reality, the original keyboard int. reads the kb_data
; in port, and places the result in "al" register, which is the same scan code
; which this routine is passing along
;
; If this computer supports int 15h, then the only thing this routine does is set the
; fbios_called_direct flag to true, telling the return code that this key should get
; passed to the computer
pass_to_computer proc
assume DS:_TEXT
;; push ax
;; push ds
cmp _vector,15h ; are we running under int 15h
je pass_to_computer_100 ; if yes, life as usual
; int 9h computers will get ot this code
cmp fFilterKeysOn,false ; is FilterKeys On ???
je pass_to_computer_25 ; if FilterKeys is off, safe to pass key in this int 9 pass
cmp ftimer_1C_active,true ; is the timerr trying to pass a key
jne pass_to_computer_25 ; if not, allow key to pass
; if stickeys is on, we need to flag that we didn't pass the key as yet
cmp fSticKeysOn,true
jne pass_to_computer_10
mov fkey_not_passed,true ; flag that timed key has not yet passed
pass_to_computer_10:
jmp pass_to_computer_end
;**************
; cmp _comp_id,1 ; is this an IBM PC or original PC/XT ??
; jne pass_to_computer_25 ; if not, don't need to bother with the following check
;
; mov ah,al ; save al, the scan code we are supposed to pass on
; in al,kb_data ; read in data from keyboard port
; cmp al,0 ; was bit 0 a 0??
; je pass_to_computer_110 ; yes, no data in buffer, skip call to orig. kybd int.
;
; cmp al,ah ; was the data at the keybd port, the same as what is supposed to be passed ?
; jne pass_to_computer_110 ; if not, don't send it
;**************
pass_to_computer_25:
mov fkey_not_passed,false ; reset this flag
push ds
pushf
call dword ptr cs:old_kybd_int
pop ds
assume ds:_TEXT
pass_to_computer_100:
mov fbios_called_direct,true
;************
; jmp short pass_to_computer_120
;
;pass_to_computer_110:
;
; mov fsilent_click,true ; set true if didn't send anything
;
;pass_to_computer_120:
;
; pop ds
; pop ax
;************
pass_to_computer_end:
ret
pass_to_computer endp
;----------------------------------------------------------------------------
;
; InjectKeys
;
; This procedure is used on computers which support the Injection of key scan codes
; into the hardware buffer to inject either a control break scan code or an alternate
; print screen (SYSREQ) scan code if the user tries to do so with StickeyKeys.
;
; This routine is also used to inject StickeyKeys modifier break codes.
; This routine should never be called if the computer type is determined not capable of
; handling scan codes injected into the hardware buffer. Currently, the only computers
; which support hardware injections are PS/1 and all PS/2 except the 25/30-86 models.
;
; Expects al=code to inject
InjectKeys proc
assume DS:_TEXT
; push cx ; DO NOT need to save any registers, called from inside timer int. only..
mov cx,995 ; approx. 15msec/15.086*10-6/sec
IK_5:
in al,status_port ; read keyboard status port
test al,inpt_buf_full ; wait till we are allowed to write to port
jz IK_8 ; yes buffers are empty, okay to do inject so far
in al,kb_ctl ; buffer not empty, check time
and al,10h ; mask to check refresh bit = 10h
cmp al,ah ; did it change? or first time thru
jz short IK_5 ; no wait for change, else cont.
mov ah,al
loop IK_5 ; have we timed out ?
jmp short IK_20 ; can't write this time thru, exit inject
IK_8:
mov cx,995 ; approx. 15msec/15.086*10-6/sec
mov al,0d2h ; code to tell keyboard output buffer that next byte
out status_port,al ; written is to I/O address hex 060 (kb_data)
IK_10:
in al,status_port ; read keyboard status port
test al,inpt_buf_full ; wait till we are allowed to write to port
jz IK_15 ; yes buffers are empty, okay to do inject so far
in al,kb_ctl ; buffer not empty, check time
and al,10h ; mask to check refresh bit = 10h
cmp al,ah ; did it change? or first time thru
jz short IK_10 ; no wait for change, else cont.
mov ah,al
loop IK_10 ; have we timed out ?
jmp short IK_20 ; can't write this time thru, exit inject
IK_15:
call Get_Key_Data ; ax has the data upon return
out kb_data,al ; send our char. which will generate an int. as if sent by kybd.
;IFDEF BUG
;; mov fserial_debugging,true
; mov portid,06
; mov portout,al
; call HexCharsOut
;; mov fserial_debugging,false
;ENDIF; BUG
mov floop_back,true ; flag that we sent a byte
IK_20:
; pop cx ; clean up stack
ret
InjectKeys endp
;----------------DEBUGGING------------------------------------------------
;
;Inject_Mouse_Low proc
;
; assume DS:_TEXT
; call click
; mov cx,995 ; approx. 15msec/15.086*10-6/sec
;
;IKM_5:
;
; in al,status_port ; read keyboard status port
; test al,inpt_buf_full ; wait till we are allowed to write to port
; jz IKM_8 ; yes buffers are empty, okay to do inject so far
;
;
; in al,kb_ctl ; buffer not empty, check time
; and al,10h ; mask to check refresh bit = 10h
; cmp al,ah ; did it change? or first time thru
; jz short IKM_5 ; no wait for change, else cont.
; mov ah,al
; loop IKM_5 ; have we timed out ?
; jmp short IKM_20 ; can't write this time thru, exit inject
;
;IKM_8:
; mov cx,995 ; approx. 15msec/15.086*10-6/sec
; mov al,0d3h ; code to tell mouse output buffer that next byte
; out status_port,al ; written is to I/O address hex 060 (kb_data)
;IKM_10:
;
; in al,status_port ; read keyboard status port
; test al,inpt_buf_full ; wait till we are allowed to write to port
; jz IKM_15 ; yes buffers are empty, okay to do inject so far
;
; in al,kb_ctl ; buffer not empty, check time
; and al,10h ; mask to check refresh bit = 10h
; cmp al,ah ; did it change? or first time thru
; jz short IKM_10 ; no wait for change, else cont.
; mov ah,al
; loop IKM_10 ; have we timed out ?
; jmp short IKM_20 ; can't write this time thru, exit inject
;
;IKM_15:
; call Get_Mouse_Data ; ax has the data upon return
; out kb_data,al ; send our char. which will generate an int. as if sent by kybd.
;
;IKM_20:
; ret
;
;Inject_Mouse_Low endp
;----------------DEBUGGING------------------------------------------------
;----------------------------------------------------------------------------
;
; kybd_echo
;
; This routine is used on computers which DO NOT support hardware injection of scan codes but
; do support int 15h, to cause an int9h to occur, such that when int15 is called, we can trap for
; it and inject our scan code. For IBM's, we will do this by using an undocumented call to 8042, which will
; cause it to do an interrupt with our data loaded into port 60h. For non IBM's, we will write to status port
; which caused the 8042 to return a byte, which we then trapped.
kybd_echo proc
assume DS:_TEXT
cmp _comp_id,5Ah ; is this an IBM product (Old AT, AT239, AT339)?
je kybd_echo_10 ; if yes, do 2nd half of kybd_echo
; if not, do the first half
cmp _comp_id,2 ; is this a New XT (1/10-5/9/86) running
je kybd_echo_10 ; if yes, do 2nd half of kybd_echo
;----------------------------------------------------------
kybd_echo_5:
xor cx,cx ; zero cx
kybd_echo_5A:
in al,status_port ; read keyboard status port
test al,both_buf_full ; check to be sure we can write to the port
jz kybd_echo_6
loop kybd_echo_5A
jmp short kybd_echo_8 ; if cannot write this time, exit out
kybd_echo_6:
mov al,020h
out status_port,al ; a write to the keyboard status port (ctrl port) generates an interrupt
; if succesful in generating an interrupt, set a flag for myself to watch for when int 9 calls int 15h
mov fint9_by_timer,true ; set flag so I know when int 9 occur, to disregard
; the scan code int 9 has, and to read the key_data_buffer
; for the scan code I want to insert
kybd_echo_8:
jmp kybd_echo_end ; exit
;----------------------------------------------------------
kybd_echo_10:
; push cx
xor cx,cx ; zero cx
kybd_echo_10A:
in al,status_port ; read keyboard status port
test al,inpt_buf_full ; check to be sure we can we can write buffer
jz kybd_echo_15
loop kybd_echo_10A
jmp kybd_echo_end ; if can't write this time, exit
kybd_echo_15:
mov al,7fh ; write command for 8042
out status_port,al ; write to port 64h
jmp $+2 ; delay
xor cx,cx ; reset cx to 0
kybd_echo_20:
in al,status_port ; read keyboard status port
test al,inpt_buf_full ; check to be sure we can we can write buffer
jz kybd_echo_25
loop kybd_echo_20
jmp short kybd_echo_end ; if can't write this time, exit
kybd_echo_25:
call Get_Key_Data ; ax has the data upon return
mov floop_back,true ; flag to be sure we get our keystroke on the way back thru int 15h
out kb_data,al ; write "al"register to port 60h
jmp $+2 ; delay
xor cx,cx ; reset cx to 0
kybd_echo_30:
in al,status_port ; read keyboard status port
test al,inpt_buf_full ; check to be sure we can we can write buffer
jz kybd_echo_35
loop kybd_echo_30
jmp short kybd_echo_end ; if can't write this time, exit
kybd_echo_35:
mov al,3fh ; read command to 8042
out status_port,al ; write to port 64h
jmp $+2 ; delay
xor cx,cx ; reset cx to 0
kybd_echo_40:
in al,status_port ; read keyboard status port
test al,inpt_buf_full ; check to be sure input buffer empties
jz kybd_echo_45
loop kybd_echo_40
jmp short kybd_echo_end ; if can't write this time, exit
kybd_echo_45:
xor cx,cx ; reset cx to 0
kybd_echo_50:
in al,status_port ; read keyboard status port
test al,out_buf_full ; check to be sure output buffer filled
jnz kybd_echo_end
loop kybd_echo_50 ; wait till not zero, (i.e. buffer is filled)
; pop cx ; clean up stack
kybd_echo_end:
ret
kybd_echo endp
;----------------------------------------------------------------------------
; Put_Key_Data
;
; This procedure loads keyboard key data into a buffer where the timer will later
; retrieve it and send it to the InjectKeys.
;
; Key scan codes may be retrieved by computers which support int 15h also. If they
; do, the fbios_called_timer flag will be set.
;
; Expects Ax= keyboard data to be stored, order is
;
; ah = 0
; al = hidden code and scan code if called twice, scan code if called once
Put_Key_Data proc
assume DS:_TEXT
push bx ; temp store of bx register
push si ; temp store of si register
mov bx,key_data_tail ; get tail pointer of key_data buffer
mov si,bx ; save pointer value
add bx,2 ; move to next word address in buffer
cmp bx,OFFSET key_data_end ; are we at the end of the buffer ?
jne PKD_5 ; no
mov bx,OFFSET key_data ; yes we are, so reset to the buffer beginning
PKD_5:
cmp bx,key_data_head ; has the buffer wrapped around ?
jne PKD_10
; if equal, buffer is full, beep high to inform user, and exit
jmp Put_Key_Data_End ; if full, error beep and leave routine
PKD_10:
mov [si],ax ; move whats in ax into address pointed to by si
mov key_data_tail,bx ; update tail pointer
Put_Key_Data_End:
pop si
pop bx
ret
Put_Key_Data endp
;----------------------------------------------------------------------------
; Get_Key_Data
;
; This procedure retrieves keyboard keystroke info. from a buffer for the timer
; to send it to InjectKeys or for int15h to retrieve.
;
; Returns ax = keyboard keystroke data stored
;
; ah = 0
; al = hidden code and scan code if called twice, scan code if called once
Get_Key_Data proc
assume DS:_TEXT
push bx ; temp store of bx register
mov bx,key_data_head ; get head pointer of key_data buffer
cmp bx,key_data_tail ; test to see if head = tail, i.e. no data to send
je Get_Key_Data_End
mov ax,[bx] ; move whats in address pointed to by bx into ax
add bx,2 ; move to next word in list
cmp bx,OFFSET key_data_end ; are we at the end of the buffer
jne GKD_5 ; no, then we are done, except update the pointer
mov bx,OFFSET key_data ; if yes, then reset to the beginning of the buffer
GKD_5:
mov key_data_head,bx ; move the head pointer to the start also
Get_Key_Data_End:
pop bx
ret
Get_Key_Data endp
;----------------------------------------------------------------------------
; beep
;
; Expects: bx = length (number of cycles)
; cx = tone (length of cycle)
;
; Changes: bx
beep proc
push ds
push cs
pop ds
assume DS:_TEXT
push ax ; save ax
IN al,kb_ctl ; get keyboard control
push ax ; save
push cx ; save cx=wave length
b_1:
and al,0fch ; set speaker bits
OUT kb_ctl,al ; send out to speaker
pop cx ; get cx=wave length
push cx ; save cx
b_2:
loop b_2 ; keep on for wave length of time
cmp fsilent_click,true ; should this be a silent click
je b_2A
or al,2 ; set speaker bit
jmp short b_2B
b_2A:
or al,0 ; NO SOUND
b_2B:
OUT kb_ctl,al ; send out to speaker
pop cx ; get cx
push cx ; save cx
b_3:
loop b_3 ; keep off for wave length of time
dec bx ; decrement length of tone
jnz b_1 ; loop if not zero
pop cx ; restore cx
pop ax ; get old keyboard control
OUT kb_ctl,al ; send out
pop ax ; restore old ax
pop ds
ret
beep endp
;----------------------------------------------------------------------------
; beep_high
; beep_low
; beep_turn_on
; beep_turn_off
; no_beep
; click
;
; The following routines provide the sound feedback.
;
; Expects: Nothing
;
; Changes: Nothing
;
beep_high proc
push ds
push cs
pop ds
assume DS:_TEXT
push cx ; save registers
push bx
mov bx,high_tone_len ; give low tone
mov cx,high_tone
beep_high_20:
call beep
pop bx ; restore registers
pop cx
pop ds
; assume DS:NOTHING
ret
beep_high endp
beep_low proc
push ds
push cs
pop ds
assume DS:_TEXT
push cx ; save registers
push bx
mov bx,low_tone_len ; give low tone
mov cx,low_tone
call beep
pop bx ; restore registers
pop cx
pop ds
; assume DS:NOTHING
ret
beep_low endp
no_beep proc
push ds
push cs
pop ds
assume DS:_TEXT
push cx ; save registers
push bx
mov bx,no_tone_len ; give no tone
mov cx,no_tone
no_beep_20:
call beep
pop bx ; restore registers
pop cx
pop ds
; assume DS:NOTHING
ret
no_beep endp
;--------------------------------------------------------------
beep_turn_on proc
push ds
push cs
pop ds
assume DS:_TEXT
push cx ; save registers
push bx
mov cx,turn_on_start_beep ; low frequency
bon_20:
mov bx,02h
call beep
sub cx,on_off_change ; rate of change
cmp cx,turn_off_start_beep ; high frequency
jg bon_20
pop bx ; restore registers
pop cx
pop ds
; assume DS:NOTHING
ret
beep_turn_on endp
;---------------------------------------------------------------
beep_turn_off proc
push ds
push cs
pop ds
assume DS:_TEXT
push cx ; save registers
push bx
mov cx,turn_off_start_beep ; high frequency
bof_20:
mov bx,02h
call beep
add cx,on_off_change ; rate of change
cmp cx,turn_on_start_beep ; low frequency
jl bof_20
pop bx ; restore registers
pop cx
pop ds
; assume DS:NOTHING
ret
beep_turn_off endp
;----------------------------------------------------------------------------
click proc
push ds
push cs
pop ds
assume DS:_TEXT
push cx ; save registers
push bx
mov bx,click_tone_len ; give click tone
mov cx,click_tone
call beep
mov fsilent_click,false ; reset to clear for PC or XT
pop bx ; restore registers
pop cx
pop ds
; assume DS:NOTHING
ret
click endp
;----------------------------------------------------------------------------
; Get_Mouse_Data
;
; This procedure retrieves mouse button info. from a buffer for the timer
; to send it to the appropriate mouse input mechanism.
;
; Returns Ax= mouse data stored
;
; 1st Mouse_Status
; 2nd Delta_X
; 3rd Delta_Y
Get_Mouse_Data proc
assume DS:_TEXT
push bx ; temp store of bx register
mov bx,mouse_data_head ; get head pointer of mouse_data buffer
cmp bx,mouse_data_tail ; test to see if head = tail, i.e. no data to send
je Get_Mouse_Data_End
mov ax,[bx] ; move whats in address pointed to by bx into ax
dec mouse_cnt ; derease counter since we took a word out of the buffer
add bx,2 ; move to next word in list
cmp bx,OFFSET mouse_data_end ; are we at the end of the buffer
jne GMD_5 ; no, then we are done, except update the pointer
mov bx,OFFSET mouse_data ; if yes, then reset to the beginning of the buffer
GMD_5:
mov mouse_data_head,bx ; move the head pointer to the start also
Get_Mouse_Data_End:
pop bx
ret
Get_Mouse_Data endp
;---------------------------------------------------------------------------
; _int2f
;
; We hook int2fh, to monitor if Windows is ever loaded after Access Dos is already
; loaded. To test, we will abort Enhanced loading and put up a message.
; AccessDOS version 1.1 allows enhanced mode windows.
_int2f proc far
cmp ax,01605h ; watch for Windows to load after us
jne int2f_50 ; this is what Windows broadcasts when it loads
push dx
and dx,001h ; mask off bit 0
cmp dx,0 ; is this Windows 3 in enhanced mode ?
pop dx
jne int2f_45 ; if not, exit to next int 2fh handler
; If we find enhanced mode, flag it accordingly instead of aborting as AccessDOS v1.00 did
;
; mov cx,1 ; return cx=1, will cause windows to abort
; pop dx ;
; jmp int2f_100
push ds
push cs
pop ds
assume DS:_TEXT
mov fwindows_after,true ; general flag for Windows standard mode loading after ADOS
mov fwindows_enh,true ; flag that we are in Windows enhanced mode
push ax
mov al,fhearing_on ; save state of hearing impaired flag(s)
mov fhearing_on_save,al
mov fhearing_on,false
mov al,fvideo_flash ; ""
mov fvideo_flash_save,al
mov fvideo_flash,false
pop ax
jmp int2f_90
int2f_45:
push ds
push cs
pop ds
assume DS:_TEXT
mov fwindows_after,true ; general flag for Windows standard mode loading after ADOS
mov fwindows_after_st,true ; flag that Windows stand. mode is starting after ADOS
push ax
mov al,fhearing_on ; save state of hearing impaired flag(s)
mov fhearing_on_save,al
mov fhearing_on,false
mov al,fvideo_flash ; ""
mov fvideo_flash_save,al
mov fvideo_flash,false
pop ax
; call serialKeysDisableFar
jmp int2f_90
int2f_50:
cmp ax,01606h ; watch for Windows to unload after us
jne int2f_75 ; this is what Windows broadcasts when it unloads
; right now don't need to check if unloading from enhanced or stand./real -- change for 1.1
push ds
push cs
pop ds
assume DS:_TEXT
;; cmp fwindows_after_st,true ; this would have been set true by a successful Windows loading
;; je int2f_70 ; if not, operator tried enhanced windows, so give error messasge
;;
;; print mesg43 ; print message to abort
;; print mesg44 ; instruct user to try Windows in Real/Standard Mode
int2f_70:
; reset flags upon exit
mov fwindows_after,false ; general flag for Windows real mode running after ADOS
mov fwindows_after_st,false ; flag that Windows stand. mode is running after ADOS
mov fwindows_enh,false
mov video_count,07fh ; reset such that it will take 5 seconds to start video
push ax
mov al,fhearing_on_save ; restore upon exit
mov fhearing_on,al
mov al,fvideo_flash_save
mov fvideo_flash,al
pop ax
; cmp fserial_keys_loaded,true ; is serial keys loaded ?
; jne int2f_72
; cmp _serialKeysOn,true ; is SerialKeys code on ?
; jne int2f_72
;
; call _serialKeysEnableFar
; call _initCommPort ; reset
; call _serialKeysStartupInit ; clear serialkeys buffers
int2f_72:
jmp short int2f_90
int2f_75:
cmp ax,01684h ; watch for Windows to load after us
jne int2f_80 ; this is what Windows broadcasts when it loads Real Mode
; NOTE, Windows doesn't broadcast REAL mode exit of 01606h !@!
push ds
push cs
pop ds
assume DS:_TEXT
mov fwindows_after,true ; general flag for Windows loading after ADOS
mov fwindows_after_re,true ; flag that Windows real mode is starting after ADOS
push ax
mov al,fhearing_on ; save state of hearing impaired flag(s)
mov fhearing_on_save,al
mov fhearing_on,false
mov al,fvideo_flash ; ""
mov fvideo_flash_save,al
mov fvideo_flash,false
pop ax
; and no way to tell when we exit, use timer to find out if we exitted
; call _serialKeysDisableFar
mov video_count,07fh ; reset such that it will take 5 seconds to start video
jmp short int2f_90
int2f_80:
cmp ax,04680h ; watch for Windows 3.0 to DOS box
je int2f_85
cmp ax,04810h ; watch for Windows 3.1 to DOS box
jne int2f_100
int2f_85:
push ds
push cs
pop ds
assume DS:_TEXT
cmp fwindows_after_st,true ; are we DOS boxing after Windows standard was started ?
jne int2f_86
jmp short int2f_88
int2f_86:
cmp fwindows_enh,true ; are we DOS boxing after Windows enhanced mode was started ?
jne int2f_87
; jmp short int2f_88
jmp short int2f_90 ; if running in enhanced mode, allow Access Utility SerialKeys
; to run as is, do not attempt tp take back the port in a DOS box
int2f_87:
cmp fwindows_after_re,true ; are we DOS boxing after Windows real was started ?
jne int2f_90 ; if not, exit as some one is asking if Windows is loaded
int2f_88:
; call click ; DEBUGGING
cmp fserial_keys_loaded,true ; is serial keys loaded ?
jne int2f_90
cmp _skWindowCompatible,true
je int2f_89
cmp _serialKeysOn,true ; is SerialKeys code on ?
jne int2f_90
int2f_89:
cmp _serialKeysOn,true
je int2f_90
call _serialKeysEnableFar
; call _initCommPort ; reset
;; call _serialKeysStartupInit ; clear serialkeys buffers
int2f_90:
pop ds
assume DS:NOTHING
int2f_100:
jmp dword ptr cs:old_2f_int ; jump to original interrupt 2fh handler
_int2f endp
;---------------------------------------------------------------------------
; _int71/74
;
; All we are doing is hooking the input of the mouse to be able to reset the
; StickeyKeys count (key_cnt) if we get real mouse activity. Turns out for the the PS/2 Model
; 25..30/86, the keyboard int9h, is shared by the keyboard and the pointing device. Therefore,
; we don't necessarily know if the interrupt was generated by the mouse or the keyboard, unless
; we do some further investigation. After looking at the new PS/2 BIOS, (or what DOS adds to it)the
; original keybd. int. has a pre-amble attached which decifers whether or not the interrupt was a
; keystroke or the pointing device. After review, I decided to set my own flag, such that if the
; keyboard int 9h routine is called by the original int ? device(mouse or keyboard), then I will
; not reset the key_cnt variable of StickeyKeys, since it was not mouse activity.
;
_intps2mou proc
push ds
push cs
pop ds
assume DS:_TEXT
mov fmouse_or_kybd,false ; set flag to false at entry
pushf
call dword ptr cs:old_ps2mou_int ; call original interrupt 71 or 74h
cmp fmouse_or_kybd,false ; if keybd int 9h. was called, this would have been set true
jne _intps2mou_25 ; if false, then it was mouse activity, and we need to reset key_cnt
mov key_cnt,false
_intps2mou_25:
pop ds
assume DS:NOTHING
iret
_intps2mou endp
;---------------------------------------------------------------------------
; sub_int33
;
; If this suroutine is called, it's because an application passed the mouse driver
; a new address to send data when ever the mouse int. occurs. If this is called, we are really only
; interested in maintaining the button status connection between MouseKeys and the real mouse.
;
; This routine is called with ax=mask=bit1=left but. pressed, bit2=left button released
; bit3=right button pressed, bit4=right button released
sub_int33 proc far
push ds
push cs
pop ds
assume DS:_TEXT
; if this routine gets called, then there was mouse activity, a mouse int. was generated, so reset StickeyKey count
mov key_cnt,0 ; reset key_cnt to 0
cmp fMouseKeysOn,true ; check to see if MouseKeys is even On ?
je sub_int33_2
jmp sub_int33_65
sub_int33_2:
cmp fmouse_button,true ; using a MouseKeys button ?
je sub_int33_4 ; if yes, then the int. was initiated by kybd, follow thru
; If not a MouseKeys int. (i.e. from the kybd), then the real mouse was moved or a real mouse button was pushed while
; MouseKeys is on, so if we want to allow the MouseKeys mouse button to override the real mouse button, then we better
; check that they agree, and if not, alter the real mouse button info accordingly.
cmp fbutton_down,true ; is the MouseKeys button down ???
jne sub_int33_3A ; if no, MouseKeys button is down, then don't need to compare status'
;********************************************************************************************************************************
; REAL MOUSE HANDLING
;
; Real mouse input while the MouseKeys mouse button is being held down, so we must act on it
; If we see that the REAL mouse depresses a button, we will assume it is a mouse capable users,
; and reset allow the real mouse to release the depressed MouseKeys button. However, do not
; let only button up information reset the MouseKeys mouse button so check to see if the real
; mouse is sending any mouse button up info ??(ax=0001 0100 ?)
;
; ax = mask passed back to application from mouse driver to inform application why mouse driver is sending data
;
; mouse moved ax = 0000 0001 (01h)
; mouse r/l button down ax = 0000 1010 (0Ah)
; mouse r/l button up ax = 0001 0100 (14h)
;
;********************************************************************************************************************************
push bx
push ax
; check if button down info
mov bx,ax ; get button info into ax
and ax,0ah ; mask ax for either right/left/both buttons down
jz sub_int33_2A ; if zero, then there was not any button pressed down
pop ax
mov fbutton_down,false ; reset MouseKeys button down flag
mov Button_Status,0 ; reset since we let MouseKeys mouse button up
cmp _fmouse_id,4 ; check if a PS/2 mouse ?
jne sub_int33_2B
mov Mouse_Status,08h ; reset Mouse_Status to button up also for PS2 mouse
jmp short sub_int33_3 ; exit
sub_int33_2B:
mov Mouse_Status,040h ; reset Mouse_Status to button up also for serial mouse
jmp short sub_int33_3 ; exit
sub_int33_2A:
mov ax,bx ; get button info into ax
and ax,014h ; mask ax for either right/left/both buttons up
pop ax ; pop has no affect on the flags status
jz sub_int33_3 ; if zero, there was no button release info, so pass along as is
; If we get here, then we have Real mouse button up info, and we want to suppress it
; Some applications need to see mouse button dowm to remain in sync, so we will try to fool them also
xor ax,ax ; zero ax mask, causing real mouse info to not pass on the fact that real mouse
; button is actually up, since we want MouseKeys mouse button to override
mov ax,01 ; first bit is set in real codes
; shr bx,1 ; move whatever up mask bit right 1 bit to a down mask bit
; or ax,bx ; logical -or- them to ax, so it looks like mouse button is down
sub_int33_3:
pop bx
sub_int33_3A:
jmp sub_int33_50 ; we can exit
;************************************************************************************************************************************
sub_int33_4:
; if we get a MouseKeys button_up key, we want to EAT the down portion of the button_click
; and not pass that along to the application. Remember, we added the button_click to a standard
; button_up to fool the mouse driver for those occassions when the user operates both MouseKeys
; and the real mouse
cmp fbutton_up,true ; did we have a button up ?
jne sub_int33_50
mov bx,0 ; clear button down mask before the application gets it
xor ax,ax ; clear ax
mov ax,14h ; mask to release both buttons
mov fbutton_up,false ; reset flag to false since we ate the button down code as designed
sub_int33_50:
push ax
mov ax,mouse_data_head ; get head pointer of mouse_data buffer
cmp ax,mouse_data_tail ; test to see if head = tail, i.e. no data to send
jne sub_int33_60 ; if not equal, data in mouse_data buffer, so don't reset this flag yet
mov fmouse_button,false ; after we have sent the ALL THE mouse info, reset this flag
sub_int33_60:
pop ax
sub_int33_65:
call dword ptr cs:sub_int33_ptr
pop ds
assume ds:NOTHING
ret
sub_int33 endp
;----------------------------------------------------------------------------
; int33h hook
;
; Hooked int33h in an attempt to watch mouse activity and to keep mousekeys in
; sync with real mouse activity, i.e. if mousekeys locks a button down, movement
; real mouse will alter this, so if we monitor for this condition, we can correct
; or keep the two mice in sync
;----------------------------------------------------------------------------
PUBLIC _int33
_int33 proc
push ds ; save registers used
push cs
pop ds
assume DS:_TEXT
cmp fmousetrapping,false ; is mouse address hooking allowed
je i33_2
jmp i33_A_50 ; if not,call original int 33h
i33_2:
;-------------
; AX=0 or 33 decimal (21h)
;-------------
cmp ax,0 ; did we get a call to mouse reset and status
je i33_3
cmp ax,21h ; did we do a software reset ?, if so, only clear mask
je i33_4
jmp short i33_5
i33_3:
mov sub_int33_ptr_off,0
mov sub_int33_ptr_seg,0
i33_4:
mov call_mask,0 ; reset call mask with 0 or 21h calls
jmp i33_A_50
;-------------
; AX= 12 decimal (0Ch) or 20 decimal (14h)
;-------------
;
; If we get here, someone is using int 33 vector with function 12 decimal, which will be
; passed an address for the mouse driver to send mouse data when a mouse int. occurs, based
; upon a mouse mask which gets set. We have to trap for this address, if we want to monitor
; what the real mouse might be doing, to keep it in sync with MouseKeys. This situation occurs
; for example, if MouseKeys has a mouse button down, and then I move the real mouse, normally the
; real mouse movement would cancel the mouse button down of MouseKeys, and we didn't want it to.
; Therefore, we need to be able to trap such conditions, and alter them to the desired condition,
; before the application actually gets the mouse info.
i33_5:
cmp ax,0Ch ; is int 33h being called with ax=12, i.e. reseting an address
je i33_5_A
cmp ax,014h
je i33_5_A
jmp short i33_8
i33_5_A:
mov call_mask,cx
i33_5_D:
pushf
call dword ptr cs:old_33_int ; call original interrupt 33h
push es
push ax
push bx
push cx
push dx
mov ax,cs
mov bx,ax
mov es,ax
assume es:_TEXT
mov dx,OFFSET sub_int33 ; our routine that gets executed if an application repoints the
; now int 33h, called with ax=12, will load our sub_int33h address
; as es:dx instead of the application's es:dx
mov cx,call_mask
mov ax,0014h ; call to swap int. routines
pushf
call dword ptr cs:old_33_int ; call original interrupt 33h
i33_5_G:
mov word ptr [sub_int33_ptr_off],dx ; save address our call returned as new sub_int33_ptr
mov word ptr [sub_int33_ptr_seg],es
pop dx
pop cx
pop bx
pop ax
pop es
assume es:nothing
jmp i33_B_50
;-------------
; AX=22 decimal (16h)
;-------------
; If we get here, application is exitting for some reason, and wants to save the state of the mouse.
; We need to put back the address the application may have originally passed to the mouse driver, to
; tell the mouse driver where to send data, so when the application resatarts, we will have an address
; we can use to rehook ourselves.
i33_8:
cmp ax,016h
je i33_8_A
jmp short i33_10
i33_8_A:
push es
push ax
push bx
push cx
push dx
mov dx,word ptr [sub_int33_ptr_off]
mov es,word ptr [sub_int33_ptr_seg]
mov bx,es
mov cx,call_mask
mov ax,0014h ; call to swap int. routines
pushf
call dword ptr cs:old_33_int ; call original interrupt 33h
pop dx
pop cx
pop bx
pop ax
pop es
jmp i33_3
;------------------------------------
; AX = 23 decimal (17h)
;------------------------------------
i33_10:
cmp ax,017h
je i33_10_A
jmp short i33_A_50
i33_10_A:
pushf
call dword ptr cs:old_33_int ; call original interrupt 33h
push es
push ax
push bx
push cx
push dx
mov ax,cs
mov bx,ax
mov es,ax
assume es:_TEXT
mov dx,OFFSET sub_int33 ; our routine that gets executed if an application repoints the
; now int 33h, called with ax=12, will load our sub_int33h address
; as es:dx instead of the application's es:dx
mov cx,01fh
mov ax,0014h ; call to swap int. routines
pushf
call dword ptr cs:old_33_int ; call original interrupt 33h
; check call_mask returned, to see if this is an active mouse application
cmp cx,0
jne i33_10_G
; we did get back a zero for call_mask
; so call back with ax = 0014h, es:dx are the address we received
; leave CX alone, as we want to pass back the original call_mask
mov ax,0014h ; call to swap int. routines
pushf
call dword ptr cs:old_33_int ; call original interrupt 33h
mov ax,0
mov cx,ax ; to rezero call mask coming up if was zero
; mov es,ax
; mov dx,ax ; clear our sub_int33_ptr upon return
i33_10_G:
mov call_mask,cx ; get new call mask
mov word ptr [sub_int33_ptr_off],dx ; save address our call returned as new sub_int33_ptr
mov word ptr [sub_int33_ptr_seg],es
pop dx
pop cx
pop bx
pop ax
pop es
assume es:nothing
jmp short i33_B_50
;-----------------------------------------------
i33_A_50:
pushf
call dword ptr cs:old_33_int ; call original interrupt 33h
;-----------------------------------------------
i33_B_50:
; at this point, we don't know if this is a MouseKeys or real mouse entry, using int. 33 or polled, so we need to
; check if button status or x,y coordinate info was requested, so we can decide if we should reset key_cnt of
; StickeyKeys.
cmp ax,03 ; request for button status and mouse postion ?
je i33_26A
jmp i33_50
; is requesting mouse button or movement info, unfortunately, this may be a polled mouse, which would do this
; request several times each second, even if no mouse activity occurred, or this may be from an int. mouse
; which didn' use int.33 to repoint the mouse vector like seen in sub_int33 routine, so we have to check if any mouse
; data is being returned, before we can judge to reset the key_cnt variable of StickeyKeys
; Not setting mouse_x/y_temp first time thru, so may miss a StickeyKeys count, but unlikely, since polling is so fast
i33_26A:
cmp bx,0 ; if bx=0, the mouse button(s) is/are up
jne i33_26B ; if not zero, then we had mouse button activity, so reset key_cnt
cmp mouse_x_temp,cl ; did mouse x data change from last poll ??
jne i33_26B
cmp mouse_y_temp,dl ; did mouse y data change from last poll ??
je i33_27 ; no change, so jump to cont
i33_26B:
mov mouse_x_temp,cl ; update mouse_x_temp position
mov mouse_y_temp,dl ; update mouse_y_temp position
mov key_cnt,0 ; reset key_cnt to 0
i33_27:
; now we can continue as normal, by first checking to see if MouseKeys is even on ???
cmp fMouseKeysOn,true
je int33_27
jmp i33_50
int33_27:
;; Okay, MouseKeys is On and Int33h, requested button info. Is there a MouseKeys keys down???
;
cmp fbutton_up,true ; did we have a button up ?
jne int33_27B
mov bx,0 ; clear the extra button down mask that MouseKeys sends, before the
; application gets it
mov fbutton_up,false ; reset flag to false since we ate the button down code as designed
jmp i33_50 ; then jump around all this checking
int33_27B:
; if no MouseKeys keys are currently down, look out for real mouse info, if get any real mouse info, and we want to
; keep the MouseKeys mouse button down if it was down, then we must alter the real mouse button info if it tries to
; let the mouse buttons up
cmp fbutton_down,true ; is the MouseKeys button down ???
je int33_28 ; if yes, then a MouseKeys button is down, and we need to check on REAL mouse status
jmp i33_50 ; if not, jump around all this checking
int33_28:
;********************************************************************************************************************************
; REAL MOUSE HANDLING
;
; Real mouse input while the MouseKeys mouse button is being held down, so we must act on it
; If the REAL mouse button is pushed, allow it to release the MouseKeys mouse button.
; Problem with a "polled" mouse routine, is that it constantly asks for mouse status updates, and MouseKeys
; buttons are one time things. If this routine gets asked for mouse status info, we have no way of
; knowing if it is MouseKeys mouse button down, or REAL mouse buttons down, since the asking is continous
; which is alot different than asking only when an action occurs (interrupt driven) as in the
; mouse routines above which handle the interrupt driven mouse. We can figure one thing out however,
; and that is that we should never see mouse button up information, as long as the fbutton_down flag
; is set within MouseKeys, unless the REAL mouse sent it.
; Next check to see if the real mouse is sending any mouse button up info ??(bx=0000 0000 ?)
; We could get REAL mouse button up info. from moving the REAL mouse or from clicking the REAL mouse button ?
; Problem is, we want to override a button up if it was from moving, and allow a button up if it was a click
cmp bx,0 ; if zero, real mouse is sending all buttons are up info.
jz int33_29 ; if zero, there is button up info, so we must alter
jmp i33_50 ; if not, jump around all this checking
int33_29:
; If we get here, then we have Real mouse button up info, and we want to suppress it, so we have to check and see what
; MouseKeys mouse button is down, and echo this condition
cmp _fmouse_id,4 ; check if a PS/2 mouse ?
jne int33_37
;-------------------------------------------------------------------------------------------------
; PS/2 Mouse
push ax ; temp store
mov ax,Mouse_Status
and al,03h ; mask off lowest bits
cmp al,01 ; is the left MouseKeys button down ?
jne int33_30
xor bx,bx ; clear what ever was in bx
or bx,01h ; or the left button pressed in bx mask
pop ax
jmp i33_50 ; exit to normal address called
int33_30:
cmp al,02h
jne int33_32
xor bx,bx ; clear what ever was in bx
or bx,02h ; or the right button pressed
pop ax
jmp i33_50
int33_32:
cmp al,03h
jne int33_34 ; if we do this jump, no buttons were down
xor bx,bx ; clear what ever was in bx
or bx,03h ; or both the left and right buttons pressed
pop ax
jmp i33_50
int33_34:
pop ax
jmp i33_50 ; if we already called int33h, jump around doing it again
;----------------------------------------------------------------------------------------------------------------------------
; serial mouse
int33_37:
cmp _fmouse_id,2 ; check if a serial mouse ?
jne i33_50
push ax ; temp store
mov ax,Mouse_Status
and al,030h ; mask off high byte low bits
cmp al,00h ; any MouseKeys buttons down
je int33_44 ; no
cmp al,020h
jne int33_40
xor bx,bx ; clear what ever was in bx
or bx,01h ; or the left button pressed in bx mask
pop ax
jmp i33_50 ; exit to normal address called
int33_40:
cmp al,010h
jne int33_42
xor bx,bx ; clear what ever was in bx
or bx,02h ; or the right button pressed
pop ax
jmp i33_50
int33_42:
cmp al,030h
jne int33_44 ; if we do this jump, we had an error ?
xor bx,bx ; clear what ever was in bx
or bx,03h ; or both the left and right buttons pressed
pop ax
jmp i33_50
int33_44:
pop ax
i33_50:
pop ds
assume DS:NOTHING
iret
_int33 endp
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
; new_timer_int
;
; Calls various procedures to handle time dependent function of the different
; features. Also calls the original timer 1Ch interrupt
_new_timer_int proc
push ds ; save registers used
push es
push si
push di
push dx
push cx
push bx
push ax
push cs
pop ds
assume DS:_TEXT
push cs
pop es
assume ES:_TEXT
; cmp ftimer_1C_active,true ; check to see if we got re-interrupted ?
; jne NT_5
; call click
; jmp NT_END_5
;NT_5:
mov ftimer_1C_active,true
; TimeOut_timer is currently called from the Togglekeys_timer routine and seems to work there ????
call FilterKeys_timer ; service FilterKeys
call ToggleKeys_timer ; service ToggleKeys
cmp fMouseKeysOn,true ; is MouseKeys On, if not don't call MouseKey_timer
jne NT_8 ; if MouseKeys is Off, make sure buffer got emptied on turn off
call MouseKeys_timer ; yes, so call the timer
NT_8:
mov bx,mouse_data_head ; get head pointer of mouse_data buffer
cmp bx,mouse_data_tail ; test to see if head = tail, i.e. no data to send
jne NT_10 ; if not equal, we have mouse data in the buffer
jmp NT_105 ; nothing to send from mouse buffer so jump to here
;---------------------------------------------------------------------------------------------
; if not equal, we have mouse_data to send, after we call, ax will have the data to send
; at this point, we should also disable the real mouse from int. and messing up mousekey
; data from the buffer. Quick and dirty way to do this would be to force the mouse clock
; line low, so the mouse would think that the host computer is inhibiting, but we have a serial
; mouse here, not the PS/2 mouse. Since we again are not inside the mouse driver, we have
; a problem of knowing when it is safe to disable the real mouse (serial this time), since we
; have no way of knowing which data byte might currently be being sent. (i.e. 1st, 2nd or
; 3rd) Therefore, we can not truely disable the real mouse safely, so we will NOT currently
; inhibit the real mouse, but inform the user that both mice are active (real serial mouse
; and MouseKeys) but that the user should not try to input data from both at the same instant
; of time......
;---------------------------------------------------------------------------------------------
; If we get here,we have mouse data in buffer, so we need to check if PS/2 mouse check
NT_10:
cmp _fmouse_id,4 ; do we have a PS/2 mouse ?
jne NT_50
;-----DEBUGGING------------------------------------------------------
;;; cmp fwindows_enh,true ; DEBUGGING
;;; jne NT_20
; call Inject_Mouse_Low
; jmp NT_110
;
;-----DEBUGGING------------------------------------------------------
NT_20:
call Get_Mouse_Data ; ax has the data upon return
mov Mouse_Status,ax
call Get_Mouse_Data ; ax has the data upon return
mov Delta_X,ax
call Get_Mouse_Data ; ax has the data upon return
mov Delta_Y,ax
call InjectMouse
jmp short NT_110
;----------------------------------------------------------------------------------------------
; If we get here,we have mouse data in buffer, and we must have a Serial Mouse
NT_50:
cmp _fmouse_id,2 ; do we have a serial mouse ?
jne NT_110
cmp _comp_id,1 ; are we on a PC/XT
je NT_60
cmp _comp_id,1Ah ; are we on a PC/XT with int 15h
je NT_60
NT_55:
cmp fslow_baud_mouse,true ; do we have a slow mouse
jne NT_65
NT_60:
cmp fslow_mouse,true ; is this first pass at slow mouse or second ?
je NT_65 ; second pas, so cont. on
mov fslow_mouse,true ; first pass, set flag and exit
jmp short NT_110
NT_65:
mov fslow_mouse,false ; reset on second pass or every pass if not a slow mouse
mov dx,_combase ; put address of combase in dx
; first check to be sure the byte we may have transmitted previously has been sent out
add dx,5
in al,dx
test al,00100000b ; bit 5, transmit buffer should be clear if okay to write
jz NT_110 ; if zero, register is not empty, jump around
; next check to be sure we are not suppossed to stop sending serial mouse data
cmp fserial_stop,true ; if set true, we should stop sending serial mouse data and flush th buffer
jne NT_70 ; if not set, okay to send so cont
; if we get here, we are suppossed to stop sending serial mouse data, but we need to be sure we have sent a complete packet (3 bytes)
; before we quit. The easiest way to do this, will be to get a byte from the buffer, and if bit 6 is set, then it is the first byte
; of another three byte packet, and then it is safe to stop sending serial mouse data
call Get_Mouse_Data ; get the next mouse data if it exists
test al,01000000b ; is bit 6 set high
jz NT_80 ; if bit 6 is 0, not forst byte, cont. on
; if we get here, bit 6 was set, it is a first byte of a packet, and it is safe to flush the mouse data buffer
mov bx,OFFSET mouse_data ; get address of mouse_data buffer
mov mouse_data_head,bx ; reset head pointer to start of buffer
mov mouse_data_tail,bx ; reset tail pointer to start of buffer
mov mouse_cnt,false ;
mov fserial_stop,false
jmp short NT_105
NT_70:
call Get_Mouse_Data ; get the next mouse data if it exists
NT_80:
mov dx,_combase ; put address of combase in dx
out dx,al
NT_90:
jmp short NT_110
NT_105:
;--------------------------------------------------------------------------------------------------------------------
; call original timer, If jump was to NT_105, then mouse data buffer is empty, and we can reset fmouse_button flag
mov fmouse_button,false ; if buffer is empty reset flag for a serial mouse
NT_110:
; ************ later setup that if this is called and serial port has input, we won't do stuff after orig. int *****************
cmp fserial_keys_loaded,true ; is serial keys loaded ?
jne NT_112
cmp _serialKeysOn,true ; is SerialKeys code on ?
jne NT_112
push ds
push es
call _kickStartSerialKeys ; check if SerialKeys needs anything
pop es
pop ds
; ******************************************************************************************************************************
NT_112:
; If jump was to NT_110, then there was mouse data in the buffer buffer is empty, and after we call orig. int, we should
; exit the timer int. since we have been inside the timer interrupt extra long doing something with mouse buffer data
pushf
call dword ptr cs:old_1C_int ; call original timer 1Ch interrupt in case any one else chained in
cmp fmouse_button,false ; is mouse data buffer empty, (i.e. we didn't do any mouse activity)
je NT_115 ; if the mouse data buffer was empty, we have the time to cont.
; inside the timer interrupt
jmp NT_END ; since we had mouse activity, exit timer interrupt
;--------------------------------------------------------------------------------------------------------------------
; check to see if we need to call InjectKeys or just fake an interrupt 9h
; because we are running on a computer which supports int 15h and not hardware injection
;--------------------------------------------------------------------------------------------------------------------
NT_115:
; We will never inject if it is not a computer which supports int 15 intercept method of some kind...
cmp _vector,15h ; does this computer support int 15 h ?
; this check should be REDUNDANT !!!!!
je NT_118 ; cont. on and do an injection method if int 15h computer
jmp NT_200 ; if not an int 15h computer, buffer should never get data and we should
; NEVER attempt to inject in any fashion
NT_118:
mov bx,key_data_head ; get head pointer of key_data buffer
cmp bx,key_data_tail ; test to see if head = tail, i.e. no data to send
jne NT_120 ; if not equal, then we may need to empty buffer
mov fint9_by_timer,false ; if equal, reset this flag
jmp NT_200
NT_120:
; We will never try to inject if keyboard and system are talking...check mode indicator flag at 40:97 in BIOS...
push es
mov ax,RAMBIOS
mov es,ax
assume es:RAMBIOS
mov al,es:[97h]
test al,01000000b ; look at keyboard mode indicator update
pop es
assume es:_TEXT
jz NT_122 ; if this flag is zero, safe to talke with keyboard since system is not
jmp NT_200 ; if this flag is set, system is talking with keyboard, jump around and
; wait tilll that interaction is complete before trying to inject
NT_122:
; We will never try to inject if we are simultaneously filling our own keystroke buffer...
cmp fkeys_injected,true ; when this is true, something is filling the buffer, so we wait
jne NT_123 ; when this is false, we can check the buffer for emptying
jmp short NT_200
NT_123:
; Need to check 8259 interrupt controller to be sure that nothing other than the timer interrupt
; which we are currently in is pending before we fake a keyboard int using software int 9h. We do this to avoid
; unwanted and dangerous extra EOI's being generated. We check "al" register upon return, and if it doesn't
; equal 1, then we have other pending interrupts and we don't do an inject method now..
call controller_check ; check if 8259 controller has any other pending interrupts
cmp al,01h ; is the timer and only the timer int. pending ?
jne NT_200 ; if not, don't insert a key just yet
; if yes, disable interrupts
cli ; disable interrupts, leaving timer int. will re-enable
NT_125:
; Buffer HAS data, and we want to empty it, but need to check which method to use.
cmp _finject_keys,true ; can we inject into the hardware ?
je NT_150 ; if yes, jump to here to do that routine
NT_130:
; If we get here, we do have a computer which supports int 15, but NOT hardware injection
; we must check for the two exception comnputers to the int15, 8042 insert of keys, they are the PS/2 Model 25/30-8086
; and the old PC or PX/XT running DOS 4+/5 with keyb.com loaded simulating int15 services
; Exception 1
; if _comp_id = 6, then we have the PS/2 Model 25/30-8086 and we must handle it by checking to see that
; that the timer didn't interrupt a keyboard interrupt, if not, cli, write to port 60h, and do and software int9h
; Exception 2
; if _comp_id = 1A, then we have the PC or PC/XT running DOS 4+/5 with keyb.com loaded and we must handle it by checking to see that
; that the timer didn't interrupt a keyboard interrupt, if not, do software int9h, and insert our code via int15h when it comes by
; since we cannot write a byte to keyboard port 60h on this computer.
cmp _comp_id,6
jne NT_142
cmp fnum_lock_was_off,true ; if we have a space saver kybd, wait till numlock kybd codes have passed
je NT_200 ; before injecting StickyKey breaks
jmp short NT_145
NT_142:
cmp _comp_id,1Ah
jne NT_148
NT_145:
cmp _comp_id,1Ah ; is is the PC or PC/XT ?
jne NT_146
mov fint9_by_timer,true ; if PC or PC/XT signal to catch in int15h on the way back
jmp short NT_147
NT_146:
call Get_Key_Data ; ax has the data upon return when doing PS/2 Model 25/20-8086
out kb_data,al ; write to kybd data port
mov floop_back,true
NT_147:
int 9h ; do a software int 9h
jmp NT_END
NT_148:
; I we get here, it was not one of the exception type computers form the abocve list, so cont. on with the kybd_echo routines...
call kybd_echo ; call our routine to insert a keystroke via 8042 undocumented feature
jmp NT_END
NT_150:
cmp fnum_lock_was_off,true ; if we have a space saver kybd, wait till numlock kybd codes have passed
je NT_200 ; before injecting StickyKey breaks
call InjectKeys ; inject buffer data, keystrokes, into keyboard hardware buffer
jmp NT_END
NT_200:
;-----------------------------------------------------------------------------------------------------------------------
; Need to check our dialog flags here. If fDialog_Action flag is false, (=0), then no action occurred and we can ignore
; any call to the respective turn on or turn off function. If fDdialog_Action flag is true (1 ), then we need to call the
; respective routine to handle turning on or off of the feature.
;-----------------------------------------------------------------------------------------------------------------------
cmp fDialog_Action,true ; any action occur in Dialog or Menu's ?
jne NT_300 ; if not, skip to next major check
cmp fDialog_Filter_off,false ; is filterkeys off ?
je NT_220 ; yes
call FilterKeys_dialog
jmp NT_END ; exit timer int. any time we do a routine
NT_220:
cmp fDialog_Stickeys_off,false ; is stickeys off ?
je NT_240 ; yes
call StickeyKeys_dialog
jmp NT_END ; exit timer int. any time we do a routine
NT_240:
cmp fDialog_Mouse_off,false ; is mousekeys off ?
je NT_260 ; yes
call MouseKeys_dialog
jmp NT_END ; exit timer int. any time we do a routine
NT_260:
cmp fDialog_Toggle_off,false ; is togglekeys off ?
je NT_280
call ToggleKeys_dialog
jmp NT_END ; exit timer int. any time we do a routine
NT_280:
cmp fDialog_TimeOut_off,false ; is timeout off ?
je NT_300
call TimeOut_dialog
jmp NT_END ; exit timer int. any time we do a routine
NT_300:
mov fDialog_Action,false ; reset this flag upon exit the above routines
;-------------------------------------------------------------------------------------------------------
; Hearing impaired attempt to display to screen a character for sound on/off
; check only 2 times a second otherwise cursor blinking is too annoying
; and also programs which take over the video like Microsoft Works will not
; start if we check the speaker port too often
;-------------------------------------------------------------------------------------------------------
cmp fhearing_on,true ; is hearing inpaired viaual cue flag set on ?
je video_1
cmp fvideo_flash,true ; is hearing impaired video flashing flag set on ?
je video_0
mov faccess_sound,false ; reset flag and
video_0:
jmp video_end ; if hearing impaired flag is not on, jump to here
; Go get the current video state and save it. If it changes from the previous video state, we
; will prevent writes to video for a short period of time in hopes of not messing up the switched
; video state.
;
; Before we do anything, we will get current video state and check it with the old. Since we asssign
; video state a ZERO to start, it will alway pause when ADOS is first enabled. We could try to read
; the video state in _Enable Proc and avoid this, if it seems to miss any beeps.
video_1:
push es
mov ax,RAMBIOS
mov es,ax
assume es:RAMBIOS
mov al,es:[49h] ; read same area int 10h would, but cause less video trouble if not
pop es ; going to write visual cue this time thru
assume es:_TEXT
; mov ah,0fh ; read current video state
; int 10h ; int 10
cmp al,video_state ; is the read video state the same as previous video state ?
je video_3 ; if yes, cont. on here
mov video_state,al ; if not, save current video state
; and set the video_count such that a 5 second delay will
; take palce while the video state is changing
mov video_count,07fh ; reset such that it will take 5 seconds to start video
; for hearing impaired if on
mov faccess_sound,false ; if ADOS produced a sound during a switch, reset that flag also
mov fswitching_video,true ; inform myself (ADOS) that video mode is switching
jmp video_end ; if switching video , jump out
video_3:
inc video_count ; increment counter each time through
cmp video_count,9 ; have we reach 1/2 second ?
je video_4
jmp video_end ; if not at 9 yet, jump around
video_4:
mov video_count,0 ; reset count to 0
mov fswitching_video,false ; inform myself (ADOS) that safe to do video
; any reason to produce a video que for some kind of sound which happened or is happening ?
cmp faccess_sound,true ; did AccesDos produce a beep
je video_20 ; yes or speaker did
video_10:
in al,kb_ctl ; read speaker control port
and al,02 ; test speaker bit on/off
jnz video_18 ; speaker bit is high, do video
jmp video_end
video_18:
mov faccess_sound,true ; use our variable to flag speaker bit toggling also
video_20:
cmp video_state,7 ; is this mode 7, Mono text
je video_22
cmp video_state,3 ; is this any of the Text video modes below 3
jle video_22
; if not, must be in a graphics mode, so
mov txt_or_grph,09h ; write a character to video graphically
jmp short video_23
video_22:
mov txt_or_grph,0ah ; write a character to video as text
video_23:
cmp fvideo_type,3 ; check if we have a CGA monitor
jne video_25 ; if not, continue
; if yes, don't write to video memory until horizontal retrace is in progress
mov dx,3dah
in al,dx ; read CGA status byte
test al,1 ; test bit 1
jnz video_25 ; if bit is set, in retrace, semi safe to write
jmp video_end
video_25:
; if not true, not a CGA monitor and we can write to video directly
mov ah,3 ; read current cursor position
mov bh,0 ; page 0
int 10h ; video interrupt
mov old_row,dh ; save current row
mov old_col,dl ; save current column
mov old_cursor_1,cl ; save current cursor type
mov old_cursor_2,ch ; save current cursor type
mov ah,2 ; prepare to set current position
mov dh,0 ; row 0
mov dl,0 ; column 0
mov bh,0 ; page 0
int 10h ; video int
video_30:
mov ah,08h ; read character and attribute at cursor
int 10h
mov old_char_1_attr,ah ; put attributes into variable
cmp al,14 ; did we just read back the hearing impaired sysbol ?
je video_50
mov old_char_1,al ; save orig. char. to rewrite
mov ah,2 ; prepare to set current position
mov dh,0 ; row 0
mov dl,1 ; column 1
mov bh,0 ; page 0
int 10h ; video int
mov ah,08h ; read character and attribute at cursor
int 10h
mov old_char_2_attr,ah ; put attributes into variable
mov old_char_2,al ; save orig. char. to rewrite
mov ah,txt_or_grph ; set video mode to write in
video_40:
cmp fsecond_cue,false
je video_45
mov cx,1 ; write 1 character
mov al,fsecond_cue ;
mov bl,old_char_2_attr
int 10h
video_45:
mov ah,2 ; prepare to set current position
mov dh,0 ; row 0
mov dl,0 ; column 0
mov bh,0 ; page 0
int 10h ; video int
mov cx,1 ; write 1 character
mov ah,txt_or_grph ; set video mode to write in
mov bl,old_char_1_attr
mov al,14 ; send ASCII character 14 to video for sounds on
int 10h
jmp video_70 ; jump to restore cursor
video_50:
mov ah,txt_or_grph ; set video mode to write in
video_66:
mov cx,1 ; write 1 character
mov faccess_sound,false
mov bl,old_char_1_attr
mov al,old_char_1 ; replace original character
int 10h
cmp fsecond_cue,false
je video_70
mov ah,2 ; prepare to set current position
mov dh,0 ; row 0
mov dl,1 ; column 1
mov bh,0 ; page 0
int 10h ; video int
mov ah,txt_or_grph ; set video mode to write in
mov bl,old_char_2_attr
mov al,old_char_2 ; replace original character
mov fsecond_cue,false ; reset fsecond_cue flag
int 10h
video_70:
mov ah,2 ; restore current cursor position
mov ch,old_cursor_2
mov cl,old_cursor_1
mov dh,old_row
mov dl,old_col
mov bh,0
int 10h
jmp NT_END ; exit timer int. any time we do a routine
video_end:
;-------------------------------------------------------------------------------------------------------
; video flash routines
cmp fvideo_flash,true ; is hearing impaired video flashing flag set on ?
je flash_10
jmp flash_end_out
flash_10:
cmp fvideo_flash_on,true ; did we already flash
je flash_40 ; if yes, jump to routine to turn video back on
cmp faccess_sound,true ; did AccesDos produce a beep
jne flash_15
mov video_cnst,4 ; yes
jmp short flash_30
flash_15:
in al,kb_ctl ; read speaker control port
and al,02 ; test speaker bit on/off
jnz flash_20 ; speaker bit is high, do video
jmp flash_end_out
flash_20:
mov faccess_sound,true ; use our variable to flag speaker bit toggling also
mov video_cnst,4 ; speaker did, keep seperateincase we change system flash length
flash_30:
;; cmp fvideo_flash_on,true ; did we already flash
;; je flash_40 ; if yes, jump to routine to turn video back on
mov fvideo_flash_on,true
call video_flash_on ; subroutine to do borderflash on
jmp short flash_end
flash_40:
inc fvideo_flash_cnt
mov al,video_cnst
cmp al,fvideo_flash_cnt
jne flash_end_out
call video_flash_off ; subroutine to do borderflash off
mov faccess_sound,false
mov fvideo_flash_cnt,0
mov fvideo_flash_on,false
flash_end:
jmp NT_END ; exit timer int. any time we do a routine
flash_end_out:
;--------------------------------- End of Video for Hearing Impaired flag ------------------------------
cmp _finject_keys,true ; if flag isn't true, donot check any further
jne NT_END
cmp fwindows_enh,true ; are we in enhanced mode ?
jne NT_END
cmp falt_esc_send,true ; safe to send codes ?
jne sound_100
mov al,038h ; put ALT make into buffer
call Put_Key_Data
mov al,001h ; put ESC make into buffer
call Put_Key_Data
mov al,081h ; put ESC break into buffer
call Put_Key_Data
mov al,0b8h ; put ALT break into buffer
call Put_Key_Data
sound_90:
mov falt_esc,false
mov falt_esc_send,false
jmp short NT_END
sound_100:
;--------------------------------- check if we ever loaded Windows Real mode, and if we did, did we exit
cmp fwindows_after_re,true ; was Windows ever startec in REAL mode ?
jne NT_500
mov ax,04680h ; use int2f to see if still loaded
int 2fh
cmp al,00 ; al = 00, if REAL mode still running
je NT_500
; if al <> 00, then REAL mode exitted, reset flags
mov video_count,07fh ; reset such that it will take 5 seconds to start video
mov fwindows_after_re,false
mov fwindows_after,false
mov al,fhearing_on_save ; restore upon exit
mov fhearing_on,al
mov al,fvideo_flash_save
mov fvideo_flash,al
NT_500:
;----------------------------------- run if and only if ADOS started after windows in enhanced mode-----------------
cmp fctrl_esc_send,true ; safe to send codes ?
jne NT_END
mov al,01dh ; put CTRL make into buffer
call Put_Key_Data
mov al,001h ; put ESC make into buffer
call Put_Key_Data
mov al,081h ; put ESC break into buffer
call Put_Key_Data
mov al,09dh ; put CTRL break into buffer
call Put_Key_Data
mov fctrl_esc,false
mov fctrl_esc_send,false
NT_END:
mov ftimer_1C_active,false
;NT_END_5:
pop ax
pop bx ; restore registers
pop cx
pop dx
pop di
pop si
pop es
assume ES:NOTHING
pop ds
assume DS:NOTHING
iret
_new_timer_int endp
;--------------------------------------------------------------------------------------------------------
controller_check proc near
assume DS:_TEXT
mov al,0Bh
out ack_port,al ; write operation command byte 3, to get ISR
jmp $+2
in al,ack_port ; read the 8259 controller port
jmp $+2
mov ah,al
mov al,08h
out ack_port,al ; write operation to rese controller
mov al,ah
ret
controller_check endp
;--------------------------------------------------------------------------------------------------------
video_flash_on proc near
assume DS:_TEXT
assume es:NOTHING
mov ax,RAMBIOS
mov es,ax
assume es:RAMBIOS
mov al,es:vid_flag
and al,0f7h
cmp fvideo_type,1
jne von_10
mov dx,3b8h
out dx,al
mov es:vid_flag,al
jmp short von_end
von_10:
cmp fvideo_type,3
jne von_30
mov dx,3d8h
out dx,al
mov es:vid_flag,al
jmp short von_end
von_30:
cmp fvideo_type,4
jne von_40
mov dx,3bah
in al,dx
mov dx,3c0h
xor al,al
out dx,al
jmp short von_end
von_40:
cmp fvideo_type,5
jne von_50
mov dx,3dah
in al,dx
mov dx,3c0h
xor al,al
out dx,al
jmp short von_end
von_50:
cmp fvideo_type,6
jne von_60
mov dx,3d8h
in al,dx
and al,0f7h
out dx,al
mov es:vid_flag,al
jmp short von_end
von_60:
cmp fvideo_type,7
jne von_end
mov ax,1201h
mov bx,0036h
int 10h
von_end:
assume es:NOTHING
ret
video_flash_on endp
;--------------------------------------------------------------------------------------------------------
video_flash_off proc near
assume DS:_TEXT
assume es:NOTHING
mov ax,RAMBIOS
mov es,ax
assume es:RAMBIOS
mov al,es:vid_flag
or al,08h
cmp fvideo_type,1
jne voff_10
mov dx,3b8h
out dx,al
mov es:vid_flag,al
jmp short voff_end
voff_10:
cmp fvideo_type,3
jne voff_30
mov dx,3d8h
out dx,al
mov es:vid_flag,al
jmp short voff_end
voff_30:
cmp fvideo_type,4
jne voff_40
mov dx,3bah
in al,dx
mov dx,3c0h
mov al,20h
out dx,al
jmp short voff_end
voff_40:
cmp fvideo_type,5
jne voff_50
mov dx,3dah
in al,dx
mov dx,3c0h
mov al,20h
out dx,al
jmp short voff_end
voff_50:
cmp fvideo_type,6
jne voff_60
mov dx,3d8h
in al,dx
or al,08h
out dx,al
mov es:vid_flag,al
jmp short voff_end
voff_60:
cmp fvideo_type,7
jne voff_end
mov ax,1200h
mov bx,0036h
int 10h
voff_end:
assume es:NOTHING
ret
video_flash_off endp
;-----------------------------------------------------------------------------------------------------------------
;*****************************************************************************************************************
; This is the beginning of the resident code of the routine which receives
; the keyboard scan code via int 15h from the keyboard interrupt, int 9h. It builds up
; the extended scan code while also passing the scan code along back to interrupt 9, most of the time.
; If after passing thru the keyboard features, it is detemined that the scan code should not pass along,
; the software returns to int. 9h with the carry flag cleared.
;
; The flags fbios_called_direct or fbios_called_timing are used to tell if the scan code should get
; passed back to interrupt 9h, or should be eaten upon passing back to interrrpt 9h.
;
; The scan code is in Al when int 15h is called with Ah = 4f
;*****************************************************************************************************************
;-----------------------------------------------------------------------------------------------------------------
_keybd_int_15 PROC far
cmp ah,4fh ; is this an int15 for the keyboard ?
je kybd_int_15_5
jmp not_kybd_int_15
kybd_int_15_5:
push ds ; save the ds and ax
push es
push bx
push ax
push cs
pop ds
assume DS:_TEXT
push cs
pop es
assume ES:_TEXT
;---------------------------------------------------------------------------------------------------------------------------
; difference here to standard int 9h routine *******************************************************************************
;---------------------------------------------------------------------------------------------------------------------------
mov fmouse_or_kybd,true ; flag any int. callers, that kybd int. 9h executed
call TimeOut_Reset ; reset the time out count
mov fbios_called_direct,false ; reset to false upon entry
;----------------------------------------------------------------------------
; we do not want to have any of the routines that process key input have to
; deal with any of the commands that are specifc to the operation of the
; keyboard, so we will pass them on directly to the BIOS routine since the
; original device driver routine also passed commands to the BIOS routine.
; See original BIOS of Tech. Ref. manuals for PC/PCXT/PCAT
;
pop ax
cmp al,kybd_Command ; is it a command (0EDh)?
jb not_command_15 ; no -->
kybd_command_50:
jmp kybd_int_15_exit ; done -->
;----------------------------------------------------------------------------
not_command_15:
; cmp fserial_keys_loaded,true ; is serial keys loaded ?
; jne kybd_int_15_5A
cmp _serialKeysOn,true ; is SerialKeys code on ?
jne kybd_int_15_5A
;---------------------------- serial port keyboard emulation ----------
; if we get here, we need to first look and see if SerialKeys injected a code
test cs:_forcedInt9Flag,true ; if flag is true, SerialKeys injected a scan code
jz kybd_int_15_5A
mov cs:_forcedInt9Flag,false ; reset flag
mov fserial_key_recieved,true ; set flag for my code modules
mov ah,04fh ; fill in the rest of a fake int 15h AX
mov al,cs:_injectByte ; inject new scan code
;IFDEF BUG
; mov portid,9
; mov portout,al
; call HexCharsOut
;ENDIF; BUG
; call click
jmp short kybd_int_15_7
kybd_int_15_5A:
;---------------------------- keyboard emulation-----------------------
; When we get here, we need to check if this is a real int9 or did the timer simulate an int9 when int 15h was called ?
cmp fint9_by_timer,true ; if the timer simulated an int9, this flag will be true
jne kybd_int_15_6 ; if false, it is a real int 9 and call to int 15
call Get_Key_Data ; if true, this is a fake int9, and we want to get
; the scan code to pass out of the key_data_buffer
; the scan code we want to pass is now in AL register
mov ah,04fh ; fill in the rest of a fake int 15h AX
jmp short kybd_int_15_7
kybd_int_15_6:
;IFDEF BUG
;; mov fserial_debugging,true
; mov portid,0
; mov portout,al
; call HexCharsOut
;; mov fserial_debugging,false
;ENDIF; BUG
cmp fkeys_injected,false
jne kybd_int_15_6C
push bx
mov bx,key_data_head ; get head pointer of key_data buffer
cmp bx,key_data_tail ; test to see if head = tail, i.e. no data to send
je kybd_int_15_6B ; if equal, then we may need to empty buffer
cmp floop_back,true
je kybd_int_15_6B
push es
push ax
mov ax,RAMBIOS
mov es,ax
assume es:RAMBIOS
mov al,es:[97h]
test al,01000000b ; look at keyboard mode indicator update
pop ax
pop es
assume es:_TEXT
jz kybd_int_15_6A ; if clear, comp. and kybd are not talking, okay to in-line inject
jmp short kybd_int_15_6B
kybd_int_15_6A:
call Put_Key_Data ; save current al
call Get_Key_Data ; get my code
mov ah,04fh ; fill in the rest of a fake int 15h AX
kybd_int_15_6B:
mov floop_back,false ; reset
pop bx
kybd_int_15_6C:
;IFDEF BUG
;; mov fserial_debugging,true
; mov portid,1
; mov portout,al
; call HexCharsOut
;; mov fserial_debugging,false
;ENDIF; BUG
kybd_int_15_7:
;---------------------------------------------------------
; first check to see if the space saver keyboard is in use
;---------------------------------------------------------
cmp fspace_saver,true ; is flag set to indicate space saver ?
jne kybd_space_saver_end ; if not, exit to next check
kybd_space_saver_15_3:
cmp al,046h ; is space saver kybd, so check is this SCROLL LOCK make key ?
jne kybd_space_saver_15_5
jmp short kybd_space_saver_15_15
kybd_space_saver_15_5:
cmp al,0C6h ; is this SCROLL LOCK break key ?
jne kybd_space_saver_end ; if not, exit
cmp fscroll_changed,true ; did we previously detect shift+numlock ?
jne kybd_space_saver_end ; if not, exit
mov fscroll_changed,false ; reset flag
mov al,0C5h ; insert NUM LOCK break into "al" register instead of scroll lock break
jmp short kybd_space_saver_end
kybd_space_saver_15_15:
cmp fscroll_changed,true ; did we already change scroll lock to num lock and this is typematic
jne kybd_space_saver_15_25 ; because if it is, it will also need to be changed, as StickyKeys would have cleared
; the shift flag by now...If a person hold both keys down, we don't have a problem, since the
; keyboard will then send a "45,C5", not a "46,C6" code
jmp short kybd_space_saver_15_30 ; fscroill was set, so keep changing makes until we get a break
kybd_space_saver_15_25:
push es
push ax
push bx
assume ES:NOTHING
mov bx,RAMBIOS
mov es,bx
ASSUME ES:RAMBIOS
mov al,es:kb_flag
test al,03h ; is a shift key also down with the scroll lock ?
jz kybd_space_saver_15_50 ; if not, cont
mov fscroll_changed,true ; if yes, set htis flag to catch on the scroll break
pop bx
pop ax
pop es
assume ES:_TEXT
kybd_space_saver_15_30:
mov al,045h ; change al to NUM LOCK make
jmp short kybd_space_saver_end
kybd_space_saver_15_50:
pop bx
pop ax
pop es
assume ES:_TEXT
kybd_space_saver_end:
;-----------------------------------------------------
; Next, need to check if we are in a control break condition
;-----------------------------------------------------
cmp fctrl_break_on,true ; was ctrl break flagged on previous int ?
jne kybd_int_15_13
cmp al,0c5h ; control break condition on, wait for "C5h" to clear
jne kybd_int_15_12 ; if not "C5h" yet, jump to end
kybd_int_15_10:
; if we get here, we are looking for the "C5h" scan code only-last part of PAUSE key
; what ever is in "ax" register doesn't need to be saved, since we are throwing it away
push cx
push si
push ax
xor cx,cx
xor ax,ax
mov fkeys_injected,true ; set flag since we are going to put keys in the buffer
kybd_int_15_11:
mov cl,ctrl_break_buf_cnt
mov bx,offset ctrl_break_buf
mov si,cx
mov al,[bx+si]
call Put_Key_Data
inc ctrl_break_buf_cnt
mov al,ctrl_break_buf_cnt
mov bl,ctrl_break_buf_len
cmp al,bl
jne kybd_int_15_11
mov ctrl_break_buf_cnt,false ; reset buffer count
mov fkeys_injected,false ; buffer stuffed, turn off flag so timer can inject
mov fctrl_break_on,false ; if it is "C5h" jump to end
pop ax
pop si
pop cx
kybd_int_15_12:
jmp keybd_int_15_end ; jump to end, since we ate a code
kybd_int_15_13:
;;----------------------------------------------------------------------------------------------------------------------
;; For this next routine, we need to check if we support Inject keys routine
cmp _finject_keys,true ; first check if we can inject ?
je kybd_win_15_5 ; if we can inject, do next code which is based on the ability to INJECT
jmp kybd_win_end ; if we CAN NOT inject, cont. at _13
;-------------------------------------------------------------------------------------------
; THIS CODE ASSUMES THE KEYBOARD AND COMPUTER ACCEPT DIRECT INJECTION OF MOUSE AND KYBD CODES
;-------------------------------------------------------------------------------------------
;-------------------------------------------------------------------------------------------
; next check to see if we are running under Windows 3.0 in the enhanced mode only
;-------------------------------------------------------------------------------------------
kybd_win_15_5:
cmp fwindows_enh,true ; is windows running ?
jne kybd_win_end
kybd_win_15_7:
cmp al,01h ; did we read in an "ESC" key ?
jne kybd_win_15_10
jmp short kybd_win_15_30 ; was an "ESC" key make , check if "ALT" key down ?
kybd_win_15_10:
cmp al,081h ; is this the "ESC" break ?
jne kybd_win_end
kybd_win_15_20: ; was an "ESC" key break
cmp falt_esc,true
jne kybd_win_15_25
mov falt_esc_send,true
mov falt_esc,false
jmp short kybd_win_end
kybd_win_15_25:
cmp fctrl_esc,true
jne kybd_win_end
mov fctrl_esc_send,true
mov fctrl_esc,false
jmp short kybd_win_end
kybd_win_15_30:
push es
push ax
push bx
assume ES:NOTHING
mov bx,RAMBIOS
mov es,bx
assume ES:RAMBIOS
mov al,es:kb_flag
test al,08h
jz kybd_win_15_50
mov falt_esc,true
jmp short kybd_win_15_95
kybd_win_15_50:
mov falt_esc,false
test al,04h
jz kybd_win_15_60
mov fctrl_esc,true
jmp short kybd_win_15_95
kybd_win_15_60:
mov fctrl_esc,false
kybd_win_15_95:
pop bx
pop ax
pop es
assume ES:_TEXT
kybd_win_end:
;;;----------------------------------------------------------------------------
;;; we do not want to have any of the routines that process key input have to
;;; deal with any of the commands that are specifc to the operation of the
;;; keyboard, so we will pass them on directly to the BIOS routine since the
;;; original device driver routine also passed commands to the BIOS routine.
;;; See original BIOS of Tech. Ref. manuals for PC/PCXT/PCAT
;;;
;; cmp al,kybd_Command ; is it a command (0F0h)?
;; jb not_command_15 ; no -->
;; jmp kybd_int_15_exit ; done -->
;;
;----------------------------------------------------------------------------
; we need to build up the two byte key codes if appropriate.
cmp al,kb_ext_code1 ; is it a hidden code prefix e0?
je is_hidden_code_15 ; yes, save -->
cmp al,kb_ext_code2 ; is it a hidden code prefix e1?
jne not_hidden_code_15 ; no, -->
is_hidden_code_15:
mov hidden_code,al ; yes, save
; at this point, we know we had a hidden code, so check for ctrl-break
; or alt-print screen for sysreq.
push es ; save es
push ax
assume ES:NOTHING
mov bx, RAMBIOS ; BIOS RAM segment at 40h
mov es,bx ; .. point ES to that!
assume ES:RAMBIOS
mov al,es:kb_flag ;
cmp hidden_code,0E1h ; is it "E1h" ?
jne kybd_int_15_20 ; if not jump to check the "E0h" code
test al,04h
jz kybd_int_15_30 ; if zero, the control key isn't pressed
mov fctrl_break_on,true ; set flag to tell us we have ctrl break condition
mov fpause_being_sent,false ; if control break, not a PAUSE
jmp short kybd_int_15_30
kybd_int_15_20:
; if it jumps here, we know we have a hidden code, and it must be "E0h" only, and all we want to check is if an alt. key is down
test al,08h
jz kybd_int_15_25 ; if not set, jump out
mov fsysreq,true ; alt. key is down with "E0h" code, MAY have sys req. in progress
jmp short kybd_int_15_30
kybd_int_15_25:
mov fsysreq,false ; alt key not down with "E0h" code
kybd_int_15_30:
pop ax
pop es
assume ES:_TEXT
kybd_int_15_35:
cmp fctrl_break_on,true ; are we in a ctrl break condition ???
jne kybd_int_15_45
jmp keybd_int_15_end ; reset without any call to original BIOS
kybd_int_15_45:
cmp al,0E1h ; is this an E1h hidden code ?
jne kybd_int_15_46
mov fpause_being_sent,true ; if "E1h" unaltered, then PAUSE key is being sent, flag this so MouseKeys
; doesn't turn off on the "45h" part of the scan code
; will alway stay on unless MouseKeys is on, detects this, and clears it
; or it is a computer which Injects, then it is turned off above kybd_int_20
kybd_int_15_46:
jmp kybd_int_15_exit ; pass the E0 or E1 code
;------------------------------------------------------------
; now build up the code and pass to first one of the routines
;------------------------------------------------------------
; last check for alt-print screen for sysreq done here
;------------------------------------------------------------
not_hidden_code_15:
; int 15h with ah = 4fh will get burnt here, since ah is replaced with hidden code and passed thru AccessDos features
; appears not to bother int 15h, 4fh, unless DOS keyb.com is running, so I reworked this
; code to maintain the "ah"=4fh ..........
push ax ; save al, ah =4fh
mov ah,hidden_code ; get current hidden code value
mov hidden_code,0 ; reset hidden code
; for passing thru keyboard features only
cmp fsysreq,true ; did we previously set this flag due to alt.+"E0h" code ?
jne kybd_int_15_48 ; if not, cont.
; if yes, then we must watch for the "B7h" scan code which is PrtSc make code
cmp al,0B7h ; is this the "37h" key scan code ?
jne kybd_int_15_48 ; cont. on if not
; if we get here, it was "B7h" scan code, so we pass the last code and inject the SysReq codes (54/D4)
push ax
mov fkeys_injected,true ; set this flag to inject
mov al,054h ; SysReq make code
call Put_Key_Data
mov al,0D4h ; SysReq break code
call Put_Key_Data
mov fsysreq,false ; we've inject sysreq, so clear htis falg
pop ax
;; mov fkeys_injected,false ; we are done stuffing buffer, so reset this flag to allow timer to inject
;; jmp short kybd_int_15_exit ; exit
kybd_int_15_48:
; Now call Handicap routines. We do not expect any registers to be
; the same. So the routine does not need to save any.
call FilterKeys ; pass on extended code in AX
pop ax ; restore ah, and al, to code to be passed or eaten
test al,break_bit ; was this code a break code ?
jz kybd_int_15_48A ; it was a make scan code, check other condition
mov fkeys_injected,false ; reset this flag if a break upon return
jmp short kybd_int_15_49 ;
kybd_int_15_48A:
cmp fSticKeysOn,true ; is StickeyKeys On ?
je kybd_int_15_48C ;
cmp fint9_by_timer,true ; is this an injected scan code by the timer ?
je kybd_int_15_48C ;
cmp al,46h
je kybd_int_15_48C ;
cmp al,54h
je kybd_int_15_48C ;
jmp short kybd_int_15_49 ; if flag gets reset on a break, okay
kybd_int_15_48C:
mov fkeys_injected,false ; if yes, reset this flag also so buffer will empty makes/breaks
kybd_int_15_49:
cmp fbios_called_timing,true ; Does the timer want to let a key pass ? (FROM FILTERKEYS ONLY)
jne kybd_int_15_50
cmp fclick_on,true
jne kybd_int_15_49A
cmp fshift_click,true
je kybd_int_15_49A
cmp current_shift,0
je kybd_int_15_49B ; if not a modifier key, call click
mov fshift_click,true ; if we clicked once, set, as non-modifier will clear
kybd_int_15_49B:
call click
kybd_int_15_49A:
mov fbios_called_timing,false ; reset flag
jmp short kybd_int_15_exit ; and allow code to pass
kybd_int_15_50:
cmp fbios_called_direct,true ; do we want to pass on the code ?
jne keybd_int_15_end ; no, so end and eat the code
;-----------------------------------------
; jumps to here, if passing scan code on as is or what ever has been stored in the AL register
kybd_int_15_exit:
mov fserial_key_recieved,false ; reset flag for my code modules
;IFDEF BUG
;; mov fserial_debugging,true
; mov portid,2
; mov portout,al
; call HexCharsOut
;; mov fserial_debugging,false
;ENDIF; BUG
pop bx
pop es
pop ds
assume DS:NOTHING
assume ES:NOTHING
stc
jmp dword ptr cs:old_kybd_int ; call old int 15 routine
;-----------------------------------------
; jumps to here if ate a code, if we eat code, return to int15 caller
; with ret 2, which in effect, through away flags on stack and allows
; our carry flag clear to return as it should
keybd_int_15_end:
mov fserial_key_recieved,false ; reset flag for my code modules
;;IFDEF BUG
;; mov fserial_debugging,true
;; mov portid,3
;; mov portout,al
;; call HexCharsOut
;; mov fserial_debugging,false
;;ENDIF; BUG
pop bx
pop es
pop ds
assume DS:NOTHING
assume ES:NOTHING
clc
ret 2
;-----------------------------------------
; jumps to here, if not int15h with Ah=4fh
not_kybd_int_15:
jmp dword ptr cs:old_kybd_int ; call old int 15 routine
_keybd_int_15 endp
;----------------------------------------------------------------------------
; keybd_int
;
; This is the beginning of the resident code of the routine which intercepts
; the keyboard interrupt, int 9h. This keyboard interrput routine should only load
; and run for IBM PC /PCXT/ PCAT Original 1/10/84 (i.e. 84 key keyboard) computers.
; Non of these computer should ever see a hidden code (i.e E0/E1), and non of these
; computers support the Extended BIOS or kb_flags1/3 or hardware keyboard injection.
;
; Since I know some PC/PCXT/PCAT users prefer 101 keyboard, and that these keyboards
; typically cheat by sending hidden codes for the extra keys expecting the BIOS
; to straighten the situation out, I can easily send on any hidden codes straight to BIOS
; where the computer will ignore them, and this may allow our code to work on
; these NASTY keyboards.
;-----------------------------------------------------------------------------
; NOTE: IF I get codes for 101 cursor keys like 2A 4D AA 2A CD AA for the --> key,
; we will still have trouble since the extra 2A and AA look like shift keys to PC/PCXT/PCAT
; Original, since it never had cursor keys, and if it did have cursor keys, they should
; send E0 2A E0 4D E0 CD E0 AA for the --> key, which would distinguish it correctly,
; and this code should handle by passing along to BIOS where the hidden codes would be ignored.
;-----------------------------------------------------------------------------
_keybd_int PROC
push ax
mov ax,1 ; watch for myself to be re-entrant
xchg cs:fint9_active_temp,al ; flag that I'm in interrupt
cmp al,1 ; which time are we ???
je kybd_int_3
mov cs:kybd_ss_save,ss
mov cs:kybd_sp_save,sp
push cs
pop ss
mov sp,offset kybd_Stack_top
kybd_int_3:
push ax ; save value of ax
push ds ; save the ds and ax
push ax
push bx
push es
push cs
pop ds
assume DS:_TEXT
push cs
pop es
assume ES:_TEXT
call TimeOut_Reset ; reset the time out count
mov fbios_called_direct,false
mov fmouse_or_kybd,true ; flag any int. callers, that kybd int. 9h executed
kybd_int_5:
cmp _comp_id,5 ; do we have an AT class machine
jne kybd_int_8 ; if not, cont.
mov al,dis_kbd ; if yes, disable keyboard
call ship_it
kybd_int_8:
in al,kb_data ; read in data from keyboard port
;IFDEF BUG
; mov portid,0
; mov portout,al
; call HexCharsOut
;ENDIF; BUG
;----------------------------------------------------------------------------
; we do not want to have any of the routines that process key input have to
; deal with any of the commands that are specifc to the operation of the
; keyboard, so we will pass them on directly to the BIOS routine since the
; original device driver routine also passed commands to the BIOS routine.
; See original BIOS of Tech. Ref. manuals for PC/PCXT/PCAT
;
cmp al,kybd_Command ; is it a command (0EDh)?
jb not_command ; no -->
kybd_int_10:
pushf ; yes, call bios routine
call dword ptr cs:old_kybd_int
jmp keybd_int_ret ; done -->
not_command:
;----------------------------------------------------------------------------
; we need to pass on hidden key codes if appropriate.
cmp al,kb_ext_code1 ; is it a hidden code prefix e0?
je is_hidden_code ; yes, eat-->
cmp al,kb_ext_code2 ; is it a hidden code prefix e1?
jne not_hidden_code ; no, -->
is_hidden_code:
mov hidden_code,al
jmp short kybd_int_10 ; allow BIOS to deal with any hidden codes we may get
not_hidden_code:
; if the hidden code is not zero, then the previous scan code was either E0/E1 and something, which
; should not exist on these computers, so I will pass them along to the original BIOS and let it
; sort them out.
mov ah,hidden_code ; get previous hidden code if any ?
mov hidden_code,0 ; reset for next key
cmp ah,0 ; was there a previous hidden code ?
je kybd_int_20 ; if not, we can cont.----else goto BIOS
; Before we send all E0 /E1 codes to BIOS, ....
; check for right alt. and ctrl. as these older computers shouldn't have these keys, but someone may
; use a 101 key keyboard on tis type of computer as the BIOS just treat the right alt. and ctrl. keys
; as the left alt. and ctrl. keys, as should I
push ax
and al,not break_bit ; be sure al is the make version of a make/break code
cmp al,38h ; an right alt. key ?
jne kybd_int_11
jmp short kybd_int_18
kybd_int_11:
cmp al,1dh ; an right ctrl. key ?
jne kybd_int_12
jmp short kybd_int_18
kybd_int_12:
cmp al,1ch ; an right enter key ?
jne kybd_int_15
jmp short kybd_int_18
kybd_int_15:
pop ax
jmp short kybd_int_10 ; allow BIOS to deal with any codes
kybd_int_18:
pop ax
kybd_int_20:
; Now call Handicap routines. We do not expect any registers to be
; the same. So the routine does not need to save any.
; sti
;IFDEF BUG
; mov portid,0
; mov portout,ah
; call HexCharsOut
; mov portid,1
; mov portout,al
; call HexCharsOut
;ENDIF; BUG
call FilterKeys ; pass on code in Al
; Disable int. and then re-enable keyboard INTs at the interrupt controller
cli ; Ints off again. NOTE: don't need
; to turn them back on because we are
; going to to an IRET when we finish.
cmp fbios_called_direct,true ; did we pass on to ROM routine?
je keybd_int_ret ; yes, so no need to reset it again
; and no need to re-enable jeyboard as original
; BIOS routine already did if it was supposed to
cmp fFilterKeysOn,true ; is FilterKeys on ???
jne keybd_int_end ; if not, this must have been a stickeys eat or something
cmp fbios_called_timing,true ; Does the timer want to let a key pass ? (FROM FILTERKEYS ONLY)
jne keybd_int_end ; if not, exit w/o out calling old int 9 kybd routine
kybd_int_25:
cmp fclick_on,true
jne kybd_int_40
cmp fshift_click,true
je kybd_int_40
cmp current_shift,0
jne kybd_int_40 ; if not a modifier key, call click
mov fshift_click,true ; if we clicked once, set, as non-modifier will clear
call click
kybd_int_40:
mov fbios_called_timing,false ; reset flag
mov fkey_not_passed,false ; reset flag
push ds
pushf
call dword ptr cs:old_kybd_int
pop ds
; any other clean up to do,like reset stickey flags ???
cmp fSticKeysOn,true
jne kybd_int_50
cmp flatch,true
jne kybd_int_50
call set_shift_states ; clear latched key(s) states
mov shift_flg,0 ; clear shifted states
kybd_int_50:
assume ds:_TEXT
jmp keybd_int_ret
;-----------------------------------------------------------------------
keybd_int_end:
; If we get here, then we did a call to FilterKeys, StickeyKeys,.....and we DID NOT want to
; call the Original BIOS, so we need to reset the kybd hardware ourselves to allow
; any other key strokes to generate interrupts.
;IFDEF BUG
; mov portid,3
; mov portout,al
; call HexCharsOut
;ENDIF; BUG
in al,kb_ctl ; reset interface chip (8042)
mov ah,al
or al,80h
jmp $ + 2 ; jump delay for fast computers
out kb_ctl,al
xchg ah,al
jmp $ + 2 ; jump delay for fast computers
out kb_ctl,al
mov al,020h ; acknowledge interrupt (8259)
jmp $ + 2 ; jump delay for fast computers
out ack_port,al
cmp _comp_id,5 ; do we have an AT class machine
jne keybd_int_ret ; if not, cont.
mov al,ena_kbd ; if yes, disable keyboard
call ship_it
keybd_int_ret:
;IFDEF BUG
; mov portid,2
; mov portout,al
; call HexCharsOut
;ENDIF; BUG
pop es
assume ES:NOTHING
pop bx
pop ax ; restore the world
pop ds
assume DS:NOTHING
pop ax ; save value of ax
cmp al,1 ; which time are we ???
je kybd_int_ret_3
mov ss,cs:kybd_ss_save
mov sp,cs:kybd_sp_save
mov cs:fint9_active_temp,0 ; clear my flag since I'm out finally
kybd_int_ret_3:
pop ax
iret ; also clears any int. which were set
_keybd_int endp
;;----------------------------------------------------------------------------
; speed_timer_int
;
; increments the counts used in the time out and key delay repeat options for setting up
; the beep sounds on the various computers. This routine is called during initialization only.
speed_timer_int PROC
push ds
push cs
pop ds
assume DS:_TEXT
dec count_down_ticks ; decrement the count
pushf
call dword ptr cs:old_1C_int ; call original interrupt
pop ds
assume DS:NOTHING
iret
speed_timer_int endp
;----------------------------------------------------------------------------
;****************************************************************************
;----------------------------------------------------------------------------
; Enable_Handicap
;----------------------------------------------------------------------------
;****************************************************************************
;----------------------------------------------------------------------------
Enable PROC NEAR
push ds ; save registers used
push es
push si
push di
push dx
push cx
push bx
push ax
assume DS:_TEXT
; reset key_data buffer to empty, i.e. point head and tail to the start of the buffer
mov bx,OFFSET key_data ; get address of key_data buffer
mov key_data_head,bx ; reset head pointer to start of buffer
mov key_data_tail,bx ; reset tail pointer to start of buffer
; reset mouse_data buffer to empty, i.e. point head and tail to the start of the buffer
mov bx,OFFSET mouse_data ; get address of mouse_data buffer
mov mouse_data_head,bx ; reset head pointer to start of buffer
mov mouse_data_tail,bx ; reset tail pointer to start of buffer
mov mouse_cnt,false ;
print mesg60 ; tell user we are loading
ena_2:
;------------------------------------------------------------------------------
; Enable serial port COMM 1/2 at 9600, n, 8, 1 if DEBUG is On
IFDEF BUG
cli
; cmp _comp_id,8 ; am I on the PS2 Model 70 ?
; je com_set_20
mov dx,0 ; com1
mov ah,0 ; init com port
jmp short com_set_30
;
;com_set_20:
;
; mov dx,1 ; com2
; mov ah,0 ; init com port
;
com_set_30:
mov al,0e3h ; 9600 baud, 1 stop bit, no parity, 8 data bits
; mov al,043h ; 300 baud, 1 stop bit, no parity, 8 data bits
int 14h
; mov fserial_debugging,true
mov al,_vector
mov portid,0fh
mov portout,al
call HexCharsOut
; mov fserial_debugging,false
sti
ENDIF;BUG
;---------------------------------------------------------------------------
; Check if DOS version 3.0 or higher, so we know if we have support for DOS int2f
mov ah,30h ; call int21h with al=30h
int 21h ; major version # in al, minor version # in ah
cmp al,3 ; if 3 or higher, then int2f supported
jge ena_4
IFDEF BUG
print mesg45 ; message that pre 3.0 version of DOS is running
ENDIF; BUG
jmp short ena_10
ena_4:
;IFDEF BUG
; print mesg46 ; running DOS 3.0 or higher
;ENDIF; BUG
;---------------------------------------------------------------------------
; check if AccesDos was started under Windows 3.0 in real, standard or enhanced mode ?
mov ax,1600h ; code to ask Windows if running enhanced ?
int 2fh ; DOS int call
test al,7fh ; if al=0 or 80h, then Windows enhanced in NOT running
jz ena_5 ; if jump, enhanced not running, go check if real or standard ?
assume DS:_TEXT
mov fwindows,true ; flag set true if we are in Windows
mov fwindows_enh,true ; flag that we are in Windows enhanced mode
mov fwindows_st_re,false
;IFDEF BUG
; print mesg41 ; tell users program started under Windows Enhanced
;ENDIF; BUG
jmp short ena_10 ; no need to hook int2f if ADOS started after Windows
ena_5:
mov ax,4680h ; code to ask Windows if real or standard
int 2fh ; DOS int call
cmp ax,0 ; if ax=0, then real or standard Windows is running
jne ena_8 ; if not, running Windows, hook int 2fh incase
assume DS:_TEXT ; Windows is started after us
mov fwindows,true ; if ax=0, then we are in real or standard mode
mov fwindows_st_re,true ; flag that we are in Windows standard or real mode
mov fwindows_enh,false
;IFDEF BUG
; print mesg42 ; tell user
;ENDIF; BUG
jmp short ena_10 ; no need to hook int2f if ADOS started after Windows
ena_8:
;-----------------------------------------------------------------------------
; get INT 2Fh vector and save away
; Need to hook into int2Fh to watch if Windows is loaded after Access Dos
assume ES:NOTHING
mov ax,352fh
int 21h
mov word ptr [old_2f_int_off],bx
mov word ptr [old_2f_int_seg],es
;-----------------------------------------------------------------------------
; get INT 2fh vector and save away
push ds ; SAVE DS for restore
mov ax,cs
mov ds,ax
mov dx,OFFSET _int2f ; hook int. 2fh also
mov ax,252fh
int 21h
pop ds
ena_10:
;-----------------------------------------------------------------------------
cmp _vector,09 ; do we hook int 9 or int 15
jne ena_10A
mov ax,3509h ; get INT 09h vector and save away
jmp short ena_10B
ena_10A:
mov ax,3515h ; get INT 15h vector and save away
ena_10B:
int 21h
mov word ptr [old_kybd_int_off],bx
mov word ptr [old_kybd_int_seg],es
;----------------------------------------------------------------------------
; Setup timer interrupt vector to point to our interrupt routine
; get INT 1Ch vector and save away in old_timer_int this time
; NOTE: Original timer 08h must have been previously restored above.
assume ES:NOTHING
mov ax,351Ch
int 21h
mov word ptr [old_1C_int_off],bx
mov word ptr [old_1C_int_seg],es
;----------------------------------------------------------------------------
; Setup timer interrupt vector to point to our speed timer interrupt routine
push ds ; SAVE DS for restore
mov ax,cs
mov ds,ax
mov dx,OFFSET speed_timer_int
mov ax,251Ch
int 21h ; set the vector
pop ds
xor ax,ax
mov count_down_ticks,2
;
; The way we will do this is to simulate the exact same set of code that
; is our beep routine. There will be some overhead, but hopefully it will
; not factor into the accuracy that much. We will keep track of how many
; times we will be able to get through the beep routine before we
; get through 1 tick of the real time timer.
ena11:
cmp count_down_ticks,1
jg ena11
;
; 46h is the value for a 2000hz square wave running on a PC. Therefore,
; there would be about 110 cycles within the 18.2 clock (54.95ms).
; If we do 5 cycles, then the count in AX would be 22. Assume that a fast
; 386 machine running at 25Mhz is about 15 times faster than a PC so AX
; in the upper range would be 330.
;
cmp fwindows,true
jne ena12 ; if Windows isn't running, don't need cli
cli
ena12:
mov cx,46h ; test tone, length of square cycle
mov bx,5 ; length of test tone
push ax ; save ax, count
IN al,kb_ctl ; get keyboard control
push ax ; save
push cx ; save cx=wave length
ena13:
and al,0ffh ; set speaker bits, really don't
OUT kb_ctl,al ; send out to speaker
pop cx ; get cx=wave length
push cx ; save cx
ena14:
loop ena14 ; keep on for wave length of time
or al,0 ; set speaker bit, really don't
OUT kb_ctl,al ; send out to speaker
pop cx ; get cx
push cx ; save cx
ena15:
loop ena15 ; keep off for wave length of time
dec bx ; decrement length of tone
jnz ena13 ; loop if not zero
pop cx ; restore cx
pop ax ; get old keyboard control
OUT kb_ctl,al ; send out
pop ax ; restore old ax
sti
;
; Now keep track of count
;
inc ax ; increment count
cmp count_down_ticks,0 ; are we done?
;; ja ena12 ; no
jg ena12 ; if count_down_ticks is >= to zero,loop again
; make this a signed check so if we miss it, FFFF
; is -1, and we will pass
;--------------------------------------------------------------------------------
; for some unknown reason, if running under Windows, especially enhanced mode, the DOS
; timer doesn't seem to handle the speed timer routine correctly all the time, so we will
; check the "ax" register value here to see if it messed up
cmp fwindows,true ; did we find out earlier that Window is running ?
jne ena_20
cmp ax,80 ; Low end 8/10 Mhz Model 50/60 value we might expect for Windows
jg ena_20 ; if ax from speed loop is higher, don't change
mov ax,150 ; otherwise override Windows loop to 150 decimal
ena_20:
; for a 4.77 MHz PC, the count in ax should be about 22
; for a 16 MHz PC, the count in ax should be about 165
; for a 25 MHz PC, the count in ax should be about 330
mov cx,ax ; save a copy in cx
mov ax,46h ; put tone value in ax
mul cx ; multiply by number of times through
mov bx,22 ; normalize to standard pc
div bx
mov high_tone,ax
mov turn_off_start_beep,ax
shl ax,1 ; multiply by 2 to get 1000hz
mov turn_on_start_beep,ax
shl ax,1 ; multiply by 2 to get 500hz
mov low_tone,ax
mov ax,turn_on_start_beep ; get 1000hz back
sub ax,turn_off_start_beep ; get difference
mov bx,100 ; 100 steps
xor dx,dx
div bx
mov on_off_change,ax
cmp ax,00 ; did on_off_change crank out to be ZERO ??
jne ena25 ; if not, things should be okay, so cont
mov on_off_change,1 ; if it was ZERO, very slow computer, so override prev. calc.
ena25:
;-----------------------------------------------------------------------------
cmp fmouse_driver,false
je ena_100 ; if we didn't find a mouse, do not need to hook into any of these
;-----------------------------------------------------------------------------
; get INT 33h vector and save away
; Need to hook into int33h for either serial or PS/2 mouse
assume ES:NOTHING
mov ax,3533h
int 21h
mov word ptr [old_33_int_off],bx
mov word ptr [old_33_int_seg],es
;-----------------------------------------------------------------------------
; PS/2 MOUSE
;
; If we get here, we found a PS/2 mouse, so we need to check which computer we are on
assume ES:NOTHING
cmp _comp_id,6 ; Int 71h is for computer id=6, 25..30/86
jne ena_70 ; if not equal, jump to other PS/2 mouse int
mov ax,3571h ; get INT 71h vector and save away
jmp short ena_75
ena_70:
; If we get here, then we should have computer id = 7 or 8
mov ax,3574h ; get INT 74h vector and save away
ena_75:
int 21h
mov word ptr [old_ps2mou_int_off],bx
mov word ptr [old_ps2mou_int_seg],es
ena_100:
;-------------------------------------------------------------------------------------
; Setup kybd interrupt vector to point to our interrupt routine
cmp _vector,09h ; do we hook int9 or int 15 ?
jne ena_102
push ds ; SAVE DS for restore
mov ax,cs
mov ds,ax
mov dx,OFFSET _keybd_int ; hook into int. 09h, keyboard
mov ax,2509h
int 21h ; set the vector
pop ds
jmp short ena_103
ena_102:
push ds ; SAVE DS for restore
mov ax,cs
mov ds,ax
mov dx,OFFSET _keybd_int_15 ; hook into int. 15h of the keyboard interrupt
mov ax,2515h
int 21h ; set the vector
pop ds
ena_103:
; if not on during a call to Enable, serialkeys will not be loaded, so we don'y have to check the other flags
cmp _serialKeysOn,true ; is SerialKeys code on ?
jne ena_104
push ds
; call _serialKeysInit ; initialize serial keys
call _serialKeysEnableFar
pop ds
ena_104:
;-------------------------------------------------------------------------------------
; Setup 1C timer interrupt vector to point to our interrupt routine
push ds ; SAVE DS for restore
mov ax,cs
mov ds,ax
mov dx,OFFSET _new_timer_int ; hook into int. 1Ch, timer
mov ax,251Ch
int 21h ; set the vector
pop ds
;---------------------------------------------------------------------------------------
cmp fmouse_driver,false ; did we load a mouse driver ???
je ena_200 ; if not, quit
;---------------------------------------------------------------------------------------
; Setup interrupt 33h vector to point to our interrupt routine. we need int33h for either serial or PS/2 mouse
push ds ; SAVE DS for restore
mov ax,cs
mov ds,ax
mov dx,OFFSET _int33 ; hook int. 33h also
mov ax,2533h
int 21h
pop ds
;-----------------------------------------------------------------------------
; get INT 71h vector and save away
; If we get here, we found a PS/2 mouse, so we need to check which computer we are on
cmp _comp_id,6 ; Int 71h is for computer id=6, 25..30/86
jne ena_140 ; if not equal, jump to other PS/2 int
push ds ; SAVE DS for restore
mov ax,cs
mov ds,ax
mov dx,OFFSET _intps2mou ; hook int. 71h also
mov ax,2571h
int 21h
pop ds
jmp ena_200
ena_140:
;-----------------------------------------------------------------------------
; get INT 74h vector and save away
; If we get here, then we should have computer id = 7 or 8
push ds ; SAVE DS for restore
mov ax,cs
mov ds,ax
mov dx,OFFSET _intps2mou ; hook int. 74h also
mov ax,2574h
int 21h
pop ds
ena_200:
pop ax
pop bx ; restore registers
pop cx
pop dx
pop di
pop si
pop es
pop ds
ret
Enable endp
_TEXT ends
end