The Art of
ASSEMBLY LANGUAGE PROGRAMMING

Chapter Nineteen (Part 4)

Table of Content

Chapter Nineteen (Part 6)

CHAPTER NINETEEN:
PROCESSES, COROUTINES AND CONCURRENCY (Part 5)
19.2.2 - Dynamic Shared Memory

19.2.2 Dynamic Shared Memory


Although the static shared memory the previous section describes is very useful, it does suffer from a few limitations. First of all, any program that uses the global shared segment must be aware of the location of every other program that uses the shared segment. This effectively means that the use of the shared segment is limited to a single set of cooperating processes at any one given time. You cannot have two independent sets of programs using the shared memory at the same time. Another limitation with the static system is that you must know the size of all variables when you write your program, you cannot create dynamic data structures whose size varies at run time. It would be nice, for example, to have calls like shmalloc and shmfree that let you dynamically allocate and free memory in a shared region. Fortunately, it is very easy to overcome these limitations by creating a dynamic shared memory manager.

A reasonable shared memory manager will have four functions: initialize, shmalloc, shmattach, and shmfree. The initialization call reclaims all shared memory in use. The shmalloc call lets a process allocate a new block of shared memory. Only one process in a group of cooperating processes makes this call. Once shmalloc allocates a block of memory, the other processes use the shmattach call to obtain the address of the shared memory block. The following code implements a dynamic shared memory manager. The code is similar to that appearing in the Standard Library except this code allows a maximum of 64K storage on the heap.

; SHMALLOC.ASM
;
; This TSR sets up a dynamic shared memory system.
;
; This TSR checks to make sure there isn't a copy already active in
; memory. When removing itself from memory, it makes sure there are
; no other interrupts chained into INT 2Fh before doing the remove.
;
;
;
; The following segments must appear in this order and before the
; Standard Library includes.

ResidentSeg     segment para public 'Resident'
ResidentSeg     ends

SharedMemory    segment para public 'Shared'
SharedMemory    ends

EndResident     segment para public 'EndRes'
EndResident     ends

                .xlist
                .286
                include         stdlib.a
                includelib      stdlib.lib
                .list


; Resident segment that holds the TSR code:

ResidentSeg     segment para public 'Resident'
                assume  cs:ResidentSeg, ds:nothing


NULL            equ     0


; Data structure for an allocated data region.
;
; Key-  user supplied ID to associate this region with a particular set
;       of processes.
;
; Next- Points at the next allocated block.
; Prev- Points at the previous allocated block.
; Size- Size (in bytes) of allocated block, not including header structure.

Region          struct
key             word    ?
next            word    ?
prev            word    ?
blksize         word    ?
Region          ends

Startmem        equ     Region ptr [0]

AllocatedList   word    0               ;Points at chain of alloc'd blocks.
FreeList        word    0               ;Points at chain of free blocks.

; Int 2Fh ID number for this TSR:

MyTSRID         byte    0
                byte    0               ;Padding so we can print it.

; PSP is the psp address for this program.

PSP             word    0

OldInt2F        dword   ?


; MyInt2F-      Provides int 2Fh (multiplex interrupt) support for this
;               TSR. The multiplex interrupt recognizes the following
;               subfunctions (passed in AL):
;
;               00h- Verify presence.   Returns 0FFh in AL and a pointer
;                                       to an ID string in es:di if the
;                                       TSR ID (in AH) matches this
;                                       particular TSR.
;
;               01h- Remove.            Removes the TSR from memory.
;                                       Returns 0 in AL if successful,
;                                       1 in AL if failure.
;
;               11h- shmalloc           CX contains the size of the block
;                                       to allocate.
;                                       DX contains the key for this block.
;                                       Returns a pointer to block in ES:DI
;                                       and size of allocated block in CX.
;                                       Returns an error code in AX. Zero
;                                       is no error, one is "key already
;                                       exists," two is "insufficient
;                                       memory for request."
;
;               12h- shmfree            DX contains the key for this block.
;                                       This call frees the specified block
;                                       from memory.
;
;               13h- shminit            Initializes the shared memory system
;                                        freeing all blocks currently in
;                                        use.
;
;               14h- shmattach          DX contains the key for a block.
;                                       Search for that block and return
;                                       its address in ES:DI. AX contains
;                                       zero if successful, three if it
;                                       cannot locate a block with the
;                                       specified key.

