title  "OS/2 16B asm special routines"
;++
;
; Copyright (c) 1991  Microsoft Corporation
;
; Module Name:
;
;   dll16.asm
;
; Abstract:
;
;   This module contains callbacks and dispatchers that help implementing
;   some of the 16b OS2 1.X APIs that cannot be done by C code.
;
; Author:
;
;   Yaron Shamir (YaronS) 30-May-1991
;
; Revision History:
;
;--

DCT_RUNABLE equ 0               ;  Create thread in a runable state
DCT_RUNABLE_HIDDEN equ 2        ;  Create thread in a runable state but with
                                ;  a high number (see os2v20.h)
.386p

EXTRN   _Save32Esp@4:PROC
EXTRN   _DosCreateThread@20:PROC
EXTRN   _LDRDoscallsSel:WORD
EXTRN   _DosExit@8:PROC
EXTRN   _DosExitList@8:PROC
EXTRN   _Od2Start16Stack:DWORD
EXTRN   _Od2ExitListInProgress:DWORD
EXTRN   _Od2Start16DS:DWORD
EXTRN   _DosFlagProcess@20:PROC
EXTRN   _DosSendSignal@12:PROC
EXTRN   _DosHoldSignal@8:PROC
EXTRN   _Od2GetTebInfoSeg@0:PROC
EXTRN   _Od2CheckForCritSectOrSuspend@0:PROC
EXTRN   _Od2GetInfoSegWasCalled:DWORD
EXTRN   _Od2GetThread1TebBackup@0:PROC

_TEXT   SEGMENT DWORD USE32 PUBLIC 'CODE'
        ASSUME  CS:FLAT, DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

;
; The Thread Starter is dispatched from the os2 server
; It get us to the 16b code, stack, DS and ES of the new thread.
; It takes as a parameter (ebp+8) a pointer to the 16 stack frame as
; created by the 16b thunk of DosCreateThread (doscalls.asm)
;
                public  _Od2ThreadStarter16
_Od2ThreadStarter16 proc

;
; Save the esp for later use by the thread when it will call APIs
;
        mov     eax, esp
        push    eax
        call    _Save32Esp@4
        call    _MoveInfoSegintoTeb
;
;       Put selector of doscalls into AX
;
        mov     ax,_LDRDoscallsSel; Selector to Doscalls

; Now fetch the 16b context frame from the new stack
; the stack frame is created by Dos16CreateThread below
;
        mov     ebx, [esp+4]
