;
;
;       Title:  VM INT 21 FILTER
;
;       Description: device driver to catch int 21 and route to OS/2
;
;       Design/Implementation Notes:
;
;       (1) To hook int 21h at initialization
;
;       (2) To act as the intermediary between real mode and protected
;           mode.
;
;       The driver itself responds to all requests by signalling done.
;
;       Modification History:
;           4/3/91    created     Mark Rogalski
;
;  B001  9/27/91  CP20 PTR AA04134  Mark Rogalski
;        Make check_map function public.
;
;  B002  9/27/91  CP20 PTR AA04213  Mark Rogalski
;        Add check for UNC "\\machine" names. If DOS network is started
;        then send request to DOS, else to OS/2.
;
; BN003 - 1/30/92  CP20 AA04471  Mark Rogalski                          ;BN003;
;         No longer need to use CurrentPDB variable, so remove code     ;BN003;
;
; BC004 - 02/07/92 CP20 B732885  Mark Rogalski                          ;BN004;
;         We should let DOS handle get DTA calls.                       ;BN004;
;
; BC005 - 02/10/92 CP20 B732823  Mark Rogalski                          ;BN005;
;         Tell OS/2 what current PDB is (for locking problems)          ;BN005;
;
; BN006 - 04/12/93 defect 66813  Lisa M. Primm                          ;BN006;
;         Allow Int21 Func5d subfunctions >=01 to pass through to DOS   ;BN006;
;
; 73436 - 09/07/93 defect 73436 Richard Mulder                          ;73436;
;         Add field Find_First_DTA for $FIND_FIRST                      ;73436;
;
; 75149 - 10/28/93 defect 75149 Change Team                             ;75149;
;         Add field fl_printer_output for $STD_PRINTER_OUTPUT           ;75149;
;
; BN007 - 01/05/94 defect 75510  John M. Hare                           ;BN007;
;         Test for Novell network to pass through to Novell's redirector;BN007;
;******************************************************************************
;

NAME    FSFILTER
TITLE   VM INT 21 FILTER

        .xlist
        include v86seg.inc
        include mvdm.inc
        include devhdr.inc
        include vmfilter.inc
        include dos3dev.inc
        include demver.inc
        include demsvc.inc
        include mvdmexp.inc
        .list

extrn  COMMAND:NEAR
extrn  v86init_Initialize:NEAR
extrn  VSFNFromHandle:NEAR
extrn  RMDevCheckName:NEAR
extrn  DOS_ABORT:NEAR
extrn  VMstart:NEAR

extrn  Dispatch:WORD
extrn  CURDRV:BYTE
extrn  CurrentPDB:WORD
;BD004 extrn  DMAADD:DWORD

CpuMode 386          ; This machine has at least this CPU

VMDD    SEGMENT DWORD PUBLIC 'CODE'

        assume cs:V86GROUP,ds:NOTHING,es:NOTHING

; NOTE   NOTE   NOTE   NOTE   NOTE   NOTE
;
; the following 4 entries must be left in this location unless the
; filter_in(stalled) function is changed correspondingly.
; checkword MUST be on a paragraph boundary!

        org     0

; Device Driver Header

            dd     -1                       ; pointer to next device
public attributes
attributes  dw      0                       ; attributes
            dw      offset VMstrategy
            dw      offset VMint
            db      'INT21F'                ; reserved

; Global variables go here

public checkword
checkword   dw      0
            db      'INT 21 Filter Program ver. 1.0'
public drivemap
drivemap    db      26 dup(0) ; a-z

public pOldInt20
public pOldInt21
public request
public DOS_DD_chain_head
public BLKCNT
public os2_handled
public find_first_flag
public search_first_flag
public fl_printer_output
public handle_map
public handle_count
public Find_First_DTA

