;==============================================================================
; MARK.ASM - mark a position in memory,
;            above which TSRs will later be cleared by RELEASE
;            MARK can be called multiple times, each RELEASE will clear
;            above the last MARK called.
; If MARK is called with something on the command line, that text will
; be stored in the program segment prefix at offset 80H, where it is
; later accessible to RELEASE for a "named" release.
;
; Syntax:    MARK [/Q] [!][MarkName]
;==============================================================================
; written for TASM
; Copyright (c) 1986,1993 Kim Kokkonen, TurboPower Software.
; May be freely distributed but not sold except by permission.
; telephone: 719-260-6641, Compuserve 76004,2611
;==============================================================================
; version 1.0  2/8/86
;     original public release
;     (thanks to Neil Rubenking for an outline of the method used)
; :
; long intervening history
; :
; version 3.0  9/24/91
;   add Quiet option
;   add code for tracking high memory
; version 3.1 11/4/91
;   no change
; version 3.2 11/22/91
;   change method of accessing high memory
;   store parent len as well as parent segment
; version 3.3 1/8/92
;   no change
; version 3.4 2/14/92
;   no change
; version 3.5 10/18/93
;   new hybrid method for finding high memory
;==============================================================================

Cseg    segment public para
        assume  cs:Cseg, ds:Cseg, es:nothing
        locals  @@

        org     81H
cmdlin  label   byte                    ;first character of command line

        org     100H
comentry: jmp     doit

;put the following in the MAP file
public idstr,vector,egasav,intcom,emscnt,emsmap

idstr   db      'MM3.5 TSR'             ;used to find this TSR
firsthimcb dw   0                       ;segment of first mcb in high mem

;**** the following will be overwritten by the vector table image *************
;success message and version number
didit  db      'MARK 3.5, Copyright 1993 TurboPower Software',13,10
didit2 db      'Marked current memory position',13,10,36
errst  db      'Invalid command line',13,10,36
;file name for testing EMS presence
emsnm  db      'EMMXXXX0',0
quiet  db      0
xmsadr  label   dword                           ;XMS control address
xmsxxx  dw      2 dup (0)
;******************************************************************************

vector equ     0120H           ;offset in code seg where vector table is copied
veclen equ     0400H           ;1024 bytes for vectors
egasav equ     vector+veclen
egalen equ     8               ;8 bytes for EGA save area
intcom equ     egasav+egalen
intlen equ     10H             ;16 bytes for interapps communication area
parent equ     intcom+intlen
parlen equ     parent+2        ;parent's PSP length
emscnt equ     parlen+2
emsmap equ     emscnt+2
;mcbcnt and mcbmap actually follow after used portion of emsmap
mcbcnt equ     emsmap+400H     ;count of allocated mcbs
mcbmap equ     mcbcnt+2        ;array of mcb segments
;newloc follows after the longest possible emsmap and mcbmap
newloc equ     mcbmap+400H     ;location of relocated code

;*****************************************************************************

findhidos proc near
;returns ax = first high mcb, or 0 if none found
        mov     ah,52H                  ;get first mcb segment
        int     21H
        mov     ax,es:[bx-2]            ;ax=first mcb

        push    ds
        assume  ds:nothing

@@1:    cmp     ax,9FFFH                ;above 640K?
        ja      @@2                     ;jump if so
        mov     ds,ax                   ;ds:[0] points to mcb
        xor     ax,ax                   ;set ax to zero in case we exit here
        cmp     byte ptr ds:[0],'Z'     ;end of mcb chain?
        je      @@2                     ;exit if so
        mov     ax,ds                   ;restore ax to mcb segment
        inc     ax                      ;skip over mcb itself
        add     ax,ds:[3]               ;add length of memory block
        jmp     @@1

@@2:    pop     ds                      ;ds = cs
        assume  ds:cseg
        ret
findhidos endp

doit   proc    near