;
;       ebx points at
;               IP:CS (of the user's start procedure)
;               ES:DS
;               SP:SS   (they inturn point to ES)
;
        lss     sp, [ebx - 26]

;
;       Restore 16-bit registers
;
        db      066h    ; force two byte pop
        pop     di
        db      066h    ; force two byte pop
        pop     si
        db      066h    ; force two byte pop
        pop     cx
        db      066h    ; force two byte pop
        pop     bx
        db      066h    ; force two byte pop
        pop     dx
                        ;
                        ; For segment registers ES, DS, we need to verify
                        ; selectors before we fix them
                        ;
        mov     bp,ax   ; save ax (doscalls selector)
        db      066H    ; force two byte pop
        pop     ax      ; ax<-saved ES
        verr    ax      ; see if the value is still valid!
        jz      SetES
        xor     ax,ax
SetES:
        mov     es,ax
;
        db      066H    ; force two byte pop
        pop     ax      ; ax<-saved DS
        verr    ax      ; see if the value is still valid!
        jz      SetDS
        xor     ax,ax
SetDS:
        mov     ds,ax
;
;
;       Before we return to the new thread, we need to setup
;       That if it returns (instead of calling DosExit) -
;       it goes into DosExitStub Thunk which will call
;       DosExit (EXIT_THREAD, 0)
;

        mov     ax,bp
        mov     bp,sp
        mov     [bp+6],ax      ; Segment
        xor     ax, ax
        mov     [bp+4],ax      ; offset 0 in Doscalls.dll
        xor     ebp,ebp

;
;       Now jump by far return
;
        db      66h, 0cah       ; 16-bit retf
        dw      0               ; no params


_Od2ThreadStarter16 endp

;
; Dos16CreateThread copies relevant info from the saved 16b frame
; as was setup in the thunk to DosCreateThread, then
; calls DosCreateThread (the 32 bit version, dlltask.c) with &TID.
;
; APIRET
; Dos16CreateThread(
;       PFNTHREAD pfnFunction(VOID),
;       PTID16 ptidThread,
;       PCHAR pbThrdStack)
;
;
                public  _Dos16CreateThread@12
_Dos16CreateThread@12 proc

;
; 16 bit segmented params pushed by apps
;
; ebx+40   pfnFun
; ebx+36   pTid
; ebx+32   pbStack
;
; 16 bit seg registers pushed by thunk
;
; ebx+24 ES
; ebx+26 DS
;

        push    ebp
        mov     ebp,esp

;
; now also (flat parameters called by the thunk)
;
; ebp+8 pfnFun
; ebp+12 pTid
; ebp+16 pbStack
;

;
; makeup a 16 bit saved state for ThreadStarter16
; we must put this on the new stack, otherwise it can
; be washed out if the calling thread is scheduled before
; the new thread
;
        cmp     word ptr[ebp+16],0 ; check for 64K stack (offset = 0)
        jnz     @F
        inc     word ptr[ebp+18]   ; sel++ to prevent underflow
@@:
        push    ecx
        mov     eax, [ebp+16]

        sub     eax,4           ; save space on stack for DosExit return
        sub     eax,2
        mov     cx, [ebx+34]    ; new thread's CS
        mov     [eax], cx
        sub     eax,2
        mov     cx, [ebx+32]    ; new thread's IP
        mov     [eax], cx
        sub     eax,2
        mov     cx, [ebx+18]    ; new thread's DS
        mov     [eax], cx
        sub     eax,2
        mov     cx, [ebx+16]    ; new thread's ES
        mov     [eax], cx
        sub     eax,2
        mov     cx, [ebx+6]     ; new thread's DX
        mov     [eax], cx
        sub     eax, 2
        mov     cx, [ebx+8]     ; new thread's BX
        mov     [eax], cx
        sub     eax, 2
        mov     cx, [ebx+10]    ; new thread's CX
        mov     [eax], cx
        sub     eax, 2
        mov     cx, [ebx+12]    ; new thread's SI
        mov     [eax], cx
        sub     eax, 2
        mov     cx, [ebx+14]    ; new thread's DI
        mov     [eax], cx
        sub     eax, 2
        mov     cx, [ebx+26]    ; new thread's SS
        mov     [eax], cx
        sub     eax,2
        mov     cx, [ebx+24]
        sub     cx,22           ; for DI:SI:CX:BX:DX:ES:DS:IP:CS:DOSEXIT
        mov     [eax], cx       ; new thread's SP
        pop     ecx

;
; Now call DosCreateThread (32b)
; The 'ThreadParameter' parameter is setup to point to the frame above
;

; StackSize  - set to default (4K for now)
;

        push    1                       ; will be rounded up

; Flags - set to be runnable immediately
;
        push    DCT_RUNABLE

; Thread Parameter
;
        push    dword ptr [ebp+16]      ; the new 16 bit stack

; pfnFun
;
        push    offset FLAT:_Od2ThreadStarter16

; pTid
;
        push    dword ptr [ebp+12]

        call    _DosCreateThread@20      ; call 32-bit version

        leave
        ret 12                             ; return to thunk

_Dos16CreateThread@12 endp

;
; PMNT16CreateThread copies relevant info from the saved 16b frame
; as was setup in the thunk to PMNTCreateThread, then
; calls DosCreateThread (the 32 bit version, dlltask.c) with &TID.
; The diffrence betwin PMNT16CreateThread and Dos16CreateThread is that
; PMNT16Create thread pushes DCT_RUNABLE_HIDDEN and Dos16CreateThread pushes
; DCT_RUNABLE
;
; APIRET
; PMNT16CreateThread(
;       PFNTHREAD pfnFunction(VOID),
;       PTID16 ptidThread,
;       PCHAR pbThrdStack)
;
;
                public  _PMNT16CreateThread@12
_PMNT16CreateThread@12 proc

;
; 16 bit segmented params pushed by apps
;
; ebx+40   pfnFun
; ebx+36   pTid
; ebx+32   pbStack
;
; 16 bit seg registers pushed by thunk
;
; ebx+24 ES
; ebx+26 DS
;

        push    ebp
        mov     ebp,esp

;
; now also (flat parameters called by the thunk)
;
; ebp+8 pfnFun
; ebp+12 pTid
; ebp+16 pbStack
;

;
; makeup a 16 bit saved state for ThreadStarter16
; we must put this on the new stack, otherwise it can
; be washed out if the calling thread is scheduled before
; the new thread
;
        push    ecx
        mov     eax, [ebp+16]

        sub     eax,4           ; save space on stack for DosExit return
        sub     eax,2
        mov     cx, [ebx+34]    ; new thread's CS
        mov     [eax], cx
        sub     eax,2
        mov     cx, [ebx+32]    ; new thread's IP
        mov     [eax], cx
        sub     eax,2
        mov     cx, [ebx+18]    ; new thread's DS
        mov     [eax], cx
        sub     eax,2
        mov     cx, [ebx+16]    ; new thread's ES
        mov     [eax], cx
        sub     eax,2
        mov     cx, [ebx+6]     ; new thread's DX
        mov     [eax], cx
        sub     eax, 2
        mov     cx, [ebx+8]     ; new thread's BX
        mov     [eax], cx
        sub     eax, 2
        mov     cx, [ebx+10]    ; new thread's CX
        mov     [eax], cx
        sub     eax, 2
        mov     cx, [ebx+12]    ; new thread's SI
        mov     [eax], cx
        sub     eax, 2
        mov     cx, [ebx+14]    ; new thread's DI
        mov     [eax], cx
        sub     eax, 2
        mov     cx, [ebx+26]    ; new thread's SS
        mov     [eax], cx
        sub     eax,2
        mov     cx, [ebx+24]
        sub     cx,22           ; for DI:SI:CX:BX:DX:ES:DS:IP:CS:DOSEXIT
        mov     [eax], cx       ; new thread's SP
        pop     ecx

;
; Now call DosCreateThread (32b)
; The 'ThreadParameter' parameter is setup to point to the frame above
;

; StackSize  - set to default (4K for now)
;

        push    1                       ; will be rounded up

; Flags - set to be runnable immediately
;
        push    DCT_RUNABLE_HIDDEN

; Thread Parameter
;
        push    dword ptr [ebp+16]      ; the new 16 bit stack

; pfnFun
;
        push    offset FLAT:_Od2ThreadStarter16

; pTid
;
        push    dword ptr [ebp+12]

        call    _DosCreateThread@20      ; call 32-bit version

        leave
        ret 12                             ; return to thunk

_PMNT16CreateThread@12 endp


        public  _DosExitStub@0
_DosExitStub@0    proc

        push    0
        push    0
        call    _DosExit@8
        leave
        ret 0

_DosExitStub@0    endp

        public  _DosExitProcessStub@0
_DosExitProcessStub@0     proc

        push    0
        push    1
        call    _DosExit@8

        leave
        ret 0

_DosExitProcessStub@0     endp

                public  _Od2JumpTo16ExitRoutine@8
_Od2JumpTo16ExitRoutine@8   proc

        mov     bx, [esp+6]
        lsl     ax,bx                      ;// see if the value is still valid!
        jz      GoodRoutine

        ;
        ;  The CS of the exit routine had become invalid, call DosExitList
        ;  to skip this routine
        ;
        pop     eax                     ;// Return addresss (ignore)
        pop     eax                     ;// ExitRoutine address (ignore)
        pop     eax                     ;// ExitReason code (ignore)

                                        ;// push now parameters for DosExitList
        push    0                       ;//
        push    3                       ;// EXLST_EXIT
        call    _DosExitList@8

GoodRoutine:
        mov     _Od2ExitListInProgress, 1
        call    _MoveInfoSegintoTeb
        pop     ecx                     ;// Return addresss (ignore)
        pop     ecx                     ;// ExitRoutine address
        pop     edx                     ;// ExitReason code
        xor     ebp,ebp
        lss     sp,_Od2Start16Stack
        mov     eax,_Od2Start16DS
        mov     ds,ax
        push    dx                      ;// call (ExitRoutine)(ExitReason)
        push    0                       ; Force GP if returning from the ExitList routine
        push    ecx
        db      066h, 0cbh              ;// do a 16-bit retf

_Od2JumpTo16ExitRoutine@8   endp


;  Entry point for 16-bit DosFlagProcess
;  Setup call to C code by passing parameters plus pointer to 16-bit registers
;
;  APIRET
;  DosFlagProcess(
;       ULONG pid,
;       ULONG fScope,
;       ULONG usFlagNum,
;       ULONG usFlagArg)
;
; ebp+8 pid
; ebp+12 fScope
; ebp+16 usFlagNum
; ebp+20 usFlagArg

                public  DosFlagProcess16@16
DosFlagProcess16@16        proc

        push    ebp
        mov     ebp,esp
        push    ebx                     ; pointer to 16-bit stack
        push    dword ptr [ebp+20]      ; usFlagArg
        push    dword ptr [ebp+16]      ; usFlagNum
        push    dword ptr [ebp+12]      ; fScope
        push    dword ptr [ebp+8]       ; pid
        call    _DosFlagProcess@20
        pop     ebp
        ret 16

DosFlagProcess16@16        endp

;  Entry point for 16-bit DosSendSignal
;  Setup call to C code by passing parameters plus pointer to 16-bit registers
;
;  APIRET
;  DosSendSignal(
;       ULONG pid,
;       ULONG usSigNumber
;       )
;
; ebp+8 pid
; ebp+12 usSigNumber

                public  DosSendSignal16@8
DosSendSignal16@8 proc

        push    ebp
        mov     ebp,esp
        push    ebx                     ; pointer to 16-bit stack
        push    dword ptr [ebp+12]      ; usSigNumber
        push    dword ptr [ebp+8]       ; pid
        call    _DosSendSignal@12
        pop     ebp
        ret 8

DosSendSignal16@8 endp

;  Entry point for 16-bit DosHoldSignal
;  Setup call to C code by passing parameters plus pointer to 16-bit registers
;
;  APIRET
;  DosHoldSignal(
;       ULONG fDisable
;       )
;
; ebp+8 fDisable

                public  DosHoldSignal16@4
DosHoldSignal16@4 proc

        push    ebp
        mov     ebp,esp
        push    ebx                     ; pointer to 16-bit stack
        push    dword ptr [ebp+8]       ; fDisable
        call    _DosHoldSignal@8
        pop     ebp
        ret 4

DosHoldSignal16@4 endp

                public  _SaveFSSeg@4
_SaveFSSeg@4 proc
        push    edi
        mov     edi, [esp+8]
        mov     eax,dword ptr fs:0
        mov     dword ptr [edi],eax
        mov     eax,dword ptr fs:4
        mov     dword ptr [edi+4],eax
        mov     eax,dword ptr fs:8
        mov     dword ptr [edi+8],eax
        mov     eax,dword ptr fs:12
        mov     dword ptr [edi+12],eax
        mov     eax,dword ptr fs:16
        mov     dword ptr [edi+16],eax
        mov     eax,dword ptr fs:20
        mov     dword ptr [edi+20],eax
        mov     eax,dword ptr fs:24
        mov     dword ptr [edi+24],eax
        mov     eax,dword ptr fs:28
        mov     dword ptr [edi+28],eax
        mov     eax,dword ptr fs:32
        mov     dword ptr [edi+32],eax
        mov     eax,dword ptr fs:36
        mov     dword ptr [edi+36],eax
        pop     edi
        ret     4
_SaveFSSeg@4 endp

                public _RestoreFSSeg@4
_RestoreFSSeg@4 proc
        push    esi
        mov     esi, [esp+8]
        mov     eax, dword ptr [esi+36]
        mov     dword ptr fs:36, eax
        mov     eax, dword ptr [esi]
        mov     dword ptr fs:0, eax
        mov     eax, dword ptr [esi+4]
        mov     dword ptr fs:4, eax
        mov     eax, dword ptr [esi+8]
        mov     dword ptr fs:8, eax
        mov     eax, dword ptr [esi+12]
        mov     dword ptr fs:12, eax
        mov     eax, dword ptr [esi+16]
        mov     dword ptr fs:16, eax
        mov     eax, dword ptr [esi+20]
        mov     dword ptr fs:20, eax
        mov     eax, dword ptr [esi+24]
        mov     dword ptr fs:24, eax
        mov     eax, dword ptr [esi+28]
        mov     dword ptr fs:28, eax
        mov     eax, dword ptr [esi+32]
        mov     dword ptr fs:32, eax
        pop     esi
        ret     4
_RestoreFSSeg@4 endp

                public  _MoveInfoSegintoTeb
_MoveInfoSegintoTeb    proc

                                        ;
                                        ; We are using this entry point
                                        ; to suspend threads going to 16
                                        ; bit when DosEnterCritSect is
                                        ; in power, and no signal processing
                                        ;
        push    ebp
        mov     ebp, esp
        push    eax
        push    ebx
        push    ecx
        push    edx
        call    _Od2CheckForCritSectOrSuspend@0
        cmp     _Od2GetInfoSegWasCalled, 0
        je      minfo2

        call    _Od2GetTebInfoSeg@0     ; eax contains pointer to InfoSeg
        cmp     dword ptr [eax+36],0
        jnz     minfo2                  ; We are already in 16 bit

        push    eax
        add     eax,40


        push    eax
        call    _SaveFSSeg@4            ;Save TEB backup copy

        call    _RestoreFSSeg@4

minfo2:
        pop     edx
        pop     ecx
        pop     ebx
        pop     eax
        pop     ebp
        ret

_MoveInfoSegintoTeb endp

;
; IMORTANT !!!
; This routine is used in thunk. EBX register must not be used inside it. This
; value will be used by signal handler as pointer to 16bit stack in the case that
; it will interrupt thread1 in thunk entry.
;
                public  _RestoreTeb
_RestoreTeb    proc

        cmp     _Od2GetInfoSegWasCalled, 0
        je      rteb2
        cmp     dword ptr fs:36,0
        jnz     rteb2                 ; We are already in 32 bit

        push    eax
        push    esi
rtebContinue1:
        mov     esi, dword ptr fs:32
rtebContinue2:

        mov     eax, dword ptr [esi]
        mov     dword ptr fs:0, eax
        mov     eax, dword ptr [esi+4]
        mov     dword ptr fs:4, eax
        mov     eax, dword ptr [esi+8]
        mov     dword ptr fs:8, eax
        mov     eax, dword ptr [esi+12]
        mov     dword ptr fs:12, eax
        mov     eax, dword ptr [esi+16]
        mov     dword ptr fs:16, eax
        mov     eax, dword ptr [esi+20]
        mov     dword ptr fs:20, eax
        mov     eax, dword ptr [esi+24]
        mov     dword ptr fs:24, eax
        mov     eax, dword ptr [esi+28]
        mov     dword ptr fs:28, eax
        mov     eax, dword ptr [esi+32]
        mov     dword ptr fs:32, eax
        mov     eax, dword ptr [esi+36]
        mov     dword ptr fs:36, eax

        pop     esi
        pop     eax
rteb2:
        ret

_RestoreTeb endp

; Restore TEB for exit list dispatcher. The address of TEB backup will be taken
; from thread1 structure.
; This function is the spouse of RestoreTeb and it is similar. The only difference that
; we try to get TEB save block directly from the thread structure.

                public  _RestoreTebForThread1
_RestoreTebForThread1    proc

        cmp     _Od2GetInfoSegWasCalled, 0
        je      rteb2
        cmp     dword ptr fs:36, 0        ;CID thread in TEB
        jnz     rteb2

        push    eax
        push    esi
        push    ebx
        push    ecx
        push    edx
        call    _Od2GetThread1TebBackup@0 ;fs:32 can be overwritten with
                                          ;enviroment pointer
        pop     edx
        pop     ecx
        pop     ebx
        test    eax, eax
        jz      rtebContinue1   ;we failed to find thread1, use
                                ;the saved pointer.
        mov     esi, eax
        jmp     rtebContinue2
_RestoreTebForThread1 endp

                public  _Od2GetFSSelector@0
_Od2GetFSSelector@0    proc

        mov     ax, fs
        ret

_Od2GetFSSelector@0 endp

;
;VOID
;Od2JumpTo16NetBiosPostDispatcher(
;    IN PVOID    pUsersPostRoutine,      // CS:IP format                              EBP+8
;    IN PVOID    UserStackFlat,          // Flat pointer to user stack                EBP+12
;    IN USHORT   UserStackSize,          // User stack size                           EBP+16
;    IN SEL      UserStackSel,           // Data selector to user stack               EBP+20
;    IN SEL      UserStackAlias,         // Code selector to user stack               EBP+24
;    IN PVOID    pNcb,                   // 16 bit SEG:OFF ptr to NCB                 EBP+28
;    IN UCHAR    NcbRetCode              // return code from NCB                      EBP+32
;    );
;
; This routine is used to call a user's 16 bit net bios post routine.
; It thunks to the 16 bit code, and thunks back on user's return.
;
; Parameters:
;       pUsersPostRoutine -- 16 bit far ptr to user's routine (which should end with a far return)
;       UserStackFlat -- a 32 bit flat pointer to a 16-bit stack for the user to use
;       UserStackSize -- a 16 bit value specifying the size of the stack
;       UserStackSel -- a 16 bit selector to the segment containing the stack
;       UserStackAlias -- a 16 bit code selector alias for the stack selector
;       pNcb -- a 16 bit far ptr to be passed to user's routine in ES:BX
;       NcbRetCode -- a byte to be passed to user's routine in AL (with AH = 0)
;
; Operation:
;
;  On the top of the user's stack it prepares a 24 byte area that looks like this:
;
;    A. 8 bytes -- a 32 bit far jump instruction to NetBiosDispatcherReturnPoint.
;                  this will be used by the user to return.
;    B. 8 bytes -- an aligned 48 bit pointer saving the current 32 bit stack pointer
;                  prior to entry into the user's stack and routine
;    C. 4 bytes -- a 16 bit far pointer to the point in the stack designated by A above.
;                  This is the return address prepared for the user.  When he RETF's on this
;                  address he will jump to area A which contains a jump instruction back to
;                  our code.  Note that the selector in this pointer is a code alias for the
;                  stack segment.
;    D. 4 bytes -- The pointer pUsersPostRoutine.  We RETF on this pointer in order to jump to
;                  the user's routine.
;
;  When the stack is ready, it loads the registers and the new stackpointer, and then does a far
;  return to jump to the user's routine.
;  On return from the user's routine, the stack is restored back to its original state, we clean
;  up, and return.
;


                public  _Od2JumpTo16NetBiosPostDispatcher@28
_Od2JumpTo16NetBiosPostDispatcher@28 proc

        push    ebp
        mov     ebp, esp

; save registers

        push    esi
        push    edi
        push    ebx
        push    es
        push    ds

;
; Save the esp for later use by the thread when it will call APIs
;
        mov     eax, esp
        push    eax
        call    _Save32Esp@4
        call    _MoveInfoSegintoTeb

;       get esi to point to end of user's stack

        mov     esi, dword ptr [ebp+12]
        mov     word ptr [ebp+18], 0
        add     esi, dword ptr [ebp+16]

;       put a 32 bit far jmp NetBiosDispatcherReturnPoint on user's stack

        mov     word ptr [esi-8], 0ea66h
        mov     dword ptr [esi-6], offset NetBiosDispatcherReturnPoint
        mov     word ptr [esi-2], cs

;       save my own stack pointer

        mov     dword ptr [esi-16], esp
        mov     word ptr [esi-12], ss

;       set up return address for user

        mov     ax, word ptr [ebp+24]
        mov     word ptr [esi-18], ax
        mov     ax, word ptr [ebp+16]
        sub     ax, 8
        mov     word ptr [esi-20], ax

;       set up user's post routine address on user stack so we can RET to it

        mov     eax, dword ptr [ebp+8]
        mov     dword ptr [esi-24], eax

;       set up ES:BX and AX for user

        les     bx, dword ptr [ebp+28]
        movzx   ax, byte ptr [ebp+32]

;       set up stack pointer for user

        sub     word ptr [ebp+16], 24
        lss     esp, [ebp+16]

;       jump to user's routine (with a 16 bit far return)

        dw      0cb66h

NetBiosDispatcherReturnPoint:

; comes here after user routine finishes (returns with 16 bit far ret)

; start off by restoring my original stack pointer

        movzx   ebp, sp
        lss     esp, [ebp]
        pop     ds
        pop     es

;

        call    _RestoreTeb

; restore original registers, and return to caller

        pop     ebx
        pop     edi
        pop     esi

        pop     ebp
        ret     28

_Od2JumpTo16NetBiosPostDispatcher@28 endp

;
; A utility routine to verify a segment for write.
; returns:
;   1 if segment reachable
;   0 if segment not reachable
; Implemented using inline i386 VERW instruction
;
; int
; IsLegalSelector(
;                 unsigned short aSelector)
;
PUBLIC  _IsLegalSelector
_IsLegalSelector PROC
        push    ebp
        mov     ebp, esp
        mov     eax, 1            ; assume Selector is reachable
        verw    WORD PTR [ebp+8]  ; verify the selector
        je      addr_ok           ; exit if reachable
        mov     eax, 0            ; address not reachable
addr_ok:
        leave
        ret
_IsLegalSelector ENDP

;
; IMPORTANT !!!
; Changing the code below (Od2SetSegmentRegisters and Od2Continue) check the follows:
; - Od2GetSegmentRegisters (dlltask.c)
; - Od2PrepareEnterToSignalHandler (dlltask.c)
; - Od2JumpTo16SignalDispatch (ldrstart.asm)
; - Entry flat and Exit flat (doscalls.asm)
; - Os2SignalGetThreadContext (srvxcpt.c)
;

        public _Od2SetSegmentRegisters@0
_Od2SetSegmentRegisters@0 proc
        mov	eax,ecx
	shr	eax,16		; ES
	verr	ax		; see if the value is still valid!
	jnz	FixES
	mov	es,ax
	jmp	ESFixed
FixES:
	xor	ax,ax
	mov	es,ax
ESFixed:
	mov	eax,edx
	shr	eax,16		; DS
	verr	ax		; see if the value is still valid!
	jnz	FixDS
	mov	ds,ax
	jmp	DSFixed
FixDS:
	xor	ax,ax
	mov	ds,ax
DSFixed:
        ret
        public _Od2SetSegmentRegistersEnd@0
_Od2SetSegmentRegistersEnd@0:
_Od2SetSegmentRegisters@0 endp

;
; The susbstitute for NtContinue. Return to the interrupted context.
; Parameter : pContext  <- esp+4
;             ret       <- esp
; pContext :
;       ES      ;0
;       DS      ;4
;       EDI     ;8
;       ESI     ;12
;       EBX     ;16
;       EDX     ;20
;       ECX     ;24
;       EAX     ;28
;       EBP     ;32
;       EIP     ;36
;       CS      ;40
;       EFLAGS  ;44
;       ESP     ;48
;       SS      ;52
; There are 3 types of context:
;       - 32bit stack, 32bit code       (32bit)
;       - 16bit stack, 16bit code       (16bit)
;       - 16bit stack, 32bit code       (thunk)
;
public  _Od2Continue@4
_Od2Continue@4 proc

        mov     eax,dword ptr [esp+4]           ;eax - pointer to context
        ;
        ; restore registers that we will never use more
        ;
        mov     ebp,dword ptr [eax+32]          ;EBP
        mov     edi,dword ptr [eax+8]           ;EDI
        mov     esi,dword ptr [eax+12]          ;ESI

        ;
        ; check if the interrupted context is 32bit
        ;
        cmp     word ptr [eax+52],23H           ;SS
        jne     Od2Continue16bit

    ;
    ; 32bit
    ;
        ; |         |
        ;  ---------    <- ESP of interrupted context
        ; | EIP     |
        ; | EAX     |
        ;  ---------
        ;
        mov     ebx,dword ptr [eax+16]          ;EBX

        sub     dword ptr [eax+48],8            ;ESP-8
        mov     edx,dword ptr [eax+48]          ;ESP
        mov     ecx,dword ptr [eax+28]          ;EAX
        mov     dword ptr [edx], ecx
        mov     ecx,dword ptr [eax+36]          ;EIP
        mov     dword ptr [edx+4],ecx

        mov     edx,dword ptr [eax+20]          ;EDX
        mov     ecx,dword ptr [eax+24]          ;ECX
        push    dword ptr [eax+44]              ;EFlags
        popfd

        mov     esp,dword ptr [eax+48]          ;ESP

        pop     eax
        ret
    ;
    ; 16bit or thunk
    ;
        ; 16 bytes of the 16bit stack beyond it's top will be used:
        ; |             |
        ;  -------------   <- SP of interrupted context
        ; | ret address |
        ; | EAX         |
        ; | EFlags      |
        ;  -------------   <- temprorary stack top (EDX)
Od2Continue16bit:
        sub     word ptr [eax+48],12            ;SP - substruct 12 bytes
        ;
        ; calculate the flat address of the temprorary stack top
        ;
        mov     ebx,dword ptr [eax+52]          ;SS
        shr     bx,3
        add     bx,3800H
        shl     ebx,16
        mov     bx,word ptr [eax+48]            ;EBX == temprorary stack top

        ;
        ; Set the values of EAX, DS and ES to 16bit stack
        ;
        mov     ecx,dword ptr [eax+28]          ;EAX
        mov     dword ptr [ebx+4],ecx
        mov     ecx,dword ptr [eax+44]          ;EFlags
        mov     dword ptr [ebx],ecx
        mov     dx,word ptr [eax+4]             ;DS
        cmp     dx,23H
        je      Od2ContinueThunkFlat
        shl     edx,16
        mov     cx,word ptr [eax]               ;ES
        cmp     dx,23H
        je      Od2ContinueThunkFlat
        shl     ecx,16

        ;
        ; check if the interrupted context was 16bit or thunk
        ;
        cmp     word ptr [eax+40],1bH           ;CS
        je      Od2ContinueThunk

    ;
    ; 16 bit
    ;
        ;
        ; Copy CS:IP to the 16bit stack
        ;
        mov     cx,word ptr [eax+40]            ;CS
        mov     word ptr [ebx+10],cx
        mov     cx,word ptr [eax+36]            ;IP
        mov     word ptr [ebx+8],cx

        mov     ebx,dword ptr [eax+16]          ;EBX
        mov     cx,word ptr [eax+24]            ;ECX
        mov     dx,word ptr [eax+20]            ;EDX

        public  _Od2ContinueStartBorder@0
_Od2ContinueStartBorder@0:
        lss     esp,[eax+48]                    ;ESP:SS
        call    _Od2SetSegmentRegisters@0
        popfd
        pop     eax
        db	66h, 0cah		        ; 16-bit retf
	dw	0			        ; 16-bit parameters

Od2ContinueThunk:

    ;
    ; Thunk to 16 bit
    ;
        ;
        ; Copy EIP to the 16bit stack
        ;
        mov     ecx,dword ptr [eax+36]          ;EIP
        mov     dword ptr [ebx+8],ecx

        mov     ebx,dword ptr [eax+16]          ;EBX
        mov     cx,word ptr [eax+24]            ;ECX
        mov     dx,word ptr [eax+20]            ;EDX

        lss     esp,[eax+48]                    ;ESP:SS
        call    _Od2SetSegmentRegisters@0
        popfd
        pop     eax
        ret
public  _Od2ContinueEndBorder@0
_Od2ContinueEndBorder@0:

Od2ContinueThunkFlat:

    ;
    ; Return to Thunk that use flat registers (or Entry flat or part of JumpTo16)
    ;
        ;
        ; Copy EIP to the 16bit stack
        ;
        mov     ecx,dword ptr [eax+36]          ;EIP
        mov     dword ptr [ebx+8],ecx

        mov     ebx,dword ptr [eax+16]          ;EBX
        mov     ecx,dword ptr [eax+24]          ;ECX
        mov     edx,dword ptr [eax+20]          ;EDX

        lss     esp,[eax+48]                    ;ESP:SS
        popfd
        pop     eax
        ret
_Od2Continue@4 endp

IFDEF PMNT

EXTRN   _PMNT_DosLockSeg@4:PROC
EXTRN   _PMNT_DosUnlockSeg@4:PROC
EXTRN   _LNotesPatchLock@4:PROC
EXTRN   _LNotesPatchUnlock@4:PROC

        public _DosLockSeg@4
_DosLockSeg@4 proc
        push    ebx
        push    dword ptr [esp+8]
        call    _PMNT_DosLockSeg@4
        test    eax,eax
        jnz     DosLockSegExit_1
        call    _LNotesPatchLock@4
        xor     eax,eax
        ret     4
DosLockSegExit_1:
        pop     ebx
        ret     4
_DosLockSeg@4 endp

        public _DosUnlockSeg@4
_DosUnlockSeg@4 proc
        push    dword ptr [esp+4]
        push    ebx
        call    _LNotesPatchUnlock@4
        call    _PMNT_DosUnlockSeg@4
        ret     4
_DosUnlockSeg@4 endp

ENDIF

_TEXT   ends
        end