home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
assemblr
/
library
/
sampler0
/
kbfix.asm
< prev
next >
Wrap
Assembly Source File
|
1984-06-25
|
33KB
|
1,044 lines
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è: <