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