MyInt2F         proc    far
                assume  ds:nothing

                cmp     ah, MyTSRID     ;Match our TSR identifier?
                je      YepItsOurs
                jmp     OldInt2F

; Okay, we know this is our ID, now check for a verify, remove, or
; return segment call.

YepItsOurs:     cmp     al, 0                   ;Verify Call
                jne     TryRmv
                mov     al, 0ffh        ;Return success.
                lesi    IDString
                iret                            ;Return back to caller.

IDString        byte    "Dynamic Shared Memory TSR",0

TryRmv:         cmp     al, 1                   ;Remove call.
                jne     Tryshmalloc

; See if we can remove this TSR:

                push    es
                mov     ax, 0
                mov     es, ax
                cmp     word ptr es:[2Fh*4], offset MyInt2F
                jne     TRDone
                cmp     word ptr es:[2Fh*4 + 2], seg MyInt2F
                je      CanRemove               ;Branch if we can.
TRDone:         mov     ax, 1                   ;Return failure for now.
                pop     es
                iret

; Okay, they want to remove this guy *and* we can remove it from memory.
; Take care of all that here.

                assume  ds:ResidentSeg

CanRemove:      push    ds
                pusha
                cli                             ;Turn off the interrupts while
                mov     ax, 0                   ; we mess with the interrupt
                mov     es, ax                  ; vectors.
                mov     ax, cs
                mov     ds, ax

                mov     ax, word ptr OldInt2F
                mov     es:[2Fh*4], ax
                mov     ax, word ptr OldInt2F+2
                mov     es:[2Fh*4 + 2], ax


; Okay, one last thing before we quit- Let's give the memory allocated
; to this TSR back to DOS.

                mov     ds, PSP
                mov     es, ds:[2Ch]            ;Ptr to environment block.
                mov     ah, 49h                 ;DOS release memory call.
                int     21h

                mov     ax, ds                  ;Release program code space.
                mov     es, ax
                mov     ah, 49h
                int     21h

                popa
                pop     ds
                pop     es
                mov     ax, 0                   ;Return Success.
                iret



; Stick BadKey here so that it is close to its associated branch (from below).
;
; If come here, we've discovered an allocated block with the
; specified key. Return an error code (AX=1) and the size of that
; allocated block (in CX).

BadKey:         mov     cx, [bx].Region.BlkSize
                mov     ax, 1                   ;Already allocated error.
                pop     bx
                pop     ds
                iret


; See if this is a shmalloc call.
; If so, on entry -
; DX contains the key.
; CX contains the number of bytes to allocate.
;
; On exit:
;
; ES:DI points at the allocated block (if successful).
; CX contains the actual size of the allocated block (>=CX on entry).
; AX contains error code, 0 if no error.

Tryshmalloc:    cmp     al, 11h                 ;shmalloc function code.
                jne Tryshmfree

; First, search through the allocated list to see if a block with the
; current key number already exists. DX contains the requested key.

                assume  ds:SharedMemory
                assume  bx:ptr Region
                assume  di:ptr Region

                push    ds
                push    bx
                mov     bx, SharedMemory
                mov     ds, bx

                mov     bx, ResidentSeg:AllocatedList
                test    bx, bx                  ;Anything on this list?
                je      SrchFreeList

SearchLoop:     cmp     dx, [bx].Key            ;Key exist already?
                je      BadKey
                mov     bx, [bx].Next           ;Get next region.
                test    bx, bx                  ;NULL?, if not, try another
                jne     SearchLoop              ; entry in the list.

; If an allocated block with the specified key does not already exist,
; then try to allocate one from the free memory list.

