;      SCCSID = @(#)thkrt.asm   6.6 92/03/18
;**************************************************************************
;       thk32rt.asm
;
;       Runtime library for 0:32 -> 16:16 thunks
;
;
;       This file contains runtime library routines for thunks. Routines
;       here include:
;
;       THK32ALIASMEM
;       THK32FREEALIAS
;       THK32HANDLEBOUNDARY
;       THK32ALLOCVARLEN
;       THK32HANDLESTRING
;       THK32DEALLOC
;       THK32MULTIPLESTRING
;
;***  Change History:                                             ***
;***   noto  11/18/93  Define which segment for Page Tuning DLL   ***
;***                                                              ***
;***   ChangeTeam 08/22/95 Added routine to handle parameters     ***
;***                       which can be a single string or a      ***
;***                       multiple string- THK32MULTIPLESTRING   ***
;***                       D129178                                ***

        .386

include basemaca.inc
include bsememf.inc
include bsexcpt.inc
include bsetib.inc
include error.inc

ALLOCMEM_FLAGS  equ     PAG_READ+PAG_WRITE+PAG_COMMIT+OBJ_TILE


CODE32_TUNE1  SEGMENT DWORD USE32 PUBLIC 'CODE'
EXTRN   DOS32GETINFOBLOCKS:NEAR                                      ; CA-ANZBUG
EXTRN   DOS32FREEMEM:NEAR
EXTRN   DOS32ALLOCMEM:NEAR
EXTRN   THK32FREEBLOCK:NEAR
CODE32_TUNE1  ENDS

CODE32_TUNE2  SEGMENT DWORD USE32 PUBLIC 'CODE'
EXTRN   THK32ALLOCBLOCK:NEAR
CODE32_TUNE2  ENDS

CODE32_TUNE4  SEGMENT DWORD USE32 PUBLIC 'CODE'
CODE32_TUNE4  ENDS

CODE32 SEGMENT DWORD USE32 PUBLIC 'CODE'
EXTRN   DOS32ALIASMEM:NEAR
CODE32 ENDS

CODE32  SEGMENT

;** thk32AliasMem       Alias a byte aligned region of memory
;
;
;
; Purpose:
;
;       This routine will create a linear alias to a byte aligned object.
;       The Dos32AliasMem function requires a page aligned address.
;       The thunk layer must be able to deal with byte aligned addresses.
;       thk32AliasMem will convert a byte aligned call into an equivalent
;       page aligned call by maskin the lower twelve bits of the address
;       to be aliased, and adding them to the length of the alias.
;
;       ENTRY
;               ESI     ulAlias         Address to be aliased
;               ECX     ulSize          Length to be aliased in bytes
;               EDI     pulAliasBuf     Buffer for new alias
;
;       EXIT
;               if success
;                       EDI = byte aligned alias
;                       EAX = 0
;               if error
;                       EAX = Error code from Dos32AliasMem
;                       EDI = UNDEFINED
;
;       USES    EAX EDI
;************************************************************************

        ASSUME  CS:FLAT,DS:FLAT,ES:FLAT,FS:NOTHING,GS:NOTHING

Procedure       THK32ALIASMEM,near

        push    ecx             ;Save length

        mov     eax,00000FFFh
        and     eax,esi         ;eax = offset from page

        push    eax             ;Save offset from start of page

        push    OBJ_TILE        ;Flag to request selector tiling
        push    edi             ;Buffer address for alias

; eax holds number of bytes between start of page and address to be
; aliased. Add this to the length to be aliased. Then, round the
; length to be aliased up to the next page.

        add     ecx,eax         ;Add offset to length
        add     ecx,0fffh       ;Round up to next page
        and     ecx,0fffff000h  ;Page align length
        push    ecx             ;Push length of alias

        xor     eax,esi         ;Page align address to be aliased
        push    eax             ;Push address to be aliased

        call    DOS32ALIASMEM   ;Create Linear Alias
        add     esp,16

        pop     ecx             ;Get Offset from page
        add     [edi],ecx       ;Add to linear alias
        mov     edi,[edi]       ;Get linear alias into edi

        pop     ecx             ;Restore length
        ret

EndProc THK32ALIASMEM

CODE32  ENDS




CODE32_TUNE4  SEGMENT

;** THK32FREEALIAS      Free a byte aligned alias
;
; Purpose:
;
;       This routine will free a linear alias. The purpose of this
;       function is to page align the address for pasing to FreeMem
;
;       ENTRY:
;               ESI     ulAlias         Alias handle to be free
;
;       EXIT:   EAX = error code from DOS32FREEMEM
;
;       USES:   EAX ESI
;
;************************************************************************

        ASSUME  CS:FLAT,DS:FLAT,ES:FLAT,FS:NOTHING,GS:NOTHING

Procedure THK32FREEALIAS,near

        and     esi,0fffff000h          ;Round off lower 12 bits
        push    esi                     ;Push page aligned address
        call    DOS32FREEMEM
        add     esp,4
        ret

EndProc THK32FREEALIAS

CODE32_TUNE4  ENDS




CODE32_TUNE2  SEGMENT

;** THK32ALLOCVARLEN    Allocate a variable length item for thunk routine
;
; This routine will allocate a variable length data item. This differs from
; THK32HANDLEBOUNDARY in that it always allocates memory, and never
; aliases it.
;
; ENTRY:
;       ECX     holds length of allocation
;
; EXIT:
;       if Sucess
;               EAX = 0
;               EDX = type of allocation performed
;
;                       1 = Stack Allocation
;                       0 = BMP allocation
;                      -1 = DosAllocMem
;
;       if Error
;               EAX = Error code from DOS32ALLOCBLOCK or DOS32ALLOCMEM
;
; USES: EAX,EDX,EDI
;
;************************************************************************
        ASSUME  CS:FLAT,DS:FLAT,ES:FLAT,FS:NOTHING,GS:NOTHING,SS:FLAT

Procedure THK32ALLOCVARLEN,near

; Check to see if the item is <= 32 bytes long. If so, then allocate
; space off the user stack.

        cmp     ecx,32          ;Maximum size allocated on stack
        ja      short thk10

        pop     eax             ;Save return address
        sub     esp,ecx
        and     sp,0FFFCh       ;Dword align
        mov     edi,esp
        push    eax             ;Restore address
        xor     eax,eax         ;Clear error condition
        mov     edx,eax
        inc     edx             ;Set edx = 1
        ret

; Check to see if item is <= 128 bytes. If so, then allocate
; a block of memory using the thunk BMP routine

thk10:  cmp     ecx,128         ;Block allocation size
        ja      short thk20

        push    ecx             ;Save size
        call    THK32ALLOCBLOCK
        pop     ecx             ;Restore size
        xor     edx,edx         ;Set EDX to indicate BMP allocation
        ret

; The buffer has been determined to be too large BMP or stack allocation.
; Here we allocate a chunk of memory using AllocMem

thk20:  push    ecx
        push    ecx             ;Allocate space for temp ptr
        mov     eax,esp
        push    ALLOCMEM_FLAGS  ;Push Alloc Flags
        push    ecx             ;Push Size
        push    eax             ;Address for Ptr
        call    DOS32ALLOCMEM
        add     esp,12          ;Remove Parameters
        pop     edi             ;Has new PTR
        pop     ecx             ;Get old length
        xor     edx,edx         ;Set EDX = -1 to indicate DosAllocMem
        dec     edx
        ret

EndProc THK32ALLOCVARLEN

CODE32_TUNE2  ENDS



CODE32_TUNE4  SEGMENT

;*** THK32HANDLEBOUNDARY
;
; This routine will handle the case where a variable sized item crosses
; a 64k boundary. On entry, the item has already been determined to
; cross the 64k boundary. This routine will either perform a complete
; setup for a copy operation, or create a linear alias to the item.
;
;
; ENTRY ESI     holds Source address
;       ECX     holds length of object
;
; EXIT
;       EDX indicates the type of allocation performed (attempted)
;
;               2 = Stack Allocation
;               1 = BMP allocation
;               0 = Linear Alias created
;              -1 = DosAllocMem
;
;       If no error,
;               EAX = 0
;               EDI holds address to new area
;
;       if Error
;               EAX = Error code from DOS32ALLOCBLOCK or DOS32ALLOCMEM
;               EDI = UNDEFINED
;
; USES  EDX,EDI
;

        ASSUME  CS:FLAT,DS:FLAT,ES:FLAT,FS:NOTHING,GS:NOTHING

Procedure THK32HANDLEBOUNDARY,near


; Check to see if the item is <= 32 bytes long. If so, then allocate
; space off the user stack.

        cmp     ecx,32          ;Maximum size allocated on stack
        ja      short thk30

        pop     eax             ;Save return address
        sub     esp,ecx
        and     esp,0FFFFFFFCH  ;Dword align
        mov     edi,esp
        push    eax             ;Restore address
        xor     eax,eax         ;Clear error condition
        mov     edx,eax
        add     edx,2
        ret

; Check to see if item is <= 128 bytes. If so, then allocate
; a block of memory using the thunk BMP routine

thk30:  cmp     ecx,128         ;Block allocation size
        ja      short thk40

        push    ecx             ;Save size
        call    THK32ALLOCBLOCK
        pop     ecx
        xor     edx,edx         ;Set EDX to indicate BMP allocation
        inc     edx
        ret

; To create an alias, we need to insure item spans 16 pages or less
; It is eligible for an alias IFF size <= 10000h - (base & 0fffh)
;

thk40:  mov     eax,esi         ;eax = base
        and     eax,0fffh       ;base & 0fffh
        mov     edx,10000h      ;edx = 10000h
        sub     edx,eax         ;edx = 10000h - (base & 0fffh)
        cmp     ecx,edx
        ja      short thk50

        ;Create Linear Alias to item

        push    edi
        mov     edi,esp         ;Alias buffer
        call    THK32ALIASMEM
        pop     edi
        xor     edx,edx         ;Zero EDX to show linear alias
        ret

; The buffer has been determined to be too large for aliasing.
; here we allocate a chunk of memory using AllocMem

thk50:  push    ecx             ;Save length
        push    ecx             ;Allocate space for temp ptr
        mov     eax,esp         ;Save address of temp
        push    ALLOCMEM_FLAGS  ;Push Flags
        push    ecx             ;Push Size
        push    eax             ;Address for Ptr
        call    DOS32ALLOCMEM
        add     esp,12          ;Remove Parameters
        pop     edi             ;Has new PTR
        pop     ecx             ;Get old length
        xor     edx,edx         ;Set EDX = -1 to indicate DosAllocMem
        dec     edx
        ret

EndProc THK32HANDLEBOUNDARY

CODE32_TUNE4  ENDS

CODE32_TUNE1  SEGMENT

;************************************************************
; THK32HANDLESTRING - Handle string parameters
;
; This routine is used by the code produced by the thunk
; compiler. It checks a null terminated string for crossing
; a 64k boundary. If it does cross the boundary, then it
; will either allocate memory and copy the string, or create
; a linear alias to the string in memory.
;
; Entry
;       [ESP+4]  Offset from EBP where Flags are located.
;                This should be a negative DWORD value that
;                can be added to EBP to produce a pointer to
;                the Alias Flags.
;
;       ESI      Address of source string
;       EDX      Mask for setting allocation flags
;
;
; Exit
;       No error
;               Carry flag clear
;               EDI holds address of string that does not cross
;                  a 64k boundary
;
;       Error
;               Carry Flag Set
;               EDI undefined
;               EAX holds error code.
;************************************************************

        ASSUME  CS:FLAT,DS:FLAT,ES:FLAT,FS:NOTHING,GS:NOTHING

Procedure THK32HANDLESTRING,near


;Determine length of string

        mov     edi,esi
        mov     ecx,61439       ;Set ecx to maximum string length
        xor     ax,ax
        repne   scasb
        or      ecx,ecx
        jz      thk290          ;jmp if no zero byte

        mov     ecx,edi         ;Give ecx length
        sub     ecx,esi

;Check Boundary Crossing
thk200:
        mov     eax,esi
        neg     ax
        jz      short thk210            ;0=full 64k of room

        cmp     ax,cx
        jb      short thk220            ;Jump if not enough room

thk210: mov     edi,esi                 ;EDI holds useable string
        xor     eax,eax                 ;Zero error code
        clc                             ;No errors
        ret     4                       ;Remove flags parameter

; The item crosses a boundary. Allocate or alias depending on size.
;
; Check to see if the item is <= 32 bytes long. If so, then allocate
; space off the user stack.

thk220: cmp     ecx,32          ;Maximum size allocated on stack
        ja      short thk230


; Current top of stack has return address, then offset to the alias
; flag. To allocate space on the stack, we need to keep these two
; values on top.

        mov     eax,esp         ;Create new ESP
        sub     eax,ecx
        and     al,0fch         ;Dword align stack
        xchg    esp,eax
        mov     edi,esp
        push    DWORD PTR [eax+4]       ;Offset of Alias flags
        push    DWORD PTR [eax]         ;Return address

        add     DWORD PTR[esp+4],8              ;Flag pointer = Stack flags

        jmp     short thk270    ;Copy string

; Check to see if item is <= 128 bytes. If so, then allocate
; a block of memory using the thunk BMP routine

thk230: cmp     ecx,128         ;Block allocation size
        ja      short thk240

        push    ecx             ;Save size
        push    edx             ;Save pointer number
        call    THK32ALLOCBLOCK
        pop     edx
        pop     ecx

        or      eax,eax         ;Check for error
        jnz     thk300          ;Jump if error

        add     DWORD PTR[esp+4],4              ;Flag pointer = BMP Flags

        jmp     short thk270

; To create an alias, we need to insure item spans 16 pages or less
; It is eligible for an alias IFF size <= 10000h - (base & 0fffh)
;

thk240: push    edx
        mov     eax,esi         ;eax = base
        and     eax,0fffh       ;base & 0fffh
        mov     edx,10000h      ;edx = 10000h
        sub     edx,eax         ;edx = 10000h - (base & 0fffh)
        cmp     ecx,edx
        pop     edx
        ja      short thk250

;Create Linear Alias to item

        push    edi
        mov     edi,esp         ;Alias buffer
        call    THK32ALIASMEM
        pop     edi

        or      eax,eax
        jnz     short thk300

        jmp     short thk280

; The buffer has been determined to be too large for aliasing.
; here we allocate a chunk of memory using AllocMem

thk250: push    ecx             ;Save length
        push    edx             ;Save mask

        push    ecx             ;Allocate space for temp ptr
        mov     eax,esp         ;Save address of temp
        push    ALLOCMEM_FLAGS  ;Push Flags
        push    ecx             ;Push Size
        push    eax             ;Address for Ptr
        call    DOS32ALLOCMEM
        add     esp,12          ;Remove Parameters
        pop     edi             ;Has new PTR

        or      eax,eax
        jnz     short thk300

        pop     edx             ;Get mask
        pop     ecx             ;Get old length

; Copy ECX bytes from ESI to EDI

thk270: push    edi

        mov     eax,3
        and     eax,ecx
        shr     ecx,2
        rep movsd
        mov     ecx,eax
        rep movsb

        pop     edi


thk280:  mov    eax,[esp+4]     ;Offset for flags
        or      [ebp+eax],edx   ;Set allocation flag


        clc                     ;Clear error condition
        ret     4



; Return invalid error parameter
;

thk290:
        stc                     ;Set carry indicates error
        mov     eax,ERROR_INVALID_PARAMETER
        ret     4


;Return error from other system call. Assume that error code is
;in EAX

thk300:
        stc
        ret     4

EndProc THK32HANDLESTRING


;D129178  - Begin
;************************************************************
; THK32MULTIPLESTRING - Handle multiple string parameters
;
; This routine checks for muliple strings where the end of the
; string is determined by two nulls. Since a parameter that is
; defined as a multiple string, can also accept single strings
; this routine checks for the single string as well.
; This routine will check for the single string case if a multiple string
; was not found. In either case, the length of the string is
; chk to determine if it crosses a 64K boundary.
; If it does cross the boundary, then it will either allocate memory
; copy the string, or create a linear alias to the string in memory.
; Routine is based on thk32handlestring
;
; Entry
;       [ESP+4]  Offset from EBP where Flags are located.
;                This should be a negative DWORD value that
;                can be added to EBP to produce a pointer to
;                the Alias Flags.
;
;       ESI      Address of source string
;       EDX      Mask for setting allocation flags
;
; Uses EAX ECX
;
; Exit
;       No error
;               Carry flag clear
;               EDI holds address of string that does not cross
;                  a 64k boundary
;
;       Error
;               Carry Flag Set
;               EDI undefined
;               EAX holds error code.
;************************************************************
Procedure THK32MULTIPLESTRING,near


;Determine length of multiple string


        xor     eax,eax                   ;clear out ax
        xor     ecx,ecx                   ;clear out cx

find_next :
        mov     al, BYTE PTR [esi+ecx]    ;find the null char
        or      al, al                    ;chk char for zero
        jnz     short next_chr            ;no null, go chk next chr
        mov     al, BYTE PTR [esi+ecx+1]  ;chk end string null
        or      al, al                    ;do we have a null ?
        jz      thk200                    ;if null then end of string

next_chr :
        inc     ecx                       ;increase char count
        cmp     ecx, 61439                ;are we at max len
        jb      short find_next           ;get next char

single_string :
        mov     edi,esi                   ;mov base address to edi
        mov     ecx,61439                 ;Set ecx to maximum string length
        xor     ax,ax
        repne   scasb
        or      ecx,ecx
        jz      thk290                    ;jmp if no zero byte

        mov     ecx,edi                   ;Give ecx length
        sub     ecx,esi
        jmp     thk200

; it should never get here
        stc
        ret     4

EndProc THK32MULTIPLESTRING

;D129178  - End


CODE32_TUNE1  ENDS


CODE32_TUNE2 SEGMENT

;************************************************************
;THK32DEALLOC
;
; This routine determines what type of allocation was performed
; on a pointer, and deallocates any resources committed for it.
;
; ENTRY
;       ESI     Address of allocated temporary memory
;       ESP+4   Offset of Allocation Flags relative to EBP
;               Must be value to be added to EBP to find
;               Alias flags.
;       EDX     Pointer Mask
;
; EXIT
;       Any memory allocated for pointer ESI will be deallocated.
;
;************************************************************

        ASSUME  CS:FLAT,DS:FLAT,ES:FLAT,FS:NOTHING,GS:NOTHING

Procedure THK32DEALLOC,near

        mov     eax,[esp+4]
        lea     eax,[ebp+eax]
        test    DWORD PTR [eax+8],edx   ;Check for stack allocation
        jnz     thk310                  ;Stack deallocated implicitly

        test    DWORD PTR [eax+4],edx   ;Check for BMP allocation
        jz      thk320

        call    THK32FREEBLOCK

thk310: ret     4

;
; We have it narrowed down to either an Alias or a AllocMem. THK32FREEALIAS
; will handle either case, so no test is needed here.

thk320: call    THK32FREEALIAS

        ret     4

EndProc THK32DEALLOC

CODE32_TUNE2 ENDS

CODE32  SEGMENT

;************************************************************
;THK32XHNDLR/PM32XHNDLR
;
; This routine will handle any pointer validation exceptions
; generated in 32->16 thunks. This routine makes the following
; assumptions:
;       1) Thunk routines are all of cdecl or syscall format (i.e
;          the routine calling the thunk pops parameters)
;       2) This exception handler will only occur because of a
;          access type exception.
;
; ENTRY
;       Stack Frame (as per exception architecture):
;               PEXCEPTIONREPORTRECORD
;               PEXCEPTIONREGISTRATIONRECORD
;                       Offset 0        Previous ERR structure
;                       Offset 4        This routine
;       (local define)  Offset 8        Stack Frame of API routine
;               PCONTEXTRECORD
;               PVOID
;
; EXIT
;               EAX     ERROR_INVALID_PARAMETER (87 decimal)
;               ESP     points to API stack, w/any parms already popped
;               [ESP]   return address of API caller
;************************************************************

        ASSUME  CS:FLAT,DS:FLAT,ES:FLAT,FS:NOTHING,GS:NOTHING,SS:FLAT

