;       SCCSID = @(#)smrgetme.asm       6.2 91/05/03
;       SCCSID @(#)smrgetme.asm 6.2 91/05/03
PAGE 60,132    ;
; ****************************************************************************
; *                                                                          *
; *                       IBM/Microsoft Confidential                         *
; *                                                                          *
; *                 Copyright (c) IBM Corporation  1987, 1990                *
; *                 Copyright (c) Microsoft Corp.  1987, 1990                *
; *                           All Rights Reserved                            *
; *                                                                          *
; ****************************************************************************

;***************************************************************************
; Source File Name:     SMRRETME.ASM
;
; Subroutine Name:      DOSSMGETMEM
;
; Descriptive Name:     Get memory from the Session Manager's heap.
;
; Function:     Manages memory requests from the base.  Memory is
;               suballocated from a segment the Session Manager
;               allocated.  Will allocate another segment if required.
;               The size of the memory is placed in the first word.
;               The real size will be 2 bytes larger than the requested
;               size to accomidate the first word being the size.
;
; Notes:
;       Dependencies:   None.
;
;       Restrictions:   Can not request more than 64K - 2 bytes.
;
; Change History:
;
;   @T52 02/09/90 TPL DCR 704 work
;   @T17 04/23/90 TPL More fixes for SM02608
;   CJ26 05/03/91 PTR B722478 Need access to DosSMGetMem selectors.
;   @V2.1SEN01 04/16/93 PTR D67459 If selector created by an application process,
;                                  other processes have no access to it.
;   MDLL 01/10/94 BJN  Page Tune segments for Merged DLL.
;   103213 11/10/94 Changeteam  see defect 88899
;                   Added DosSubSet after DosAllocSeg that was added above
;
; Entry Point:
;       Linkage:  EXTRN DOSSMGETMEM:FAR
;                       PUSH@   DWORD  Address (Returned)
;                       PUSH    WORD   Size
;                       CALL    DOSSMGETMEM
;
; Inputs:
;       WORD    Address     Address to put new Selector:Offset in.
;       WORD    Size        Size of request in bytes.
;
; Exit Normal:
;       AX      ReturnCode Value = 0, memory allocated
;
; Exit Errors:
;       AX      ReturnCode    DosSubAlloc    return codes
;                             DosReAllocSeg  return codes
;                             DosAllocSeg    return codes
;
; Effects:      All other registers preserved.
;
; Internal References:
;
;          Routines:
;
; External References:
;          Routines:
;
;***************************************************************************
ifdef I1
;
;  Repeat
;    Point to 1st memory selector
;    If Selector not allocated yet
;      DOSALLOCSEG (4K, Givable, Getable,)
;
;      If No Error
;        Save new segment Size
;        DOSSUBSET
;      Else
;        Just exit with bad Return Code
;      EndIf
;    EndIf
;
;    Add 2 bytes to the size for the reserved word, and make
;    size a multiple of 4, so we can use STOSD instead of STOSB
;    DOSSUBALLOC (Passing Offset Back to Caller)
;
;    If No Error
;      Copy Selector to caller's data
;      0 out memory (using STOSD)
;    Else                              ; DosSubAlloc Failed
;      If Segment <= 60K (ie. we can grow it by 4K)
;        DOSREALLOCSEG
;
;        If No Error
;          Save new segment size
;          DOSSUBSET
;        Else
;          Point to next segment
;        EndIf
;      Else This Seg > 60K, so try another seg
;        Point to next segment
;      EndIf
;
;    EndIf
;  Until DosSubAlloc Worked OR
;  Until Out of memory segments
;
;***************************************************************************
else

.386P

   SESMGR   EQU  -1                     ; Needed for pmshl.inc

   EXTRN MemorySegments : WORD          ; Array of 64K segments
   EXTRN SegmentSizes   : WORD          ; Array of sizes of MemorySegments

   EXTRN DOSSMINITSGCB  : NEAR          ; Initialization Routine
   EXTRN DOSALLOCSEG    : FAR
   EXTRN DOSREALLOCSEG  : FAR
   EXTRN DOSSUBALLOC    : FAR
   EXTRN DOSSUBSET      : FAR
   EXTRN DOSGETSEG      : FAR                                           ;CJ26
.xlist
   INCLUDE SMRGETME.INC
.xcref
   INCLUDE ERROR2.INC
   INCLUDE STRUC.INC
   INCLUDE SMREQUS.INC           ; General Equates

CDATA   EQU     <OS2CHAR>                                               ;MDLL
CDATA   SEGMENT WORD PUBLIC 'FAR_DATA' USE16                            ;MDLL
CDATA   ENDS                                                            ;@T52

.cref
.list

CODE_TUNE1   SEGMENT WORD PUBLIC 'CODE' USE16                           ;MDLL
   ASSUME CS:CODE_TUNE1,DS:CDATA,SS:NOTHING,ES:NOTHING                  ;MDLL

    PUBLIC   DOSSMGETMEM
DOSSMGETMEM  PROC FAR

;************************************************************************
;*  First try suballocating from what we have.  If that fails, see if   *
;*  we are still under 64K, then reallocate.  If we are too close to    *
;*  64K, the we'll have to allocate a new 4K segment.  Reallocate,      *
;*  and Allocate in 4K pages.                                           *
;*                                                                      *
;*  Data used:                                                          *
;*  SegmentSizes   DW  #_Of_64K_Segments DUP(0) ; Size of each segment  *
;*  MemorySegments DW  #_Of_64K_Segments DUP(0) ; Selector of segments  *
;*  @V2.1SEN01: If the new selector was allocated on behalf of an app.  *
;*  process, then once that process dies, the selector becomes invalid. *
;*  Logic here extended to remove the inalid selector if access to it   *
;*  appended and allocate a new one for the process.                    *
;************************************************************************

   Local_Bytes  EQU  0
   ENTER Local_Bytes,0

   PUSHA                               ; Save Registers
   PUSH  DS
   PUSH  ES

   MOV  AX, CDATA                                                       ;@T52
   MOV  DS, AX

   LES  DI, SS:[BP].GetMemAddress      ; Where to store Offset
   XOR  SI, SI                         ; Start with first segment
   MOV  CX, 1                          ; Flag to stay in .REPEAT loop

   .REPEAT
     MOV  DX, MemorySegments[SI]       ; Get Selector into register for speed
     .IF < zero DX>                    ; Selector not allocated yet     ;@T17
       MOV  BX, 4 * 1024               ; 4K
       LEA  AX, MemorySegments[SI]     ; Get @ to put selector into
;@T17  ADD  AX, SI                     ; AX = Base + Index

       PUSH BX                         ; Create a 4K segment
       PUSH DS                         ; @ of where to put selector
       PUSH AX
       PUSH 00000011B                  ; Givable, Getable Segment
       CALL DOSALLOCSEG

       .IF < zero AX >                 ; If DosAllocSeg worked          ;@T17
         MOV  SegmentSizes[SI], BX     ; Save new segment Size
         MOV  DX, MemorySegments[SI]   ; Get Selector into register for speed

         PUSH DX                       ; Selector
         PUSH 1                        ; Indicate initializing Segment
         PUSH BX                       ; Size of segment
         CALL DOSSUBSET                ; SHOULDN'T get any errors from here.
       .ELSE                           ; DosAllocSeg failed
         jmp MemExit                   ; Just exit with bad Return Code
       .ENDIF                          ; DosAllocSeg Test
     .ELSE                             ; Need access to selector        ;CJ26
       VERW DX                         ; Do we have access?             ;CJ26
       .IF  < nz >                     ; no, get access to it           ;CJ26
         PUSH  DX                                                       ;CJ26
         CALL  DosGetSeg                                                ;CJ26
         .IF < AX ne 0>                 ; If DosGetSeg failed            ;@2.1SEN01
                                        ; remove the bogus selector and allocate a new one.
           LEA  AX, MemorySegments[SI]  ; Get @ to put selector into
           MOV  BX, 4 * 1024            ; 4K
           PUSH BX                      ; Create a segment of the 4K size.
           PUSH DS                      ; @ of where to put selector
           PUSH AX
           PUSH 00000011B               ; Givable, Getable Segment
           CALL DOSALLOCSEG
           .IF < zero AX >              ; If DosAllocSeg worked
             MOV DX, MemorySegments[SI] ;
             MOV SegmentSizes[SI], BX   ; Save new segment size.

             PUSH DX                    ; Selector                      ;88899
             PUSH 1                     ; Indicate initializing Segment ;88899
             PUSH BX                    ; Size of segment               ;88899
             CALL DOSSUBSET             ; SHOULDN'T get any errors      ;88899
           .ELSE
             .LEAVE                     ; Just exit with bad Return Code
           .ENDIF                       ; DosAllocSeg Test
         .ENDIF
       .ENDIF                                                           ;CJ26
     .ENDIF                            ; Selector allocated test

;    ***************************************************************
;    *  If there was an error from DosAllocSeg, DX will be 0, and  *
;    *  DosSubAlloc, and DosRealloc will simply return an error,   *
;    *  then fall out of the loop.  To be absolutly "clean" we     *
;    *  should put a .IF <CX ne 0> test before the next section,   *
;    *  but we shouldn't need it since the next two DOS calls will *
;    *  fail with a 0 selector.                                    *
;    ***************************************************************
     MOV  BX, SS:[BP].GetMemSize       ; Number of Bytes to allocate

;    ***************************************************************
;    *  Add 2 bytes to the size for the reserved word, and make    *
;    *  size a multiple of 4, so we can use STOSD instead of STOSB *
;    ***************************************************************
     ADD  BX, 6                        ; Add 2 + 4 bytes
     AND  BX, NOT 3                    ; BX = BX DIV 4

     PUSH DX                           ; Selector of a 64K heap
     PUSH ES                           ; Return Offset here (ES:DI)
     PUSH DI
     PUSH BX                           ; Number of Bytes to allocate
     CALL DOSSUBALLOC

     .IF < zero AX>                                                     ;@T17
       XOR  EAX, EAX                   ; Make sure all 32 bits are clear
       MOV  ES:[DI][2], DX             ; Copy Selector to caller's data
;      ******************
;      *  0 out memory  *
;      ******************
       LES  DI, ES:[DI]                ; ES:[DI] --> Block of memory
       MOV  CX, BX                     ; CX = byte count
       SHR  CX, 2                      ; Since we'll do a DWORD at a time
       PUSH DI                         ; Save offset
       CLD
       REP  STOSD                      ; Store a 0
       POP  DI                         ; Restore pointer
       MOV  ES:[DI], BX                ; Put size in first word
     .ELSE                             ; DosSubAlloc Failed
       MOV  BX, SegmentSizes[SI]       ; Get size
       ADD  BX, 4 * 1024               ; Increase by 4K
       .IF < nc >                      ; If <= 60K (ie. we can grow it by 4K)
         PUSH BX                       ; Number of bytes to realloc
         PUSH DX                       ; Selector of 64K heap
         CALL DOSREALLOCSEG            ; Try to grow the segment

         .IF < zero AX>                ; DosReallocSeg worked so do DosSubSet
           MOV  SegmentSizes[SI], BX   ; Save new segment size
           PUSH DX                     ; Selector
           PUSH 0                      ; Indicate increasing size of Segment
           PUSH BX                     ; size of segment
           CALL DOSSUBSET              ; SHOULDN'T get any errors from here.
         .ELSE                         ; If error on DosReallocSeg
           ADD  SI, TYPE MemorySegments; Try again with the next segment
         .ENDIF                        ; Test for error on DosReallocSeg
       .ELSE                           ; This Seg > 60K, so try another seg
         ADD  SI, TYPE MemorySegments  ; Try again with the next segment
       .ENDIF                          ; Segments testing

     .ENDIF                            ; DosSubAlloc Test
   .UNTIL < CXZ > NEAR OR              ; DosSubAlloc Worked
   .UNTIL < SI ae <MaxSMSegments * (TYPE MemorySegments)> > NEAR ; Out of memory
MemExit:
   POP  ES                             ; Restore Registers
   POP  DS

   MOV AX_Reg[-Local_Bytes], AX        ; Save AX on stack for POPA
   POPA

   LEAVE
   RET    GetMemLen                     ; Return to caller

DOSSMGETMEM  ENDP
CODE_TUNE1   ENDS                                                       ;MDLL
endIf
   END