SrchFreeList:   mov     bx, ResidentSeg:FreeList
                test    bx, bx                  ;Empty free list?
                je      OutaMemory

FirstFitLp:     cmp     cx, [bx].BlkSize        ;Is this block big enough?
                jbe     GotBlock
                mov     bx, [bx].Next           ;If not, on to the next one.
                test    bx, bx                  ;Anything on this list?
                jne     FirstFitLp

; If we drop down here, we were unable to find a block that was large
; enough to satisfy the request. Return an appropriate error

OutaMemory:     mov     cx, 0                   ;Nothing available.
                mov     ax, 2                   ;Insufficient memory error.
                pop     bx
                pop     ds
                iret

; If we find a large enough block, we've got to carve the new block
; out of it and return the rest of the storage to the free list. If the
; free block is at least 32 bytes larger than the requested size, we will
; do this. If the free block is less than 32 bytes larger, we will simply
; give this free block to the requesting process. The reason for the
; 32 bytes is simple: We need eight bytes for the new block's header
; (the free block already has one) and it doesn't make sense to fragment
; blocks to sizes below 24 bytes. That would only increase processing time
; when processes free up blocks by requiring more work coalescing blocks.

GotBlock:       mov     ax, [bx].BlkSize        ;Compute difference in size.
                sub     ax, cx
                cmp     ax, 32                  ;At least 32 bytes left?
                jbe     GrabWholeBlk            ;If not, take this block.