pOldInt20          dd  ?               ; old 21 handler address
pOldInt21          dd  ?               ; old 21 handler address
request            dd  ?
DOS_DD_chain_head  dd  ?
check_type         db  ?
BLKCNT             db  0
os2_handled        db  0
find_first_flag    db  0
search_first_flag  db  0
fl_printer_output  db  0               ;                                  75149
wait_flag          db  0
Find_First_DTA     dd  ?               ;Address of DTA for $FIND_FIRST


handle_map         db  256 dup(0ffh)
handle_count       db  256 dup(0h)
fspec_buf          db  128 dup(?)
vm_save_dx         dw  ?
public vm_save_drive
vm_save_drive      db  ?

;*** VMstrategy - Device driver strategy routine
;
;       ENTRY   ES:BX request packet
;
;       FUNCTION: save address of request packet and return
;
public VMstrategy
VMstrategy proc far

        mov     word ptr cs:[request],bx
        mov     word ptr cs:[request+2],es
        ret
VMstrategy endp


;*** VMInt - Device driver interrupt routine
;
;       ENTRY   None
;
;       FUNCTION: handle DD init
;
public VMInt
VMInt   proc far

        pusha
        SaveReg <ds,es>
        lds     bx,cs:[request]        ; get request packet
        ASSUME ds:V86GROUP

        mov     word ptr [bx+3],8101h  ; set status (done,error,unknown unit)
        cmp     byte ptr [bx+2],0      ; is this init code?
        jnz     init_done              ; no? then return

        mov     word ptr [bx+3],100h   ; set status (done)
        mov     ax,0                   ; load es with segment 0
        mov     es,ax
        les     di,es:[24]             ; load es:di with int 6 vector
        cmp     byte ptr es:[di],63h   ; see if it points to ARPL instruction
        jne     not_v86                ; if not then do not install

        call    VMstart                ; else set-up everything
        jmp     init_done

not_v86:
        mov     byte ptr [bx+13],0     ; zero number of devices in req. packet
        mov     word ptr [bx+14],0     ; zero ending address
        mov     word ptr [bx+16],cs

init_done:
        RestoreReg <es,ds>
        popa
        ret

VMint   endp


;*** VMInt21 - VM Int 21 handler
;
;       This function catches int 21h's.
;
;       It routes os/2 specific functions.
;       All other functions are passed down the chain.
;
;       Since this lives in VM memory, we try to keep it as short
;       as possible.
;
;       ENTRY   AX = function call
;
;       GLOBAL EFFECTS
;               None
;
;       METHOD
;
;       Figure out which subfunction is being called, and go to
;       protect mode to execute function.  If it's not something
;       we should pay attention to, pass the call down the
;       chain.

public VMInt21
VMInt21 proc far
        assume cs:V86GROUP,ds:NOTHING,es:NOTHING

;BD004        sti                     ; Interrupts are okay now.
        cli                           ;BN004  DO NOT ALLOW INTERRUPTS YET!!

IF FILTER_DEBUG
        cmp     ah,2h
        je      pass_to_next
        cmp     ah,8h
        je      pass_to_next

        cmp     ah,29h                ; check if we want debugger invoked
        jne     over65                ; invoked by saying DB: at prompt
        cmp     byte ptr ds:[si],'D'
        jne     over65
        cmp     byte ptr ds:[si+1],'B'
        jne     over65
        cmp     byte ptr ds:[si+2],':'
        jne     over65
        mov     wait_flag,1

over65:
        cmp     wait_flag,0
        je      over70
        call    my_debugger
ENDIF

over70:
        push    bx
        xor     bx,bx           ; get function addr from table
        mov     bl,ah
        shl     bx,1
        cmp     cs:Dispatch[bx],0  ; if 0 then go to real int 21
        pop     bx
        je      short pass_to_next

        cmp     ah,5dh                                      ;BN006;
        jne     short its_ours                              ;BN006;
        cmp     al,00h                                      ;BN006;
        jne     short pass_to_next                          ;BN006;