;find first mcb in high memory, if any
        mov     ax,5802h
        int     21H                     ;get umb link status
        or      al,al                   ;high memory already linked?
        jz      @@u1                    ;jump if not

        call    findhidos               ;find mcb using linked chain

        or      ax,ax                   ;valid mcb seg returned?
        jz      @@u9                    ;jump if not
        mov     cx,ax
        jmp     @@9                     ;found first umb

@@u1:   mov     ax,5803H
        mov     bx,1
        int     21H                     ;link umb
        jc      @@u9                    ;jump if failed

        call    findhidos               ;find mcb using linked chain

        push    ax                      ;save result
        mov     ax,5803H
        xor     bx,bx
        int     21H                     ;unlink umb
        pop     ax                      ;restore result

        or      ax,ax                   ;valid mcb seg returned?
        jz      @@u9                    ;jump if not
        mov     cx,ax
        jmp     @@9                     ;found first umb

@@u9:   mov     ax,3000h                ;get DOS version
        int     21H
        cmp     al,3
        jb      @@7                     ;no XMS driver possible
        mov     ax,4300h
        int     2Fh                     ;multiplex call for XMS
        cmp     al,80h                  ;proper signature?
        jne     @@7                     ;no XMS driver
        mov     ax,4310h                ;get XMS control address
        int     2Fh
        mov     xmsxxx,bx               ;save it
        mov     xmsxxx[2],es
        mov     ah,10h
        mov     dx,0FFFFh
        call    xmsadr                  ;ask to allocate FFFF paras of UMB
        cmp     bl,0B0h                 ;will fail with B0 if UMBs avail
        je      @@0
        cmp     bl,0B1h                 ;will fail with B1 if UMBs all allocated
        jne     @@7                     ;no UMBs exist
@@0:    int     12H
        mov     cl,6
        shl     ax,cl                   ;get segment of top of memory

@@1:    mov     es,ax
        cmp     byte ptr es:[0000h],'M' ;potential mcb?
        jnz     @@6                     ;not an mcb, try next segment
@@2:    mov     cx,ax                   ;save potential start mcb in cx
@@3:    inc     ax
        add     ax,es:[0003h]           ;ax = start of next mcb
        jc      @@5                     ;can't be an mcb if we wrapped
        mov     es,ax                   ;address of next mcb
        mov     dl,es:[0000h]
        cmp     dl,'M'
        jz      @@3                     ;good start mcb
        cmp     dl,'Z'
        jz      @@9                     ;good end mcb
@@5:    mov     ax,cx                   ;restore last start segment
@@6:    cmp     ax,0FFFFh               ;top of memory?
        je      @@7
        inc     ax                      ;try next segment
        jmp     @@1

@@7:    xor     cx,cx                   ;no matching UMB
@@9:    mov     firsthimcb,cx           ;store first high mcb

;relocate ourselves out of the way
        push    cs
        pop     es
        mov     di,newloc
        push    di                      ;will act as a return address
        mov     si,offset parse
        mov     cx,lastcode-parse
        cld
        rep     movsb
        ret                             ;"return" to the relocated code

;scan command line for /Q option
parse:  mov     si,offset cmdlin
        cld
get1:   lodsb
        cmp     al,13                   ;end of command line?
        je      pmess
        cmp     al,'/'                  ;option switch?
        je      getop
        cmp     al,'-'
        jne     get1                    ;loop around
getop:  mov     di,si                   ;save option position
        lodsb
        cmp     al,'a'
        jb      noup
        cmp     al,'z'
        ja      noup
        and     al,0DFH                 ;uppercase
noup:   cmp     al,'Q'
        jne     error
        mov     quiet,1                 ;set quiet flag
        dec     di
        mov     al,' '
        stosb                           ;clear option
        stosb
        jmp     get1                    ;loop around

error:  mov     dx,offset errst
        mov     ah,9
        int     21H
        mov     ax,4C01H
        int     21H

