;       SCCSID = @(#)msp.asm    6.4 91/09/30
title   Msp - Memory Suballocation Package
name    Msp

.sall
page    80, 132

;/********************** START OF SPECIFICATIONS **********************/
;/*                                                                   */
;/* SOURCE FILE NAME:   Msp.asm                                       */
;/*                                                                   */
;/* DESCRIPTIVE NAME:   Memory Suballocation Package                  */
;/*                                                                   */
;/* COPYRIGHT: (TOP SOURCE FILE ONLY)                                 */
;/*                                                                   */
;/* STATUS: (TOP SOURCE FILE ONLY)  1.0                               */
;/*                                                                   */
;/* FUNCTION:                                                         */
;/*     These routines implement the CP/DOS memory suballocation      */
;/*     dynamic link package, specifically the following calls:       */
;/*                                                                   */
;/*             MspAlloc -  allocate a block of memory                */
;/*             MspFree -   free a block of memory                    */
;/*             MspSet -    set memory segment                        */
;/*                                                                   */
;/*     Msp manages blocks of memory within a segment that is         */
;/*     allocated by the requestor. It is assumed that any segment    */
;/*     handles are valid selectors to segments. There is no limit to */
;/*     the number of segments Msp can manage, and since the requestor*/
;/*     is responsible for allocating the segment initially, there are*/
;/*     no special considerations for resource cleanup. All Msp calls */
;/*     furnish the selector of a segment. All blocks are allocated   */
;/*     and freed usimg offset arithmetic for speed. Note there       */
;/*     is a one time ROOTSIZ overhead per segment managed by Msp. The*/
;/*     minimum allocation multiplier is MINSIZ bytes. The segment is */
;/*     managed by only keeping track of unallocated storage. This    */
;/*     allows us to maintain only one list of blocks (free), and it  */
;/*     is kept in ascending order of addresses within the segment so */
;/*     that neighboring blocks can be easily coalesced when memory   */
;/*     is freed. Race conditions between threads under the same      */
;/*     process are controlled by a semaphore  in each segment.       */
;/*     No Selector verification is performed by MSP. The hardware    */
;/*     will trap illegal selector references                         */
;/*                                                                   */
;/*                                                                   */
;/* NOTES:                                                            */
;/*    DEPENDENCIES:                                                  */
;/*    RESTRICTIONS:                                                  */
;/*    PATCH LABEL: (TOP SOURCE FILE ONLY)                            */
;/*                                                                   */
;/* ENTRY POINTS:   DosSubSet, DosSubAlloc, DosSubFree.               */
;/*                                                                   */
;/* EXTERNAL REFERENCES:  DosSemRequest, DosSemClear                  */
;/*                                                                   */
;/* change list:                                                      */
;/*    10/31/89 - B708407 - SubAlloc jumps past sem clear on error    */
;/*                                                                   */
;/*                                                                   */
;/*********************** END OF SPECIFICATIONS ***********************/

page

ifndef FamilyAPI
.286p
endif

.xlist
include struc.inc
include basemaca.inc
include error2.inc
include msp.inc
include infoseg.inc
include dc1segs.inc                                             ; DCR 704
include dc1mmpha.inc
.list

ifndef FamilyAPI
        EXTRN   DOSSEMREQUEST:FAR                       ; @@
        EXTRN   DOSSEMCLEAR:FAR                         ; @@
endif

DOSSUBCODE      segment                                         ; DCR 704


page

;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME:    DosSubAlloc                                  */
;/*                                                                  */
;/* DESCRIPTIVE NAME:   Allocate a block of memory.                  */
;/*                                                                  */
;/* FUNCTION:                                                        */
;/*     DosSubAlloc is called when the requestor wishes to allocate a*/
;/*     block of memory from within a Msp segment. MspAlloc uses     */
;/*     First Fit algorithm to do its allocation. Note that there is */
;/*     ROOTSIZE one time overhead incurred for Msp to manage this   */
;/*     segment for the requestor. A request for zero (0) bytes      */
;/*     returns an error. All sizes are rounded up to a multiple     */
;/*     of four (4) bytes.                                           */
;/* NOTES:                                                           */
;/*                                                                  */
;/* ENTRY POINT:    DosSubAlloc                                      */
;/*    LINKAGE:     Call Far                                         */
;/*                                                                  */
;/* INPUT:                                                           */
;/*    SYMBOLIC NAME:   SegmentSelector                              */
;/*    DESCRIPTION:     Selector to Msp Segment                      */
;/*    TYPE:            Word on stack                                */
;/*                                                                  */
;/*    SYMBOLIC NAME:   @BlockOffset                                 */
;/*   DESCRIPTION:     Address in user data seg for allocated block */
;/*    TYPE:            Dword on stack                               */
;/*                                                                  */
;/*    SYMBOLIC NAME:   RequestSize                                  */
;/*    DESCRIPTION:     Size of allocation request                   */
;/*    TYPE:            Word on stack                                */
;/*                                                                  */
;/* EXIT-NORMAL:    AX = NO_ERROR                                    */
;/*                 BlockOffset = Offset of allocated block.         */
;/*                                                                  */
;/* EXIT-ERROR:     AX = ERROR_DOSSUB_NOMEM                          */
;/*                     BlockOffset = Size of largest free block.    */
;/*                     Allocate failed.                             */
;/*                 AX = ERROR_DOSSUB_BADSIZE                        */
;/*                     Invalid size parameter                       */
;/*                                                                  */
;/* EFFECTS:    Uses No Registers.                                   */
;/*                                                                  */
;/* INTERNAL REFERENCES:                                             */
;/*    ROUTINES:    None                                             */
;/*                                                                  */
;/* EXTERNAL REFERENCES:                                             */
;/*    ROUTINES:            DosSemRequest,  DosSemClear              */
;/*                                                                  */
;/*                                                                  */
;/*********************** END OF SPECIFICATIONS **********************/
page

;***DT  preDOSSUBALLOC - DosSubAlloc pre-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_PRE_DOSSUBALLOC>,
;          TP    = .preDOSSUBALLOC,
;          TYPE  = (PRE,API),
;          GROUP = MSP,
;          DESC  = "(OS) DosSubAlloc Pre-Invocation",
;          REGS  = (DS,AX),
;          FMT   = "Selector = <FMT_WORD>  Size = <FMT_WORD>"

;***DT  postDOSSUBALLOC - DosSubAlloc post-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_POST_DOSSUBALLOC>,
;          TP    = .postDOSSUBALLOC,
;          TYPE  = (POST,API),
;          GROUP = MSP,
;          DESC  = "(OS) DosSubAlloc Post-Invocation",
;          REGS  = (BX,AX),
;          FMT   = "Offset = <FMT_WORD>  Return Code = <FMT_WORD>"

public  DOSSUBALLOC
DOSSUBALLOC     proc    far
; 546864
        assume  cs:DOSSUBCODE

        push    bp
        mov     bp,sp

; save registers used
;

        push    bx
        push    cx
        push    ds
        push    es
        push    si
        push    di

; setup registers
;
        mov     ds, [bp].AllocSeg       ; ds points to Msp segment
        mov     si, [bp].AllocSize      ; si = size
        les     bp, [bp].AllocAddr      ; es:bp points to return word
        xor     cx, cx                  ; cx = largest block size seen (0)

        PUBLIC  preDOSSUBALLOC
preDOSSUBALLOC:                         ; RAS pre-invocation trace
        MMPHPreDOSSubAlloc              ; Performance Hook (241B)

; probe return address - can't take prot fault after getting semaphore
;
        mov     word ptr es:[bp], cx

; round up request size to multiple of 4
;
        add     si, 3
        and     si, not 3

; check if request size = 0 (ERROR_DOSSUB_BADSIZE)
;
        jz      dsa20

ifndef FamilyAPI
    push    ds                          ; @@ semaphore handle  (DWORD)
    push    [0].MspSem                  ; @@ semaphore handle
    push    -1                          ; @@ time out (infinite wait) (DWORD)
    push    -1                          ; @@ time out (infinite wait)
    call    FAR PTR DosSemRequest       ; @@ DosSemRequest(ds:[0].MspSem)
endif

        mov     bx, ds:[0].MspNext      ; Current = First.MspNext
        or      bx, bx                  ; @@ this is to pack the semaphore
        jz      dsa40

        xor     di, di
        mov     ax, [bx].MspSize        ; Current Block Size


; main allocation loop (infinite with exit at loopend label)
;

dsa10:  cmp     si, ax                  ; If RequestSize <= Current Block Size
        jbe     dsa50                   ;    Go satisfy suballocation request

        cmp     [bx].MspNext, 0         ; If Last Block on Free Chain
        je      dsa30                   ;    Go return largest block size found

        mov     di, bx                  ; Previous = Current
        mov     bx, [bx].MspNext        ; Current = Current.MspNext
        mov     ax, [bx].MspSize        ; Current.MspSize
        cmp     ax, cx                  ; If Current.MspSize <= Largest
        jbe     dsa10                   ;    Go around again

        mov     cx, ax                  ; Largest = Current.MspSize
        jmp     short dsa10             ; Go around again

; (ERROR_DOSSUB_BADSIZE)
;
        ;align   4  54686                ; Dword align jump target
dsa20:  mov     ax, ERROR_DOSSUB_BADSIZE
        jmp     short dsa90

; Last Block on Free Chain
;
        ;align   4  54686                ; Dword align jump target
dsa30:  mov     word ptr es:[bp], cx    ; Return largest block size

; (ERROR_DOSSUB_NOMEM)
;

dsa40:  mov     si, ERROR_DOSSUB_NOMEM  ; Put return code in si temporarily
        jmp     short dsa80             ; @@ ptr B708407

; SubAlloc the block now
;
        ;align   4     54686             ; Dword align jump target
dsa50:  jne     dsa60                   ; Jump if not exact fit


        mov     ax, [bx].MspNext        ; Previous.MspNext =
        mov     [di].MspNext, ax        ; Current.MspNext
        jmp     short dsa70             ; Go finish up

        ;align   4   54686               ; Dword align jump target
dsa60:  lea     ax,[bx][si]             ; ax = new free block
        mov     [di].MspNext, ax        ; Link new free to prev
        mov     di, ax                  ; now use di for new free block
        mov     ax, [bx].MspNext
        mov     [di].MspNext, ax        ; all linked up
        mov     ax, [bx].MspSize        ; get old size
        sub     ax, si                  ; size of new free block
        mov     [di].MspSize, ax        ; save new free size

dsa70:  mov     word ptr es:[bp], bx    ; return offset of allocated block
        xor     si, si                  ; si = NO_ERROR (temporarily)

dsa80:                                  ; @@ ptr B708407

ifndef FamilyAPI
    push    ds                          ; @@ semaphore handle  (DWORD)
    push    [0].MspSem                  ; @@ semaphore handle
    call    FAR PTR DosSemClear         ; @@ DosSemClear(ds:[0].MspSem)
endif

        mov     ax, si                  ; ax = return code

dsa90:

        PUBLIC  postDOSSUBALLOC
postDOSSUBALLOC:                        ; RAS post-invocation trace
        MMPHPostDOSSubAlloc             ; Performance Hook (249B)

; restore register state
;
        pop     di
        pop     si
        pop     es
        pop     ds
        pop     cx
        pop     bx

        pop     bp

        ret     8

DOSSUBALLOC     endp

page

;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME:    DosSubFree                                   */
;/*                                                                  */
;/* DESCRIPTIVE NAME:   Free a block of memory.                      */
;/*                                                                  */
;/* FUNCTION:                                                        */
;/*     DosSubFree is called when the requestor wishes to free a     */
;/*     block of memory within the Msp segment. When a block is freed*/
;/*     neighboring blocks are coalesced if possible. The free block */
;/*     chain is kept in ascending address order. At minimum MINSIZ  */
;/*     bytes can be freed due to the size of the fields to maintain */
;/*     a free block. For validation of the request we check for     */
;/*     attempts to free into unallocated memory, and out of the     */
;/*     segment. A request to free zero (0) bytes returns an error.  */
;/*     All sizes are rounded up to a multiple of four (4) bytes.    */
;/*                                                                  */
;/* NOTES:                                                           */
;/*                                                                  */
;/* ENTRY POINT:    DosSubFree                                       */
;/*    LINKAGE:     Call Far                                         */
;/*                                                                  */
;/* INPUT:                                                           */
;/*    SYMBOLIC NAME:   SegmentSelector                              */
;/*    DESCRIPTION:     Selector to Msp Segment                      */
;/*    TYPE:            Word on stack                                */
;/*                                                                  */
;/*    SYMBOLIC NAME:   BlockOffset                                  */
;/*    DESCRIPTION:     Offset of block to be freed                  */
;/*    TYPE:            Word on stack                                */
;/*                                                                  */
;/*    SYMBOLIC NAME:   Size                                         */
;/*    DESCRIPTION:     Size of block to be freed                    */
;/*    TYPE:            Word on stack                                */
;/*                                                                  */
;/* EXIT-NORMAL:    AX = NO_ERROR                                    */
;/*                                                                  */
;/* EXIT-ERROR:     AX = ERROR_DOSSUB_OVERLAP                        */
;/*                      Attempt to free into unallocated memory or  */
;/*                      out of the segment.                         */
;/*                 AX = ERROR_DOSSUB_BADSIZE                        */
;/*                      Invalid Size Parameter                      */
;/*                                                                  */
;/* EFFECTS:    Uses No Registers.                                   */
;/*                                                                  */
;/* INTERNAL REFERENCES:                                             */
;/*    ROUTINES:    None.                                            */
;/*                                                                  */
;/* EXTERNAL REFERENCES:                                             */
;/*    ROUTINES:            DosSemRequest,  DosSemClear              */
;/*                                                                  */
;/*********************** END OF SPECIFICATIONS **********************/
page

;***DT  preDOSSUBFREE - DosSubFree pre-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_PRE_DOSSUBFREE>,
;          TP    = .preDOSSUBFREE,
;          TYPE  = (PRE,API),
;          GROUP = MSP,
;          DESC  = "(OS) DosSubFree Pre-Invocation",
;          REGS  = (DS,SI,AX),
;          FMT   = "Selector = <FMT_WORD>  Offset = <FMT_WORD>",
;          FMT   = "Size = <FMT_WORD>"

;***DT  postDOSSUBFREE - DosSubFree post-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_POST_DOSSUBFREE>,
;          TP    = .postDOSSUBFREE,
;          TYPE  = (POST,API),
;          GROUP = MSP,
;          DESC  = "(OS) DosSubFree Post-Invocation",
;          REGS  = (AX),
;          FMT   = "Return Code = <FMT_WORD>"

public  DOSSUBFREE
DOSSUBFREE      proc    far
;align 54686
        assume  cs:DOSSUBCODE

        push    bp
        mov     bp,sp

; save registers used
;
        push    bx
        push    cx
        push    ds
        push    si
        push    di

; setup registers
;
        mov     ds, [bp].FreeSeg        ; ds points to Msp segment
        mov     si, [bp].FreeOffset     ; si = offset of block to free
        mov     bp, [bp].FreeSize       ; bp = size

        PUBLIC  preDOSSUBFREE
preDOSSUBFREE:                          ; RAS pre-invocation trace
        MMPHPreDOSSubFree               ; Performance Hook (241D)

; round up size to MINSIZ bytes
;
        add     bp, 3
        and     bp, not 3

; check if request size = 0 (ERROR_DOSSUB_BADSIZE)
;
        jz      dsf110

ifndef FamilyAPI
    push    ds                          ; @@ semaphore handle  (DWORD)
    push    [0].MspSem                  ; @@ semaphore handle
    push    -1                          ; @@ time out (infinite wait)  (DWORD)
    push    -1                          ; @@ time out (infinite wait)
    call    FAR PTR DosSemRequest       ; @@ DosSemRequest(ds:[0].MspSem)
endif

        xor     bx, bx                  ; bx = ptr to first free block

; Now we search the free chain. This loop locates the
; current and previous pointers to the free chain around the block to
; be freed.
;
; bx = current block on free list
; si = block to be freed
; di = previous block on free list
;
        cmp     bx, si                  ; If attempt to free free memory
        je      dsf120                  ;    Go return error

dsf10:  mov     di, bx                  ; Previous = Current
        mov     bx, [bx].MspNext        ; Current = Current.MspNext
        or      bx,bx                   ; Current = last pointer?
        je      dsf20                   ;    Yes -- Exit loop

        cmp     bx, si                  ; Current < block to free?
        jb      dsf10                   ;    Yes -- go around again
        je      dsf120                  ; Go if attempt to free free memory

; check for overlap on the free request
; #1 block to free < top of prev free block
; #2 top of block to free > bottom of next free block
; #2b has a conditional depending on whether we are at end of free list
; or not. if so, bx = 0 from exit on freeloop (above). In this case we see
; if he tried to free out of the top of the segment. Otherwise bx = next
; free block and we check if he is overlapping into a higher mem free block
; di = prev, use cx for scratch, bx = next, si = block to free addr
; (next ~ current)
;

dsf20:  mov     cx, di                  ; cx = Previous
        or      di, di                  ; If Previous = First Block
        jz      dsf30                   ;    Go around (Top of Previous = 0)

        add     cx, [di].MspSize        ; cx = Top of Previous

dsf30:  lea     ax,[bp][si]             ; ax = top of block to free
        cmp     si, cx                  ; If FreeBlock < Top of Previous
        jb      dsf120                  ;    Go if overlap detected

        or      bx,bx                   ; If Current is First block
        jnz     dsf40                   ;    Go if not First block

        mov     bx, [bx].MspSegSize
        add     bx, ROOTSIZ-1
        dec     ax                      ; 64K consideration
        cmp     ax, bx                  ; If Top of FreeBlock out of segment
        ja      dsf120                  ;    Go if overlap detected
        xor     bx, bx                  ; reset bx = 0 for First block
        jmp     short dsf50             ; Jump to continue

        ;align   4    54686              ; Dword align jump target
dsf40:  cmp     ax, bx             ; If Top of FreeBlock > Bottom of Current
        ja      dsf120             ;    Go if overlap detected


; DEVIATION from Pseudo-code for PERformance (Threaded Code)
; here we have finished the checks on the free request and are attempting
; to coalesce.
; check if the top of the previous block (di) is the same as the bottom
; of the block to be freed(si)
;

dsf50:  cmp     cx, si                  ; if equal we coalesce w/prev
        jne     dsf60                   ;    Go if not contiguous

; combine the block to be freed (si) with the previous block (di)
;
        add     [di].MspSize, bp        ; prev.MspSize += size to be freed
        add     cx, bp                  ; compute new previous top
        cmp     cx, bx                  ; if top == current
        je      dsf70                   ;    Go attempt to coalesce
        jmp     short dsf80             ; Go finish up

; here we link the block being freed onto the free chain, then check if
; the block to be freed (si) can be coalesced up with the current (bx) block
;

dsf60:  mov     [di].MspNext, si        ; prev.MspNext = new free block
        mov     [si].MspNext, bx        ; new.MspNext = current free block
        mov     [si].MspSize, bp        ; new.MspSize = size of new block

        mov     di, si                ; make prev look right for combine_w_next
        add     si, bp                ; si = top of new free block

        cmp     si, bx                  ; if prev (freed) top != current
        jne     dsf80                   ;    Go finish up

dsf70:  or      bx, bx                  ; if (freed) top is at 64K boundary
        jz      dsf80                   ;    Go finish up

; here we want to coalesce the block pointed to by previous (di) with
; the current block (bx). it is assumed that the blocks are already known
; to be contiguous and linked on the free chain in ascending address order
;
        mov     cx, [bx].MspNext        ; cx = current.MspNext
        mov     [di].MspNext, cx        ; prev.MspNext = cx

        mov     cx, [bx].MspSize        ; cx = current.MspSize
        add     [di].MspSize, cx        ; prev.MspSize += cx

; set error code to NO_ERROR
;

dsf80:  xor     si, si                  ; si = NO_ERROR (temporarily)

dsf90:

ifndef FamilyAPI
    push    ds                          ; @@ semaphore handle  (DWORD)
    push    [0].MspSem                  ; @@ semaphore handle
    call    FAR PTR DosSemClear         ; @@   DosSemClear(ds:[0].MspSem)
endif

        mov     ax, si                  ; ax = return code

dsf100:

        PUBLIC  postDOSSUBFREE
postDOSSUBFREE:                         ; RAS post-invocation trace
        MMPHPostDOSSubFree              ; Performance Hook (249D)

        pop     di
        pop     si
        pop     ds
        pop     cx
        pop     bx
        pop     bp

        ret     6

; request size = 0 (ERROR_DOSSUB_BADSIZE)
;

dsf110: mov     ax, ERROR_DOSSUB_BADSIZE
        jmp     short dsf100

; (ERROR_DOSSUB_OVERLAP)
;

dsf120: mov     si, ERROR_DOSSUB_OVERLAP  ; si = return code (temporarily)
        jmp     short dsf90

DOSSUBFREE     endp

page

;/********************** START OF SPECIFICATIONS *********************/
;/*                                                                  */
;/* SUBROUTINE NAME:    DosSubSet                                    */
;/*                                                                  */
;/* DESCRIPTIVE NAME:   Set or initialize an Msp Segment             */
;/*                                                                  */
;/* FUNCTION:                                                        */
;/*     DosSubSet is called when a requestor wishes to set or init   */
;/*     a segment for use by the Msp package. If the flags parameter */
;/*     indicates INIT, then the segment is initialized into a free  */
;/*     list of one block. If the flags do not indicate INIT, it is  */
;/*     assumed that the requestor is setting the segment. To set    */
;/*     or grow the segment the user must first call DOSREALLOC to   */
;/*     grow the segment before calling MspSet to set it. A size of  */
;/*     zero (0) implies 64K. The size will be rounded up to a       */
;/*     multiple of 4. If the user specifies a grow request and the  */
;/*     difference is less than 4, rounding up could cause a fault   */
;/*     to occur when the thread refernces it.                       */
;/*                                                                  */
;/* NOTES:                                                           */
;/*                                                                  */
;/* ENTRY POINT:    DosSubSet                                        */
;/*    LINKAGE:     Call Far                                         */
;/*                                                                  */
;/* INPUT:  (Parameters are pushed on the Stack in the order of      */
;/*          Appearance.)                                            */
;/*                                                                  */
;/*    SYMBOLIC NAME:   SegmentSelector                              */
;/*    DESCRIPTION:     Selector to Msp Segment                      */
;/*    TYPE:            Word on stack                                */
;/*                                                                  */
;/*    SYMBOLIC NAME:   Flags                                        */
;/*    DESCRIPTION:     Parameter flags indicates INIT(1) or SET(0)  */
;/*    TYPE:            Word on stack                                */
;/*                                                                  */
;/*    SYMBOLIC NAME:   SetSize                                      */
;/*    DESCRIPTION:     Size of set request (Use 0 for 64K)          */
;/*    TYPE:            Word on stack                                */
;/*                                                                  */
;/* EXIT-NORMAL:        AX = NO_ERROR                                */
;/*                                                                  */
;/* EXIT-ERROR:         AX = ERROR_DOSSUB_SHRINK                     */
;/*                          Attempt to shrink size of segment.      */
;/*                     AX = ERROR_DOSSUB_BADFLAG                    */
;/*                          Invalid flag parameter                  */
;/*                     AX = ERROR_DOSSUB_BADSIZE                    */
;/*                          Invalid size parameter                  */
;/*                                                                  */
;/* EFFECTS:    Uses No Registers.                                   */
;/*                                                                  */
;/* INTERNAL REFERENCES:                                             */
;/*    ROUTINES:    None                                             */
;/*                                                                  */
;/* EXTERNAL REFERENCES:                                             */
;/*    ROUTINES:            DosSemRequest,  DosSemClear              */
;/*                                                                  */
;/*********************** END OF SPECIFICATIONS **********************/
page

;***DT  preDOSSUBSET - DosSubSet pre-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_PRE_DOSSUBSET>,
;          TP    = .DOSSUBSET,
;          TYPE  = (PRE,API),
;          GROUP = MSP,
;          DESC  = "(OS) DosSubSet Pre-Invocation",
;          MEM   = (<R3SP_ARGS>+4,D,2),
;          MEM   = (<R3SP_ARGS>+2,D,2),
;          MEM   = (<R3SP_ARGS>+0,D,2),
;          FMT   = "Selector = <FMT_C_WORD>  Flags = <FMT_C_WORD>",
;          FMT   = "Size = <FMT_C_WORD>"

;***DT  postDOSSUBSET - DosSubSet post-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_POST_DOSSUBSET>,
;          TP    = .postDOSSUBSET,
;          TYPE  = (POST,API),
;          GROUP = MSP,
;          DESC  = "(OS) DosSubSet Post-Invocation",
;          REGS  = (AX),
;          FMT   = "Return Code = <FMT_WORD>"

public  DOSSUBSET
DOSSUBSET      proc    far
; align 4 54686
    assume  cs:DOSSUBCODE

    push    bp
    mov     bp,sp

;   save registers used


    push    bx
    push    cx
    push    dx
    push    si
    push    di
    push    ds

    MMPHPreDOSSubSet            ; Performance Hook (241F)

;   check if flags are valid

    test    [bp].SetFlags, not 1
    .if nz
        mov     ax, ERROR_DOSSUB_BADFLAG
        jmp     sdone
    .endif

    mov     ax, [bp].SetSize    ;   ax = newsize of MspSegment

;   round up request size to multiple of 4

    add     ax, 3
    and     ax, not 3

;   check if size is between 0 and ROOTSIZ+MINSIZ

    .if < ax a 0 > AND
    .if < ax b ROOTSIZ+MINSIZ >
        mov     ax, ERROR_DOSSUB_BADSIZE
        jmp     sdone
    .endif

;   subtract ROOTSIZ to prepare for 64K size checks


    sub     ax, ROOTSIZ


;   address MSP segment

    mov     ds, [bp].SetSeg     ;   ds:bx = MspSegment

    .if < [bp].SetFlags eq INIT >

        xor     bx,bx               ;   bx = ptr to first free block
        mov     [bx].MspNext, ROOTSIZ;   1st & last free block
        mov     [bx].MspSize, 0     ;   size of 1st free block
        mov     [bx].MspSegSize, ax ;   init size of MspSegment
        mov     word ptr ds:[0].MspSem,0    ;  @@
        mov     word ptr ds:[0].MspSem+2,0  ;  @@
        add     bx, ROOTSIZ         ;   move to next free block
        mov     [bx].MspNext, 0     ;   make this one last
        mov     [bx].MspSize, ax    ;   fill in size - ROOTSIZ
        xor     ax, ax              ;   ax = NO_ERROR
    .else                           ;   SET Condition
ifndef FamilyAPI
        push    ax                  ;   save ax
        push    ds                  ;   @@ semaphore handle  (DWORD)
        push    [0].MspSem          ;   @@ semaphore handle
        push    -1                  ;   @@ time out (infinite wait) (DWORD)
        push    -1                  ;   @@ time out (infinite wait)
        call    FAR PTR DosSemRequest       ;   @@ DosSemRequest(ds:[0].MspSem)
        pop     ax                  ;   @@ restore ax
endif

        mov     dx, ds:[0].MspSegSize ; dx = oldsize

        .if < ax a dx >                 ;   If Grow

            xor     bx, bx

            .while < [bx].MspNext ne 0 >;   Search to last free block
                mov     bx, [bx].MspNext
            .endwhile

            mov     ds:[0].MspSegSize, ax ;   set new seg size in 1st block
            sub     ax, dx              ;   ax = change in size of segment
            add     dx, ROOTSIZ
            mov     di, bx              ;   get pointer to last free block

            .if < bx ne 0 >
                add     di, [bx].MspSize    ;   get top of last block
            .endif

            .if < di eq dx >  ;   If top of last = OldMspSegSize
                add     [bx].MspSize, ax;   Grow last free block
            .else
                                            ;   else create a new last
                mov     di, dx              ;   new is at old seg end
                mov     [di].MspNext, 0     ;   mark last block
                mov     [di].MspSize, ax    ;   set size to change in seg
                mov     [bx].MspNext, di    ;   link new block in to free
            .endif
            xor     ax, ax              ;   ax = NO_ERROR

        .else
            .if ne                      ;   if ax ne dx ERROR_DOSSUB_SHRINK
                    mov     ax, ERROR_DOSSUB_SHRINK
            .else
                    xor     ax, ax      ;   ax = NO_ERROR
            .endif
        .endif

ifndef FamilyAPI
        push    ax
        push    ds                  ;   @@ semaphore handle  (DWORD)
        push    [0].MspSem          ;   @@ semaphore handle
        call    FAR PTR DosSemClear ;   @@ DosSemClear(ds:[0].MspSem)
        pop     ax
endif

    .endif

sdone:
        PUBLIC  postDOSSUBSET
postDOSSUBSET:                  ; RAS post-invocation trace
    MMPHPostDOSSubSet           ; Performance Hook (249F)

    ;   restore register state
    pop     ds
    pop     di
    pop     si
    pop     dx
    pop     cx
    pop     bx

    pop     bp

    ret     6

DOSSUBSET      endp

DOSSUBCODE   ends
        end

