windows-nt/Source/XPSP1/NT/ds/nw/vwipxspx/tsr/vwipxspx.asm
2020-09-26 16:20:57 +08:00

712 lines
18 KiB
NASM

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