its_ours:                                                   ;BN006;
        SaveReg <ax,bx>         ; PDB and DTA might have changed so get them
        mov     ah,51h
        pushf                          ; get current PDB from DOS
        call    [pOldInt21]
        cmp     cs:[CurrentPDB],bx     ; if the same then do function
        je      over80

        SaveReg <es,ds,dx>
        mov     cs:[CurrentPDB],bx     ;BC005 Save currentPDB
        mov     es,bx                  ;BN005 get currentPDB and
        SVC     SVC_DEMSETCURRENTPDB   ;BN005 tell OS/2 what it is

        mov     ah,2fh
        pushf
        call    cs:[pOldInt21]         ; get DTA address from DOS
;BD004        mov     word ptr cs:[DMAADD],bx
;BD004        mov     word ptr cs:[DMAADD+2],es
        push    es                     ; tell os2 where DTA is
        pop     ds
        mov     dx,bx
        SVC     SVC_DEMSETDTA
        RestoreReg <dx,ds,es>

over80: RestoreReg <bx,ax>
        jmp     COMMAND         ; else jump to our processor

pass_to_next:                   ; It's not ours; maybe somebody else will
        cli                     ; claim it.
        jmp     [pOldInt21]     ; to the next handler.

VMInt21 endp


IF FILTER_DEBUG

; debuger program to show me what INT 21's are being called
; ENTRY - NONE

my_debugger proc near

        push    ax
        call    print_number           ; print ax  (INT 21 function number)
        mov     al,','                 ; print comma
        call    print_char

get_inp:
        mov     ah,8h                  ; get keyboard input
        int     21h

        cmp     al,'c'                 ; is it for curr drive?
        je      cur_drive
        cmp     al,'r'                 ; is it req for regs?
        je      regs
        cmp     al,'a'                 ; is it req for actual pdb?
        je      get_pdb
        cmp     al,'o'                 ; is it req to turn off debug?
        jne     md10
        mov     wait_flag,0

md10:   pop     ax
        ret

cur_drive:
        mov     al,'C'
        call    print_char
        mov     al,'='
        call    print_char
        movzx   ax,cs:[CURDRV]
        call    print_number
        mov     al,';'
        call    print_char
        jmp     get_inp

get_pdb:
        push    bx
        mov     ah,51h
        pushf                   ; set up stack frame for call to DOS
        call    [pOldInt21]

        mov     al,'A'
        call    print_char
        mov     al,'='
        call    print_char
        mov     ax,bx
        call    print_number
        mov     al,';'
        call    print_char
        pop     bx
        jmp     get_inp

regs:
        mov     al,'R'
        call    print_char
        mov     al,'='
        call    print_char
        mov     ax,bx
        call    print_number
        mov     al,' '
        call    print_char
        mov     ax,cx
        call    print_number
        mov     al,' '
        call    print_char
        mov     ax,dx
        call    print_number
        mov     al,' '
        call    print_char
        mov     ax,ds
        call    print_number
        mov     al,':'
        call    print_char
        mov     ax,si
        call    print_number
        mov     al,' '
        call    print_char
        mov     ax,es
        call    print_number
        mov     al,':'
        call    print_char
        mov     ax,di
        call    print_number
        mov     al,' '
        call    print_char
        mov     ax,ss
        call    print_number
        mov     al,':'
        call    print_char
        mov     ax,sp
        call    print_number
        mov     al,';'
        call    print_char
        jmp     get_inp

my_debugger endp


; print a hex number on the screen
; ENTRY:  AX - number to print

public print_number
print_number proc near

        pushf
        push    dx
        push    cx
        push    ax

pn10:   mov     cl,12
        shr     ax,cl                  ; print first digit of command #
        call    print_digit

        pop     ax
        push    ax
        mov     al,ah
        and     al,0fh
        call    print_digit

        pop     ax                     ; print first digit of sub func
        push    ax
        mov     cl,4
        shr     al,cl
        call    print_digit

        pop     ax
        push    ax
        and     al,0fh
        call    print_digit

pn20:   pop     ax
        pop     cx
        pop     dx
        popf
        ret

