[net.micro.pc] Help on MASM/DOS-interrupt-handler question

dean@ucbruby.berkeley.edu (04/07/86)

[chomp chomp chomp...]

The assembly program below was written as an attempt to do a small, quick,
and dirty keyboard remapping routine for a friend.  It attempts to intercept
the DOS keyboard read/look/status interrupt (16h) and translate the character
and scan codes returned.  I attempted to intercept and translate both READ
and LOOK (non-destructive reads) function requests.  The routine essentially
never worked until I removed the check for LOOK requests (see the ";*****"
commented lines in the listing).  At present, the routine works, but only
without the LOOK request check.  

The symptoms it displayed until then were, I think, a bit bizarre and I would
like any help in interpreting them.  By using DEBUG and inserting debugging 
statements, I determined the following:
    1) When it failed, it failed at installation, either freezing the
       keyboard or causing the screen to fill with garbage.  The ROM keyboard
       routine was still working (I think), since the keyboard buffer would
       beep when full and ^C's echoed as "^C" on the screen.
    2) It failed at the moment the new interrupt routine was being inserted
       in the DOS interrupt vector table.
  * 3) Attempts to isolate the failure succeeded when all code except the
       JMP to the old keyboard routine were removed (it still failed if any
       code containing the PUSHF; CALL sequence was present).  Note that
       removal of the PUSHF; CALL sequence allowed proper *installation* of
       the interrupt, as well as proper execution of the routine (though
       of course, it now did nothing).
  * 4) In desperation I eventually tried removing the innocuous looking
       lines that checked for the "LOOK" function.  Then everything worked.

In summary, the presence of the PUSHF; CALL sequence *and* the test-for-LOOK
code in the replacement interrupt handler seem to cause the *installation*
of the handler to go bye-bye!

Someone please explain.  By mail if I am doing something patently stupid that
everyone notices, or maybe post it to the net if it is subtle and of general
interest.

Equipment and software used:
    Zenith Z-150 running MS-DOS 2.?  (same on a Z-158 running DOS 2.?)
    MASM 1.27
    MS-Link 2.40
    Exe2bin 1.5

_Many_ thanks to any who take the trouble to look this over!

    Dean Pentcheff, Dep't of Zoology, U of California, Berkeley, CA 94720
    dean@ucbruby

=========== MASM LISTING BEGINS HERE =============
page            ,132
; remap.asm
; Quick and dirty keyboard remapping
; Dean Pentcheff  4/86
; Interrupt shell based on: Wadlow, T. (1985) Writing desk accessories.
;     _Byte_ (Fall) 10(11):105-22.

dos_keyboard_io         equ     16h     ; keyboard i/o vector number
dos_function            equ     21h     ; dos function interrupt number
dos_terminate_resident  equ     27h     ; terminate-but-stay-resident interrupt
dos_read_keyboard       equ     00h     ; dos destructive keyboard read function
dos_look_keyboard       equ     01h     ; dos non-destr. keyboard read function
get_vector              equ     35h     ; dos get-interrupt-address function
set_vector              equ     25h     ; dos set-interrupt-address function

cseg    segment
        assume  cs:cseg,ds:cseg ; tell the assembler about segment registers
        org     100h            ; all .COM programs begin at 100h

start:
        jmp     initialize      ; run installation code first

;       All Data Definitions
;       --------------------
old_keyboard_io dd      ?       ; storage for old kbd int. handler
include         remap.def       ; include character and scan code map tables
                                ;   (two 256 byte tables: char_map_tab and
                                ;    scan_map_tab which map characters and
                                ;    scan codes)

;       New Keyboard Interrupt Routine
;       ------------------------------
keyboard_interceptor    proc far
        assume  cs:cseg,ds:cseg ; set default segments for assembler

        sti                     ; turn interrupts back on

;****** cmp     ah,dos_look_keyboard ; check for LOOK function...
;****** je      do_translate         ;   if so, prepare to translate
        cmp     ah,dos_read_keyboard ; check for READ function...
        jne     no_translate         ;   if not, just jump to old interrupt

do_translate:
        pushf                   ; simulate normal Keyboard Interrupt
        assume  ds:nothing
        call    old_keyboard_io

        cmp     al,00h          ; do we have a "special" keypress?
        je      return          ; if yes, don't screw around with it
        pushf                   ; save clobberable stuff
        push    bx
        mov     bx,offset char_map_tab ; set up for character translate
        xlat    char_map_tab    ; translate character
        xchg    ah,al           ; swap character and scan code
        mov     bx,offset scan_map_tab ; set up for scan code translate
        xlat    scan_map_tab    ; translate scan code
        xchg    ah,al           ; unswap character and scan code
        pop     bx              ; restore clobbered stuff
        popf

return:
        iret                    ; return to caller

no_translate:
        assume  ds:nothing
        jmp     old_keyboard_io ; do normal interrupt, let it return to caller

keyboard_interceptor    endp

;       Installation Code (discarded after installation)
;       ------------------------------------------------
initialize:
        mov     bx,cs                   ; make sure dseg=cseg
        mov     ds,bx
        mov     al,dos_keyboard_io      ; get old kbd interrupt address via
        mov     ah,get_vector           ;   dos function call
        int     dos_function
        mov     old_keyboard_io,bx      ; store old kbd interrupt address
        mov     old_keyboard_io[2],es

        mov     bx,cs                   ; DS<-current segment for dos fn call
        mov     ds,bx
        mov     dx,offset keyboard_interceptor ; DX<-new kbd interrupt offset
        mov     al,dos_keyboard_io      ; set new kbd interrupt address via
        mov     ah,set_vector           ;   dos function call
        int     dos_function

        mov     bx,cs                   ; set up to discard unneeded code
        mov     ds,bx                   ;   set data segment appropriately
        mov     dx,offset initialize    ;   set offset above which to discard
        int     dos_terminate_resident  ; terminate but leave handler resident

cseg ends
        end start
====== MASM LISTING ENDS HERE ==================================

perkins@bnrmtv.UUCP (Henry Perkins) (04/09/86)

> The assembly program below was written as an attempt to do a small, quick,
> and dirty keyboard remapping routine for a friend.  It attempts to intercept
> the DOS keyboard read/look/status interrupt (16h) and translate the character
> and scan codes returned.

[lots of assembly code, and request for enlightenment, deleted]

A basic problem with your interrupt service routine is that is mucks
about with the flags before passing them on to the original routine.  You
do STI and CMP instructions, then PUSHF -- this passes flags substantially
different from what was intended.  You should instead do something like:

	PUSH    BP
	MOV     BP,SP
	PUSH    [BP+6]          ; Get the original flags off the stack
-- 

{hplabs,amdahl,3comvax}!bnrmtv!perkins          --Henry Perkins