; Okay, the free block is larger than the requested size by more than 32
; bytes. Carve the new block from the end of the free block (that way
; we do not have to change the free block's pointers, only the size.

                mov     di, bx
                add     di, [bx].BlkSize        ;Scoot to end, minus 8
                sub     di, cx                  ;Point at new block.

                sub     [bx].BlkSize, cx        ;Remove alloc'd block and
                sub     [bx].BlkSize, 8         ; room for header.

                mov     [di].BlkSize, cx        ;Save size of block.
                mov     [di].Key, dx            ;Save key.

; Link the new block into the list of allocated blocks.

                mov     bx, ResidentSeg:AllocatedList
                mov     [di].Next, bx
                mov     [di].Prev, NULL         ;NULL previous pointer.
                test    bx, bx                  ;See if it was an empty list.
                je      NoPrev
                mov     [bx].Prev, di           ;Set prev ptr for old guy.

NoPrev:         mov     ResidentSeg:AllocatedList, di
RmvDone:        add     di, 8                   ;Point at actual data area.
                mov     ax, ds                  ;Return ptr in es:di.
                mov     es, ax
                mov     ax, 0                   ;Return success.
                pop     bx
                pop     ds
                iret


; If the current free block is larger than the request, but not by more
; that 32 bytes, just give the whole block to the user.

GrabWholeBlk:   mov     di, bx
                mov     cx, [bx].BlkSize        ;Return actual size.
                cmp     [bx].Prev, NULL         ;First guy in list?
                je      Rmv1st
                cmp     [bx].Next, NULL         ;Last guy in list?
                je      RmvLast

; Okay, this record is sandwiched between two other in the free list.
; Cut it out from among the two.

                mov     ax, [bx].Next           ;Save the ptr to the next
                mov     bx, [bx].Prev           ; item in the prev item's
                mov     [bx].Next, ax           ; next field.

                mov     ax, bx                  ;Save the ptr to the prev
                mov     bx, [di].Next           ; item in the next item's
                mov     [bx].Prev, bx           ; prev field.
                jmp     RmvDone



; The block we want to remove is at the beginning of the free list.
; It could also be the only item on the free list!

Rmv1st:         mov     ax, [bx].Next
                mov     FreeList, ax            ;Remove from free list.
                jmp     RmvDone

; If the block we want to remove is at the end of the list, handle that
; down here.

RmvLast:        mov     bx, [bx].Prev
                mov     [bx].Next, NULL
                jmp     RmvDone

                assume  ds:nothing, bx:nothing, di:nothing




; This code handles the SHMFREE function.
; On entry, DX contains the key for the block to free. We need to
; search through the allocated block list and find the block with that
; key. If we do not find such a block, this code returns without doing
; anything. If we find the block, we need to add its memory to the
; free pool. However, we cannot simply insert this block on the front
; of the free list (as we did for the allocated blocks). It might
; turn out that this block we're freeing is adjacent to one or two
; other free blocks. This code has to coalesce such blocks into
; a single free block.

Tryshmfree:     cmp     al, 12h
                jne     Tryshminit


; First, search the allocated block list to see if we can find the
; block to remove. If we don't find it in the list anywhere, just return.

                assume  ds:SharedMemory
                assume  bx:ptr Region
                assume  di:ptr Region

                push    ds
                push    di
                push    bx

                mov     bx, SharedMemory
                mov     ds, bx
                mov     bx, ResidentSeg:AllocatedList

                test    bx, bx                  ;Empty allocated list?
                je      FreeDone
SrchList:       cmp     dx, [bx].Key            ;Search for key in DX.
                je      FoundIt
                mov     bx, [bx].Next
                test    bx, bx                  ;At end of list?
                jne     SrchList
FreeDone:       pop     bx
                pop     di                      ;Nothing allocated, just
                pop     ds                      ; return to caller.
                iret


; Okay, we found the block the user wants to delete. Remove it from
; the allocated list. There are three cases to consider:
; (1) it is at the front of the allocated list, (2) it is at the end of
; the allocated list, and (3) it is in the middle of the allocated list.

FoundIt:        cmp     [bx].Prev, NULL         ;1st item in list?
                je      Free1st
                cmp     [bx].Next, NULL         ;Last item in list?
                je      FreeLast

; Okay, we're removing an allocated item from the middle of the allocated
; list.

                mov     di, [bx].Next           ;[next].prev := [cur].prev
                mov     ax, [bx].Prev
                mov     [di].Prev, ax
                xchg    ax, di
                mov     [di].Next, ax           ;[prev].next := [cur].next
                jmp     AddFree

; Handle the case where we are removing the first item from the allocation
; list. It is possible that this is the only item on the list (i.e., it
; is the first and last item), but this code handles that case without any
; problems.

Free1st:        mov     ax, [bx].Next
                mov     ResidentSeg:AllocatedList, ax
                jmp     AddFree

; If we're removing the last guy in the chain, simply set the next field
; of the previous node in the list to NULL.

FreeLast:       mov     di, [bx].Prev
                mov     [di].Next, NULL

; Okay, now we've got to put the freed block onto the free block list.
; The free block list is sorted according to address. We have to search
; for the first free block whose address is greater than the block we've
; just freed and insert the new free block before that one. If the two
; blocks are adjacent, then we've got to merge them into a single free
; block. Also, if the block before is adjacent, we must merge it as
; well. This will coalesce all free blocks on the free list so there
; are as few free blocks as possible and those blocks are as large as
; possible.

AddFree:        mov     ax, ResidentSeg:FreeList
                test    ax, ax                  ;Empty list?
                jne     SrchPosn

; If the list is empty, stick this guy on as the only entry.

                mov     ResidentSeg:FreeList, bx
                mov     [bx].Next, NULL
                mov     [bx].Prev, NULL
                jmp     FreeDone

; If the free list is not empty, search for the position of this block
; in the free list:

SrchPosn:       mov     di, ax
                cmp     bx, di
                jb      FoundPosn
                mov     ax, [di].Next
                test    ax, ax                  ;At end of list?
                jne     SrchPosn

; If we fall down here, the free block belongs at the end of the list.
; See if we need to merge the new block with the old one.

                mov     ax, di
                add     ax, [di].BlkSize        ;Compute address of 1st byte
                add     ax, 8                   ; after this block.
                cmp     ax, bx
                je      MergeLast

; Okay, just add the free block to the end of the list.

                mov     [di].Next, bx
                mov     [bx].Prev, di
                mov     [bx].Next, NULL
                jmp     FreeDone

; Merge the freed block with the block DI points at.

MergeLast:      mov     ax, [di].BlkSize
                add     ax, [bx].BlkSize
                add     ax, 8
                mov     [di].BlkSize, ax
                jmp     FreeDone

; If we found a free block before which we are supposed to insert
; the current free block, drop down here and handle it.

FoundPosn:      mov     ax, bx                  ;Compute the address of the
                add     ax, [bx].BlkSize        ; next block in memory.
                add     ax, 8
                cmp     ax, di                  ;Equal to this block?
                jne     DontMerge

; The next free block is adjacent to the one we're freeing, so just
; merge the two.

                mov     ax, [di].BlkSize        ;Merge the sizes together.
                add     ax, 8
                add     [bx].BlkSize, ax
                mov     ax, [di].Next           ;Tweak the links.
                mov     [bx].Next, ax
                mov     ax, [di].Prev
                mov     [bx].Prev, ax
                jmp     TryMergeB4

; If the blocks are not adjacent, just link them together here.

DontMerge:      mov     ax, [di].Prev
                mov     [di].Prev, bx
                mov     [bx].Prev, ax
                mov     [bx].Next, di

; Now, see if we can merge the current free block with the previous free blk.

TryMergeB4:     mov     di, [bx].Prev
                mov     ax, di
                add     ax, [di].BlkSize
                add     ax, 8
                cmp     ax, bx
                je      CanMerge
                pop     bx
                pop     di              ;Nothing allocated, just
                pop     ds              ; return to caller.
                iret

; If we can merge the previous and current free blocks, do that here:

CanMerge:       mov     ax, [bx].Next
                mov     [di].Next, ax
                mov     ax, [bx].BlkSize
                add     ax, 8
                add     [di].BlkSize, ax
                pop     bx
                pop     di
                pop     ds
                iret

                assume  ds:nothing
                assume  bx:nothing
                assume  di:nothing

; Here's where we handle the shared memory initializatin (SHMINIT) function.
; All we got to do is create a single block on the free list (which is all
; available memory), empty out the allocated list, and then zero out all
; shared memory.

Tryshminit:     cmp     al, 13h
                jne     TryShmAttach

; Reset the memory allocation area to contain a single, free, block of
; memory whose size is 0FFF8h (need to reserve eight bytes for the block's
; data structure).

                push    es
                push    di
                push    cx

                mov     ax, SharedMemory        ;Zero out the shared
                mov     es, ax                  ; memory segment.
                mov     cx, 32768
                xor     ax, ax
                mov     di, ax
        rep     stosw


; Note: the commented out lines below are unnecessary since the code above
; has already zeroed out the entire shared memory segment.
; Note: we cannot put the first record at offset zero because offset zero
; is the special value for the NULL pointer. We'll use 4 instead.

                mov     di, 4
;               mov     es:[di].Region.Key, 0           ;Key is arbitrary.
;               mov     es:[di].Region.Next, 0          ;No other entries.
;               mov     es:[di].Region.Prev, 0          ; Ditto.
                mov     es:[di].Region.BlkSize, 0FFF8h  ;Rest of segment.
                mov     ResidentSeg:FreeList, di

                pop     cx
                pop     di
                pop     es
                mov     ax, 0           ;Return no error.
                iret


; Handle the SHMATTACH function here. On entry, DX contains a key number.
; Search for an allocated block with that key number and return a pointer
; to that block (if found) in ES:DI. Return an error code (AX=3) if we
; cannot find the block.

TryShmAttach:   cmp     al, 14h         ;Attach opcode.
                jne     IllegalOp
                mov     ax, SharedMemory
                mov     es, ax

                mov     di, ResidentSeg:AllocatedList
FindOurs:       cmp     dx, es:[di].Region.Key
                je      FoundOurs
                mov     di, es:[di].Region.Next
                test    di, di
                jne     FoundOurs
                mov     ax, 3           ;Can't find the key.
                iret

FoundOurs:      add     di, 8           ;Point at actual data.
                mov     ax, 0           ;No error.
                iret


; They called us with an illegal subfunction value. Try to do as little
; damage as possible.

IllegalOp:      mov     ax, 0           ;Who knows what they were thinking?
                iret
MyInt2F         endp
                assume  ds:nothing
ResidentSeg     ends


; Here's the segment that will actually hold the shared data.

SharedMemory    segment para public 'Shared'
                db      0FFFFh dup (?)
SharedMemory    ends






cseg            segment para public 'code'
                assume  cs:cseg, ds:ResidentSeg

; SeeIfPresent- Checks to see if our TSR is already present in memory.
;               Sets the zero flag if it is, clears the zero flag if
;               it is not.

SeeIfPresent    proc    near
                push    es
                push    ds
                push    di
                mov     cx, 0ffh        ;Start with ID 0FFh.
IDLoop:         mov     ah, cl
                push    cx
                mov     al, 0           ;Verify presence call.
                int     2Fh
                pop     cx
                cmp     al, 0           ;Present in memory?
                je      TryNext
                strcmpl
                byte    "Dynamic Shared Memory TSR",0
                je      Success

TryNext:        dec     cl              ;Test USER IDs of 80h..FFh
                js      IDLoop
                cmp     cx, 0           ;Clear zero flag.
Success:        pop     di
                pop     ds
                pop     es
                ret
SeeIfPresent    endp



; FindID-       Determines the first (well, last actually) TSR ID available
;               in the multiplex interrupt chain. Returns this value in
;               the CL register.
;
;               Returns the zero flag set if it locates an empty slot.
;               Returns the zero flag clear if failure.

FindID          proc    near
                push    es
                push    ds
                push    di

                mov     cx, 0ffh        ;Start with ID 0FFh.
IDLoop:         mov     ah, cl
                push    cx
                mov     al, 0           ;Verify presence call.
                int     2Fh
                pop     cx
                cmp     al, 0           ;Present in memory?
                je      Success
                dec     cl              ;Test USER IDs of 80h..FFh
                js      IDLoop
                xor     cx, cx
                cmp     cx, 1           ;Clear zero flag
Success:        pop     di
                pop     ds
                pop     es
                ret
FindID          endp



Main            proc
                meminit

                mov     ax, ResidentSeg
                mov     ds, ax

                mov     ah, 62h         ;Get this program's PSP
                int     21h             ; value.
                mov     PSP, bx

; Before we do anything else, we need to check the command line
; parameters. If there is one, and it is the word "REMOVE", then remove
; the resident copy from memory using the multiplex (2Fh) interrupt.

                argc
                cmp     cx, 1           ;Must have 0 or 1 parms.
                jb      TstPresent
                je      DoRemove
Usage:          print
                byte    "Usage:",cr,lf
                byte    " shmalloc",cr,lf
                byte    "or shmalloc REMOVE",cr,lf,0
                ExitPgm


; Check for the REMOVE command.

DoRemove:       mov     ax, 1
                argv
                stricmpl
                byte    "REMOVE",0
                jne     Usage

                call    SeeIfPresent
                je      RemoveIt
                print
                byte    "TSR is not present in memory, cannot remove"
                byte    cr,lf,0
                ExitPgm

RemoveIt:       mov     MyTSRID, cl
                printf
                byte    "Removing TSR (ID #%d) from memory...",0
                dword   MyTSRID

                mov     ah, cl
                mov     al, 1                   ;Remove cmd, ah contains ID
                int     2Fh
                cmp     al, 1                   ;Succeed?
                je      RmvFailure
                print
                byte    "removed.",cr,lf,0
                ExitPgm

RmvFailure:     print
                byte    cr,lf
                byte    "Could not remove TSR from memory.",cr,lf
                byte    "Try removing other TSRs in the reverse order "
                byte    "you installed them.",cr,lf,0
                ExitPgm



; Okay, see if the TSR is already in memory. If so, abort the
; installation process.

TstPresent:     call    SeeIfPresent
                jne     GetTSRID
                print
                byte    "TSR is already present in memory.",cr,lf
                byte    "Aborting installation process",cr,lf,0
                ExitPgm


; Get an ID for our TSR and save it away.

GetTSRID:       call    FindID
                je      GetFileName
                print
                byte    "Too many resident TSRs, cannot install",cr,lf,0
                ExitPgm


; Things look cool so far, so install the interrupts

GetFileName:    mov     MyTSRID, cl
                print
                byte    "Installing interrupts...",0


; Patch into the INT 2Fh interrupt chain.

                cli                             ;Turn off interrupts!
                mov     ax, 0
                mov     es, ax
                mov     ax, es:[2Fh*4]
                mov     word ptr OldInt2F, ax
                mov     ax, es:[2Fh*4 + 2]
                mov     word ptr OldInt2F+2, ax
                mov     es:[2Fh*4], offset MyInt2F
                mov     es:[2Fh*4+2], seg ResidentSeg
                sti                             ;Okay, ints back on.

; We're hooked up, the only thing that remains is to initialize the shared
; memory segment and then terminate and stay resident.

                printf
                byte    "Installed, TSR ID #%d.",cr,lf,0
                dword   MyTSRID

                mov     ah, MyTSRID             ;Initialization call.
                mov     al, 13h
                int     2Fh

                mov     dx, EndResident         ;Compute size of program.
                sub     dx, PSP
                mov     ax, 3100h               ;DOS TSR command.
                int     21h
Main            endp
cseg            ends

sseg            segment para stack 'stack'
stk             db      256 dup (?)
sseg            ends

zzzzzzseg       segment para public 'zzzzzz'
LastBytes       db      16 dup (?)
zzzzzzseg       ends
                end     Main

We can modify the two applications from the previous section to try out this code:

; SHMAPP3.ASM
;
; This is a shared memory application that uses the dynamic shared memory
; TSR (SHMALLOC.ASM). This program inputs a string from the user and
; passes that string to SHMAPP4.ASM through the shared memory area.
;
;
                .xlist
                include         stdlib.a
                includelib      stdlib.lib
                .list

dseg            segment para public 'data'
ShmID           byte    0
dseg            ends

cseg            segment para public 'code'
                assume  cs:cseg, ds:dseg, es:SharedMemory

; SeeIfPresent- Checks to see if the shared memory TSR is present in memory.
;               Sets the zero flag if it is, clears the zero flag if
;               it is not. This routine also returns the TSR ID in CL.

SeeIfPresent    proc    near
                push    es
                push    ds
                push    di
                mov     cx, 0ffh        ;Start with ID 0FFh.
IDLoop:         mov     ah, cl
                push    cx
                mov     al, 0           ;Verify presence call.
                int     2Fh
                pop     cx
                cmp     al, 0           ;Present in memory?
                je      TryNext
                strcmpl
                byte    "Dynamic Shared Memory TSR",0
                je      Success

TryNext:        dec     cl              ;Test USER IDs of 80h..FFh
                js      IDLoop
                cmp     cx, 0           ;Clear zero flag.
Success:        pop     di
                pop     ds
                pop     es
                ret
SeeIfPresent    endp



; The main program for application #1 links with the shared memory
; TSR and then reads a string from the user (storing the string into
; shared memory) and then terminates.

Main            proc
                assume  cs:cseg, ds:dseg, es:SharedMemory
                mov     ax, dseg
                mov     ds, ax
                meminit

                print
                byte    "Shared memory application #3",cr,lf,0

; See if the shared memory TSR is around:

                call    SeeIfPresent
                je      ItsThere
                print
                byte    "Shared Memory TSR (SHMALLOC) is not loaded.",cr,lf
                byte    "This program cannot continue execution.",cr,lf,0
                ExitPgm


; Get the input line from the user:

ItsThere:       mov     ShmID, cl
                print
                byte    "Enter a string: ",0

                lea     di, InputLine           ;ES already points at proper seg.
                getsm

; The string is in our heap space. Let's move it over to the shared
; memory segment.

                strlen
                inc     cx                      ;Add one for zero byte.
                push    es
                push    di

                mov     dx, 1234h               ;Our "key" value.
                mov     ah, ShmID
                mov     al, 11h                 ;Shmalloc call.
                int     2Fh

                mov     si, di                  ;Save as dest ptr.
                mov     dx, es

                pop     di                      ;Retrive source address.
                pop     es
                strcpy                          ;Copy from local to shared.

                print
                byte    "Entered '",0
                puts
                print
                byte    "' into shared memory.",cr,lf,0


Quit:           ExitPgm                         ;DOS macro to quit program.
Main            endp

cseg            ends

sseg            segment para stack 'stack'
stk             db      1024 dup ("stack ")
sseg            ends

zzzzzzseg       segment para public 'zzzzzz'
LastBytes       db      16 dup (?)
zzzzzzseg       ends

                end     Main
; SHMAPP4.ASM
;
; This is a shared memory application that uses the dynamic shared memory
; TSR (SHMALLOC.ASM). This program assumes the user has already run the
; SHMAPP3 program to insert a string into shared memory. This program
; simply prints that string from shared memory.
;
                .xlist
                include         stdlib.a
                includelib      stdlib.lib
                .list

dseg            segment para public 'data'
ShmID           byte    0
dseg            ends

cseg            segment para public 'code'
                assume  cs:cseg, ds:dseg, es:SharedMemory

; SeeIfPresent- Checks to see if the shared memory TSR is present in memory.
;               Sets the zero flag if it is, clears the zero flag if
;               it is not. This routine also returns the TSR ID in CL.

SeeIfPresent    proc    near
                push    es
                push    ds
                push    di
                mov     cx, 0ffh                ;Start with ID 0FFh.
IDLoop:         mov     ah, cl
                push    cx
                mov     al, 0           ;Verify presence call.
                int     2Fh
                pop     cx
                cmp     al, 0           ;Present in memory?
                je      TryNext
                strcmpl
                byte    "Dynamic Shared Memory TSR",0
                je      Success

TryNext:        dec     cl              ;Test USER IDs of 80h..FFh
                js      IDLoop
                cmp     cx, 0           ;Clear zero flag.
Success:        pop     di
                pop     ds
                pop     es
                ret
SeeIfPresent    endp



; The main program for application #1 links with the shared memory
; TSR and then reads a string from the user (storing the string into
; shared memory) and then terminates.

Main            proc
                assume  cs:cseg, ds:dseg, es:SharedMemory
                mov     ax, dseg
                mov     ds, ax
                meminit

                print
                byte    "Shared memory application #4",cr,lf,0

; See if the shared memory TSR is around:

                call    SeeIfPresent
                je      ItsThere
                print
                byte    "Shared Memory TSR (SHMALLOC) is not loaded.",cr,lf
                byte    "This program cannot continue execution.",cr,lf,0
                ExitPgm

; If the shared memory TSR is present, get the address of the shared segment
; into the ES register:

ItsThere:       mov     ah, cl          ;ID of our TSR.
                mov     al, 14h         ;Attach call
                mov     dx, 1234h       ;Our "key" value
                int     2Fh

; Print the string input in SHMAPP3:

                print
                byte    "String from SHMAPP3 is '",0

                puts

                print
                byte    "' from shared memory.",cr,lf,0


Quit:           ExitPgm                 ;DOS macro to quit program.
Main            endp

cseg            ends

sseg            segment para stack 'stack'
stk             db      1024 dup ("stack ")
sseg            ends

zzzzzzseg       segment para public 'zzzzzz'
LastBytes       db      16 dup (?)
zzzzzzseg       ends
                end     Main

Chapter Nineteen (Part 4)

Table of Content

Chapter Nineteen (Part 6)

Chapter Nineteen: Processes, Coroutines and Concurrency (Part 5)
29 SEP 1996