print_digit:
        add     al,30h
        cmp     al,3ah
        jl      over1
        add     al,7
over1:  mov     dl,al
        mov     ah,02h
        int     21h
        ret

print_char:
        push    dx
        mov     dl,al
        mov     ah,2
        int     21h
        pop     dx
        ret

print_number endp

ENDIF


;*** VMInt20 - VM Int 20 handler
;
;       This function catches int 20h's.
;
;       It closes all the OS/2 files then passes
;       control down the chain.
;
;       ENTRY   None
;
;       GLOBAL EFFECTS
;               Closes OS/2 files
;

public VMInt20
VMInt20 proc far

        call    DOS_ABORT              ; close all OS/2 files
        jmp     [pOldInt20]            ; let DOS handle terminate

VMInt20 endp



;***DD  VMcheck_handle - VM FILTER routine to check file handle
;
;
;       ENTRY   BX - file handle
;
;       EXIT    BX - VSFN  or pop stack and return
;
;       METHOD
;
public VMcheck_handle
VMcheck_handle  proc near

        call    VSFNFromHandle          ; get SFN from JFT slot
        jc      dos_handle              ; return to DOS if error
        jz      dos_handle              ; if DOS handle return to DOS
        clc
        ret

dos_handle:
        mov     cs:os2_handled,0        ; let dos handle INT
        add     sp,2                    ; dump return address from stack
        stc                             ; make sure carry is set
        ret                             ; now return to COMMAND

VMcheck_handle  endp


;*** VMcheck routines - determine if INT should be routed to OS/2

public VMcheck_drive
public VMcheck_fname
public VMcheck_fcb
public VMcheck_ioctl
public VMcheck_extended
VMcheck proc near


;***DD  VMcheck_drive - VM FILTER routine to check if OS/2 drive
;
;       ENTRY   DL - drive number
;       EXIT    OS/2 drive: straight return.
;               DOS drive: return to COMMAND, os2_handled=FALSE
;
VMcheck_drive:                          ; entry point to check drive #
        push    ax
        mov     al,dl
        call    check_map               ; go check drive map
        jnc     os2_drive1
        call    check_dos               ; check if valid dos drive
        jnc     dos_drive1

os2_drive1:
        mov     dl,al                   ; save drive number for os2 use
        pop     ax                      ; restore bx
        ret

dos_drive1:
        mov     cs:os2_handled,0        ; let DOS handle this INT
        pop     ax                      ; restore ax
        add     sp,2                    ; dump return address (so we'll
        ret                             ; now return to COMMAND)


;***DD  VMcheck_ioctl - VM FILTER routine to check if OS/2 drive
;
;       ENTRY   BL - drive number
;       EXIT    OS/2 drive: straight return.
;               DOS drive: return to COMMAND, os2_handled=FALSE
;
VMcheck_ioctl:                          ; entry point to check drive for ioctl
        push    ax
        mov     al,bl
        call    check_map               ; go check drive map
        jnc     os2_drive2
        call    check_dos               ; check if valid dos drive
        jnc     dos_drive1

os2_drive2:
        mov     bl,al
        pop     ax
        ret


;***DD  VMcheck_fcb   - VM FILTER routine to check if OS/2 drive
;
;       ENTRY   DS:DX - address of FCB
;       EXIT    OS/2 drive: save drive number, map drive
;               DOS drive: return to COMMAND
;
VMcheck_fcb:                            ; entry point to check drive # in FCB
        push    ax
        push    bx
        mov     bx,dx                   ; get offset of fcb
        cmp     byte ptr ds:[bx],0ffh   ; see if extended FCB
        jne     cdo30                   ; no? go get drive #
        add     bx,7                    ; yes? add offset to bx
cdo30:  mov     al,byte ptr ds:[bx]     ; get drive #, (0=curr,1=a,..)
        call    check_map               ; go check drive map
        jnc     os2_drive3
        call    check_dos               ; check if valid dos drive
        jnc     dos_drive3