;print message if not quiet
pmess:  cmp     quiet,0
        jnz     chkems
        mov     dx,offset didit
        mov     ah,9
        int     21H                     ;write success message

;determine whether EMS is present
chkems: mov     dx,offset emsnm
        mov     ax,3D00H
        int     21H
        jnc     emspres                 ;EMS driver installed
        xor     bx,bx
        jmp     short stocnt

;EMS present, close the open handle first
emspres:
        mov     bx,ax
        mov     ah,3EH
        int     21H

;store the EMS page map
        mov     ah,4DH
        mov     di,emsmap
        xor     bx,bx                   ;required by some versions of EMM
        cld                             ;required by some versions of EMM
        int     67H

;store the number of active EMS handles
stocnt:
        mov     ds:[emscnt],bx          ;count of active handles

;store the interrupt vector table (overwrites initial messages)
        xor     ax,ax
        mov     ds,ax                   ;source address segment 0
        assume  ds:nothing
        xor     si,si                   ;offset 0
        mov     di,vector               ;destination offset, es=cs already
        mov     cx,veclen shr 1         ;count of words to store
        rep     movsw                   ;copy vectors to our table

;store the EGA save area
        mov     ax,40H
        mov     ds,ax                   ;point to BIOS data area
        mov     si,00A8H                ;EGA save area pointer
        mov     di,egasav
        mov     cx,egalen shr 1
        rep     movsw                   ;copy information to our save area

;store the interapplications communication area
        mov     si,00F0H                ;interapps communication area address
        mov     di,intcom
        mov     cx,intlen shr 1
        rep     movsw                   ;copy information to our save area

;store the parent's PSP segment
        push    cs
        pop     ds                      ;ds = cs
        assume  ds:cseg
        mov     ax,ds:[16h]             ;get parent's PSP from our PSP
        mov     ds:[parent],ax          ;store in data save area
        dec     ax
        mov     es,ax
        mov     ax,es:[0003h]           ;get length of parent's psp
        mov     ds:[parlen],ax          ;store in data save area

;store the mcb chain
        mov     ah,52H                  ;get first mcb segment
        int     21H
        mov     ax,es:[bx-2]            ;ax=first mcb
        push    cs
        pop     es                      ;es=cs

        mov     di,cs:[emscnt]          ;get starting address of mcbmap
        shl     di,1
        shl     di,1
        add     di,emsmap
        mov     si,di                   ;cs:[si] -> mcbcnt
        add     di,2                    ;cs:[di] -> mcbmap
        xor     cx,cx                   ;cx will count mcbs
        cld
        push    ds
        assume  ds:nothing

mcbnext:
        stosw                           ;store mcb segment held by ax
        mov     ds,ax                   ;ds:[0] points to mcb
        mov     ax,ds:[1]               ;get mcb owner
        stosw                           ;store it
        inc     cx                      ;increment count
        cmp     byte ptr ds:[0],'Z'     ;end of mcb chain?
        je      mcbdone
        mov     ax,ds                   ;restore ax to mcb segment
        inc     ax                      ;skip over mcb itself
        add     ax,ds:[3]               ;add length of memory block
        jmp     mcbnext

mcbdone:mov     ax,firsthimcb           ;check for high memory
        or      ax,ax
        jz      mcbend
        mov     firsthimcb,0            ;only do it once
        jmp     mcbnext

mcbend: pop     ds
        assume  ds:cseg

;terminate and stay resident
gores:  mov     [si],cx                 ;store mcbcnt
        mov     dx,di                   ;dx = past last used mcb segment
        add     dx,000FH                ;add paragraph roundup
        mov     cl,4
        shr     dx,cl                   ;convert to paragraphs
        mov     ax,3100H
        int     21H                     ;terminate but stay resident

lastcode:
doit    endp

Cseg    ends
        end     ComEntry
