;       SCCSID = @(#)thk32bmp.asm       6.1 90/11/17
;      SCCSID = @(#)thk32bmp.asm        1.7 90/02/06
;
;        IBM/Microsoft Confidential
;
;        Copyright (c) IBM Corporation 1987, 1989
;        Copyright (c) Microsoft Corporation 1987, 1989
;
;        All Rights Reserved
;
;
;***************************************************************************


;***  Change History                                              ***
;***   noto  11/18/93  Define which segment for Page Tuning DLL   ***
;***                                                              ***



include basemaca.inc
include bsememf.inc
include dc1segs.inc                                             ; DCR 704


CODE32_TUNE1  SEGMENT DWORD USE32 PUBLIC 'CODE'
        EXTRN   DOS32ALLOCMEM:NEAR
        EXTRN   DOS32SETMEM:NEAR
CODE32_TUNE1  ENDS

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


;***************************************************************************
;
; Block allocation package for the 32 bit thunk layer.
;
;
;These routines implement a very fast block allocator for use at
;ring 3. These routines are very closely tied to the thunk layer,
;and are private entry points.
;
;On first call to AllocBlock, we reserve 64k, and the first page
;is committed. Each additional page is committed as needed. Once
;a page has been committed, it stays committed for the duration of
;the process.
;
;If the condition arises where the first 64k of memory is consumed,
;then another 64k block is allocated, and is linked to the first via
;a pointer in the first DWORD of the BMP area. Additional 64k blocks
;may be linked as needed.
;
;
;- Each page of the allocation area contains 16 - 128 byte blocks.
;
;- The bitmap for each page fits into a single 32 bit word.
;
;- The first block of a 64k area is a header, and contains a bitmap of the
;  entire block allocation area, and a chain to the next block.
;
;  Note that the first page of a 64k block is always committed, so the page 0
;  committed flag is actually used as a link to the next allocated block.
;  A link with value 0xffffffff is a end of list marker.
;
;The layout of the first block is:
;
;
;+-----------------------+      0
;| page 0 Comitted flag  | <** Actually a pointer to next block
;+-----------------------+      4
;| Bitmap of page 0     |
;+-----------------------+      8
;| page 1 Comitted flag  |
;+-----------------------+      12
;| Bitmap of page 1     |
;+-----------------------+      16
;| page 2  Comitted flag |
;+-----------------------+      20
;| Bitmap of page 2     |
;+-----------------------+      ...
;|      ...             |
;+-----------------------+      ...
;       ...
;       ...
;+-----------------------+      128
;|                      |
;|                      |
;|                      |
;|                      |
;|                      |
;|      DATA            |
;|                      |
;|                      |
;|                      |
;+-----------------------+      256
;|                      |
;|                      |
;|                      |
;|                      |
;|                      |
;|      DATA            |
;|                      |
;|                      |
;|                      |
;|                      |
;+-----------------------+      384
;       ...
;
;
;- Initially, the semaphore is clear, and the address of the BMP area is
;  NULL.
;
;  Note that the first area in the chain is special, in that its pointer
;  is stored in a DWORD in the DATA segment. All subsequent 64k blocks
;  are stored in the previous block.
;
;- On allocation of the BMP area, Block zero of Page zero is allocated to
;  hold the bitmaps.
;
;- Bitmaps use a 1 to indicate available, 0 to indicate allocated.
;
;
;***************************************************************************

        .386

DATA16          SEGMENT                                         ; DCR 704

thkbmpSemaphore DD      0               ;Semaphore init clear
thkbmpAddr      DD      0               ;Address of BMP area

DATA16          ENDS




CODE32_TUNE2  SEGMENT                                           ; DCR 704

;** thk32AllocBlock
;
; Allocates one block from the thunk BMP area.
;
; ENTRY         NONE
;
; EXIT
;
;       EAX =   -1 = Could not allocate block
;                0 = Success
;               Other = Error code propagated from system
;
;       IF Success
;               EDI holds linear address to block
;
; USES  EAX EDI
;***************************************************************************

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

Procedure THK32ALLOCBLOCK,near

        push    ecx                     ;Save scratch registers
        push    edx

thk60:  xor     eax,eax                 ;Try grabbing semaphore
        dec     eax
        xchg    eax,DWORD PTR thkbmpSemaphore
        or      eax,eax
        jnz     thkSleep

        mov     edx,thkbmpAddr          ;get address of first page
        or      edx,edx                 ;Has memory been allocated?
        jz      thkInitiateBlock        ;Jump if not

thk63:
        xor     ecx,ecx                 ;Preset page counter to -1
        dec     ecx

thk65:  inc     ecx                     ;Try next page
        cmp     cl,16                   ;Must be < 16 pages
        je      thkNoBlocksLeft         ;No more pages available

;Test to see if page ECX has been committed
;The flag is at thkbmppage0 + page# * 8

        test    DWORD PTR [edx+(ecx*8)],-1
        jz      thkCommit

;Test to see if any blocks are available in page ECX
;The bits are at (thkbmppage0 + page# * 8 + 4)

        test    DWORD PTR [edx+(ecx*8)+4],0FFFFFFFFh

        jz      short thk65             ;Jump if all allocated

;Because of the above test,
;We are insured of finding at least one bit set

        bsf     edi,DWORD PTR [edx+(ecx*8)+4]
        btr     DWORD PTR [edx+(ecx*8)+4],edi

thk80:                          ;Calculate address of block
        shl     ecx,12          ;ecx gets page offset
        shl     edi,7           ;eax gets block offset

        add     edi,ecx
        add     edi,edx         ;Address of block
        xor     eax,eax

; This is the exit routine which must be executed by all paths
; Clear the semaphore and restore the two scratch registers

thkAllocExit:
        xor     ecx,ecx
        mov     thkbmpSemaphore,ecx             ;Clear semaphore
        pop     edx
        pop     ecx
        ret

;If the semaphore was taken, then it was taken by another thread
;of current process that is in one of these routines. Sleeping for
;1 time will block this thread, and allow the other to finish.
;The 1 is required, since we want to insure that this thread sleeps.
;If we use 0, and this thread is the highest priority thread, then
;we will deadlock.

thkSleep:
        push    1                       ;Give up time slice
        call    Dos32Sleep              ;by sleeping for 1 millisec

        add     esp,4                   ;Remove param
        jmp     thk60

;If failure, then eax = -1 and exit

thkError:
        mov     eax,-1                  ;Failure exit routine
        jmp     short thkAllocExit


;This label is called on the first call to the allocation routine. This
;is the special case where no blocks currently exist. We need to fake
;the address of the current block, so that the pointer to the list will
;be placed in the DATA segment.

thkInitiateBlock:

        mov     edx,offset FLAT:thkbmpAddr

;Here, we must make two system calls. The first reserves a 64k section
;of memory by calling AllocMem. The second call commits the first page
;of the region allocated.

thkAllocMoreMem:                        ;Entry point for allocation

        push    edx                     ;Save target address


        push    (OBJ_TILE OR PAG_READ OR PAG_WRITE)

        push    0ffffh                  ;Reserve 64k of address space

        push    edx                     ;Address to store allocation

        call    DOS32ALLOCMEM

        add     esp,12                  ;Remove parameters from stack

        pop     edx                     ;Restore target address

        mov     edx,[edx]               ;Get address of BMP

        or      eax,eax
        jnz     thkAllocExit            ;Error out

;We now have a valid address in edx. We need to commit the
;first page before we touch it.
;We preset the block number on this page to be block #1
;This reserves the bitmap block from the newly created BMP area.

        xor     ecx,ecx                 ;Set page # to zero
        push    1                       ;Set to block #1 in first page
        jmp     short thk74



;This label is only called when a page must be committed.
;This entry point is used when a page is committed. We already know
;that the block #0 will be empty.

thkCommit:
        push    0                       ;Push the block #


thk74:
        push    ecx                     ;save page #
        push    edx                     ;Save base address

        push    (PAG_COMMIT OR PAG_READ OR PAG_WRITE)

        push    4096                    ;Setmem on one page

                                        ;Calc region offset based on page
        shl     ecx,12                  ;number
        add     ecx,edx                 ; Add base address
        push    ecx
        call    DOS32SETMEM
        add     esp,12

        pop     edx                     ;Recall base address
        pop     ecx                     ;Recall page #
        or      eax,eax                 ;Check for error
        jnz     thkAllocExit                    ;Jump if error

;Set page committed flag to -1. EAX is assumed zero from above test

        dec     eax                     ;Flag page as committed
        mov     DWORD PTR [edx+(ecx*8)],eax

;Set bitmap to so that block 0 is allocated.

;If this is the first block in the 64k area, then
;Block zero holds the bitmaps and allocation flags
;
;Otherwise, block zero is allocated for the user.

        dec     eax                     ;eax=0xfffffffe

        mov     DWORD PTR [edx+(ecx*8)+4],eax

;At this point, ecx has page #
;Now, jump back into find block and complete call
;But first, load edi with the block number in this page

        pop     edi                     ;Load the block number
        or      edi,edi

;If edi is not zero, then this is the first page in a 64k BMP area.
;If it is the first page, then we need to allocate block 1 for the user

        jz      thk80

        btr     DWORD PTR [edx+(ecx*8)+4],1

        jmp     thk80

; At this point, we have determined that there are no blocks left to be
; allocated. We try picking up the next BMP area from the chain link in
; the current block.
;

thkNoBlocksLeft:

        mov     eax,edx                 ;Save current block ptr
        mov     edx,[edx]               ;Get chain to next block

        cmp     edx,-1                  ;Is it -1?
        jne     thk63                   ;If not, it is a pointer to
                                        ;next chain.

;
; At this point, the chain pointer is -1, and therefore we need to alloc
; another block and place it into the chain.
;
        mov     edx,eax
        jmp     thkAllocMoreMem

EndProc THK32ALLOCBLOCK

CODE32_TUNE2 ENDS




CODE32_TUNE1  SEGMENT


;** thk32FreeBlock
;
;       Deallocate a block in the thunk BMP area.
;
;
; ENTRY ESI = Block address to deallocate
;
; EXIT:
;       No Error
;               EAX = 0
;
;
;       Error
;               EAX = -1
;
; USES  ESI,EAX,EDX
;
;***************************************************************************


        ASSUME  CS:FLAT,DS:FLAT,ES:FLAT,SS:FLAT

Procedure THK32FREEBLOCK,near

        push    ecx                     ;Save scratch register
thk95:
        xor     ecx,ecx
        inc     ecx                     ;Try to grab semaphore
        xchg    ecx,DWORD PTR thkbmpSemaphore
        or      ecx,ecx
        jnz     thkFreeSleep                    ;Block if didn't get


; here, we have semaphore. Check the block pointer to be freed against
; the lower bound of the BMP area.

        mov     edx,thkbmpAddr          ;get address of first page
        or      edx,edx
        jz      thkFreeError

        mov     eax,esi                 ;Save it in EAX for possible reload
                                        ;if a link must be loaded

thk96:
        cmp     esi,edx                 ;Check lower bound of BMP area
        jb      thkFreeLink             ;Its too low, try the next block


; Calculate the page and block number based on the address passed in.
;
        sub     esi,edx                 ;eax gets offset
        mov     ecx,0FFFh               ;Load page mask
        and     ecx,esi                 ;get page offset
        shr     ecx,7                   ;div 128 gets block #
        shr     esi,12                  ;div 4096 gets page #

; The page # is used to check the upper bound.
;
        cmp     esi,15                  ;check upper bound
        ja      thkFreeLink             ;Page # > BMP package

;At this point, eax holds page #, ebx holds block #
;
        test    DWORD PTR [edx+(esi*8)],-1
        jz      thkFreeError            ;Block has not been comitted

        bts     [edx+(esi*8)+4],ecx
        jc      thkFreeError            ;If carry set, then block wasn't
                                        ;allocated in the first place
        xor     eax,eax                 ;return code

thkFreeExit:
        mov     thkbmpSemaphore,0       ;Clear semaphore
        pop     ecx
        ret

;If the semaphore was taken, then it was taken by another thread
;of current process that is in one of these routines. Sleeping for
;1 ms will block this thread, and allow the other to finish.

thkFreeSleep:
        push    1                       ;Give up time slice
        call    DOS32SLEEP
        add     esp,4                   ;Remove parameter
        jmp     short thk95


; If the block wasn't addressed inside the current BMP area, then it is one of
; two things.
;
; Either it is a completely random number, or it is an address
; that was allocated in another BMP area.
;


thkFreeLink:

        mov     esi,eax                 ;Restore the original pointer
        mov     edx,[edx]
        cmp     edx,-1
        jne     thk96

thkFreeError:
        mov     eax,-1                  ;Failure return
        jmp     short thkFreeExit



EndProc THK32FREEBLOCK

CODE32_TUNE1 ENDS

        END