os2_drive3:
        xchg    byte ptr ds:[bx],al     ; save new drive number in fcb
        mov     cs:vm_save_drive,al     ; save old number in variable
        pop     bx
        pop     ax
        ret

dos_drive3:
        mov     cs:os2_handled,0        ; let DOS handle this INT
        pop     bx
        pop     ax
        add     sp,2                    ; dump return address (so we'll
        ret                             ; now return to COMMAND)


;***DD  VMcheck_extended - VM FILTER routine to check if OS/2 drive
;
;       ENTRY   DS:SI - address of ascii filespec
;       EXIT    OS/2 drive: straight return.
;               DOS drive: return to COMMAND
;
VMcheck_extended:                       ; entry point to check drive on extended open
        mov     cs:vm_save_dx,dx        ; save dx
        mov     dx,si                   ; put filespec offset in DX
        mov     cs:check_type,0
        jmp     short cdo50             ; go check drive letter


;***DD  VMcheck_fname - VM FILTER routine to check if OS/2 drive
;
;       ENTRY   DS:DX - address of ascii filespec
;       EXIT    OS/2 drive: straight return.
;               DOS drive: return to COMMAND
;
VMcheck_fname:
        mov     cs:check_type,1
cdo50:  SaveReg <ax,bx>
        mov     bx,dx                   ; get filespec offset from DX
        cmp     byte ptr ds:[bx+1],':'  ; see if drive is specified in name
        jne     use_current             ; no? then check for DOS device or UNC
        mov     al,byte ptr ds:[bx]     ; yes? get drive letter from filespec
        cmp     al,'A'                  ; check bounds of drive letter
        jl      dos_drive5              ; if bad letter, let DOS handle it
        cmp     al,'z'
        jg      dos_drive5
        sub     al,'A' - 1              ; subtract base  (a:=1,b:=2,...)
        cmp     al,20h                  ; if less than 32 it was a capital
        jl      cdo52                   ; so continue
        sub     al,20h                  ; else subtract 32 to capitalize
cdo52:  call    check_map               ; see if registered drive
        jnc     os2_drive5              ; yes? do mapping and send to os2
        call    check_dos               ; check to see if DOS knows it
        jnc     dos_drive5              ; yes? let DOS handle INT
        jmp     cdo55                   ; no? just send it to OS2 then

os2_drive5:
        add     al,'A' - 1              ; change drive number to letter
        mov     bx,dx                   ; get filespec offset
        cmp     byte ptr ds:[bx],al     ; does drive letter need to change?
        je      short cdo55             ; no? then return
        add     al,20h
        cmp     byte ptr ds:[bx],al     ; check lower case letter too
        je      short cdo55             ; return if match
        sub     al,20h

        SaveReg <cx,es,di,si>           ; changing drive letter so need to
        push    cs                      ; use our own buffer for file spec
        pop     es                      ; get segment of new file spec buffer
        mov     di,offset V86GROUP:fspec_buf
        stosb                           ; store new drive letter in buffer
        mov     si,bx                   ; ds:si = old fspec buffer
        inc     si                      ; don't copy drive letter
        mov     cx,127                  ; copy no more than what may be there
        repnz movsb                     ; copy fspec to new buffer
        RestoreReg <si,di,es,cx>

        push    cs                      ; now set up regs to point to new buf
        pop     ds
        mov     dx,offset V86GROUP:fspec_buf

cdo55:  cmp     cs:check_type,0         ; if extended name check then update si
        jne     short cdo60
        mov     si,dx                   ; move new buffer offset to si
        mov     dx,cs:vm_save_dx        ; restore dx
cdo60:  RestoreReg <bx,ax>
        ret

use_current:                            ; if request for curr drive
        cmp     word ptr ds:[bx],5c5ch  ; see if UNC name            ;BN002
        jne     cdo65                   ; no? skip over net check    ;BN002
        mov     ax,0B800h               ; see if DOS LAN installed   ;BN002
        int     2fh                     ; AL non zero if installed   ;BN002
        cmp     al,0                                                 ;BN002
        je      NOVELL_installed        ; yes? Now try for Novell installed ;BN002    ; BN007 ;
                                        ; before sending to OS/2                      ; BN007 ;
        jmp     dos_drive5              ; no? send request to DOS    ;BN002

