|
|
page ,132 if 0
/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
vwipxspx.asm
Abstract:
Contains handlers for 16-bit (DOS) netware IPX/SPX emulation. Creates TSR
Contents: start InstallationCheck InstallVdd InstallInterruptHandlers VwIpxEntryPoint VwIpxDispatcher VwIpx7ADispatcher DispatchWithFeeling VwIpxEsrFunction
Author:
Richard L Firth (rfirth) 30-Sep-1993
Environment:
DOS Real mode only
Revision History:
30-Sep-1993 rfirth Created
--*/
endif
; ; DOS include files ; .xlist .xcref include ..\..\..\..\public\sdk\inc\isvbop.inc ; NTVDM BOP mechanism include dossym.inc ; includes MS-DOS version etc include pdb.inc ; PSP defines include syscall.inc ; AssignOper include segorder.inc ; load order of 'redir' segments include debugmac.inc ; debug display macros include asmmacro.inc ; jumps which may be short or near include messages.inc ; internationalisationable (prestidigitation) messages .cref .list
InitStack segment stack para 'stack'
dw 256 dup (?)
InitStack ends
InitDataStart
bad_ver_msg db NLS_MSG_001,c_CR,c_LF BAD_VER_MSG_LEN equ $-bad_ver_msg db '$' ; for INT 21/09 display string
already_loaded_msg db NLS_MSG_002,c_CR,c_LF ALREADY_LOADED_MSG_LEN equ $-already_loaded_msg
cannot_load_msg db NLS_MSG_003,c_CR, c_LF CANNOT_LOAD_MSG_LEN equ $-cannot_load_msg
; ; strings used to load/dispatch NWIPXSPX.DLL ;
DllName db "VWIPXSPX.DLL",0 InitFunc db "VwInitialize",0 DispFunc db "VwDispatcher",0
InitDataEnd
InitCodeStart assume cs:InitCode assume ds:nothing assume es:nothing assume ss:nothing
public start start proc near
; ; when we start up we could be on any old PC - even an original, so don't ; assume anything other than a model-T processor ;
.8086
; ; Set the data segment while we're at it - all paths set it sooner ; or later. NOTE: es will point to the PSP until we change it! ;
mov dx,InitData mov ds,dx
assume ds:InitData
; ; first off, get the DOS version. If we're not running on NT (VDM) then this ; TSR's not going to do much, so exit. Exit using various methods, depending ; on the DOS version (don't you hate compatibility?) ;
mov ah,30h int 21h jc ancient_version ; version not even supported
; ; version is 2.0 or higher. Check it out. al = major#, ah = minor# ;
cmp al,major_version jne invalid_version
; ; okay, we're at least 5.0. But are we NT? ;
mov ax,3306h int 21h jc invalid_version ; ? cmp bl,5 jne invalid_version cmp bh,50 jne invalid_version
; ; what do you know? We're actually running on NT (unless some evil programmer ; has pinched int 21h/30h and broken it!). Enable minimum instruction set ; for NTVDM (286 on RISC). ;
.286c
; ; perform an installation check. Bail if we're there dude ((C) Beavis & Butthead) ;
call InstallationCheck jnz already_here ; nope - IPX/SPX support installed already
; ; We should find some way of deferring loading the 32-bit DLL until an ; IPX/SPX function is called, to speed-up loading. However, if we later find we ; cannot load the DLL, it may be too late: there is no way of consistently ; returning an error and we cannot unload the TSR ;
call InstallVdd ; returns IRQ in BX jc initialization_error call InstallInterruptHandlers
assume es:nothing
; ; free the environment segment ;
mov es,es:[PDB_environ] mov ah,49h int 21h ; free environment segment
; ; finally terminate and stay resident ;
mov dx,ResidentEnd sub dx,ResidentStart ; number of paragraphs in resident code add dx,10h ; additional for PSP (PDB) mov ax,3100h int 21h ; terminate and stay resident
; ; here if the MS-DOS version check (Ah=30h) call is not supported ;
ancient_version: mov dx,InitData mov ds,dx
assume ds:InitData
mov dx,offset bad_ver_msg mov ah,9 ; cp/m-style write to output int 21h
; ; safe exit: what we really want to do here is INT 20H, but when you do this, ; CS must be the segment of the PSP of this program. Knowing that CD 20 is ; embedded at the start of the PSP, the most foolproof way of doing this is ; to jump (using far return) to the start of the PSP ;
push es xor ax,ax push ax retf ; terminate
; ; we are running on a version of DOS >= 2.00, but its not NT, so we still can't ; help. Display the familiar message and exit, but using a less programmer- ; hostile mechanism ;
invalid_version: mov dx,offset bad_ver_msg mov cx,BAD_VER_MSG_LEN jmp short print_error_message_and_exit
; ; if we cannot initialize 32-bit support (because we can't find/load the DLL) ; then put back the hooked interrupt vectors as they were when this TSR started, ; display a message and fail to load the redir TSR ;
initialization_error: mov dx,offset cannot_load_msg mov cx,CANNOT_LOAD_MSG_LEN jmp short print_error_message_and_exit
; ; The DOS version's OK, but this TSR is already loaded ;
already_here: mov dx,offset already_loaded_msg mov cx,ALREADY_LOADED_MSG_LEN
print_error_message_and_exit: mov bx,1 ; bx = stdout handle mov ah,40h ; write to handle int 21h ; write (cx) bytes @ (ds:dx) to stdout mov ax,4c01h ; terminate program int 21h ; au revoir, cruel environment
start endp
; *** InstallationCheck ; * ; * Test to see if this module is already loaded ; * ; * ENTRY nothing ; * ; * EXIT ZF = 0: loaded ; * ; * USES AX ; * ; * ASSUMES nothing ; * ; ***
InstallationCheck proc mov ax,7a00h int 2fh or al,al ret InstallationCheck endp
; *** InstallVdd ; * ; * Load VWIPXSPX.DLL into the NTVDM process context ; * ; * ENTRY nothing ; * ; * EXIT CF = 1: error ; * CF = 0: VWIPXSPX loaded ok ; * AX = VDD handle ; * BX = IRQ used by call-back functions (ESR) ; * ResidentCode:VddHandle updated ; * ResidentCode:IrqValue updated ; * ; * USES AX, BX, SI, DI ; * ; * ASSUMES nothing ; * ; ***
InstallVdd proc push ds push es mov ax,InitData mov ds,ax
assume ds:InitData
mov es,ax mov si,offset DllName ; ds:si = library name mov di,offset InitFunc ; es:di = init function name mov bx,offset DispFunc ; ds:bx = dispatcher function name
RegisterModule ; returns carry if problem
mov si,ResidentCode mov ds,si
assume ds:ResidentCode
mov VddHandle,ax mov IrqValue,bx pop es
assume es:nothing
pop ds
assume ds:nothing
ret InstallVdd endp
; *** InstallInterruptHandlers ; * ; * Sets the interrupt handlers for all the ints we use - 2F, 7A ; * ; * ENTRY BX = IRQ for call-backs ; * ES = PSP segment ; * ; * EXIT Old2FHandler contains the original interrupt 2F vector ; * Old7AHandler contains the original interrupt 7A vector ; * OldIrqHandler contains original IRQ vector ; * ; * USES AX, BX, CX, DX ; * ; * ASSUMES nothing ; * ; ***
InstallInterruptHandlers proc push es ; PSP segment - destroyed by INT 21/35h push ds mov dx,ResidentCode mov ds,dx
assume ds:ResidentCode
; ; get and set call-back IRQ ;
mov ah,35h mov al,bl mov cl,bl ; cl = IRQ number int 21h mov word ptr OldIrqHandler,bx mov word ptr OldIrqHandler+2,es mov al,cl mov ah,25h mov dx,offset ResidentCode:VwIpxEsrFunction int 21h
; ; get and set 2F handler ;
mov ax,352Fh int 21h mov word ptr Old2FHandler,bx mov word ptr Old2FHandler+2,es mov dx,offset ResidentCode:VwIpxEntryPoint mov ax,252Fh int 21h
; ; get and set 7A handler ;
mov ax,357Ah int 21h mov word ptr Old7AHandler,bx mov word ptr Old7AHandler+2,es mov dx,offset ResidentCode:VwIpx7ADispatcher mov ax,257Ah int 21h pop ds ; restore segment registers
assume ds:nothing
pop es
assume es:nothing
ret InstallInterruptHandlers endp
InitCodeEnd
page
; ; code from here on will be left in memory after initialisation ;
ResidentCodeStart
assume cs:ResidentCode assume ds:nothing assume es:nothing assume ss:nothing
Old2FHandler dd ? Old7AHandler dd ? OldIrqHandler dd ?
IrqValue dw ?
VddHandle dw ?
; *** VwIpxEntryPoint ; * ; * The INT 2Fh handler that recognizes the Netware IPX request code (7A). ; * Also chains INT 2F/AX=1122 ; * ; * ENTRY AX = 7A00h ; * ; * EXIT AL = 0FFh ; * ES:DI = address of routine to call when submitting IPX/SPX ; * requests ; * ; * USES ; * ; * ASSUMES nothing ; * ; ***
VwIpxEntryPoint proc cmp ax,7a00h jne @f mov di,cs mov es,di mov di,offset VwIpxDispatcher dec al iret
; ; not 7A00h. Check for 1122h (IFSResetEnvironment). If yes, then this is DOS ; calling the IFS chain to notify that the app is terminating. When we have ; notified the DLL, chain the IFS request ;
@@: cmp ax,7affh jne try1122 mov di,cs mov es,di mov di,offset VwIpxDispatcher or bx,bx jz @f mov cx,8000h mov si,7 iret @@: mov cx,14h mov si,200h iret
try1122:cmp ax,1122h jne @f
; ; DOS Calls INT 2F/AX=1122 for every terminating app, including this one. We ; can't differentiate between a TSR and a non-TSR. Let the DLL handle it ;
push ax push bx push cx mov ah,51h int 21h mov cx,bx ; cx = PDB of terminating program/TSR mov bx,-1 ; bx = dispatch code mov ax,VddHandle ; ax = VDD handle DispatchCall pop cx pop bx pop ax @@: jmp Old2FHandler ; chain int 2F VwIpxEntryPoint endp
; *** VwIpxDispatcher ; * ; * All DOS IPX/SPX calls are routed here by the netware libraries. Just ; * BOP on through to the other side ; * ; * This routine just transfers control to 32-bit world, where all work is ; * done ; * ; * ENTRY BX = netware IPX/SPX dispatch code ; * others - depends on function ; * ; * EXIT depends on function ; * ; * USES depends on function ; * ; * ASSUMES nothing ; * ; ***
VwIpxDispatcher proc far pushf ; apparently we don't modify flags call DispatchWithFeeling popf ret VwIpxDispatcher endp
; *** VwIpx7ADispatcher ; * ; * Older Netware apps make the call to IPX/SPX via INT 7A. Same function ; * as VwIpxDispatcher ; * ; * This routine just transfers control to 32-bit world, where all work is ; * done ; * ; * ENTRY BX = netware IPX/SPX dispatch code ; * others - depends on function ; * ; * EXIT depends on function ; * ; * USES depends on function ; * ; * ASSUMES nothing ; * ; ***
VwIpx7ADispatcher proc call DispatchWithFeeling iret VwIpx7ADispatcher endp
; *** DispatchWithFeeling ; * ; * Performs the dispatch for VrIpxDispatcher and VrIpx7ADispatcher. Checks ; * requested function for return code in AX: either returns value in AX ; * or restores AX to value on input ; * ; * This routine just transfers control to 32-bit world, where all work is ; * done ; * ; * ENTRY BX = netware IPX/SPX dispatch code ; * others - depends on function ; * ; * EXIT depends on function ; * ; * USES depends on function ; * ; * ASSUMES 1. Dispatch codes are in range 0..255 (ie 0 in BH) ; * ; ***
DispatchWithFeeling proc push bp push ax ; caller value
; ; some APIs (IPXOpenSocket, IPXScheduleIPXEvent, SPXEstablishConnection, and ; others...) pass a parameter in AX. Since AX is being used for the VDD ; handle, we have to commandeer another register to hold our AX value. BP is ; always a good candidate ;
mov bp,ax ; grumble, mutter, gnash, gnash push cx ; required if IPXOpenSocket push bx ; dispatch code or bx,bx ; IPXOpenSocket? jz @f ; yus ma'am cmp bl,3 ; IPXSendPacket? jz @f ; yus ma'am again jmp short carry_on_dispatching ; ooo-err missus
; ; IPXOpenSocket et IPXSendPacket: We need an extra piece of info - the PDB of ; the process making this request. This is so we can clean-up at program ; termination ;
@@: push bx mov ah,51h ; get DOS PDB int 21h ; this call can be made any time mov cx,bx pop bx
carry_on_dispatching: mov ax,VddHandle DispatchCall mov bp,sp
; ; BX and [BP] will be the same value except for SPXInitialize which is the only ; function that returns something in BX ;
xchg bx,[bp] ; bx = dispatch code, [bp] = returned bx
; ; if this call returns something in AX (or AL) don't pop the AX value we pushed. ; If not a call which returns something in AX then restore the caller's AX. You ; can rest assured some assembler programmer has made use of the fact that some ; calls modify AX and the others leave it alone (presumably...?) ;
or bl,bl ; 0x00 = IPXOpenSocket jz @f cmp bl,2 ; 0x02 = IPXGetLocalTarget jz @f cmp bl,4 ; 0x04 = IPXListenForPacket jz @f cmp bl,6 ; 0x06 = IPXCancelEvent jz @f cmp bl,8 ; 0x08 = IPXGetIntervalMarker jz @f cmp bl,10h ; 0x10 = SPXInitialize jz spx_init cmp bl,11h ; 0x11 = SPXEstablishConnection jz @f cmp bl,15h ; 0x15 = SPXGetConnectionStatus jz @f cmp bl,1ah ; 0x1A = IPXGetMaxPacketSize jz @f pop cx ; original dispatch code pop cx ; original cx pop ax ; original ax pop bp ; original bp ret
; ; here if this call returns something in AX/AL ;
@@: pop cx ; original dispatch code pop cx ; original cx pop bp ; don't restore AX pop bp ret
; ; here if the call was SPXInitialize which returns values in AX, BX, CX, DX ;
spx_init: pop bx ; bx = major/minor SPX version # pop bp ; caller cx - NOT restored pop bp ; caller ax - NOT restored pop bp ; caller bp - restored ret DispatchWithFeeling endp
; *** VwIpxEsrFunction ; * ; * This routine makes the call to the ESR as defined in the ECB. We must ; * set up our stack, save the registers (except SS & SP), then call the ; * ESR. ; * ; * Control will not be transferred here for an ECB which has a NULL ESR ; * field ; * ; * ENTRY AL = 0 for AES or 0FFh for IPX ; * ES:SI = ECB address ; * ; * EXIT depends on function ; * ; * USES depends on function ; * ; * ASSUMES nothing ; * ; ***
VwIpxEsrFunction proc
; ; Novell documentation states all registers except SS and SP are saved before ; calling ESR and that INTERRUPTS ARE DISABLED ;
pusha push ds push es mov ax,VddHandle mov bx,-2 DispatchCall ; get ECB jc @f call dword ptr es:[si][4] ; branch to the ESR mov al,20h out 0a0h,al ; clear slave pic out 20h,al ; " master " pop es pop ds popa iret @@: pop es pop ds popa jmp OldIrqHandler VwIpxEsrFunction endp
ResidentCodeEnd
end start
|