ThunkExceptionRegistrationRecord        STRUC
TERR_PRV        DD      ?       ; EXCEPTIONREGISTRATIONRECORD prev_structure
TERR_EXH        DD      ?       ; EXCEPTIONREGISTRATIONRECORD ExceptionHandler
TERR_EBX        DD      ?       ; Saved EBX register
TERR_EDI        DD      ?       ; Saved EDI register
TERR_ESI        DD      ?       ; Saved ESI register
TERR_ESP        DD      ?       ; Saved ESP register
TERR_CHN        DD      ?       ; Saved exception handler chain head
ThunkExceptionRegistrationRecord        ENDS

Procedure THK32XHNDLR,near

        mov     eax,[esp+4]                     ; (EAX) = -> Xcpt Report Rec
        mov     eax,[eax].ExceptionNum          ; (EAX) = exception number
        cmp     eax,XCPT_ACCESS_VIOLATION       ; Is this access violation?
        je      short TEHTrap                   ; Yes, handle exception
        mov     eax,XCPT_CONTINUE_SEARCH        ; No, don't handle exception
        ret                                     ;  and return to dispatcher

TEHTrap:
        mov     eax,[esp+8]                     ; (EAX) = -> Xcpt Reg Rec
        mov     ecx,[eax].TERR_CHN              ; (ECX) = handler chain head
        mov     fs:[tib_pexchain],ecx           ; Restore handler chain head
        mov     ebx,[eax].TERR_EBX              ; Restore EBX
        mov     edi,[eax].TERR_EDI              ; Restore EDI
        mov     esi,[eax].TERR_ESI              ; Restore ESI
        mov     eax,[eax].TERR_ESP              ; (EAX) = ESP
        mov     esp,eax                         ; Restore ESP
        mov     ebp,[esp-4]                     ; Restore EBP from stack
        mov     eax,ERROR_INVALID_PARAMETER     ; Set return code
        ret                                     ; Return to user

EndProc THK32XHNDLR

CODE32  ENDS

        END