NOVELL_installed:                                                                     ; BN007 ;
        SaveReg <cx>                    ; Save CX since this will be                  ; BN007 ;
                                        ; destroyed with return station number        ; BN007 ;
        mov     ax,0DC00h               ; Ask NOVELL for a station number             ; BN007 ;
                                        ; Station number > 0 == installed             ; BN007 ;
        xor     cx,cx                   ; Clear cx for returned station number        ; BN007 ;
        int     21h                     ; CX non zero if Novell installed             ; BN007 ;
        mov     ax,cx                   ; Save returned station number in AX          ; BN007 ;
                                        ; to allow for CX to be restored              ; BN007 ;
        RestoreReg <cx>                 ; Restore CX to original value                ; BN007 ;
        cmp     al,0                    ; if station number == 0 then                 ; BN007 ;
        je      cdo60                   ; yes? send request to OS/2                   ; BN007 ;
        jmp     dos_drive5              ; no? send request to DOS (Novell installed ) ; BN007 ;

cdo65:  SaveReg <cx,es,di,si>           ; first check if trying to open DOS
        call    RMDevCheckName          ; device
        RestoreReg <si,di,es,cx>
        jnc     dos_drive5              ; yes? then let dos handle open
                                        ; no? then check if current drive
        mov     al,0                    ; is registered  (0 means cur)
        call    check_map
        jnc     short cdo55             ; return if current drive is mapped

dos_drive5:
        mov     cs:os2_handled,0        ; let DOS handle this INT
        pop     bx                      ; restore bx
        pop     ax
        add     sp,2
        ret

;*** check_map  - internal VM FILTER routine to check if OS/2 drive
;
;       FUNCTION:  check drive map to see if mapped to OS/2
;
;       ENTRY   AL - drive number to check (0=curr,1=A,2=B,..)
;       EXIT    OS/2 drive: AL = mapped drive # (A=1,B=2,..), Carry clear
;               DOS drive: AL unchanged, carry set
;
public check_map                        ; BN001
check_map:                              ; entry for checking drive map
        push    bx
        movzx   bx,al                   ; load index register
        or      bl,bl                   ; test drive number, (0=curr,1=a,..)
        jnz     cdo10                   ; if for specific drive then skip over
        cmp     cs:[CURDRV],-1          ; else see if current drive is on dos
        je      dos_drive               ; yes? then let dos handle int
        jmp     short cdo15             ; else return (ok for os2 to use curr)

cdo10:  dec     bl                            ; subtract 1 to make array index
cdo11:  cmp     byte ptr cs:drivemap[bx],0    ; see if DOS drive letter
        jz      dos_drive                     ; yes? stop processing
        mov     al,byte ptr cs:drivemap[bx]   ; no? do drive re-map
        sub     al,'A' - 1                    ; make drive #  (A=1,B=2,..)
cdo15:  pop     bx
        clc                             ; return mapped os2 drive
        ret

dos_drive:
        pop     bx                      ; return that drive is not mapped
        stc
        ret

;*** check_dos  - internal VM FILTER routine to check if DOS drive
;
;       FUNCTION:  call DOS INT function to see if it recognizes drive
;
;       ENTRY   AL - drive number to check (0=curr,1=A,2=B,..)
;       EXIT    if DOS drive then carry clear
;
public check_dos
check_dos:                              ; entry for checking drive
        SaveReg <ax,bx,dx>
        mov     bl,al                   ; get drive number to check
        mov     ax,4409h
        pushf                           ; set up stack frame for DOS call
        call    [pOldInt21]             ; return here!
        RestoreReg <dx,bx,ax>           ; carry will be set if
        ret                             ; dos did not recognized drive letter


VMcheck endp

VMDD   ends

        end
