home *** CD-ROM | disk | FTP | other *** search
- page ,132
- title kbfix - 127 character buffer & screen display of ibm-pc shift status
- ;****************************************************************
- ;* module name = kbfix
- ;*
- ;* copyright(C) 1984 Skip Gilbrech
- ;* 90 Lexington Ave. #10-G
- ;* New York, NY 10016
- ;* 212-685-0551
- ;*
- ;* This program may be freely copied/altered for any non-commercial
- ;* purpose but may not be sold or used in any way as part of any
- ;* profit-making venture without permission of the author.
- ;*
- ;* author = skip gilbrech
- ;* date written = 01/84
- ;*
- ;* environment:
- ;* system = ibm pc (dos 2.0 - but should work on any version)
- ;* processor = microsoft 8086 macro assembler
- ;*
- ;* This program will print out the status of the caps-, num-, and scroll-
- ;* lock keys on the upper right-hand corner of the ibm-pc screen in reverse-
- ;* video, and will beep from low to high when one of these keys is toggled on,
- ;* and from high to low when it is toggled off. The display is updated after
- ;* every keystroke. The program writes directly to the video ram to avoid the
- ;* overhead of rom-bios calls: I found this approach resulted in a much
- ;* cleaner display. The program should work with both monochrome and color 80
- ;* column monitors.
- ;*
- ;* The program is invoked only once and is installed through the dos
- ;* 'terminate but stay resident' function.
- ;*
- ;* I have incorporated an optional 127-character keyboard buffer to replace
- ;* the standard 15 provided by ibm. The buffer is installed only if a '/b'
- ;* switch is encountered on the command line.
- ;*
- ;* Installation of the buffer also allows fixing a problem which I found
- ;* irritating: control-s (pause output) and control-c (abort) only worked
- ;* if they were the only key in the buffer, i.e., if you typed ahead while
- ;* something else was happening, then changed your mind, you had to use
- ;* control-numlock or control-break to pause or abort. This is understandable:
- ;* since bios doesn't treat control-s or control-c in any special way, dos
- ;* would have to scan the whole buffer to detect these keys in any but the
- ;* first position. Now, if either of these keys is detected, the buffer
- ;* is cleared and the character is placed in the first buffer position.
- ;*
- ;* The program permanently occupies 1152 bytes when installed with the buffer;
- ;* without the buffer, it uses 672 bytes.
- ;*
- ;* The program should be independent of any particular rom-bios version, as
- ;* the keyboard interrupt routine gains control only after calling the vector
- ;* at the original int 9 (keyboard interrupt) location. I have no idea about
- ;* this program's compatibility with other keyboard enhancement programs, as
- ;* I don't have any of them (although a friend tells me that Smartkey works
- ;* fine if it is installed AFTER this program).
- ;*
- ;* I have been using various versions of this program for several months,
- ;* now, and haven't run into any destructive bugs, but I can't accept
- ;* responsibility for any damage it may cause. I would appreciate hearing
- ;* about any problems, however, at the number or address above, or on
- ;* Compuserve (71445,534).
- ;*
- ;* This ideas (and some of the code) for this program come from a variety
- ;* of public-domain sources, and the program evolved slowly over several
- ;* months, so if I miss giving credit to someone, please accept my apologies.
- ;*
- ;* The ideas of printing out the key status on the upper-right hand corner of
- ;* the screen and intercepting the interrupt routine after it has executed
- ;* come from Tony A. Rhea's KEYSTAT program. His copyright notice is
- ;* reproduced below:
- ;*
- ;* KEYSTAT -- Copyright (C) 1983 Tony A. Rhea
- ;* This program may be copied for non-commercial use.
- ;* Adapted from KEYFLAGS (PC-World, Oct. 83, page 266) by Morton Kaplon
- ;*
- ;* The bios beep routines were taken from the KEYLOC program by:
- ;*
- ;* John Black
- ;* 5225 Pooks Hill Rd. #1715 N.
- ;* Bethesda MD. 20814
- ;*
- ;* The basic idea for the enlarged keyboard buffer came from a program
- ;* on the Compuserve IBM-PC SIG called KEYBOARD.ASM. There was no name
- ;* on the file I downloaded.
- ;*
- ;****************************************************************
- page
- ;****************************************************************
- ;*
- ;* constants
- ;*
- ;****************************************************************
-
- ; general equates:
-
- false equ 0
- true equ not false
-
- cr equ 0dh ; carriage return
- lf equ 0ah ; line feed
- bell equ 7 ; ascii bell
- ctrl_s equ 13h ; control-s
- ctrl_c equ 3 ; control-c
- spc equ ' ' ; space, blank
- tab equ 9 ; tab
-
- ; program equates:
-
- bytes_per_row equ 160 ; num. bytes per row in video ram
- rowcaps equ 0 ; row for cap symbol
- colcaps equ 77 ; column for cap symbol
- rownums equ 0 ; row for num symbol
- colnums equ 78 ; column for num symbol
- rowscroll equ 0 ; row for scroll symbol
- colscroll equ 79 ; column for scroll symbol
- blank equ ' ' ; display this if a mode is not active
- capschar equ 'C' ; character to signify capslock state
- numchar equ 'N' ; character to signify numlock state
- scrollchar equ 'S' ; character to signify scroll lock state
- normal equ 7 ; display attribute (7=normal white on black)
- reverse equ 112 ; display attr. (112=reverse video - sg)
- blankword equ ((normal * 256) + blank) ; words for video ram
- capsword equ ((reverse * 256) + capschar) ;
- numword equ ((reverse * 256) + numchar) ;
- scrollword equ ((reverse * 256) + scrollchar) ;
-
- ; dos and bios equates:
-
- dosint equ 21h ; interrupt number for dos functions
- print_string equ 9 ; dos print console string function
- get_vers equ 30h ; dos get version number function
- get_int_vec equ 35h ; dos 2.0 get interrupt vector function
- set_int_vec equ 25h ; dos set interrupt vector function
-
- kb_int_no equ 9 ; interrupt number for keyboard interrupt
- kb_io_int_no equ 16h ; interrupt number for keyboard i/o
- capsmask equ 40h ; mask for kb_flag - capslock on
- nummask equ 20h ; mask for kb_flag - numlock on
- scrollmask equ 10h ; mask for kb_flag - scrolllock on
-
- ; hardware specific equates:
-
- inta01 equ 21h ; port adr. for 8259 ocw 1 and icw's 2, 3 & 4
- ena_irq1 equ 11111101b ; mask-enable irq1 (keyboard) interrupt
- dis_irq1 equ 00000010b ; mask-disable irq1 interrupt
- stat_6845 equ 03dah ; 6845 status register
-
- ; for beep routine:
-
- h_cycles equ 30
- h_half equ 300
- l_cycles equ h_cycles / 3
- l_half equ h_half * 3
- kb_ctl equ 61h
-
- page
-
- ;**************************************************************************
-
- int_vecs segment at 0
- int_vecs ends
-
- ;**************************************************************************
-
- bios_data segment at 40h
-
- org 17h
- kb_flag db ? ; holds state of caps- and numlock, etc.
-
- org 1ah ; keyboard buffer data area
- bios_buffer_head dw ? ; ptr to next char to be gotten
- bios_buffer_tail dw ? ; ptr to last char entered in buffer
- bios_kb_buffer dw 16 dup (?) ; keyboard buffer
- bios_kb_buffer_end label word ; end of buffer
-
- org 49h
- crt_mode db ? ; crt mode - mono/color, etc.
-
- org 80h
- bios_buffer_start dw ? ; in new bios, ptr to beginning of buf
- bios_buffer_end dw ? ; in new bios, ptr to end of buf
-
- bios_data ends
-
- ;**************************************************************************
-
- monoram segment at 0b000h ; monochrome ram
- monoram ends
-
- ;**************************************************************************
-
- colorram segment at 0b800h ; color/graphics ram
- colorram ends
-
- page
- ;**************************************************************************
-
- cseg segment
- assume cs:cseg,ds:cseg,es:nothing
-
- org 80h ; command line parms
- cmd_ct label byte ; number of chars. in command line
-
- org 100h ; for .COM file
- entry:
- jmp init ; ck for residency, init if not resident
-
- last_kb_flag db ? ; kb_flag when last entered routine
- buf_flag db false ; set to true when/if new keyboard buffer
- ; is installed
- orig_kb_int_vec dd ? ; pointer to orig. kb_int service routine
-
- ;**************************************************************************
- ;* This is the start of the new keyboard interrupt handler code.
- ;*
- ;* The numlock and capslock keys tend to bounce when hit quickly, and
- ;* this can cause another keyboard interrupt before the beeping is done
- ;* (resulting in unpleasant beeps) since the bios has already sent out an
- ;* eoi to the 8259.
- ;* To solve the problem, and to simplify the keyboard buffer manipulations
- ;* (assuming the buffer is installed), interrupts will not be re-enabled
- ;* before pushing the flags and calling the original routine. When we regain
- ;* control, interrupts will still be disabled, so we can mask off the keyboard
- ;* interrupt before re-enabling other interrupts.
-
- kb_int_hdlr proc ; entry point of keyboard (hardware)
- ; interrupt handler
-
- pushf ; (IRET will pop 3 words off the stack)
- call cs:orig_kb_int_vec ; kb_flag will be updated on return
-
- ; interrupts are disabled -- mask off just the keyboard interrupt and re-enable
-
- push ax ; save AX before using
- in al,inta01 ; get ocw1 from 8259
- or al,dis_irq1 ; turn on the mask bit
- out inta01,al ; write modified byte to 8259
- sti ; reenable ints
-
- push bx ; save other regs. used
- push cx ;
- push dx ;
- push si ;
- push ds ;
- push es ;
-
- mov ax,cs ; now establish data addressability
- mov ds,ax ; off of DS
- mov ax,bios_data ; and set ES to bios_data segment
- mov es,ax ;
-
- cmp buf_flag,false ; see if our keyboard buffer is in use
- je kb_int1 ; if not, continue
- call get_from_bios_buf ; else, check for char to move
-
- kb_int1:
- mov al,es:kb_flag ; get bios keyboard status flag
-
- ; check capslock state - if on, show reverse-video 'C' else show blank
-
- mov dx,blankword ; assume blank to be sent
- push dx ; save for later
- push dx ; and again..
- test al,capsmask ; al contains <kb_flag>
- jz cl1 ; if bit not set, nothing to do
- mov dx,capsword ; else send out the caps symbol
- cl1:
- mov bh,rowcaps ; row for cap symbol
- mov bl,colcaps ; column for cap symbol
- call dispchar ;
-
- ; check numlock state - if on, show reverse-video 'N' else show blank
-
- pop dx ; restore blankword
- test al,nummask ; check the flag
- jz nl1 ; if bit not set, nothing to do
- mov dx,numword ; else, send out the numlock symbol
- nl1:
- mov bh,rownums ; row for num symbol
- mov bl,colnums ; column for num symbol
- call dispchar ;
-
- ; check scroll lock state - if on, show reverse-video 'S' else show blank
-
- pop dx ; restore blankword
- test al,scrollmask ; check the flag
- jz sl1 ; if bit not set, nothing to do
- mov dx,scrollword ; else, send out the scroll lock symbol
- sl1:
- mov bh,rowscroll ; row for scroll symbol
- mov bl,colscroll ; column for scroll symbol
- call dispchar ;
-
- mov bl,last_kb_flag ; get last kb_flag
- cmp al,bl ; see if anything's changed
- je int_exit ; if not, go home
-
- ; else, check if caps-, num- or scroll-lock has changed state
-
- mov dl,capsmask ; capslock state changed?
- call chk_chg ;
- jnz got_diff ; if difference, check it out
-
- mov dl,nummask ; numlock state changed?
- call chk_chg ;
- jnz got_diff ; if difference, check it out
-
- mov dl,scrollmask ; scroll lock state changed?
- call chk_chg ;
- jz int_exit ; if not, return
-
- got_diff:
- jc beep_dn ; carry set if transition was from on to off
- call low_beep ; otherwise, beep low then high
- call high_beep ;
- jmp short int_exit ;
-
- beep_dn:
- call high_beep ; beep high then low
- call low_beep ;
-
- int_exit:
- mov last_kb_flag,al ; al has current flag - store as last flag
-
- pop es ; restore saved registers
- pop ds ;
- pop si ;
- pop dx ;
- pop cx ;
- pop bx ;
-
- ; now disable ints and turn the keyboard back on -- interrupts will be
- ; re-enabled when the flags are popped during our IRET
-
- cli ;
- in al,inta01 ; get ocw1 from 8259
- and al,ena_irq1 ; shut off the mask bit
- out inta01,al ; write modified byte to 8259
-
- pop ax ;
- iret ;
-
- kb_int_hdlr endp
-
- page
- ;**************************************************************************
- ;* compare current flag (in <al>) with last flag (in <bl>) using mask in <dl>
- ;* set zero flag if no change -- set carry flag if bit changed from 1 to 0
-
- chk_chg proc near
-
- push ax
- push bx
- and al,dl ; isolate bit in al
- and bl,dl ; do same for bl
- cmp al,bl ; carry will be set if curr. flag (al) is 0
- ; while last flag (bl) was 1
- pop bx
- pop ax
- ret
-
- chk_chg endp
-
- ;**************************************************************************
- ;* display char/attrib. in dl/dh at row/column in bh/bl of video ram page 0
- ;* supports b/w or color monitors, but row/column won't be right on 40
- ;* column displays
- ;* on entry, ES points to bios_data seg - all registers preserved
-
- dispchar proc near
-
- push ax ; save registers used
- push bx ;
- push di ;
- push es ; save extra seg
-
- mov ax,bytes_per_row ; num. bytes per row in video ram
- mul bh ; calc. row offset
- sub bh,bh ; make BH = 0
- shl bl,1 ; col * 2 = column offset
- add ax,bx ; AX = offset into video ram
- mov di,ax ; move offset into DI
-
- cmp es:crt_mode,7 ; 7 = mono. display
- jne co_80 ; no, then jump to color routine
- mov ax,monoram ; set ES to mono. ram segment
- mov es,ax ;
- mov ax,dx ; get char/attrib in AX
- stosw ; place in video ram
-
- dc_exit:
- pop es ; restore extra segment
- pop di ;
- pop bx ;
- pop ax ;
- ret
-
- ; -- display char. on color monitor. DX has char/attrib., DI points to
- ; offset in video ram. Must wait for horizontal retrace to avoid screwing
- ; up the display.
-
- co_80:
- mov ax,colorram ; set ES to color ram segment
- mov es,ax ;
- mov bx,dx ; save char/attrib. in BX
- mov dx,stat_6845 ; 6845 status reg port in DX
-
- co_80_1:
- in al,dx ; get 6845 status port
- test al,1 ; test bit 0 (on = horiz. retrace active)
- jnz co_80_1 ; loop until inactive
- cli ; no ints while waiting or writing
-
- co_80_2:
- in al,dx ; get status again
- test al,1 ; test for retrace active
- jz co_80_2 ; no? loop to wait until active
-
- mov ax,bx ; get char/attrib. into AX
- stosw ; place in video ram
- sti ; re-enable interrupts
-
- mov dx,bx ; restore entering value of DX
- jmp dc_exit ; and return
-
- dispchar endp
-
- ;**************************************************************************
- ;* from KEYLOC
-
- low_beep proc near
-
- push dx
- push bx
- mov bx,l_cycles
- mov dx,l_half
- call beep
- pop bx
- pop dx
- ret
-
- low_beep endp
-
- high_beep proc near
-
- push dx
- push bx
- mov bx,h_cycles
- mov dx,h_half
- call beep
- pop bx
- pop dx
- ret
-
- high_beep endp
-
- ;**************************************************************************
- ;* Adapted from BIOS Beep routine
- ;
- ; bx = # of cycles
- ; dx = length of half cycle
-
- beep proc near
-
- push ax
- push cx
- in al,kb_ctl
- push ax
- k65:
- and al,0FCh
- out kb_ctl,al
- mov cx,dx
- k66: loop k66
- or al,2
- out kb_ctl,al
- mov cx,dx
- k67: loop k67
- dec bx
- jnz k65
- pop ax
- out kb_ctl,al
- pop cx
- pop ax
- ret
-
- beep endp
-
- ;**************************************************************************
- ;* this is the end of the code which will be installed if the keyboard
- ;* buffer handler is NOT used -- kb_int_len will be used as the length
- ;* of code for the residency test in any case, since it is sufficient to
- ;* determine if the program has been installed in some form.
-
- kb_int_end label near ; area after this usable by dos if
- ; buffer not installed
-
- kb_int_len equ (offset kb_int_end - offset kb_int_hdlr)
-
- page
- ;**************************************************************************
- ;* the following data locations will be used for our replacement keyboard
- ;* buffer if it is installed
-
- our_buffer_head dw ? ; ptr to next char to be gotten
- our_buffer_tail dw ? ; ptr to last char entered in buffer
- our_kb_buffer dw 128 dup (?) ; keyboard buffer
- our_kb_buffer_end label word ; end of buffer
-
- ; This is essentially a copy of the bios keyboard i/o interrupt handler
- ; modified to check our buffer for chars rather than the bios buffer
- ; As in the bios routine, only AX and flags are changed.
-
- kb_io_int_hdlr proc far
-
- sti ; reenable ints
- push bx ;
- push si ;
- push ds ;
- push es ;
-
- mov bx,cs ; est. data addressability off of DS
- mov ds,bx ;
- mov bx,bios_data ; and set ES to bios_data segment
- mov es,bx ;
-
- or ah,ah ; ah=0
- jz k1 ; ascii read
- dec ah ; ah=1
- jz k2 ; ascii status
- dec ah ; ah=2
- jz k3 ; shift status
- jmp short int10_end ; invalid parameter, exit
-
- ;----- read the key to figure out what to do
-
- k1: ; ascii read
- sti ; interrupts back on during loop
- nop ; allow an interrupt to occur
- cli ; interrupts back off
- call ck_clr_bios_buf ; buf cleared (by other than kb_int)?
- mov bx,our_buffer_head ; get pointer to head of buffer
- cmp bx,our_buffer_tail ; test end of buffer
- jz k1 ; loop until something in buffer
- mov ax,[bx] ; get scan code and ascii code
- inc bx ; move pointer to next position
- inc bx ;
- cmp bx,offset our_kb_buffer_end ; at end of buffer?
- jne k5 ; no, continue
- mov bx,offset our_kb_buffer ; yes, reset to buf beginning
- k5:
- mov our_buffer_head,bx ; store value in variable
- jmp short int10_end ; return
-
- ;----- ascii status
-
- k2:
- cli ; interrupts off
- call ck_clr_bios_buf ; buf cleared (by other than kb_int)?
- mov bx,our_buffer_head ; get head pointer
- cmp bx,our_buffer_tail ; if equal (z=1) then nothing there
- mov ax,[bx] ;
- sti ; interrupts back on
- pop es ; recover registers
- pop ds ;
- pop si ;
- pop bx ;
- ret 2 ; throw away flags
-
- ;----- shift status
-
- k3:
- mov al,es:kb_flag ; get the shift status flags
-
- int10_end:
- pop es ;
- pop ds ;
- pop si ;
- pop bx ;
- iret ;
-
- kb_io_int_hdlr endp
-
- ;**************************************************************************
- ;* Called from our keyboard interrupt handler after the keystroke has been
- ;* processed by the bios (or by whomever is handling keyboard input).
- ;*
- ;* At this point, the keyboard interrupt is disabled, so no need to disable
- ;* interrupts when manipulating keyboard data.
- ;*
- ;* If there is a new character available, it will be checked for control-s and
- ;* control-c, and if either is found, our buffer will be cleared and that char.
- ;* will become the 1st and only char in it. Dos apparently checks only the
- ;* first position in the buffer for these two characters, and will not pause
- ;* or abort a command if anything else has been entered. Of course, ctrl-break
- ;* and ctrl-numlock will still work, but for those who came from a cp/m
- ;* background, it's an annoying feature.
- ;*
- ;* On entry: DS is set to our CS, ES points to the <bios_data> segment
- ;* AX,BX,SI changed.
-
- get_from_bios_buf proc near
-
- call ck_clr_bios_buf ;
- mov bx,offset bios_kb_buffer + 2 ; point past dummy entry
- cmp bx,es:bios_buffer_tail ; is there a char?
- je get_from_exit ; no, just exit
- mov es:bios_buffer_tail,bx ; set tail = head
- mov ax,es:[bx] ; get the char.
- cmp al,ctrl_s ; see if control-s
- jne get_from_0 ; no, continue
- call init_our_buf ; clear our buf
- get_from_0:
- cmp al,ctrl_c ; see if control-c
- jne get_from_1 ; no, continue
- call init_our_buf ; clear our buf
- get_from_1:
- mov si,our_buffer_tail ; get ptr to next pos in buffer
- mov bx,si ; save the value
- inc si ; bump to next position
- inc si ;
- cmp si,offset our_kb_buffer_end ; wrapped?
- jne get_from_2 ; no, continue
- mov si,offset our_kb_buffer ; else, set ptr to beginning of buf
-
- get_from_2:
- cmp si,our_buffer_head ; see if there's room in the buf
- jne get_from_3 ; continue if so
- call err_beep ; if not, beep, discard data
- ret ;
-
- get_from_3:
- mov [bx],ax ; store the value
- mov our_buffer_tail,si ; store ptr to next pos in buf
-
- get_from_exit:
- ret
-
- get_from_bios_buf endp
-
- ;* Check if the bios buffer has been cleared by seeing if tail is set
- ;* to the beginning of the buffer. Normally, tail is equal to either
- ;* beginning + 2 (if no data in buf) or beginning + 4 (if data).
- ;* If cleared, clear our buffer and reset the bios buffer to its normal
- ;* condition. At this point, interrupts are either disabled entirely,
- ;* or the keyboard interrupt has been masked off.
-
- ck_clr_bios_buf proc near
-
- cmp es:bios_buffer_head,offset bios_kb_buffer ; same?
- ja ck_clr_exit ; if not, buf not cleared
- call init_bios_buf ; else, reset bios buffer
- call init_our_buf ; and reset our buffer
- ck_clr_exit:
- ret
-
- ck_clr_bios_buf endp
-
- ; set bios buffer to start 1 position higher than normal -- can then check
- ; for reset by seeing if head has been moved to normal starting point.
-
- init_bios_buf proc near
-
- mov bx,offset bios_kb_buffer + 2 ;
- mov es:bios_buffer_head,bx ;
- mov es:bios_buffer_tail,bx ;
- ret
-
- init_bios_buf endp
-
- ; initialize our keyboard buffer
-
- init_our_buf proc near
-
- mov si,offset our_kb_buffer ; and reset our buffer
- mov our_buffer_head,si ;
- mov our_buffer_tail,si ;
- ret
-
- init_our_buf endp
-
- ; buffer-full beep -- essentially the same as the bios routine
-
- err_beep proc near
-
- push bx
- push dx
- mov bx,80h ; num. cycles for 1/12 second tone
- mov dx,48h ; half cycle time for tone
- call beep
- pop dx
- pop bx
- ret
-
- err_beep endp
-
- kb_io_int_end l(FΩ⌠(è█g(: ∙ ┼ ▌ê ≡F≡ O@ @g≡gè: <