My thesis

AB.ASM Augmented Backup

Old school source code management :-)
; FILENAME: AB.ASM
P386
IDEAL
JUMPS
LOCALS @_

INCLUDE "USEFUL.INC"

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
segment code
        org     100h
        assume cs:code, ds:code, es:nothing, ss:code
start:

        jmp     main


        REM     --  if you've finish coding a program (C/C++ or Pascal)
        REM         after 3 months(an average span of medium-size
        REM         software development) which coding frequency is
        REM         4 hours every day and saving the program is 5 minutes,
        REM         this number(5000) in theory could let you retrieve
        REM         your initial project code roughly 3 months ago,
        REM         allowing other programmer, who wants tinker with your
        REM         code and see how you gradually develop your program
        MAX_FILE_COUNT          = 5000

        label   oldTSRAddr      dword
                oldTSROfs       dw ?
                oldTSRSeg       dw ?


        REM                     -- backup is false tentatively
        canWeBackup                     db 0

        handleOfFileToBackup            dw ?
        handleOfDuplicate               dw ?

        REM             -- length does not include the NULL byte terminator
        filenameLength                  dw ?

        extensionList                   db ' .PAS .INC .C .H .CPP .HPP'
                                        db ' .ASM .MAC .CXX .HXX .HDR ', 0

        fileNameExtLastCharOffset       dw ?
        fileNameExtLength               dw ?
        nextFnameExtLastCharOffset      dw ?

        fileToBackup                    db DOS_MAXPATHLEN dup(?)
        filenameToAdjust                db DOS_MAXPATHLEN dup(?)

        fnameMostSignificantExtOffset   dw ?

        REM             -- the NULL is copied when you copy the digit
        REM                  extension to the extension fileToBackup.
        REM                this is the digit filename
        toDigit        db 8 dup(?), NULL


        REM             -- pascal is a special case in our TSR program
        REM                I found out that Turbo Pascal Editor,
        REM                version 7 in particular don't directly save the
        REM                source code to a .PAS, rather it renames the
        REM                editor temporary file which has an extension
        REM                of .$$$ to a .PAS
        pascalFNameOffset               dw ?

        buffer                          db 1 dup(?)

        mcbAsciiNameLength      dw ?

        struc   langNameList
                len     dw ?
                desc    db 8 dup(NULL)
        ends

        pascalLang      langNameList< 5, 'TURBO' >
                        langNameList< 2, 'TP' >
                        langNameList< 3, 'TPX' >
                        langNameList< 2, 'BP' >
                        langNameList< 3, 'BPX' >
                        langNameList< 6, 'PASCAL' >
                        db NULL

        ccplusLang      langNameList< 2, 'TC' >
                        langNameList< 3, 'TCX' >
                        langNameList< 2, 'BC' >
                        langNameList< 3, 'BCX' >
                        langNameList< 2, 'QC' >
                        langNameList< 3, 'QCX' >
                        langNameList< 1, 'C' >
                        db NULL


        langToDetect    dw ?


        dta             db 256 dup(?)

        prevDTASeg              dw ?
        prevDTAOffs             dw ?


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isLanguageInMem?
                push    ax
                push    bx
                push    cx
                push    ds
                push    si
                push    es
                push    di




        @_firstMCB:
                mov     ah, DOS_GET_INITIAL_MCB
                pushf
                call    [cs:oldTSRAddr]
                

                push    cs
                pop     ds

                mov     es, [word es:bx-2]


;............................................................................
        @_traverseMCB:

                mov     bx, [cs:langToDetect]

        @_loopPascalLang:

                mov     di, 0
                mov     cx, 8
        @_findMCBAsciiNameLength:
                inc     di
                cmp     [(mcb es:di).asciiName], NULL
                loopne  @_findMCBAsciiNameLength

                mov     [cs:mcbAsciiNameLength], di

                mov     cx, [(langNameList cs:bx).len]

                REM     -- if not same string length scan the
                REM        next language name
                cmp     cx, [cs:mcbAsciiNameLength]
                        ohNo    @_scanNextLanguageName

                lea     di, [es:mcb.asciiName]
                lea     si, [(langNameList cs:bx).desc]


                cld
                repe    cmpsb
                        ohYes   @_found


        @_scanNextLanguageName:

                add     bx, size langNameList
                cmp     [(langNameList cs:bx).desc], NULL
                        ohYes   @_stopScanningLang

                jmp     @_loopPascalLang

        @_stopScanningLang:

        @_scanTheNextMCB:

                cmp     [es:mcb.linkIndicator], 'Z'
                je      @_notFound

                mov     ax, es
                inc     ax

                add     ax, [es:mcb.paragraphLen]
                mov     es, ax

                jmp     @_traverseMCB

;............................................................................
        @_found:
                ste
                jmp     @_finish

;............................................................................
        @_notFound:
                cle
                jmp     @_finish

        @_finish:
                
                pop     di
                pop     es
                pop     si
                pop     ds
                pop     cx
                pop     bx
                pop     ax

                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-        
        proc    isPascalInMem?
        REM     Input(s):
        REM        langToDetect -- pass here to offset of name list of
        REM                        the language
        REM     Output(s):
        REM        Equality/Zero Flag -- if present in memory
;............................................................................
                mov     [cs:langToDetect], offset pascalLang
                call    isLanguageInMem?
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isCCPlusplusInMem?
        REM     Input(s):
        REM        langToDetect -- pass here to offset of name list of
        REM                        the language
        REM     Output(s):
        REM        Equality/Zero Flag -- if present in memory
;............................................................................
                mov     [cs:langToDetect], offset ccplusLang
                call    isLanguageInMem?
                ret
        endp



;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        REM     -- you might think that 10 can be stored in byte variable
        REM          of course it is, but since we want to obtain a quotient
        REM          of double word we must divide it by 2 double words.
        REM       EDX(high doubleword:remainder) EAX(low doubleword:quotient)
        by10            dd      10

        proc    regEAXConvertTo8Digit
                push    ebx
                push    ecx
                push    edx
                push    eax

                mov     cx, 8
                mov     bx, 8
                mov     edx, 0

        @_divideBy10:
                div     [cs:by10]
                dec     bx
                add     dl, '0'
                mov     [cs:toDigit+bx], dl
                mov     edx, 0
                loop    @_divideBy10

                pop     eax
                pop     edx
                pop     ecx
                pop     ebx

                ret
        endp



;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    createNewName
                pusha
                pushf


                push    cs
                pop     ds

                mov     ah, DOS_GET_DTA
                pushf
                call    [cs:oldTSRAddr]

                push    es
                pop     [prevDTASeg]

                push    bx
                pop     [prevDTAOffs]


                mov     ah, DOS_SET_DTA
                mov     dx, offset dta
                pushf
                call    [cs:oldTSRAddr]


                push    cs
                pop     es


                mov     cx, [cs:filenameLength]
                mov     di, offset fileToBackup
                add     di, cx

                mov     al, '.'
                std
                repne   scasb

                REM     -- create a subdirectory out of fileToBackup
                add     di, 3
                mov     [byte es:di], '@'
                mov     [byte es:di+1], 'B'
                mov     [byte es:di+2], NULL


                REM     -- we have no use for carry
                mov     ah, DOS_CREATE_DIRECTORY
                mov     dx, offset fileToBackup
                pushf
                call    [cs:oldTSRAddr]



                mov     [byte es:di+2], '\'
                mov     [byte es:di+3], NULL

                REM     -- for example a file is named
                REM        MP1.C this will create a number filename
                REM        in subdirectory MP1.C@B
                REM     -- skip past the directory's at sign
                add     di, 3
                mov     [cs:fnameMostSignificantExtOffset], di

                jmp     @_startOfFindANewFilename

;............................................................................;
        @_startOfFindANewFilename:
                REM     -- the number filename
                mov     eax, 0

                jmp     @_findANewFilename

;............................................................................
        @_findANewFileName:

                call    regEAXConvertTo8Digit

                REM     -- if we exhaust filenames number list adjust ...
                cmp     eax, MAX_FILE_COUNT + 1
                        ohYes   @_adjustListOfFileHistory

                jmp     @_createAUniqueFilename


        @_createAUniqueFilename:
                inc     eax

                REM     -- preserved the file counter
                push    eax


                mov     di, [cs:fnameMostSignificantExtOffset]
                REM     -- 8 digit number filename plus NULL character
                mov     cx, 9
                mov     si, offset toDigit
                cld
                rep     movsb

                mov     ah, DOS_FILE_FIND_FIRST
                mov     cx, DOS_NORMAL_FILE
                mov     dx, offset fileToBackup
                pushf
                call    [cs:oldTSRAddr]

                REM     -- restore the file counter
                pop     eax

                REM     -- file is existing
                jnc     @_findANewFileName


                REM     -- the above loop is terminated when
                REM        we found a file name that does not already
                REM        exist

                jmp     @_finally

;............................................................................
        @_adjustListOfFileHistory:
                pusha


                REM     -- we will remove the first file from the list
                mov     eax, 0
                call    regEAXConvertTo8Digit

                mov     di, [cs:fnameMostSignificantExtOffset]
                REM     -- 8 digit number filename plus NULL character
                mov     cx, 9
                mov     si, offset toDigit
                cld
                rep     movsb

                mov     ah, DOS_FILE_DELETE
                mov     dx, offset fileToBackup
                pushf
                call    [cs:oldTSRAddr]


                REM     -- for ax := 0 to MAX_FILE_COUNT - 1 do
                REM             adjust filenames
                mov     eax, 0
        @_adjustThis:


                mov     si, offset fileToBackup
                mov     di, offset filenameToAdjust
                mov     cx, DOS_MAXPATHLEN
                cld
                rep     movsb


                REM     -- construct the name of file to be rename
                call    regEAXConvertTo8Digit
                mov     di, [cs:fnameMostSignificantExtOffset]
                REM     -- 8 digit number filename plus NULL character
                mov     cx, 9
                mov     si, offset toDigit
                cld
                rep     movsb

                REM     -- construct the new name for file to renamed
                inc     eax
                call    regEAXConvertTo8Digit
                mov     di, [cs:fnameMostSignificantExtOffset]
                REM     -- filenameToBackup and filenameToAdjust is
                REM        adjacent in memory, so it is just
                REM        okay to do the following light trick:
                REM        di += DOS_MAXPATHLEN; which essentially
                REM        will point DI to the new name or the
                REM        filenameToAdjust's dot character
                add     di, DOS_MAXPATHLEN
                mov     si, offset toDigit
                REM     -- 8 digit number filename plus NULL character
                mov     cx, 9
                cld
                rep     movsb

                REM     -- preserve the file counter
                push    eax

                REM     -- now we can rename the file
                mov     ah, DOS_FILE_RENAME
                mov     dx, offset filenameToAdjust
                mov     di, offset fileToBackup
                pushf
                call    [cs:oldTSRAddr]

                REM     -- restore the file counter
                pop     eax

                cmp     eax, MAX_FILE_COUNT
                        ohYes   @_stopRenaming
                jmp     @_adjustThis

;............................................................................
        @_stopRenaming:


                REM     -- from MAX_FILE_NUMBER to MAX_FILE_NUMBER - 1
                REM        so when create a unique filename is performed
                REM        it has no knowing that MAX_FILE_NUMBER
                REM        adjustments were made, then after inc eax
                REM        is performed, register EAX holds
                REM        the number MAX_FILE_NUMBER again
                popa
                mov     eax, MAX_FILE_COUNT - 1
                jmp     @_createAUniqueFilename


;............................................................................
        @_finally:

                mov     ah, DOS_SET_DTA

                push    [cs:prevDTAOffs]
                pop     dx

                push    [cs:prevDTASeg]
                pop     ds

                pushf
                call    [cs:oldTSRAddr]

                popf
                popa
                ret
        endp



;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    copyByteByByte

                pusha

                push    cs
                pop     ds


                REM     -- go the beginning of file, from there we will
                REM        do a byte by byte read of the file to backup
                mov     bx, [cs:handleOfFileToBackup]
                mov     ah, DOS_FILE_MOVE_PTR
                mov     al, DOS_FILE_BEGIN
                REM     -- longword offset is in CX:DX pair,
                REM        conventionally longword is put in DX:AX pair,
                REM        but since AX register is use in function
                REM        the designer of DOS opted to put the
                REM        longword file offset in CX:DX
                mov     cx, 0
                mov     dx, 0
                pushf
                call    [cs:oldTSRAddr]


                mov     ah, DOS_FILE_CREATE
                mov     dx, offset fileToBackup
                mov     cx, 00
                pushf
                call    [cs:oldTSRAddr]
                mov     [cs:handleOfDuplicate], ax

                jmp     @_doTheCopying

;............................................................................
        @_doTheCopying:
                mov     dx, offset buffer
        @_byteRead:
                mov     bx, [cs:handleOfFileToBackup]
                mov     ah, DOS_FILE_READ
                mov     cx, size buffer
                pushf
                call    [cs:oldTSRAddr]

                REM     -- if read size is 0 then no more to read
                REM        so just end the loop
                cmp     ax, 0
                        ohYes   @_finally

        @_byteWrite:
                mov     cx, ax
                mov     bx, [cs:handleOfDuplicate]
                mov     ah, DOS_FILE_WRITE
                pushf
                call    [cs:oldTSRAddr]

                jmp     @_byteRead

;............................................................................
        @_finally:

                mov     bx, [cs:handleOfDuplicate]
                mov     ah, DOS_FILE_CLOSE
                pushf
                call    [cs:oldTSRAddr]

                popa

                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    extensionMatch?

        REM     Input  Register(s):
        REM         DS:DX = offset of filename

        REM     Output Register(s):
        REM         DS:DX = offset of filename

        REM         Zero/Equality Flag:
        REM             Set when TRUE    -- z / e
        REM             Clear when FALSE -- nz / ne
;............................................................................

                REM     -- for an unknown reason Turbo Pascal
                REM        editor seems to be lacking in stack space
                REM        whenever we push all the registers(using PUSHA)
                REM        the rename function can't continue, so I
                REM        opt to preserve only those registers that
                REM        are used in this function


                push    ax
                push    cx
                push    dx
                push    es
                push    ds
                push    si
                push    di


                push    ds
                pop     es
        
                REM     -- find the offset of NULL character
                REM        dx holds the offset of the filename
                REM        of the interrupted program
                mov     di, dx
                REM     -- scan the AsciiZ NULL character
                mov     al, NULL
                mov     cx, DOS_MAXPATHLEN
                cld
                repne   scasb
        
                REM     -- inc cx instr. excludes the null byte from 
                REM        string length
                inc     cx
                mov     [cs:filenameLength], DOS_MAXPATHLEN
                sub     [cs:filenameLength], cx


                REM     -- 2 is the distance of the DI register termination
                REM        back to the character before the NULL character
                sub     di, 2
        
                REM     -- from the NULL character offset - 2 is the last
                REM        character
                mov     [cs:fileNameExtLastCharOffset], di

                REM     -- the following instr. is use for the 
                REM        comparison(cmpsb) of the asciiz filename
                REM        against the extension list
                push    cs
                pop     es
        
                mov     [cs:nextFnameExtLastCharOffset], offset extensionList
        
                jmp     @_tryExtIfMatchExtList

;............................................................................
        @_tryExtIfMatchExtList:
        
                mov     di, [cs:nextFnameExtLastCharOffset]
        
        @_tryFindTheDotCharacter:
                inc     di
        
                cmp     [byte cs:di], '.' 
                        ohYes   @_countTheCharacter
        
                cmp     [byte cs:di], NULL
                        ohYes   @_noExtMatch
        
                jmp     @_tryFindTheDotCharacter
        
        @_countTheCharacter:
                mov     cx, 0
        
        @_thenCount:
                inc     di
                inc     cx
                cmp     [byte cs:di], ' '
                        ohNo    @_thenCount

                REM     -- exclude the space character                
                dec     cx
                mov     [cs:fileNameExtLength], cx 
                dec     di
                mov     [cs:nextFnameExtLastCharOffset], di
                
                mov     cx, [cs:fileNameExtLength]
                mov     si, [cs:fileNameExtLastCharOffset]
                mov     di, [cs:nextFnameExtLastCharOffset]
                std
                repe    cmpsb
                        ohYes   @_extMatch
        
                mov     di, [cs:nextFnameExtLastCharOffset]
                
                jmp     @_tryFindTheDotCharacter

        @_extMatch:
                
                pop     di
                pop     si
                pop     ds
                pop     es
                pop     dx
                pop     cx
                pop     ax
                

                ste
                ret

        @_noExtMatch:

                pop     di
                pop     si
                pop     ds
                pop     es
                pop     dx
                pop     cx
                pop     ax
                
                cle
                ret                
        endp

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    dosIntercept
                jmp     @_beginning

                                tsrMarker db 'AB-MLTH'

        @_beginning:
                pushf


                REM     -- Pascal 7 Editor is a special case, it doesn't
                REM        directly saves the source file to .PAS, instead
                REM        it save it first to temporary file then save
                REM        rename it to .PAS
                cmp     ah, DOS_FILE_RENAME
                        ohYes   @_tryRename

                cmp     ah, DOS_FILE_CREATE
                        ohYes   @_tryExtension

                cmp     ah, DOS_FILE_CREATE_SAFE
                        ohYes   @_tryExtension

                cmp     ah, DOS_FILE_CLOSE
                        ohYes   @_tryBackup


                jmp     @_toOldRoutine


        
;............................................................................
                REM     -- this is a special case for Turbo Pascal 7 Editor
                REM        which does not directly save source code to its
                REM        Pascal filename, rather, it renames the editor
                REM        swap file which has an extension of .$$$ to .PAS
        @_tryRename:
                call    isCCPlusplusInMem?
                        ohNo    @_tryIfPascalRename

                jmp     @_continueInterceptingRename

        @_tryIfPascalRename:
                call    isPascalInMem?
                        ohNo    @_toOldRoutine

                jmp     @_continueInterceptingRename

        @_continueInterceptingRename:



                mov     [cs:pascalFNameOffset], di

                push    ds
                push    dx

                REM     -- from the calling rename:
                REM           DS:DX = ES:DI
                REM        where ES:DI is the new name of the rename
                REM           and DS:DX is the filename to test its
                REM           extension
                push    es
                pop     ds
                push    di
                pop     dx
                call    extensionMatch?
                        ohNo    @_pascalNoMatch

                jmp     @_backupPascalFilenameAndHandle

;............................................................................
        @_pascalNoMatch:

                REM     -- no match so do the ff:
                REM          restore the previous registers,
                REM          then the flag of the interrupted program
                pop     dx
                pop     ds
                popf

                pushf
                call    [cs:oldTSRAddr]
                        jc      @_contNoMatchFinal
                
              @_contNoMatchFinal:

                retf    2
;............................................................................          
        @_backupPascalFilenameAndHandle:

                REM     -- no match so do the ff:
                REM          restore the previous registers,
                pop     dx
                pop     ds


                push    ds
                push    si
                push    es
                push    di

                REM     -- then from the rename do the copy string of
                REM           of the renamed's new name to the
                REM           fileToBackup 
                REM        assign: ES:DI = DS:SI
                REM           destination:
                REM              ES = CS
                REM              DI = offset fileToBackup
                REM          source:(the new name of to be renamed temp file)
                REM              DS = ES
                REM              SI = [cs:pascalFNameOffset] --> previously
                REM                   this is the DI, but we save
                REM                   first DI to the variable
                REM                   pascal filename offset to avoid
                REM                   confusion in variable assignments
                push    es
                pop     ds
                mov     si, [cs:pascalFNameOffset]                
                push    cs
                pop     es
                mov     di, offset fileToBackup                                
                mov     cx, [cs:filenameLength]
                inc     cx
                cld
                rep     movsb

                pop     di
                pop     es
                pop     si
                pop     ds


                REM     -- restore the flag of the interrupted program
                popf   
                REM     -- the rename
                pushf
                call    [cs:oldTSRAddr]

                jnc     @_cont

                push    ax
                push    dx
                mov     ah, 02
                mov     dl, 176
                pushf
                call    [cs:oldTSRAddr]
                pop     dx
                pop     ax



                retf    2

        @_cont:                

                REM     -- preserve rename's flag and registers
                pushf
                push    ax
                push    ds
                push    dx


                push    cs
                pop     ds
                mov     ah, DOS_FILE_OPEN
                mov     al, 0
                mov     dx, offset fileToBackup
                pushf
                call    [cs:oldTSRAddr]
                        jnc     @_continueBackuppingPascalHandle
                        
                jmp     @_doNotBackupPascalHandleAndDoNotCloseHandle


;............................................................................
        @_continueBackuppingPascalHandle:
                mov     [cs:handleOfFileToBackup], ax
                setnc   [cs:canWeBackup]                

                REM     -- restore rename's registers and flag
                pop     dx
                pop     ds
                pop     ax
                popf

                REM     -- preserve it we are doing the file close
                pushf
                push    ax
                push    bx

                REM     -- when the file is close it will be
                REM        be backupped
                mov     ah, DOS_FILE_CLOSE
                mov     bx, [cs:handleOfFileToBackup]
                int     21h

                REM     -- restore the rename's previous registers
                REM        and flag, not from the file close
                pop     bx
                pop     ax
                popf

                retf    2

;............................................................................
        @_doNotBackupPascalHandleAndDoNotCloseHandle:

                REM     -- cannot open a file so just do the ff:
                REM          restore previous registers and,
                REM          restore flag of the interrupted
                REM          program
                pop     dx
                pop     ds
                pop     ax
                popf

                REM     -- return to interrupted program
                retf    2

;............................................................................
        @_tryExtension:
        REM     -- before we invoke the hooked FILE CREATE/FILE CREATE SAFE
        REM        we test first if the filename's extension if it
        REM        match our filename extension list

                call    isCCPlusplusInMem?
                        ohNo    @_tryIfPascalExtension

                jmp     @_continueInterceptingExtension

        @_tryIfPascalExtension:
                call    isPascalInMem?
                        ohNo    @_toOldRoutine

                jmp     @_continueInterceptingExtension

        @_continueInterceptingExtension:
                        


                call    extensionMatch?
                        ohYes   @_backupFilenameAndHandle

                jmp     @_noExtensionMatch
                                      

;............................................................................
        @_backupFilenameAndHandle:

                pusha
                push    cs
                pop     es
                mov     di, offset fileToBackup
                mov     si, dx
                mov     cx, [cs:filenameLength]

                BUG     -- this just one line that is missing from the
                BUG        distributed diskette cause a very erroneous
                BUG        critical error when the file is saved

                inc     cx
                cld
                rep     movsb
                popa


                REM     -- restore previous state of flag
                REM           this  flag comes from the interrupted program
                REM           not from this routine
                REM        then invoke the hooked routine which is
                REM           FILE CREATE/FILE CREATE SAFE
                popf
                pushf
                call    [cs:oldTSRAddr]

                setnc   [cs:canWeBackup]
                mov     [cs:handleOfFileToBackup], ax

                REM     -- return to the interrupted program,
                REM        but do not load the flags from the stack
                REM        instead leave it as it is
                retf    2



;............................................................................
        @_noExtensionMatch:
        REM     -- filename's extension does not match our filenames
        REM          extension list, so just invoke the hooked routine
        REM          which is the FILE CREATE/FILE CREATE SAFE
                jmp     @_toOldRoutine


;............................................................................
        @_tryBackup:
                cmp     bx, [cs:handleOfFileToBackup]
                        ohNo    @_toOldRoutine

                cmp     [cs:canWeBackup], 1
                        ohYes   @_sound

                mov     [cs:canWeBackup], 0

                jmp     @_toOldRoutine

;............................................................................
        @_sound:
              
                call    createNewName
                call    copyByteByByte

                mov     [cs:canWeBackup], 0

                jmp     @_toOldRoutine


;............................................................................
        @_toOldRoutine:

                popf
                jmp     [cs:oldTSRAddr]
        endp

        tsrSize = ( ( $ - start ) + 256 + 16 ) / 16
        

        hookedIntr = 21h




;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    freeEnvironment

        REM     Output Register(s):
        REM             ES - segment address of the Environment Offset

;............................................................................

                push    ax

                mov     ah, DOS_RELEASE_MEM
                mov     es, [ds:ENVIRONMENT_SEGMENT]
                int     21h

                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isTSRInMem?

        REM     Output Register(s):
        REM         ES - segment address of the TSR

        REM         Zero/Equality Flag:
        REM             Set when TRUE    -- z / e
        REM             Clear when FALSE -- nz / ne
        
;............................................................................ 

                mov     ah, DOS_INTR_GET_VECT
                mov     al, 21h
                int     21h

                REM     -- 8 is an arbitrary value, it can be 
                REM        any number greater than one, but for 
                REM        the program to surely detect if it really match
                REM        we choose more or less 8  
                mov     cx, 8
                mov     si, offset tsrMarker
                mov     di, bx
                REM     -- we add 3 to DI, the tsrmarker offset
                REM        from the beginning of jmp instruction is 3
                add     di, 3
                cld
                repe    cmpsb
                        ohYes   @_alreadyInMem
                
                jmp     @_notInMem
;............................................................................
        @_notInMem:
                REM     -- the ff. will force clear the equality
                cle
                jmp     @_finally

;............................................................................
        @_alreadyInMem:
                ste
                jmp     @_finally


;............................................................................
        @_finally:
                ret                        
        endp
        
;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    removeTSRInMem

        REM     Input Register(s):
        REM             ES - segment address of the TSR

        REM     Output Register(s):
        REM         Zero/Equality Flag:
        REM             Set when SUCCESS   -- z / e
        REM             Clear when FAILED  -- nz / ne
        REM         Carry Flag:
        REM             Set when FAILED    -- c
        REM             Clear when SUCCESS -- nc


;............................................................................
                push    ax
;............................................................................
        @_tryRelease:
                mov     ah, DOS_RELEASE_MEM
                int     21h
                jc      @_clearEqual

                REM     -- success removing
                ste
                jmp     @_finally

;............................................................................
        @_clearEqual:
                REM     -- no success in removing
                cle                
                jmp     @_finally

;............................................................................
        @_finally:
                pop     ax
                ret                                        
        endp
        
        
;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    putCodeInMem

        REM     -- returns nothing.
        REM        this function does not return to the calling function
        REM        instead it directly returns to DOS
        REM        so it is okay not to preserve used registers

;............................................................................
                REM     -- we have no use for environment strings
                call    freeEnvironment
        
                mov     ah, DOS_INTR_GET_VECT
                mov     al, hookedIntr
                int     21h

                mov     [oldTSROfs], bx
                mov     [oldTSRSeg], es

                mov     ah, DOS_INTR_SET_VECT
                mov     al, hookedIntr
                lea     dx, [dosIntercept]
                int     21h

                mov     ah, DOS_KEEP_TSR
                mov     dx, tsrSize
                int     21h

        endp
        

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isCommandLineEmpty?

        REM     -- returns equal(e/z) when true otherwise not equal(ne/nz)

;............................................................................
                REM     -- do we have a command line? test the length
                pusha
                cmp     [byte es:COMMAND_LINE_LEN_OFFSET], 00h
                        jz      @_noCommandLine

                mov     di, 81h
                mov     ch, 0
                mov     cl, [byte es:80h]
                mov     al, ' '
                cld
                repe    scasb
                        je      @_noCommandLine


                clz
                jmp     @_finally

        @_noCommandLine:
                ste
                jmp     @_finally

        @_finally:
                popa
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isCommandLineSlashU?

        REM     Output Register(s):
        REM         Zero/Equality Flag:
        REM             Set when TRUE    -- z / e
        REM             Clear when FALSE -- nz / ne


;............................................................................        
                push    ax
                push    cx
                push    dx
                push    di
                push    si

                REM    -- scan for slash character
                mov     al, '/'
                mov     ch, 0
                mov     cl, [byte ds:COMMAND_LINE_LEN_OFFSET]
                mov     di, COMMAND_LINE_OFFSET
                cld
                repne   scasb
                        ohNo    @_finally
                        
                REM     -- convert the next character to uppercase
                mov     ax, DOS_CHAR_UP_CASE
                mov     dl, [byte di]
                int     21h
        
        
                mov     [byte di], dl
                cmp     [byte di], 'U'
                        ohNo    @_finally

                jmp     @_finally

;............................................................................
        @_finally:
                pop     si
                pop     di
                pop     dx
                pop     cx
                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    restoreOldRoutine

        REM     Input Register(s):
        REM             ES - segment address of the TSR

        REM     Output Register(s):
        REM             None

;............................................................................
                push    ax
                push    dx
                push    ds

                mov     ax, [es:oldTSRSeg]
                mov     ds, ax

                mov     ah, DOS_INTR_SET_VECT
                mov     al, hookedIntr
                mov     dx, [es:oldTSROfs]
                int     21h

                pop     ds
                pop     dx
                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    motd
                newLine

                jmp     @_motdj

                @_michael db 'Vrxsz~w;Yn~u'

                @_terminator db '$'

        

        @_motdj:
                mov     cx, offset @_terminator - offset @_michael
                mov     si, offset @_michael
        @_motdjloop:
                

                xor     [byte ptr si], 27
                inc     si
                loop    @_motdjloop




                mov     ah, 09h
                mov     dx, offset @_michael
                int     21h

                newLine
                newLine
                puts    '"Programming today is a race between software'
                newLine
                puts    '  engineers striving to build bigger and better'
                newLine
                puts    '  idiot-proof programs, and the universe'
                newLine
                puts    '  trying to produce bigger and better idiots.'
                newLine
                puts    '  So far, the universe is winning."'
                newLine
                newLine
                puts    '   -- Rich Cook'
                newLine
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    main

;............................................................................

        @_tryCommandLine:
                call    isCommandLineEmpty?
                        ohYes    @_tryInstall

                call    isCommandLineSlashU?
                        ohYes    @_tryUninstall

                puts    'Unknown Option'
                jmp     @_finally

;............................................................................
        @_tryUninstall:
                call    isTSRInMem?
                        ohNo    @_tsrNotYetInMem

                call    removeTSRInMem
                        ohNo    @_cantRemoveTSRInMem

                call    restoreOldRoutine

                puts    'Augmented Backups Unloaded'
                jmp     @_finally



;............................................................................
        @_cantRemoveTSRInMem:
                puts    'Cant''t Remove Augmented Backups in Memory'
                jmp     @_finally

;............................................................................
        @_tryInstall:
                call    isTSRInMem?
                        ohYes   @_tsrAlreadyInMem

                puts    'Augmented Backups Loaded'

                call    motd
                call    putCodeInMem
                jmp     @_finally

;............................................................................
        @_tsrNotYetInMem:
                puts    'Augmented Backups Not Yet In Memory'
                jmp     @_finally

;............................................................................
        @_tsrAlreadyInMem:
                puts    'Augmented Backups Already in Memory'
                jmp     @_finally

;............................................................................
        @_finally:
                call    motd
                mov     ah, DOS_EXIT
                int     21h

;............................................................................
        endp
ends

end     start

; MICHAEL BUEN




AK.ASM

Augmented Key is a keyboard automator for Turbo C and Turbo Pascal. I love keyboard shortcuts, there's no mouse shortcuts yet at the time :p heheh

; FILENAME: AK.ASM
P386
IDEAL
JUMPS
LOCALS @_


include 'useful.inc'

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
segment code
        org     100h
        assume cs:code, ds:code, es:nothing, ss:code
start:
        jmp     main



        REM     -- Backspace and CarriageReturn key respectively
        BS                      = 08h
        NL                      = 0Dh
        TAB                     = 09h

        REM   -- we use the alternate keyboard cursor movements(Wordstar-like)
        REM      hotkey, as the Turbo C++ editor refuse
        REM      to distinguish LEFT from CTRL+LEFT, and also
        REM      with RIGHT from CTRL+RIGHT when we
        REM      press the hotkey CTRL+ENTER.

        REM        -- The Ctrl+S in Pascal and C/C++ is move left
        REM             Ctrl+D Ctrl+E and Ctrl+X are right, up, down
        REM             respectively
        KB_CTRL_S               = 13h
        KB_CTRL_X               = 18h
        KB_CTRL_E               = 05h
        KB_CTRL_D               = 04h

        KB_UP                   = 4800h
        KB_DOWN                 = 5000h
        KB_LEFT                 = 4B00h
        KB_RIGHT                = 4D00h


        KB_CTRL_RIGHT_BRACKET   = 1B1Dh
        KB_CTRL_LEFT_BRACKET    = 1A1Bh

        INDENT_HOTKEY           = KB_CTRL_RIGHT_BRACKET
        UNINDENT_HOTKEY         = KB_CTRL_LEFT_BRACKET


        UP                      = KB_CTRL_E
        DOWN                    = KB_CTRL_X
        LEFT                    = KB_CTRL_S
        RIGHT                   = KB_CTRL_D

        REM                     -- ASCII code of Enter
        KB_CTRL_ENTER_ASCII     = 1C0Ah
        REM                     -- Scan code of Enter
        SHORTCUT_HOTKEY         = KB_CTRL_ENTER_ASCII

        KB_SOFTWARE_INTR        = 16h


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        label   dosBusyFlagAddr dword
                dosBusyFlagOfs  dw ?
                dosBusyFlagSeg  dw ?



        struc   keyTemplates
                label   shortcut        word
                firstKey                db ' '
                secondKey               db ' '
                textToInsertOffset      dw ?
        ends


        stuffing        db FALSE
        ptrToText       dw ?


        label   keyBuff word
                firstKeyBuff    db 0
                secondKeyBuff   db 0

        C_DoubleKeys    keyTemplates< 'e', 'i', C_elseIf >
                        keyTemplates< 's', 't', C_struct >
                        keyTemplates< 's', 'w', C_switch >
                        keyTemplates< 'i', 'e', C_ifElse >
                        keyTemplates< 'c', 'a', C_case >
                        keyTemplates< 'c', 'o', C_continue >
                        keyTemplates< 'c', 'l', C_class >
                        keyTemplates< '#', 'i', C_include >
                        keyTemplates< '#', 'd', C_define >
                        keyTemplates< '#', '#', C_includeU >
                        keyTemplates< 'i', 'n', C_intFunc >
                        keyTemplates< 'v', 'o', C_voidFunc >
                        keyTemplates< 'd', 'o', C_doubleFunc >
                        keyTemplates< 'c', 'p', C_cprintf >
                        keyTemplates< 'c', 's', C_cscanf >
                        db      NULL, NULL

        C_SingleKeys    keyTemplates< , 'i', C_if >
                        keyTemplates< , 'w', C_while >
                        keyTemplates< , 'u', C_unsigned >
                        keyTemplates< , 'e', C_else >
                        keyTemplates< , 'd', C_do >
                        keyTemplates< , 'f', C_for >
                        keyTemplates< , 'b', C_break >
                        keyTemplates< , 'r', C_return >
                        keyTemplates< , 'g', C_gotoxy >
                        keyTemplates< , 'p', C_printf >
                        keyTemplates< , 's', C_scanf >
                        keyTemplates< , '!', C_program >
                        db      NULL, NULL


        Pas_DoubleKeys  keyTemplates< 'w', 'h', Pas_while >
                        keyTemplates< 'w', 'i', Pas_with >
                        keyTemplates< 'r', 'u', Pas_repeatUntil >
                        keyTemplates< 'r', 'e', Pas_recordEnd >
                        keyTemplates< 'f', 'o', Pas_for >
                        keyTemplates< 'f', 'd', Pas_for_down >
                        keyTemplates< 'f', 'u', Pas_function >
                        keyTemplates< 'c', 'a', Pas_case >
                        keyTemplates< 'c', 'o', Pas_continue >
                        keyTemplates< 'b', 'e', Pas_beginEnd >
                        keyTemplates< 'b', 'r', Pas_break >
                        keyTemplates< 'i', 'e', Pas_ifElse >
                        keyTemplates< 'e', 'i', Pas_elseIf >
                        keyTemplates< 'i', 'n', Pas_integer >
                        keyTemplates< 'o', 'b', Pas_object >
                        db      NULL, NULL

        Pas_SingleKeys  keyTemplates< , 'p', Pas_procedure >
                        keyTemplates< , 'e', Pas_else >
                        keyTemplates< , 'i', Pas_if >
                        keyTemplates< , 'a', Pas_array >
                        keyTemplates< , 'w', Pas_writeln >
                        keyTemplates< , 'r', Pas_readln >
                        keyTemplates< , 'g', Pas_gotoxy >
                        keyTemplates< , '!', Pas_program >
                        db      NULL, NULL

        REM             -- Pascal double keys
        Pas_while       db BS, BS, 'do', NL
                        db 'begin', NL, NL
                        db 'end;', LEFT, LEFT, LEFT, LEFT
                        db UP, UP, UP
                        db 'while  '
                        db LEFT
                        db NULL

        Pas_with        db BS, BS, 'do', NL
                        db 'begin', NL, NL
                        db 'end;', LEFT, LEFT, LEFT, LEFT
                        db UP, UP, UP
                        db 'with  '
                        db LEFT
                        db NULL

        Pas_repeatUntil db BS, BS
                        db 'repeat', NL, NL
                        db 'until ;', LEFT
                        db NULL

        Pas_recordEnd   db 'cord', NL, NL
                        db 'end;'
                        db NULL

        Pas_for         db BS, BS, ':=  to  do', NL
                        db 'begin', NL, NL
                        db 'end;', LEFT, LEFT, LEFT, LEFT
                        db UP, UP, UP
                        db 'for  '
                        db LEFT
                        db NULL

        Pas_for_down    db BS, BS, ':=  downto  do', NL
                        db 'begin', NL, NL
                        db 'end;', LEFT, LEFT, LEFT, LEFT
                        db UP, UP, UP
                        db 'for  '
                        db LEFT
                        db NULL

        Pas_function    db BS, BS, ':;', NL
                        db 'begin', NL, NL
                        db 'end;', NL
                        db UP, UP, UP, UP
                        db 'function '
                        db NULL

        Pas_case        db BS, BS, 'of', NL
                        db ':', NL
                        db ':', NL
                        db 'end;', LEFT, LEFT, LEFT, LEFT
                        db UP, UP, UP
                        db 'case  '
                        db LEFT
                        db NULL

        Pas_continue    db 'ntinue;', NL
                        db NULL

        Pas_beginEnd    db 'gin', NL, NL
                        db 'end;', LEFT, LEFT, LEFT, LEFT
                        db UP, TAB
                        db NULL

        Pas_break       db 'eak;', NL
                        db NULL

        Pas_ifElse      db BS, BS, 'then', NL
                        db 'begin', NL, NL
                        db 'end', NL
                        db 'else', NL
                        db 'begin', NL, NL
                        db 'end;', LEFT, LEFT, LEFT, LEFT
                        db UP, UP, UP, UP
                        db UP, UP, UP
                        db 'if  '
                        db LEFT
                        db NULL

        Pas_elseIf      db BS, BS, 'then', NL
                        db 'begin', NL, NL
                        db 'end', LEFT, LEFT, LEFT
                        db UP, UP, UP
                        db 'else if  '
                        db LEFT
                        db NULL

        Pas_integer     db 'teger;', NL
                        db NULL

        Pas_object      db BS, BS, 'object', NL
                        db 'private', NL, NL
                        db 'public', NL, NL
                        db 'end;', LEFT, LEFT, LEFT, LEFT
                        db UP, UP, UP, UP, UP
                        db NULL

        REM             -- Pascal Single Keys
        Pas_procedure   db BS, ';', NL
                        db 'begin', NL, NL
                        db 'end;', NL
                        db UP, UP, UP, UP
                        db 'procedure '
                        db NULL

        Pas_else        db 'lse', NL
                        db 'begin', NL, NL
                        db 'end;', LEFT, LEFT, LEFT, LEFT
                        db UP
                        db NULL

        Pas_if          db BS, 'then', NL
                        db 'begin', NL, NL
                        db 'end;', LEFT, LEFT, LEFT, LEFT
                        db UP, UP, UP
                        db 'if  '
                        db LEFT
                        db NULL

        Pas_array       db 'rray [ .. ] of'
                        db LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT
                        db NULL

        Pas_writeln     db 'riteln;', LEFT
                        db NULL

        Pas_readln      db 'eadln;', LEFT
                        db NULL

        Pas_gotoxy      db 'otoxy( ,  );'
                        db LEFT, LEFT, LEFT, LEFT, LEFT
                        db NULL

        Pas_program     db BS
                        db 'uses crt, dos, printer, graph;', NL
                        db 'var', NL
                        db NL
                        db NL
                        db 'begin', NL
                        db NL
                        db 'end.', LEFT, LEFT, LEFT, LEFT
                        db UP, UP, UP, UP, TAB
                        db NULL


        REM             -- C double keys
        C_elseIf        db BS, BS, ')',NL
                        db '{', NL, NL
                        db '}', LEFT
                        db UP, UP, UP
                        db 'else if(  '
                        db LEFT
                        db NULL

        C_struct        db 'ruct', NL
                        db '{', NL
                        db NL
                        db '};', LEFT, LEFT
                        db UP, UP, UP
                        db RIGHT, RIGHT, RIGHT, RIGHT
                        db RIGHT, RIGHT
                        db ' '
                        db NULL


        C_switch        db BS, BS, ')', NL
                        db '{', NL
                        db 'case :',NL, NL
                        db '    break;', NL, NL, BS
                        db 'case :',NL, NL
                        db '    break;', NL, NL, BS
                        db 'default:', NL, NL
                        db '    break;', NL, BS
                        db '}', LEFT
                        db UP, UP, UP, UP, UP, UP, UP
                        db UP, UP, UP, UP, UP, UP
                        db 'switch(  '
                        db LEFT
                        db NULL

        C_ifElse        db BS, BS, ')', NL
                        db '{', NL, NL
                        db '}', NL
                        db 'else', NL
                        db '{', NL, NL
                        db '}', LEFT
                        db UP, UP, UP, UP
                        db UP, UP, UP
                        db 'if(  '
                        db LEFT
                        db NULL

        C_case          db 'se:', NL, NL
                        db '    break;'
                        db LEFT, LEFT, LEFT, LEFT, LEFT, LEFT
                        db UP, UP, ' '
                        db NULL

        C_continue      db 'ntinue;', NL
                        db NULL

        C_class         db 'ass', NL
                        db '{', NL
                        db 'private:', NL, NL
                        db 'protected:', NL, NL
                        db 'public:', NL, NL
                        db '};', LEFT, LEFT
                        db UP, UP, UP, UP, UP, UP, UP, UP
                        db RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, ' '
                        db NULL

        C_include       db 'nclude <.h>', LEFT, LEFT, LEFT
                        db NULL

        C_define        db 'efine '
                        db NULL

        C_includeU      db BS, 'include ".h"', LEFT, LEFT, LEFT
                        db NULL

        C_intFunc       db 't ()', NL
                        db '{', NL
                        db NL
                        db '}', NL
                        db UP, UP, UP, UP, RIGHT, RIGHT, RIGHT, RIGHT
                        db NULL


        C_voidFunc      db 'id ()', NL
                        db '{', NL
                        db NL
                        db '}', NL
                        db UP, UP, UP, UP, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT
                        db NULL

        C_doubleFunc    db 'uble ()', NL
                        db '{', NL
                        db NL
                        db '}', NL
                        db UP, UP, UP, UP
                        db RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT
                        db NULL


        C_cprintf       db 'rintf( "",  );'
                        db LEFT, LEFT, LEFT, LEFT, LEFT, LEFT
                        db NULL

        C_cscanf        db 'canf( "",  );'
                        db LEFT, LEFT, LEFT, LEFT, LEFT, LEFT
                        db NULL




        REM             -- C single keys
        C_if            db BS, ')', NL
                        db '{', NL, NL
                        db '}', LEFT
                        db UP, UP, UP
                        db 'if(  '
                        db LEFT
                        db NULL

        C_while         db BS, ')', NL
                        db '{', NL, NL
                        db '}', LEFT
                        db UP, UP, UP
                        db 'while(  '
                        db LEFT
                        db NULL

        C_unsigned      db 'nsigned '
                        db NULL

        C_else          db 'lse', NL
                        db '{', NL, NL
                        db '}', LEFT
                        db UP
                        db NULL

        C_do            db 'o', NL
                        db '{', NL
                        db '}', NL
                        db 'while(  );'
                        db LEFT, LEFT, LEFT
                        db NULL

        C_for           db BS, '; ;  )', NL
                        db '{', NL, NL
                        db '}', LEFT
                        db UP, UP, UP
                        db 'for( '
                        db NULL

        C_break         db 'reak;', NL
                        db NULL

        C_return        db 'eturn '
                        db NULL


        C_printf        db 'rintf( "",  );'
                        db LEFT, LEFT, LEFT, LEFT, LEFT, LEFT
                        db NULL

        C_scanf         db 'canf( "",  );'
                        db LEFT, LEFT, LEFT, LEFT, LEFT, LEFT
                        db NULL

        C_gotoxy        db 'otoxy( ,  );'
                        db LEFT, LEFT, LEFT, LEFT, LEFT
                        db NULL


        C_program       db BS
                        db '#include <stdio.h>', NL
                        db '#include <io.h>', NL
                        db '#include <conio.h>', NL
                        db '#include <math.h>', NL
                        db NL
                        db 'void main()', NL
                        db '{', NL
                        db NL
                        db '}', LEFT, UP, TAB
                        db NULL


        CPas_indent     db 0Bh, 'I'
                        db NULL

        CPas_unindent   db 0Bh, 'U'
                        db NULL



        doublekeysPtr   dw ?
        singlekeysPtr   dw ?


        REM     -- prev address of keyboard interrupt 16h
        label   preAKAddr       dword
                preAKOffset     dw ?
                preAKSegment    dw ?

        initialMCBSegment       dw ?
        mcbAsciiNameLength      dw ?

        struc   langNameList
                len     dw ?
                desc    db 8 dup(NULL)
        ends


        pascalLang      langNameList< 5, 'TURBO' >
                        langNameList< 2, 'TP' >
                        langNameList< 3, 'TPX' >
                        langNameList< 2, 'BP' >
                        langNameList< 3, 'BPX' >
                        langNameList< 6, 'PASCAL' >
                        db NULL

        ccplusLang      langNameList< 2, 'TC' >
                        langNameList< 3, 'TCX' >
                        langNameList< 2, 'BC' >
                        langNameList< 3, 'BCX' >
                        langNameList< 2, 'QC' >
                        langNameList< 3, 'QCX' >
                        langNameList< 1, 'C' >
                        db NULL



        langToDetect    dw ?



;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isLanguageInMem?
                push    ax
                push    bx
                push    cx
                push    ds
                push    si
                push    es
                push    di


        @_firstMCB:
                push    [cs:initialMCBSegment]
                pop     es

                push    cs
                pop     ds

;............................................................................
        @_traverseMCB:

                mov     bx, [cs:langToDetect]

        @_loopLang:

                mov     di, 0
                mov     cx, 8
        @_findMCBAsciiNameLength:
                inc     di
                cmp     [(mcb es:di).asciiName], NULL
                loopne  @_findMCBAsciiNameLength

                mov     [cs:mcbAsciiNameLength], di

                mov     cx, [(langNameList cs:bx).len]

                REM     -- if not same string length scan the
                REM        next language name
                cmp     cx, [cs:mcbAsciiNameLength]
                        ohNo    @_scanNextLanguageName

                lea     di, [es:mcb.asciiName]
                lea     si, [(langNameList cs:bx).desc]


                cld
                repe    cmpsb
                        ohYes   @_found


        @_scanNextLanguageName:

                add     bx, size langNameList
                cmp     [(langNameList cs:bx).desc], NULL
                        ohYes   @_stopScanningLang

                jmp     @_loopLang

        @_stopScanningLang:

        @_scanTheNextMCB:

                cmp     [es:mcb.linkIndicator], 'Z'
                je      @_notFound

                mov     ax, es
                inc     ax

                add     ax, [es:mcb.paragraphLen]
                mov     es, ax

                jmp     @_traverseMCB

;............................................................................
        @_found:
                ste
                jmp     @_finish

;............................................................................
        @_notFound:
                cle
                jmp     @_finish

        @_finish:

                pop     di
                pop     es
                pop     si
                pop     ds
                pop     cx
                pop     bx
                pop     ax

                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isPascalInMem?
        REM     Input(s):
        REM        langToDetect -- pass here to offset of name list of
        REM                        the language
        REM     Output(s):
        REM        Equality/Zero Flag -- if present in memory
;............................................................................
                mov     [cs:langToDetect], offset pascalLang
                call    isLanguageInMem?
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isCCPlusplusInMem?
        REM     Input(s):
        REM        langToDetect -- pass here to offset of name list of
        REM                        the language
        REM     Output(s):
        REM        Equality/Zero Flag -- if present in memory
;............................................................................
                mov     [cs:langToDetect], offset ccplusLang
                call    isLanguageInMem?
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    getInitialMCBSegment
        REM     Purpose:
        REM             Get initial MCB Segment, which is very useful
        REM               in traversing memory links, to see if a particular
        REM               program is in memory
        REM             In our program this is use for inquiring the
        REM               language loaded in memory (Pascal or C/C++?)
        REM     Input(s):
        REM             None
        REM     Output(s):
        REM             initialMCBSegment: data type = word
;............................................................................
                push    ax
                push    es
                push    bx

                mov     ah, DOS_GET_INITIAL_MCB
                int     21h

                mov     ax, [word es:bx-2]
                mov     [cs:initialMCBSegment], ax

                pop     bx
                pop     es
                pop     ax
                ret
        endp




;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    insertAtOurBuffer
        REM     Purpose:
        REM             Simulate keyboard buffer reading
        REM     Input(s):
        REM             None
        REM     Output(s):
        REM             keybuff: data type = word
        REM             has two components:
        REM                 firstkeyBuff
        REM                 secondkeyBuff
;............................................................................
                pusha
                mov     ah, BIOS_GET_CURSOR_POS
                REM     -- 0 is page number
                mov     bh, 0
                int     10h

                mov     cx, dx

                REM     -- DH is row DL is column
                dec     dl
                mov     ah, BIOS_SET_CURSOR_POS
                int     10h
                mov     ah, BIOS_READ_CHAR_AT_POS
                int     10h
                mov     [cs:secondKeyBuff], al

                REM     -- DH is row DL is column
                dec     dl
                mov     ah, BIOS_SET_CURSOR_POS
                int     10h
                mov     ah, BIOS_READ_CHAR_AT_POS
                int     10h
                mov     [cs:firstKeyBuff], al


                mov     ah, BIOS_SET_CURSOR_POS
                REM     -- 0 is page number
                mov     bh, 0
                mov     dx, cx
                int     10h

                popa
                ret
        endp



;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    keyIntercept
                jmp     @_beginning

                tsrMarker       db 'AK-MLTH'

        @_beginning:

                pushf
                push    es
                push    di
                les     di, [cs:dosBusyFlagAddr]
                cmp     [byte ptr es:di], 0
                        ohYes    @_continueBeginning
        @_donotContinueBeginning:
                pop     di
                pop     es
                popf
                jmp     @_doOld
        @_continueBeginning:
                pop     di
                pop     es
                popf


                cmp     ah, BIOS_OLD_READKEY
                        ohYes   @_tryReadkey

                cmp     ah, BIOS_NEW_READKEY
                        ohYes   @_tryReadkey

                cmp     ah, BIOS_OLD_KEYSTAT
                        ohYes   @_tryKeystat

                cmp     ah, BIOS_NEW_KEYSTAT
                        ohYes   @_tryKeystat


;............................................................................
        @_tryReadkey:
                cmp     [cs:stuffing], TRUE
                        ohYes   @_fakeReadkey

                jmp     @_realReadkey

;............................................................................
        @_realReadkey:
                pushf
                call    [cs:preAKAddr]
                cmp     ax, SHORTCUT_HOTKEY
                        ohYes   @_readKeyShortcutContinue

                cmp     ax, INDENT_HOTKEY
                        ohYes   @_readkeyIndent

                cmp     ax, UNINDENT_HOTKEY
                        ohYes   @_readkeyUnindent

                jmp     @_readkeyShortcutNotTriggerBackToCaller

        @_readkeyIndent:
                mov     [cs:stuffing], TRUE
                mov     [cs:ptrToText], offset CPas_indent
                jmp     @_fakeReadkey

        @_readkeyUnindent:
                mov     [cs:stuffing], TRUE
                mov     [cs:ptrToText], offset CPas_unindent
                jmp     @_fakeReadkey


        @_readkeyShortcutContinue:

                call    insertAtOurBuffer

                call    isPascalInMem?
                        ohYes   @_readkeyPascal
                call    isCCPlusplusInMem?
                        ohYes   @_readkeyCCPlusplus

                jmp     @_doOld


;............................................................................
        @_readkeyPascal:
                mov     [cs:doublekeysPtr], offset Pas_doubleKeys
                mov     [cs:singlekeysPtr], offset Pas_singleKeys
                jmp     @_readkeyLanguage

;............................................................................
        @_readkeyCCPlusplus:
                mov     [cs:doublekeysPtr], offset C_doubleKeys
                mov     [cs:singlekeysPtr], offset C_singleKeys
                jmp     @_readkeyLanguage

;............................................................................
        @_readkeyLanguage:


                push    dx
                push    bx
                mov     dx, [cs:keyBuff]
                mov     bx, [cs:doublekeysPtr]
        @_readkeyDoublekeyCheckKeysLoop:
                cmp     [(keyTemplates cs:bx).shortcut], NULL
                        ohYes   @_readkeyDoublekeyShortcutNotAvailable
                cmp     [(keyTemplates cs:bx).shortcut], dx
                        ohYes   @_readkeyDoublekeyShortcutAvailable
                add     bx, size keyTemplates
                jmp     @_readkeyDoublekeyCheckKeysLoop
        @_readkeyDoublekeyShortcutAvailable:
                REM     -- clear first our own keyboard buffer
                mov     [cs:keyBuff], 0000h
                mov     bx, [(keyTemplates cs:bx).textToInsertOffset]
                mov     [cs:ptrToText], bx
                mov     [cs:stuffing], TRUE
                jmp     @_readkeyDoublekeyCheckKeysStop
        @_readkeyDoublekeyShortcutNotAvailable:
                mov     [cs:ptrToText], 0
                mov     [cs:stuffing], FALSE
                jmp     @_readkeyDoublekeyCheckKeysStop
        @_readkeyDoublekeyCheckKeysStop:
                pop     bx
                pop     dx

                cmp     [cs:stuffing], TRUE
                je      @_fakeReadkey

                push    dx
                push    bx
                mov     dl, [cs:secondKeybuff]
                mov     bx, [cs:singlekeysPtr]
        @_readkeySinglekeyCheckKeysLoop:
                cmp     [(keyTemplates cs:bx).secondKey], NULL
                        ohYes   @_readkeySinglekeyShortcutNotAvailable
                cmp     [(keyTemplates cs:bx).secondKey], dl
                        ohYes   @_readkeySinglekeyShortcutAvailable
                add     bx, size keyTemplates
                jmp     @_readkeySinglekeyCheckKeysLoop
        @_readkeySinglekeyShortcutAvailable:
                REM     -- clear first our own keyboard buffer
                mov     [cs:keyBuff], 0000h
                mov     bx, [(keyTemplates cs:bx).textToInsertOffset]
                mov     [cs:ptrToText], bx
                mov     [cs:stuffing], TRUE
                jmp     @_readkeySinglekeyCheckKeysStop
        @_readkeySinglekeyShortcutNotAvailable:
                mov     [cs:ptrToText], 0
                mov     [cs:stuffing], FALSE
                jmp     @_readkeySinglekeyCheckKeysStop
        @_readkeySinglekeyCheckKeysStop:
                pop     bx
                pop     dx

                cmp     [cs:stuffing], TRUE
                je      @_fakeReadkey

                iret

;............................................................................
        @_fakeReadkey:
                push    bx
                mov     bx, [cs:ptrToText]
                mov     al, [byte cs:bx]

                mov     ah, 0

                inc     [cs:ptrToText]
                mov     bx, [cs:ptrToText]
                cmp     [byte cs:bx], NULL
                setne   [cs:stuffing]
                pop     bx

                iret

;............................................................................
        @_readkeyShortcutNotTriggerBackToCaller:
                iret

;............................................................................
        @_tryKeystat:
                cmp     [cs:stuffing], TRUE
                        ohYes   @_fakeKeystat

                jmp     @_realKeystat

;............................................................................
        @_realKeystat:
                pushf
                call    [cs:preAKAddr]
                REM     -- Zero keys Available so just back to caller
                        jz      @_keystatBackToCaller

                cmp     ax, SHORTCUT_HOTKEY
                        ohYes   @_keyStatShortcutContinue

                cmp     ax, INDENT_HOTKEY
                        ohYes   @_keystatIndent

                cmp     ax, UNINDENT_HOTKEY
                        ohYes   @_keystatUnindent

                jmp     @_keystatBackToCaller


        @_keystatIndent:
                mov     ah, BIOS_OLD_READKEY
                pushf
                call    [cs:preAKAddr]
                mov     [cs:stuffing], TRUE
                mov     [cs:ptrToText], offset CPas_indent
                jmp     @_fakeKeystat

        @_keystatUnindent:
                mov     ah, BIOS_OLD_READKEY
                pushf
                call    [cs:preAKAddr]
                mov     [cs:stuffing], TRUE
                mov     [cs:ptrToText], offset CPas_unindent
                jmp     @_fakeKeystat

        @_keystatShortcutContinue:

                REM     -- we do this just to remove the hotkey
                REM        so it won't be evaluated again,
                REM        if you try to remove this
                REM        three lines of code you'll see
                REM        the insertion of keys will go into
                REM        infinitum
                mov     ah, BIOS_OLD_READKEY
                pushf
                call    [cs:preAKAddr]

                call    insertAtOurBuffer

                call    isPascalInMem?
                        ohYes   @_keystatPascal
                call    isCCPlusplusInMem?
                        ohYes   @_keystatCCPlusplus

                stz
                retf    2

;............................................................................
        @_keystatPascal:
                mov     [cs:doublekeysPtr], offset Pas_doubleKeys
                mov     [cs:singlekeysPtr], offset Pas_singleKeys
                jmp     @_keystatLanguage

;............................................................................
        @_keystatCCPlusplus:
                mov     [cs:doublekeysPtr], offset C_doubleKeys
                mov     [cs:singlekeysPtr], offset C_singleKeys
                jmp     @_keystatLanguage

;............................................................................
        @_keystatLanguage:


                push    dx
                push    bx
                mov     dx, [cs:keyBuff]
                mov     bx, [cs:doublekeysPtr]
        @_keystatDoubleCheckKeysLoop:
                cmp     [(keyTemplates cs:bx).shortcut], NULL
                        ohYes   @_keystatDoubleShortcutNotAvailable
                cmp     [(keyTemplates cs:bx).shortcut], dx
                        ohYes   @_keystatDoubleShortcutAvailable
                add     bx, size keyTemplates
                jmp     @_keystatDoubleCheckKeysLoop
        @_keystatDoubleShortcutAvailable:
                REM     -- clear first our own keyboard buffer
                mov     [cs:keyBuff], 0000h
                mov     bx, [(keyTemplates cs:bx).textToInsertOffset]
                mov     [cs:ptrToText], bx
                mov     [cs:stuffing], TRUE
                jmp     @_keystatDoubleCheckKeysStop
        @_keystatDoubleShortcutNotAvailable:
                mov     [cs:ptrToText], 0
                mov     [cs:stuffing], FALSE
                jmp     @_keystatDoubleCheckKeysStop
        @_keystatDoubleCheckKeysStop:
                pop     bx
                pop     dx

                cmp     [cs:stuffing], TRUE
                je      @_fakekeystat

                push    dx
                push    bx
                mov     dl, [cs:secondKeybuff]
                mov     bx, [cs:singlekeysPtr]
        @_keystatSingleCheckKeysLoop:
                cmp     [(keyTemplates cs:bx).secondKey], NULL
                        ohYes   @_keystatSingleShortcutNotAvailable
                cmp     [(keyTemplates cs:bx).secondKey], dl
                        ohYes   @_keystatSingleShortcutAvailable
                add     bx, size keyTemplates
                jmp     @_keystatSingleCheckKeysLoop
        @_keystatSingleShortcutAvailable:
                REM     -- clear first our own keyboard buffer
                mov     [cs:keyBuff], 0000h
                mov     bx, [(keyTemplates cs:bx).textToInsertOffset]
                mov     [cs:ptrToText], bx
                mov     [cs:stuffing], TRUE
                jmp     @_keystatSingleCheckKeysStop
        @_keystatSingleShortcutNotAvailable:
                mov     [cs:ptrToText], 0
                mov     [cs:stuffing], FALSE
                jmp     @_keystatSingleCheckKeysStop
        @_keystatSingleCheckKeysStop:
                pop     bx
                pop     dx

                cmp     [cs:stuffing], TRUE
                je      @_fakekeyStat

                mov     [cs:keyBuff], 0
                stz
                retf    2

;............................................................................
        @_fakeKeystat:
                push    bx
                mov     bx, [cs:ptrToText]
                mov     al, [byte cs:bx]

                mov     ah, 0
                cmp     al, NULL
                        ohNo    @_continueKeystatOrdinaryKey
                mov     [cs:stuffing], FALSE
                stz
                retf    2


        @_continueKeystatOrdinaryKey:
                jmp     @_keystatInsert

        @_keystatInsert:
                pop     bx
                REM     -- clear zero meaning simulate key availability
                clz

                retf    2


;............................................................................
        @_keystatShortcutNotTriggerBackToCaller:
                clz
                retf    2

;............................................................................
        @_keystatBackToCaller:
                retf    2

;............................................................................
        @_doOld:
                jmp     [cs:preAKAddr]
        endp


        tsrSize = ( ( $ - start ) + 256 + 16 ) / 16







;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    freeEnvironment

        REM     Output Register(s):
        REM             ES - segment address of the Environment

;............................................................................

                push    ax

                mov     ah, DOS_RELEASE_MEM
                mov     es, [ds:ENVIRONMENT_SEGMENT]
                int     21h

                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isTSRInMem?

        REM     Output Register(s):
        REM         ES - segment address of the TSR

        REM         Zero/Equality Flag:
        REM             Set when TRUE    -- z / e
        REM             Clear when FALSE -- nz / ne

;............................................................................

                push    si
                push    di
                push    ax
                push    cx

                mov     ah, DOS_INTR_GET_VECT
                mov     al, KB_SOFTWARE_INTR
                int     21h

                REM     -- 8 is an arbitrary value, it can be
                REM        any number greater than one, but for
                REM        the program to surely detect if TS
                REM        signature really match we choose
                REM        more or less 8
                mov     cx, 8
                mov     si, offset tsrMarker
                mov     di, bx
                REM     -- we add 3 to DI, the tsrmarker offset
                REM        from the beginning of jmp instruction is 3
                add     di, 3
                cld
                repe    cmpsb
                        ohYes   @_alreadyInMem

                jmp     @_notInMem
;............................................................................
        @_notInMem:
                REM     -- the ff. will force clear the equality
                cle
                jmp     @_finally

;............................................................................
        @_alreadyInMem:
                ste
                jmp     @_finally


;............................................................................
        @_finally:
                pop     cx
                pop     ax
                pop     di
                pop     si
                ret
        endp

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    removeTSRInMem

        REM     Input Register(s):
        REM             ES - segment address of the TSR

        REM     Output Register(s):
        REM         Zero/Equality Flag:
        REM             Set when SUCCESS   -- z / e
        REM             Clear when FAILED  -- nz / ne
        REM         Carry Flag:
        REM             Set when FAILED    -- c
        REM             Clear when SUCCESS -- nc


;............................................................................
                push    ax
;............................................................................
        @_tryRelease:
                mov     ah, DOS_RELEASE_MEM
                int     21h
                jc      @_clearEqual

                REM     -- success removing
                ste
                jmp     @_finally

;............................................................................
        @_clearEqual:
                REM     -- no success in removing
                cle
                jmp     @_finally

;............................................................................
        @_finally:
                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    putCodeInMem

        REM     -- returns nothing.
        REM        this function does not return to the calling function
        REM        instead it directly returns to DOS
        REM        so it is okay not to preserve used registers

;............................................................................
                REM     -- we have no use for environment strings
                call    freeEnvironment

                mov     ah, DOS_GET_BUSY_FLAG_ADDR
                int     21h
                mov     [dosBusyFlagOfs], bx
                mov     [dosBusyFlagSeg], es

                mov     ah, DOS_INTR_GET_VECT
                mov     al, KB_SOFTWARE_INTR
                int     21h

                mov     [preAKOffset], bx
                mov     [preAKSegment], es


                mov     ah, DOS_INTR_SET_VECT
                mov     al, KB_SOFTWARE_INTR
                mov     dx, offset keyIntercept
                int     21h

                mov     ah, DOS_KEEP_TSR
                mov     dx, tsrSize
                int     21h

        endp



;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isCommandLineEmpty?

        REM     -- returns equal(e/z) when true otherwise not equal(ne/nz)

;............................................................................
                REM     -- do we have a command line? test the length
                pusha
                cmp     [byte es:COMMAND_LINE_LEN_OFFSET], 00h
                        jz      @_noCommandLine

                mov     di, 81h
                mov     ch, 0
                mov     cl, [byte es:80h]
                mov     al, ' '
                cld
                repe    scasb
                        je      @_noCommandLine


                clz
                jmp     @_finally

        @_noCommandLine:
                ste
                jmp     @_finally

        @_finally:
                popa
                ret
        endp



;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isCommandLineSlashU?

        REM     Output Register(s):
        REM         Zero/Equality Flag:
        REM             Set when TRUE    -- z / e
        REM             Clear when FALSE -- nz / ne


;............................................................................
                push    ax
                push    cx
                push    dx
                push    di
                push    si

                REM    -- scan for slash character
                mov     al, '/'
                mov     ch, 0
                mov     cl, [byte ds:80h]
                mov     di, 81h
                cld
                repne   scasb
                        ohNo    @_finally

                REM     -- convert the next character to uppercase
                mov     ax, DOS_CHAR_UP_CASE
                mov     dl, [byte di]
                int     21h


                mov     [byte di], dl
                cmp     [byte di], 'U'
                        ohNo    @_finally

                jmp     @_finally

;............................................................................
        @_finally:
                pop     si
                pop     di
                pop     dx
                pop     cx
                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    restoreOldRoutine

        REM     Input Register(s):
        REM             ES - segment address of the TSR

        REM     Output Register(s):
        REM             None

;............................................................................
                push    ax
                push    dx
                push    ds

                mov     ax, [es:preAKSegment]
                mov     ds, ax

                mov     ah, DOS_INTR_SET_VECT
                mov     al, KB_SOFTWARE_INTR
                mov     dx, [es:preAKOffset]
                int     21h

                pop     ds
                pop     dx
                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-




        proc    motd
                newLine

                jmp     @_motdj

                @_michael db 'Vrxsz~w;Yn~u'

                @_terminator db '$'

        

        @_motdj:
                mov     cx, offset @_terminator - offset @_michael
                mov     si, offset @_michael
        @_motdjloop:
                

                xor     [byte ptr si], 27
                inc     si
                loop    @_motdjloop




                mov     ah, 09h
                mov     dx, offset @_michael
                int     21h


                newLine
                newLine
                puts    '"From the programmer''s point of view,'
                newLine
                puts    '     a user is just a peripheral that types'
                newline
                puts    '     in when the program issues a read request."'
                newLine
                newLine
                puts    '   -- From Java Book(the source Sun)'
                newLine
                ret
        endp


                




;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    main
                call    getInitialMCBSegment

        @_tryCommandLine:
                call    isCommandLineEmpty?
                        ohYes    @_tryInstall

                call    isCommandLineSlashU?
                        ohYes    @_tryUninstall

                puts    'Unknown Option'
                jmp     @_finally

;............................................................................
        @_tryUninstall:
                call    isTSRInMem?
                        ohNo    @_tsrNotYetInMem

                call    removeTSRInMem
                        ohNo    @_cantRemoveTSRInMem

                call    restoreOldRoutine

                puts    'Augmented Keys Unloaded'
                jmp     @_finally



;............................................................................
        @_cantRemoveTSRInMem:
                puts    'Cant''t Remove Augmented Keys in Memory'
                jmp     @_finally

;............................................................................
        @_tryInstall:
                call    isTSRInMem?
                        ohYes   @_tsrAlreadyInMem

                puts    'Augmented Keys Loaded'

                call    motd
                call    putCodeInMem
                jmp     @_finally

;............................................................................
        @_tsrNotYetInMem:
                puts    'Augmented Keys not yet in memory'
                jmp     @_finally

;............................................................................
        @_tsrAlreadyInMem:
                puts    'Augmented Keys already in memory'
                jmp     @_finally

;............................................................................
        @_finally:
                call    motd
                mov     ah, DOS_EXIT
                int     21h

;............................................................................
        endp
ends

end     start



; MICHAEL BUEN


AH.ASM

Augumented Help. Extends Turbo C/C++ and Turbo Pascal help functionality

; FILENAME: AH.ASM
P386
IDEAL
JUMPS
LOCALS @_


include 'useful.inc'

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
segment code
        org     100h
        assume cs:code, ds:code, es:nothing, ss:code
start:
        jmp     main


        KB_PAGE_UP              = 4900h
        KB_PAGE_DOWN            = 5100h
        KB_ESCAPE               = 011Bh
        KB_TAB                  = 0F09h
        KB_SHIFT_TAB            = 0F00h
        KB_ENTER                = 1C0Dh
        KB_INSERT               = 5200h
        KB_HOME                 = 4700h

        KB_F11                  = 8500h

        KB_CTRL_F10             = 6700h
        SHORTCUT_HOTKEY         = KB_CTRL_F10
        KB_SOFTWARE_INTR        = 16h

        KB_ALT_PLUS             = 8300h

        PASTE_HOTKEY            = KB_ALT_PLUS

        REM        -- The Ctrl+S in Pascal and C/C++ is move left
        REM             Ctrl+D Ctrl+E and Ctrl+X are right, up, down
        REM             respectively
        KB_CTRL_S               = 13h
        KB_CTRL_X               = 18h
        KB_CTRL_E               = 05h
        KB_CTRL_D               = 04h

        UP                      = KB_CTRL_E
        DOWN                    = KB_CTRL_X
        LEFT                    = KB_CTRL_S
        RIGHT                   = KB_CTRL_D

        REM -- this special symbol is use in pasting an example
        HOME                    = 3

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        label   dosBusyFlagAddr dword
                dosBusyFlagOfs  dw ?
                dosBusyFlagSeg  dw ?

        REM     -- prev address of keyboard interrupt 16h
        label   preAHAddr       dword
                preAHOffset     dw ?
                preAHSegment    dw ?

        initialMCBSegment       dw ?
        mcbAsciiNameLength      dw ?

        struc   langNameList
                len     dw ?
                desc    db 8 dup(NULL)
        ends

        WIN_X1            = 7
        WIN_Y1            = 3
        WIN_X2            = 73
        WIN_Y2            = 21
        WIN_FGCOLOR       = YELLOW
        WIN_BGCOLOR       = CYAN
        WIN_TITLEFGCOLOR  = WHITE
        WIN_TITLEBGCOLOR  = RED

        WIN_RELATED_FGCOLOR = BLACK
        WIN_RELATED_BGCOLOR = CYAN
        SHADOW_COLOR      = ( BLACK shl 4 ) or LIGHTGRAY


        cursorRowLoopVar        db ?
        cursorColLoopVar        db ?


        REM                     -- 4000 is 80x25 = 2000 chars + another 2000
        REM                             for the chars' attribute
        SCREEN_BUFFER_SIZE      = 4000
        helpWinBackSave         db SCREEN_BUFFER_SIZE dup(?)

        label   prevCursorPos   word
                prevCursorCol   db ?
                prevCursorRow   db ?


        pascalLang      langNameList< 5, 'TURBO' >
                        langNameList< 2, 'TP' >
                        langNameList< 3, 'TPX' >
                        langNameList< 2, 'BP' >
                        langNameList< 3, 'BPX' >
                        langNameList< 6, 'PASCAL' >
                        db NULL

        ccplusLang      langNameList< 2, 'TC' >
                        langNameList< 3, 'TCX' >
                        langNameList< 2, 'BC' >
                        langNameList< 3, 'BCX' >
                        langNameList< 2, 'QC' >
                        langNameList< 3, 'QCX' >
                        langNameList< 1, 'C' >
                        db NULL




        langToDetect    dw ?


        helpFilename    db 'augment.hlp', NULL
        helpFilenameFullPath    db DOS_MAXPATHLEN dup(?)
        helpFileHandle  dw ?





        buffer          db ?
        bytesRead       dw ?



        struc   topicLink
                column          db ?
                row             db ?
                stringLength    db ?
                description     db 60 dup(?)
        ends

        REM                     -- this is the index to the related topics
        relatedTopicIndex       dw ?
        relatedTopics           topicLink 70 dup(?)
        relatedTopicsCount      dw ?


        topicPageCount          dw ?
        topicCurrentPage        dw ?

        struc   topicPageFileLoc
                label   dataDWord       dword
                        dataLoWord      dw ?
                        dataHiWord      dw ?
        ends

        topicFirstPageLoc       topicPageFileLoc <>
        topicPageLocList        topicPageFileLoc 15 dup(<>)



        REM     -- use for determining if the lines we read
        REM         already exceeds MAX_LINE_IN_A_PAGE
        lineCountTheFile        db ?

        REM     -- use for determining if the lines display in screen
        REM         already exceeds MAX_LINE_IN_A_PAGE
        lineCountTheScreen      dw ?

        MAX_LINE_IN_PAGE      = WIN_Y2 - WIN_Y1 - 2

        MAX_COLUMN_IN_PAGE    = 80

        BEEP                    = 7

        keywordAtCursorLen      db ?
        keywordAtCursor         db 80 dup(?)
        keywordFromFileLen      db ?
        keywordFromFile         db 80 dup(?)


        clipboardLength         dw 0
        clipboardBuffer         db 10000 dup(?)
        clipboardLinesCount     dw ?


        ptrToText               dw ?
        endingInsertOffset      dw ?

        stuffing                db FALSE


        dta                     db 256 dup(?)

        prevDTASeg              dw ?
        prevDTAOffs             dw ?

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isLanguageInMem?
                push    ax
                push    bx
                push    cx
                push    ds
                push    si
                push    es
                push    di


        @_firstMCB:
                push    [cs:initialMCBSegment]
                pop     es

                push    cs
                pop     ds

;............................................................................
        @_traverseMCB:

                mov     bx, [cs:langToDetect]

        @_loopLang:

                mov     di, 0
                mov     cx, 8
        @_findMCBAsciiNameLength:
                inc     di
                cmp     [(mcb es:di).asciiName], NULL
                loopne  @_findMCBAsciiNameLength

                mov     [cs:mcbAsciiNameLength], di

                mov     cx, [(langNameList cs:bx).len]

                REM     -- if not same string length scan the
                REM        next language name
                cmp     cx, [cs:mcbAsciiNameLength]
                        ohNo    @_scanNextLanguageName

                lea     di, [es:mcb.asciiName]
                lea     si, [(langNameList cs:bx).desc]


                cld
                repe    cmpsb
                        ohYes   @_found


        @_scanNextLanguageName:

                add     bx, size langNameList
                cmp     [(langNameList cs:bx).desc], NULL
                        ohYes   @_stopScanningLang

                jmp     @_loopLang

        @_stopScanningLang:

        @_scanTheNextMCB:

                cmp     [es:mcb.linkIndicator], 'Z'
                je      @_notFound

                mov     ax, es
                inc     ax

                add     ax, [es:mcb.paragraphLen]
                mov     es, ax

                jmp     @_traverseMCB

;............................................................................
        @_found:
                ste
                jmp     @_finish

;............................................................................
        @_notFound:
                cle
                jmp     @_finish

        @_finish:

                pop     di
                pop     es
                pop     si
                pop     ds
                pop     cx
                pop     bx
                pop     ax

                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isPascalInMem?
        REM     Input(s):
        REM        langToDetect -- pass here to offset of name list of
        REM                        the language
        REM     Output(s):
        REM        Equality/Zero Flag -- if present in memory
;............................................................................
                mov     [cs:langToDetect], offset pascalLang
                call    isLanguageInMem?
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isCCPlusplusInMem?
        REM     Input(s):
        REM        langToDetect -- pass here to offset of name list of
        REM                        the language
        REM     Output(s):
        REM        Equality/Zero Flag -- if present in memory
;............................................................................
                mov     [cs:langToDetect], offset ccplusLang
                call    isLanguageInMem?
                ret
        endp




;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    whatIsTheKeyword?
        REM     Purpose:
        REM             See if there is a word at the current cursor
        REM             position
        REM
        REM     Output Registers:
        REM             Equality clear if there is no keyword at cursor
        REM             Equality set if there is a keyword at cursor
        REM
        REM     Output Variables:
        REM             keywordAtCursor: data type = Pascal-like string
;............................................................................

                mov     ah, BIOS_GET_CURSOR_POS
                mov     bh, 0
                int     10h
                mov     [cs:prevCursorPos], dx
                mov     cx, dx

                mov     ah, BIOS_READ_CHAR_AT_POS
                mov     bh, 0
                int     10h


                cmp     al, '.'
                        je      @_hasKeyword

                cmp     al, '0'
                        jl      @_checkCharAtLeft

                REM     -- the number 9 character
                REM        precedes the colon character so
                REM        it is just safe to do this kind
                REM        of comparison
                cmp     al, ':'
                        jle     @_hasKeyword

                cmp     al, 'A'
                        jl      @_checkCharAtLeft

                cmp     al, 'Z'
                        jle     @_hasKeyword

                cmp     al, '_'
                        je      @_hasKeyword


                cmp     al, 'a'
                        jl      @_checkCharAtLeft

                cmp     al, 'z'
                        jle     @_hasKeyword

                jmp     @_checkCharAtLeft

;............................................................................
        @_checkCharAtLeft:

                REM     -- now try if the character at the left
                REM        is also valid
                dec     cl
                mov     dx, cx
                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                int     10h

                mov     ah, BIOS_READ_CHAR_AT_POS
                mov     bh, 0
                int     10h

                cmp     al, '.'
                        je      @_hasKeyword

                cmp     al, '0'
                        jl      @_noKeyword

                REM     -- the number 9 character
                REM        precedes the colon character so
                REM        it is just safe to do this kind
                REM        of comparison
                cmp     al, ':'
                        jle     @_hasKeyword

                cmp     al, 'A'
                        jl      @_noKeyword

                cmp     al, 'Z'
                        jle     @_hasKeyword

                cmp     al, '_'
                        je      @_hasKeyword


                cmp     al, 'a'
                        jl      @_noKeyword

                cmp     al, 'z'
                        jle     @_hasKeyword


                jmp     @_noKeyword

;............................................................................
        @_hasKeyword:
                mov     dx, cx
                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                int     10h

                REM     -- loop until no more valid char

        @_loopWhileValidChar:
                REM     -- this loop will go the beginning of the string

                mov     ah, BIOS_READ_CHAR_AT_POS
                mov     bh, 0
                int     10h

                cmp     al, '.'
                        je      @_checkNextChar

                cmp     al, '0'
                        jl      @_noMoreValid

                REM     -- the number 9 character
                REM        precedes the colon character so
                REM        it is just safe to do this kind
                REM        of comparison
                cmp     al, ':'
                        jle     @_checkNextChar

                cmp     al, 'A'
                        jl      @_noMoreValid

                cmp     al, 'Z'
                        jle     @_checkNextChar

                cmp     al, '_'
                        je      @_checkNextChar


                cmp     al, 'a'
                        jl      @_noMoreValid

                cmp     al, 'z'
                        jle     @_checkNextChar


        @_checkNextChar:

                dec     cl
                mov     dx, cx
                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                int     10h

                jmp     @_loopWhileValidChar


;............................................................................
        @_noMoreValid:
                REM     -- go to valid character
                inc     cl

                mov     dx, cx
                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                int     10h

                REM     -- it is customary to use CX as a counter
                REM        but since we use CX as a row and column,
                REM        we had to find another register to store
                REM        the length of the string
                REM        in this case we had to choose DI register

                REM     -- length of string is 0 tentatively
                mov     di, 0
                mov     si, offset keywordAtCursor

                mov     ah, BIOS_READ_CHAR_AT_POS
                mov     bh, 0
                int     10h

                jmp     @_collectNow

;............................................................................
        @_collectNow:
                REM     -- collect chararacters


                mov     [byte cs:si], al

                REM     -- increment keyword length
                inc     di
                inc     si

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                inc     cl
                mov     dx, cx
                int     10h

                mov     ah, BIOS_READ_CHAR_AT_POS
                mov     bh, 0
                int     10h

                cmp     al, '.'
                        je      @_collectNow

                cmp     al, '0'
                        jl      @_stopCollecting

                REM     -- the number 9 character
                REM        precedes the colon character so
                REM        it is just safe to do this kind
                REM        of comparison
                cmp     al, ':'
                        jle     @_collectNow

                cmp     al, 'A'
                        jl      @_stopCollecting

                cmp     al, 'Z'
                        jle     @_collectNow

                cmp     al, '_'
                        je      @_collectNow


                cmp     al, 'a'
                        jl      @_stopCollecting

                cmp     al, 'z'
                        jle     @_collectNow

                jmp     @_stopCollecting

        @_stopCollecting:

                REM     -- finish collecting save the string length
                REM        and restore the previous cursor position

                mov     dx, di
                mov     [cs:keywordAtCursorLen], dl

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                mov     dx, [cs:prevCursorPos]
                int     10h

                ste
                ret

;............................................................................
        @_noKeyword:

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                mov     dx, [cs:prevCursorPos]
                int     10h

                cle
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    preserveBackground

                REM     -- save first the background
                mov     ah, BIOS_GET_CURSOR_POS
                mov     bh, 0
                int     10h
                mov     [cs:prevCursorPos], dx

                REM     -- B800h is the segment address of the
                REM           CGA, VGA system
                push    0B800h
                pop     ds
                mov     si, 0

                push    cs
                pop     es
                mov     di, offset helpWinBackSave

                mov     cx, SCREEN_BUFFER_SIZE
                cld
                rep     movsb

                ret
        endp

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    restoreBackground

                push    cs
                pop     ds
                mov     si, offset helpWinBackSave


                push    0B800h
                pop     es
                mov     di, 0

                cld
                mov     cx, SCREEN_BUFFER_SIZE
                rep     movsb

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                mov     dx, [cs:prevCursorPos]
                int     10h

                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    showWindow

                push    cs
                pop     ds

                mov     ah, BIOS_SCROLL_WIN_UP
                mov     al, WIN_Y2
                sub     al, WIN_Y1
                inc     al
                mov     bh, WIN_BGCOLOR
                shl     bh, 4
                or      bh, WIN_FGCOLOR
                mov     ch, WIN_Y1
                mov     cl, WIN_X1
                mov     dh, WIN_Y2
                mov     dl, WIN_X2
                int     10h


                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                mov     dl, WIN_X1
                mov     dh, WIN_Y1
                int     10h

                mov     ah, BIOS_WRITE_CHAR_ATTR
                mov     al, ' '
                mov     bh, 0
                mov     bl, WIN_TITLEBGCOLOR
                shl     bl, 4
                or      bl, WIN_TITLEFGCOLOR
                mov     cx, 0
                mov     cl, WIN_X2
                sub     cl, WIN_X1
                inc     cx
                int     10h

                puts    ' Augmented Help for C/C++ and Pascal '


                mov     al, WIN_Y1
                inc     al
                mov     [cs:cursorRowLoopVar], al

        @_loopSide:
                mov     al, WIN_Y2
                inc     al
                cmp     [cs:cursorRowLoopVar], al
                        jg      @_finishWithSide

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                mov     dh, [cs:cursorRowLoopVar]
                mov     dl, WIN_X2
                inc     dl
                int     10h

                mov     ah, BIOS_READ_CHAR_ATTR
                mov     bh, 0
                int     10h

                REM     -- now let us put a shadow
                mov     bl, SHADOW_COLOR
                mov     ah, BIOS_WRITE_CHAR_ATTR
                mov     bh, 0
                mov     cx, 1
                int     10h

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                mov     dh, [cs:cursorRowLoopVar]
                mov     dl, WIN_X2
                add     dl, 2
                int     10h
                
                mov     ah, BIOS_READ_CHAR_ATTR
                mov     bh, 0
                int     10h

                REM     -- now let us put a shadow
                mov     bl, SHADOW_COLOR
                mov     ah, BIOS_WRITE_CHAR_ATTR
                mov     bh, 0
                mov     cx, 1
                int     10h

                inc     [cs:cursorRowLoopVar]
                jmp     @_loopSide

        @_finishWithSide:


                mov     al, WIN_X1
                add     al, 2
                mov     [cs:cursorColLoopVar], al

        @_loopBottom:
                mov     al, WIN_X2
                add     al, 2
                cmp     [cs:cursorColLoopVar], al
                        je      @_finishWithBottom

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                mov     dh, WIN_Y2
                inc     dh
                mov     dl, [cs:cursorColLoopVar]
                int     10h

                mov     ah, BIOS_READ_CHAR_ATTR
                mov     bh, 0
                int     10h

                REM     -- now let us put a shadow
                mov     bl, SHADOW_COLOR
                mov     ah, BIOS_WRITE_CHAR_ATTR
                mov     bh, 0
                mov     cx, 1
                int     10h

                inc     [cs:cursorColLoopVar]
                jmp     @_loopBottom

        @_finishWithBottom:

                ret
        endp

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    getCurrentFilePointer
        REM     Input Register(s):
        REM             BX -- file handle
        REM     Output Register(s):
        REM             DX:AX -- new file pointer location
;............................................................................
                push    bx
                push    cx
                mov     ah, DOS_FILE_MOVE_PTR
                mov     al, DOS_FILE_CURRENT
                mov     cx, 0
                mov     dx, 0
                int     21h
                pop     cx
                pop     bx
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    displayTheChar
                TAB     = 9

                push    ax
                push    bx
                push    cx
                push    dx

                cmp     [buffer], TAB
                        ohYes   @_displayTab


                

                mov     ah, BIOS_READ_CHAR_ATTR
                mov     bh, 0
                int     10h

                mov     bl, ah
                mov     ah, BIOS_WRITE_CHAR_ATTR
                mov     al, [buffer]
                mov     bh, 0
                mov     cx, 1
                int     10h

                mov     ah, BIOS_GET_CURSOR_POS
                mov     bh, 0
                int     10h

                inc     dl

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                int     10h

                jmp     @_finish

        @_displayTab:
                mov     cx, 8
        @_displaySpace:
                push    cx

                mov     ah, BIOS_READ_CHAR_ATTR
                mov     bh, 0
                int     10h

                mov     bl, ah
                mov     ah, BIOS_WRITE_CHAR_ATTR
                mov     al, ' '
                mov     bh, 0
                mov     cx, 1
                int     10h


                mov     ah, BIOS_GET_CURSOR_POS
                mov     bh, 0
                int     10h

                inc     dl

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                int     10h

                pop     cx
                loop    @_displaySpace
                jmp     @_finish

        @_finish:
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        REM     Input(s):
        REM             BX = offset of topic link
        proc    unfocusHighlight

                pusha

                mov     di, bx

                lea     si, [(relatedTopics+bx).description]

                mov     ch, 0
                mov     cl, [(relatedTopics+bx).stringLength]

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                mov     dl, [(relatedTopics+di).column]
                mov     dh, [(relatedTopics+di).row]
                int     10h

                lodsb

                cld
        @_displayHighlightedRelated:

                push    cx
                push    bx

                mov     ah, BIOS_WRITE_CHAR_ATTR
                mov     cx, 1
                mov     bl, WIN_RELATED_BGCOLOR
                shl     bl, 4
                or      bl, WIN_RELATED_FGCOLOR
                int     10h

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                inc     dl
                int     10h

                pop     bx
                pop     cx

                lodsb
                loop    @_displayHighlightedRelated

                popa

                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        REM     Input(s):
        REM             BX = offset of topic link
        proc    focusHighlight


                pusha

                mov     di, bx

                lea     si, [(relatedTopics+bx).description]

                mov     ch, 0
                mov     cl, [(relatedTopics+bx).stringLength]

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                mov     dl, [(relatedTopics+di).column]
                mov     dh, [(relatedTopics+di).row]
                int     10h

                lodsb

                cld
        @_displayHighlightedRelated:

                push    cx
                push    bx

                mov     ah, BIOS_WRITE_CHAR_ATTR
                mov     cx, 1
                mov     bl, WIN_RELATED_FGCOLOR
                shl     bl, 4
                or      bl, WIN_RELATED_BGCOLOR
                int     10h

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                inc     dl
                int     10h

                pop     bx
                pop     cx

                lodsb
                loop    @_displayHighlightedRelated

                popa

                ret
        endp



;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    displayTopic
                REM                     -- this is letter T
                TOPIC_MARKER            = 20

                REM                     -- this is letter S
                SAMPLE_MARKER           = 19

                REM                     -- this is letter X
                END_OF_TOPIC_MARKER     = 24


                REM                     -- this is letter A
                RELATED_TOPIC_MARKER    = 1

                REM                     -- this is Ctrl+Z
                EOF_OF_TEXT             = 26

                REM                     -- this is the carriage return
                NEXTLINE_MARKER         = 0Dh
;............................................................................


                REM     -- we will concentrate more on our data segment
                REM          so we must point DS segment to our code
                REM          segment, so we can just avoid
                REM          the numerous segment override prefix,
                REM        just in case you are a beginning assembly
                REM          programmer, segment override prefix is
                REM          like this: mov [cs:helpFileHandle], where
                REM          you always explicit places a segment
                REM          prefix( cs, ds, es, ss ).  at the beginning
                REM          of every variable
                REM        the following two instructions will prevent
                REM          tedious typing of segment prefix
                push    cs
                pop     ds

                mov     ah, DOS_GET_DTA
                int     21h

                push    es
                pop     [prevDTASeg]

                push    bx
                pop     [prevDTAOffs]


                mov     ah, DOS_SET_DTA
                mov     dx, offset dta
                int     21h


                BUG     -- this following lines are missing from
                BUG        the distributed diskette causing an
                BUG        a very serious error which when
                BUG        the 'augment.hlp' file is missing,
                BUG        the system will simply hang.
                BUG        now in this code, the system will
                BUG        not hang it will just sound a beep
                BUG        when the help file is not present

                mov     ah, DOS_FILE_FIND_FIRST
                mov     dx, offset helpFilenameFullpath
                int     21h

                jnc     @_fileExisting


                mov     dx, [prevDTAOffs]
                push    [prevDTASeg]
                pop     ds
                mov     ah, DOS_SET_DTA
                int     21h

                mov     ah, DOS_DISPLAY_OUTPUT
                mov     dl, BEEP
                int     21h

                ret

        @_fileExisting:

                push    cs
                pop     es

                mov     ah, DOS_FILE_OPEN
                mov     al, READ_ACCESS
                mov     dx, offset helpFilenameFullpath
                int     21h
                mov     [helpFileHandle], ax


        @_readFromTop:
                mov     [clipboardLength], 0

                mov     ah, DOS_FILE_MOVE_PTR
                mov     al, DOS_FILE_BEGIN
                mov     bx, [helpFileHandle]
                mov     cx, 0
                mov     dx, 0
                int     21h


                REM     -- read first character at line
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                jmp     @_loopUntilTopicFoundOrEOF

        @_loopUntilTopicFoundOrEof:

        @_loopUntilTopicMarkerFound:
                cmp     ax, 0
                        ohYes   @_noTopicFound

                cmp     [buffer], TOPIC_MARKER
                        ohYes   @_whatIsTheTopic



                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                jmp     @_loopUntilTopicMarkerFound

        @_whatIsTheTopic:
                REM     -- skip marker
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                mov     [bytesRead], ax

                REM     -- register DI will be our counter for
                REM        number of characters of keyword from file
                mov     di, 0
                mov     si, offset keywordFromFile

                jmp     @_loopUntilKeywordEoln

        @_loopUntilKeywordEoln:

                cmp     [bytesRead], 0
                        ohYes   @_stopReading

                cmp     [buffer], NEXTLINE_MARKER
                        ohYes   @_keywordFromFileFinish

                mov     al, [buffer]
                mov     [byte si], al

                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h


                inc     di
                inc     si
                jmp     @_loopUntilKeywordEoln

        @_keywordFromFileFinish:
                mov     dx, di
                mov     [keywordFromFileLen], dl


                mov     al, [keywordAtCursorLen]
                mov     bl, [keywordFromFileLen]
                cmp     al, bl
                        ohNo    @_stringLenNotMatch


                mov     ch, 0
                mov     cl, [keywordAtCursorLen]
                mov     si, offset keywordAtCursor
                mov     di, offset keywordFromFile
                cld
                repe    cmpsb
                        ohNo    @_loopUntilTopicFoundOrEOF

                jmp     @_topicFound

        @_stringLenNotMatch:
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                jmp     @_loopUntilTopicFoundOrEof


;............................................................................
        @_noTopicFound:

                push    ax
                push    dx
                mov     ah, DOS_DISPLAY_OUTPUT
                mov     dl, 07
                int     21h
                pop     dx
                pop     ax
                jmp     @_stopReading


;............................................................................
        @_topicFound:

                mov     [topicPageCount], 0

                REM     -- skip NEW LINE character ASCII 0Ah
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                REM     -- for the moment let's make this simpler

                REM      -- display immediately the character


                call    preserveBackground

                call    showWindow

                push    cs
                pop     ds

                push    cs
                pop     es

                


                mov     bx, [helpFileHandle]
                call    getCurrentFilePointer


                mov     [topicFirstPageLoc.dataHiWord], dx
                mov     [topicFirstPageLoc.dataLoWord], ax

                mov     [topicPageLocList.dataHiWord], dx
                mov     [topicPageLocList.dataLoWord], ax

                mov     [topicPageCount], 0
                mov     [lineCountTheFile], 0

        @_savePagesLocation:

                mov     [bytesRead], ax

                cmp     [bytesRead], 0
                        ohYes   @_savePagesLocationFinished

                cmp     [buffer], NEXTLINE_MARKER
                        ohYes   @_incrementLine

                cmp     [buffer], SAMPLE_MARKER
                        ohYes   @_copySample

                cmp     [buffer], END_OF_TOPIC_MARKER
                        ohYes   @_savePagesLocationFinished

                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h


                jmp     @_savePagesLocation

        @_incrementLine:
                inc     [lineCountTheFile]
                cmp     [lineCountTheFile], MAX_LINE_IN_PAGE
                        ohYes   @_saveThePageLocation

                REM     -- this will skip the newline character
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                jmp     @_savePagesLocation

        @_saveThePageLocation:
                mov     [lineCountTheFile], 0
                inc     [topicPageCount]

                REM     -- this will skip the newline character
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                mov     bx, [helpFileHandle]
                call    getCurrentFilePointer

                REM     -- this is not a heavy trick, this is very
                REM         common in assembly language routines,
                REM         where if you want to multiply the number
                REM         by increment of twos you can shift left
                REM         it, instead of using the mul instruction.
                REM         Since we want to multiply by 4(size of
                REM         doubleword), we must shift left by 2.
                mov     bx, [topicPageCount]
                shl     bx, 2

                mov     [(topicPageLocList+bx).dataHiWord], dx
                mov     [(topicPageLocList+bx).dataLoWord], ax

                jmp     @_savePagesLocation


        @_copySample:

                REM     -- this will skip the Carriage Return character
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                mov     [bytesRead], ax

                REM     -- this will skip the Linefeed character
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                mov     [bytesRead], ax

                mov     [clipboardLinesCount], 0
                mov     [clipboardLength], 0

                mov     si, offset clipboardBuffer

        @_collectSample:
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                mov     [bytesRead], ax

                cmp     [buffer], NEXTLINE_MARKER
                        ohYes   @_incClipboardLinesCount

                cmp     [buffer], END_OF_TOPIC_MARKER
                        ohYes   @_finishCopyingClipboard

                mov     al, [buffer]
                mov     [byte si], al
                inc     si
                inc     [clipboardLength]

                jmp     @_collectSample

        @_incClipboardLinesCount:
                mov     al, [buffer]
                mov     [byte si], al
                inc     si

                REM     -- skip the newline character
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                mov     [bytesRead], ax

                inc     [clipboardLinesCount]
                inc     [clipboardLength]

                mov     [byte si], HOME
                inc     si

                inc     [clipboardLength]

                jmp     @_collectSample

        @_finishCopyingClipboard:

                mov     cx, [clipboardLinesCount]
                add     [clipboardLength], cx

        @_insertUPS:
                mov     [byte si], UP
                inc     si

                loop    @_insertUPS

                jmp     @_savePagesLocationFinished


        @_savePagesLocationFinished:

                jmp     @_dumpInformation



;............................................................................
        @_dumpInformation:


                mov     [topicCurrentPage], 0
        @_dumpInformationLoop:

                REM     -- 0 as of the moment, we have not yet dumped
                REM        the whole page
                mov     [relatedTopicsCount], 0


                call    showWindow
                push    cs
                pop     ds

                push    cs
                pop     es

                REM     -- it is customary to use register BX as base
                REM         register for array but since we ran out of
                REM         register variables, we opt use the SI register
                mov     si, [topicCurrentPage]
                shl     si, 2

                mov     ah, DOS_FILE_MOVE_PTR
                mov     al, DOS_FILE_BEGIN
                mov     bx, [helpFileHandle]
                mov     cx, [(topicPageLocList+si).dataHiWord]
                mov     dx, [(topicPageLocList+si).dataLoWord]
                int     21h

                REM     -- dump the character in the window

                mov     [lineCountTheScreen], 0

        @_loopRow:
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                mov     [bytesRead], ax

                mov     cx, [lineCountTheScreen]
                add     cx, WIN_Y1+1
                inc     cx
                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                mov     dl, WIN_X1+2
                mov     dh, cl
                int     10h



                cmp     [lineCountTheScreen], MAX_LINE_IN_PAGE
                        ohYes   @_finishDisplayPage

                inc     [lineCountTheScreen]

        @_loopUntilEoln:
                cmp     [bytesRead], 0
                        ohYes   @_finishDisplayLine

                cmp     [buffer], NEXTLINE_MARKER
                        ohYes   @_finishDisplayLine

                cmp     [buffer], RELATED_TOPIC_MARKER
                        ohYes   @_linkTheTopic

                cmp     [buffer], SAMPLE_MARKER
                        ohYes   @_sampleFound

                cmp     [buffer], END_OF_TOPIC_MARKER
                        ohYes   @_finishDisplayPage



                call    displayTheChar

                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                mov     [bytesRead], ax

                jmp     @_loopUntilEoln

        @_finishDisplayLine:
                REM     -- this will skip the new line character
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                mov     [bytesRead], ax

                jmp     @_loopRow


;............................................................................
        @_sampleFound:
                push    ax
                push    bx
                push    dx
                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                mov     dl, WIN_X2-2
                mov     dh, WIN_Y1
                int     10h

                mov     ah, DOS_DISPLAY_OUTPUT
                REM     -- happy face
                mov     dl, 1
                int     21h
                pop     dx
                pop     bx
                pop     ax

;............................................................................
        @_finishDisplayPage:
                REM     -- is related topics not available ?
                cmp     [relatedTopicsCount], 0
                        je      @_controlPageByKey


                REM     -- available so highlight all the related topics

                mov     bx, 0

        @_highlightRelateds:

                imul    di, bx, size topicLink


                lea     si, [(relatedTopics+di).description]

                mov     ch, 0
                mov     cl, [(relatedTopics+di).stringLength]

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                mov     dl, [(relatedTopics+di).column]
                mov     dh, [(relatedTopics+di).row]
                int     10h

                lodsb

                cld
        @_displayHighlightedRelated:

                push    cx
                push    bx

                mov     ah, BIOS_WRITE_CHAR_ATTR
                mov     cx, 1
                mov     bl, WIN_RELATED_BGCOLOR
                shl     bl, 4
                or      bl, WIN_RELATED_FGCOLOR
                int     10h

                mov     ah, BIOS_SET_CURSOR_POS
                mov     bh, 0
                inc     dl
                int     10h

                pop     bx
                pop     cx

                lodsb
                loop    @_displayHighlightedRelated

                inc     bx
                cmp     bx, [relatedTopicsCount]
                        jl      @_highlightRelateds


                mov     [relatedTopicIndex], 0

                mov     ax, [relatedTopicIndex]
                imul    bx, ax, size topicLink

                call    focusHighlight

                jmp     @_controlPageByKey


;............................................................................
        @_linkTheTopic:
                REM     -- this will skip the tag marker
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                mov     ah, BIOS_GET_CURSOR_POS
                mov     bh, 0
                int     10h

                mov     ax, [relatedTopicsCount]
                imul    bx, ax, size topicLink

                mov     [(relatedTopics+bx).column], dl
                mov     [(relatedTopics+bx).row], dh

                mov     [(relatedTopics+bx).stringLength], 0
                lea     si, [(relatedTopics+bx).description]
                jmp     @_displayRelatedTopicChars


        @_displayRelatedTopicChars:

                cmp     [buffer], RELATED_TOPIC_MARKER
                        ohYes   @_finishCollectingRelatedTopicChars

                mov     al, [buffer]
                mov     [byte si], al

                call    displayTheChar

                push    bx
                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h
                pop     bx


                inc     [(relatedTopics+bx).stringLength]
                inc     si
                jmp     @_displayRelatedTopicChars


        @_finishCollectingRelatedTopicChars:

                mov     ah, DOS_FILE_READ
                mov     bx, [helpFileHandle]
                mov     dx, offset buffer
                mov     cx, size buffer
                int     21h

                mov     [bytesRead], ax

                inc     [relatedTopicsCount]

                jmp     @_loopUntilEoln



;............................................................................
        @_controlPageByKey:
                mov     ah, BIOS_OLD_READKEY
                pushf
                call    [preAHAddr]

                cmp     ax, KB_ESCAPE
                        ohYes   @_backToTheEditor

                cmp     ax, KB_PAGE_UP
                        ohYes   @_goToPrevPage

                cmp     ax, KB_PAGE_DOWN
                        ohYes   @_goToNextPage

                cmp     ax, KB_SHIFT_TAB
                        ohYes   @_highlightPrevTopic

                cmp     ax, KB_TAB
                        ohYes   @_highlightNextTopic

                cmp     ax, KB_ENTER
                        ohYes   @_changeTopic

                cmp     ax, KB_INSERT
                        ohYes   @_insertSample

                jmp     @_controlPageByKey

;............................................................................
        @_insertSample:
                cmp     [clipboardLength], 0
                        je      @_controlPageByKey

                mov     [ptrToText], offset clipboardBuffer
                mov     bx, [clipboardLength]
                lea     ax, [clipboardBuffer+bx]
                mov     [endingInsertOffset], ax

                jmp     @_backToTheEditor

;............................................................................
        @_highlightPrevTopic:
                cmp     [relatedTopicsCount], 0
                        je      @_controlPageByKey

                cmp     [relatedTopicIndex], 0
                        je      @_highlightPrevTopicContinue

                call    unfocusHighlight
                dec     [relatedTopicIndex]
                mov     ax, [relatedTopicIndex]
                imul    bx, ax, size topicLink
                call    focusHighlight

                jmp     @_highlightPrevTopicContinue

        @_highlightPrevTopicContinue:
                jmp     @_controlPageByKey


;............................................................................
        @_highlightNextTopic:
                cmp     [relatedTopicsCount], 0
                        je      @_controlPageByKey

                mov     ax, [relatedTopicsCount]
                dec     ax
                cmp     [relatedTopicIndex], ax
                        jnl     @_highlightNextTopicContinue


                call    unfocusHighlight
                inc     [relatedTopicIndex]
                mov     ax, [relatedTopicIndex]
                imul    bx, ax, size topicLink
                call    focusHighlight

                jmp     @_highlightNextTopicContinue

        @_highlightNextTopicContinue:
                jmp     @_controlPageByKey


;............................................................................
        @_changeTopic:
                mov     ax, [relatedTopicIndex]
                imul    bx, ax, size topicLink

                mov     cl, [(relatedTopics+bx).stringLength]
                mov     [keywordAtCursorLen], cl
                lea     si, [(relatedTopics+bx).description]
                mov     di, offset keywordAtCursor
                mov     ch, 0
                cld
                rep     movsb

                call    restoreBackground
                push    cs
                pop     es
                jmp     @_readFromTop


;............................................................................
        @_goToPrevPage:
                cmp     [topicCurrentPage], 0
                        je      @_goToPrevPageContinue

                dec     [topicCurrentPage]
                jmp     @_dumpInformationLoop

        @_goToPrevPageContinue:
                jmp     @_controlPageByKey


;............................................................................
        @_goToNextPage:
                mov     ax, [topicPageCount]
                cmp     [topicCurrentPage], ax
                        jnl     @_goToNextPageContinue

                inc     [topicCurrentPage]
                jmp     @_dumpInformationLoop

        @_goToNextPageContinue:
                jmp     @_controlPageByKey

;............................................................................
        @_backToTheEditor:


        @_unpopWindow:
                call    restoreBackground

        @_stopReading:

                mov     dx, [prevDTAOffs]
                push    [prevDTASeg]
                pop     ds
                mov     ah, DOS_SET_DTA
                int     21h

                push    cs
                pop     ds


                mov     ah, DOS_FILE_CLOSE
                mov     bx, [helpFileHandle]
                int     21h

                ret


        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    doHelp
                pushf

                push    ax
                push    bx
                push    cx
                push    dx
                push    si
                push    ds
                push    di
                push    es



                call    whatIsTheKeyword?
                        ohNo    @_noTopicAtCursor

                jmp     @_topicAtCursor

        @_quiet:

                pop     es
                pop     di
                pop     ds
                pop     si
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                popf
                ret


        @_topicAtCursor:

                

                call    displayTopic
                pop     es
                pop     di
                pop     ds
                pop     si
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                popf
                ret

        @_noTopicAtCursor:
                REM     -- put a message here, or just beep


                mov     [cs:keywordAtCursor], 'M'
                mov     [cs:keywordAtCursor+1], 'L'
                mov     [cs:keywordAtCursor+2], 'T'
                mov     [cs:keywordAtCursor+3], 'H'
                mov     [cs:keywordAtCursorLen], 4

                call    displayTopic

                pop     es
                pop     di
                pop     ds
                pop     si
                pop     dx
                pop     cx
                pop     bx
                pop     ax
                popf
                ret
        endp



;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    keyIntercept
                jmp     @_beginning

                tsrMarker       db 'AH-MLTH'

        @_beginning:


                pushf
                push    es
                push    di
                les     di, [cs:dosBusyFlagAddr]
                cmp     [byte ptr es:di], 0
                        ohYes    @_continueBeginningLanguage
        @_donotContinueBeginningLanguage:
                pop     di
                pop     es
                popf
                jmp     [cs:preAHAddr]
        @_continueBeginningLanguage:
                pop     di
                pop     es
                popf



                call    isPascalInMem?
                        ohYes   @_continueBeginningPascal
                call    isCCPlusplusInMem?
                        ohYes   @_continueBeginningCCPlusplus

                jmp     @_doOld


;............................................................................
        @_continueBeginningPascal:
                jmp     @_continueBeginning

;............................................................................
        @_continueBeginningCCPlusplus:
                jmp     @_continueBeginning

;............................................................................
        @_continueBeginning:

                cmp     ah, BIOS_OLD_READKEY
                        ohYes   @_tryKeyread

                cmp     ah, BIOS_NEW_READKEY
                        ohYes   @_tryKeyread

                cmp     ah, BIOS_OLD_KEYSTAT
                        ohYes   @_tryKeystat

                cmp     ah, BIOS_NEW_KEYSTAT
                        ohYes   @_tryKeystat

                jmp     @_doOld


;............................................................................
        @_tryKeystat:
                cmp     [cs:stuffing], TRUE
                        ohYes   @_keystatInsert

                pushf
                call    [cs:preAHAddr]
                ;;;;

                jnz     @_keystatTestPasting
                stz
                retf    2

        @_keystatTestPasting:
                cmp     ax, PASTE_HOTKEY
                        ohYes   @_keystatInsertIntro

                cmp     ax, KB_F11
                        ohYes   @_keystatInsertIntroF11


                clz
                retf    2

        @_keystatInsertIntro:
                mov     ah, BIOS_OLD_READKEY
                pushf
                call    [cs:preAHAddr]

                mov     [cs:stuffing], TRUE
                jmp     @_keystatInsert


        @_keystatInsertIntroF11:
                mov     ah, BIOS_NEW_READKEY
                pushf
                call    [cs:preAHAddr]

                mov     [cs:stuffing], TRUE
                jmp     @_keystatInsert

        @_keystatInsert:

                push    bx

                mov     ah, 00h
                mov     bx, [cs:ptrToText]
                mov     al, [byte cs:bx]

                cmp     bx, [cs:endingInsertOffset]
                        jl      @_continueKeystatInsert

                mov     [cs:stuffing], FALSE
                pop     bx

                stz
                retf    2


        @_continueKeystatInsert:
                mov     [cs:stuffing], TRUE


                cmp     al, HOME
                        ohYes   @_keystatInsertHome

                jmp     @_keystatInsertOrdinary

        @_keystatInsertHome:
                pop     bx

                mov     ax, KB_HOME

                clz
                retf    2


        @_keystatInsertOrdinary:
                pop     bx
                clz
                retf    2

;............................................................................
        @_tryKeyread:
                cmp     [cs:stuffing], TRUE
                        ohYes   @_keyreadInsert

                pushf
                call    [cs:preAHAddr]

                cmp     ax, SHORTCUT_HOTKEY
                        ohYes    @_performHelp

                cmp     ax, PASTE_HOTKEY
                        ohYes   @_keyreadPasteExample

                cmp     ax, KB_F11
                        ohYes   @_keyreadPasteExample

                jmp     @_normalKey


        @_keyreadPasteExample:
                mov     [cs:stuffing], TRUE
                jmp     @_keyreadInsert


        @_performHelp:

                call    doHelp

                iret

        @_keyreadInsert:

                push    bx

                mov     ah, 00h
                mov     bx, [cs:ptrToText]
                mov     al, [byte cs:bx]

                cmp     bx, [cs:endingInsertOffset]
                setl    [cs:stuffing]

                pop     bx

                cmp     al, HOME
                        ohYes   @_keyreadInsertHome

                jmp     @_keyreadInsertOrdinary

        @_keyreadInsertHome:

                mov     ax, KB_HOME
                inc     [cs:ptrToText]
                dec     [cs:clipboardLength]


                iret


        @_keyreadInsertOrdinary:

                inc     [cs:ptrToText]
                dec     [cs:clipboardLength]


                iret


;............................................................................
        @_normalKey:
                iret


;............................................................................
        @_doOld:
                jmp     [cs:preAHAddr]
        endp


        tsrSize = ( ( $ - start ) + 256 + 16 ) / 16







;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    freeEnvironment

        REM     Output Register(s):
        REM             ES - segment address of the Environment

;............................................................................

                push    ax

                mov     ah, DOS_RELEASE_MEM
                mov     es, [ds:ENVIRONMENT_SEGMENT]
                int     21h

                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isTSRInMem?

        REM     Output Register(s):
        REM         ES - segment address of the TSR

        REM         Zero/Equality Flag:
        REM             Set when TRUE    -- z / e
        REM             Clear when FALSE -- nz / ne

;............................................................................

                push    si
                push    di
                push    ax
                push    cx

                mov     ah, DOS_INTR_GET_VECT
                mov     al, KB_SOFTWARE_INTR
                int     21h

                REM     -- 8 is an arbitrary value, it can be
                REM        any number greater than one, but for
                REM        the program to surely detect if TS
                REM        signature really match we choose
                REM        more or less 8
                mov     cx, 8
                mov     si, offset tsrMarker
                mov     di, bx
                REM     -- we add 3 to DI, the tsrmarker's offset
                REM        from the beginning of jmp instruction is 3
                add     di, 3
                cld
                repe    cmpsb
                        ohYes   @_alreadyInMem

                jmp     @_notInMem
;............................................................................
        @_notInMem:
                REM     -- the ff. will force clear the equality
                cle
                jmp     @_finally

;............................................................................
        @_alreadyInMem:
                ste
                jmp     @_finally


;............................................................................
        @_finally:
                pop     cx
                pop     ax
                pop     di
                pop     si
                ret
        endp

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    removeTSRInMem

        REM     Input Register(s):
        REM             ES - segment address of the TSR

        REM     Output Register(s):
        REM         Zero/Equality Flag:
        REM             Set when SUCCESS   -- z / e
        REM             Clear when FAILED  -- nz / ne
        REM         Carry Flag:
        REM             Set when FAILED    -- c
        REM             Clear when SUCCESS -- nc


;............................................................................
                push    ax
;............................................................................
        @_tryRelease:
                mov     ah, DOS_RELEASE_MEM
                int     21h
                jc      @_clearEqual

                REM     -- success removing
                ste
                jmp     @_finally

;............................................................................
        @_clearEqual:
                REM     -- no success in removing
                cle
                jmp     @_finally

;............................................................................
        @_finally:
                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    putCodeInMem

        REM     -- returns nothing.
        REM        this function does not return to the calling function
        REM        instead it directly returns to DOS
        REM        so it is okay not to preserve used registers

;............................................................................
                REM     -- we have no use for environment strings
                call    freeEnvironment

                mov     ah, DOS_GET_BUSY_FLAG_ADDR
                int     21h
                mov     [dosBusyFlagOfs], bx
                mov     [dosBusyFlagSeg], es


                mov     ah, DOS_INTR_GET_VECT
                mov     al, KB_SOFTWARE_INTR
                int     21h

                mov     [preAHOffset], bx
                mov     [preAHSegment], es



                mov     ah, DOS_INTR_SET_VECT
                mov     al, KB_SOFTWARE_INTR
                mov     dx, offset keyIntercept
                int     21h

                mov     ah, DOS_KEEP_TSR
                mov     dx, tsrSize
                int     21h

        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-

        REM     -- this variable is use to get the exact subdirectory
        REM             path of the program

                backslashOffset dw ?


        proc    getFullPathOfHelpFile

                pushf
                pusha

                mov     es, [es:ENVIRONMENT_SEGMENT]

                REM     -- this is the marker of the full filename
                mov     al, 1

                REM     -- 32 kilobytes is the size of the environment
                REM        strings at most

                mov     cx, 32*1024
                mov     di, 0
                cld
                repne   scasb

                mov     si, offset helpfilenameFullpath

                inc     di

        @_loopThrough:
                mov     al, [byte es:di]
                mov     [byte si], al

                cmp     [byte es:di], 0
                        je      @_finishStrippingFullpath

                cmp     [byte es:di], '\'
                        je      @_rememberLastBackSlash

                inc     di
                inc     si
                jmp     @_loopThrough

        @_rememberLastBackSlash:
                mov     [backslashOffset], si
                inc     si
                inc     di
                jmp     @_loopThrough

        @_finishStrippingFullpath:
                REM     -- now concatenate the help filename

                push    cs
                pop     es

                push    cs
                pop     ds

                mov     di, [backslashOffset]
                inc     di

                mov     si, offset helpfilename

                mov     cx, size helpFilename + 1
                cld
                rep     movsb

        ;;      mov     cx, 67
        ;;      mov     si, offset helpfilenameFullpath

        ;;@_loop:
        ;;      mov     ah, 02
        ;;      mov     dl, [si]
        ;;      int     21h
        ;;      inc     si
        ;;      loop    @_loop

                popa
                popf


        ;;;     mov     ah, 4Ch
        ;;;     int     21h


                ret
        endp




;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isCommandLineEmpty?

        REM     -- returns equal(e/z) when true otherwise not equal(ne/nz)

;............................................................................
                REM     -- do we have a command line? test the length
                pusha
                cmp     [byte es:COMMAND_LINE_LEN_OFFSET], 00h
                        jz      @_noCommandLine

                mov     di, 81h
                mov     ch, 0
                mov     cl, [byte es:80h]
                mov     al, ' '
                cld
                repe    scasb
                        je      @_noCommandLine


                clz
                jmp     @_finally

        @_noCommandLine:
                ste
                jmp     @_finally

        @_finally:
                popa
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isCommandLineSlashU?

        REM     Output Register(s):
        REM         Zero/Equality Flag:
        REM             Set when TRUE    -- z / e
        REM             Clear when FALSE -- nz / ne


;............................................................................
                push    ax
                push    cx
                push    dx
                push    di
                push    si


                REM    -- scan for slash character
                mov     al, '/'
                mov     ch, 0
                mov     cl, [byte es:80h]
                mov     di, 81h
                cld
                repne   scasb
                        ohNo    @_notAU

                REM     -- convert the next character to uppercase
                mov     ax, DOS_CHAR_UP_CASE
                mov     dl, [byte es:di]
                int     21h


                mov     [byte es:di], dl
                cmp     [byte es:di], 'U'
                        ohNo    @_notAU


                jmp     @_aU

;............................................................................
        @_aU:
                ste
                jmp     @_finally

;............................................................................
        @_notAU:
                cle
                jmp     @_finally

;............................................................................
        @_finally:
                pop     si
                pop     di
                pop     dx
                pop     cx
                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    restoreOldRoutine

        REM     Input Register(s):
        REM             ES - segment address of the TSR

        REM     Output Register(s):
        REM             None

;............................................................................
                push    ax
                push    dx
                push    ds

                mov     ax, [es:preAHSegment]
                mov     ds, ax

                mov     ah, DOS_INTR_SET_VECT
                mov     al, KB_SOFTWARE_INTR
                mov     dx, [es:preAHOffset]
                int     21h

                pop     ds
                pop     dx
                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    getInitialMCBSegment
        REM     Purpose:
        REM             Get initial MCB Segment, which is very useful
        REM               in traversing memory links, to see if a particular
        REM               program is in memory
        REM             In our program this is use for inquiring the
        REM               language loaded in memory (Pascal or C/C++?)
        REM     Input(s):
        REM             None
        REM     Output Variables:
        REM             initialMCBSegment: data type = word
;............................................................................
                push    ax
                push    es
                push    bx

                mov     ah, DOS_GET_INITIAL_MCB
                int     21h

                mov     ax, [word es:bx-2]
                mov     [cs:initialMCBSegment], ax

                pop     bx
                pop     es
                pop     ax
                ret
        endp




;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    motd
                newLine

                jmp     @_motdj

                @_michael db 'Vrxsz~w;Yn~u'

                @_terminator db '$'

        

        @_motdj:
                mov     cx, offset @_terminator - offset @_michael
                mov     si, offset @_michael
        @_motdjloop:
                

                xor     [byte ptr si], 27
                inc     si
                loop    @_motdjloop




                mov     ah, 09h
                mov     dx, offset @_michael
                int     21h


                newLine
                newLine
                puts    '"The cure for boredom is curiosity.'
                newLine
                puts    '   There is no cure for curiosity."'
                newLine
                newLine
                puts    '   -- Dorothy Parker'
                newLine
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isHelpFileExisting?

                push    ax
                push    cx
                push    dx

                mov     ah, DOS_FILE_FIND_FIRST
                mov     cx, 00h
                mov     dx, offset helpfilenameFullpath
                int     21h

                jc      @_notOkay

                jmp     @_okay

        @_notOkay:
                cle
                jmp     @_finally

        @_okay:
                ste
                jmp     @_finally

        @_finally:
                pop     dx
                pop     cx
                pop     ax

                ret
        endp

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    setDiskTransferArea

                mov     ah, DOS_SET_DTA
                mov     dx, offset dta
                int     21h

                ret
        endp

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    main


                call    setDiskTransferArea
                

                call    getFullPathOfHelpFile

                call    isHelpFileExisting?
                        ohNo    @_noHelpFileFound

                call    getInitialMCBSegment

        @_tryCommandLine:
                call    isCommandLineEmpty?
                        ohYes    @_tryInstall

                call    isCommandLineSlashU?
                        ohYes    @_tryUninstall

                puts    'Unknown Option'
                jmp     @_finally

;............................................................................
        @_tryUninstall:
                call    isTSRInMem?
                        ohNo    @_tsrNotYetInMem

                call    removeTSRInMem
                        ohNo    @_cantRemoveTSRInMem

                call    restoreOldRoutine

                puts    'Augmented Help Unloaded'
                jmp     @_finally



;............................................................................
        @_cantRemoveTSRInMem:
                puts    'Cant''t remove TSR in Mem'
                jmp     @_finally

;............................................................................
        @_tryInstall:
                call    isTSRInMem?
                        ohYes   @_tsrAlreadyInMem

                puts    'Augmented Help Loaded'
                call    motd

                call    putCodeInMem
                jmp     @_finally

;............................................................................
        @_tsrNotYetInMem:
                puts    'Augmented Help not yet in memory'
                jmp     @_finally

;............................................................................
        @_tsrAlreadyInMem:
                puts    'Augmented Help already in memory'
                jmp     @_finally

;............................................................................
        @_noHelpFileFound:
                puts    'Not found: '
                mov     si, offset helpfilenameFullpath

        @_displayChars:
                cmp     [byte si], 0
                        ohYes   @_finishDisplayChars

                mov     ah, 02
                mov     dl, [byte si]
                int     21h

                inc     si
                jmp     @_displayChars

        @_finishDisplayChars:

                newLine
                jmp     @_finally

;............................................................................
        @_finally:
                call    motd
                mov     ah, DOS_EXIT
                int     21h

;............................................................................
        endp
ends

end     start

; MICHAEL BUEN





The program library. The old school .NET framework, heheh :D

; FILENAME: USEFUL.INC


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        macro   REM
        endm

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        macro   BUG
        endm


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-

        DOS_MAXPATHLEN          = 67

        REM -- DOS interrupt 0x21
                DOS_DISPLAY_OUTPUT      = 02h
        REM     Input(s):
        REM             DL - 8 bit character data

;............................................................................
        REM -- DOS interrupt 0x21
                DOS_FILE_CREATE         = 3Ch
        REM     Input(s):
        REM             CX      - File Attribute
        REM                       0000h    Normal
        REM                       0002h    Hidden
        REM                       0004h    System
        REM                       0006h    Hidden+System
        REM             DS:DX   - Pointer to ASCIIZ filename
        REM     Output(s):
        REM             Carry Flag Clear if Successful
        REM             AX      - File Handle
        REM             Carry Flag Set if Error
        REM             AX      - Error Code
                                PATH_NOT_FOUND          = 03h
                                NO_HANDLES_AVAILABLE    = 04h
                                ACCESS_DENIED           = 05h
        

;............................................................................
        REM -- DOS Interrupt 21h
                DOS_FILE_CREATE_SAFE    = 5Bh
        REM     Input(s):
        REM             CX      - File Attribute
        REM                       0000h    Normal
        REM                       0002h    Hidden
        REM                       0004h    System
        REM                       0006h    Hidden+System
        REM             DS:DX   - Pointer to ASCIIZ filename
        REM     Output(s):
        REM             Carry Flag Clear if Successful
        REM             AX      - File Handle
        REM             Carry Flag Set if Error
        REM             AX      - Error Code
                                PATH_NOT_FOUND          = 03h
                                NO_HANDLES_AVAILABLE    = 04h
                                ACCESS_DENIED           = 05h
                                FILE_ALREADY_EXIST      = 50h


;............................................................................
        REM -- DOS Interrupt 21h
                DOS_FILE_CLOSE          = 3Eh
        REM     Input(s):
        REM             BX      - File Handle
        REM     Output(s):
        REM             Carry Flag Clear if Successful
        REM             Carry Flag Set if Error
        REM             AX      - Error Code
                                INVALID_HANDLE         = 06h
                                

;............................................................................
        REM -- DOS Interrupt 21h
                DOS_FILE_OPEN           = 3Dh
        REM     Input(s):
        REM             AL - Access Mode
        REM             Bit
        REM             76543210
        REM             .....000 Read Access
        REM             .....001 Write Access
        REM             .....010 Read/Write Access
                        READ_ACCESS             = 0h
                        WRITE_ACCESS            = 1h
                        READ_WRITE_ACCESS       = 2h
        REM             DS:DX - ASCIIZ Filename
        REM     Output(s):
        REM             AX - File Handle
        REM             Carry Flag Clear if Successful
        REM             Carry Flag Set if Error
        REM             Error Codes in AX:
                                INVALID_FUNCTION        = 01h
                                FILE_NOT_FOUND          = 02h
                                PATH_NOT_FOUND          = 03h
                                NO_HANDLES_AVAILABLE    = 04h
                                ACCESS_DENIED           = 05h
                                INVALID_ACCESS_CODE     = 0Ch

;............................................................................
        REM -- DOS interrupt 21h
                DOS_FILE_WRITE          = 40h
        REM     Input(s):
        REM             BX      - File Handle
        REM             CX      - Number of Bytes
        REM             DS:DX   - Pointer to Buffer
        REM     Output(s):
        REM             AX      - number of bytes rad
        REM             Carry Flag Clear if Successful
        REM             Carry Flag Set if error
        REM             Error Code in AX:
                                ACCESS_DENIED          = 05h
                                INVALID_HANDLE         = 06h


;............................................................................
        REM -- DOS interrupt 21h
                DOS_FILE_READ           = 3Fh
        REM     Input(s):
        REM             BX      - File Handle
        REM             CX      - Number of Bytes
        REM             DS:DX   - Pointer to Buffer
        REM     Output(s):
        REM             AX      - number of bytes rad
        REM             Carry Flag Clear if Successful
        REM             Carry Flag Set if error
        REM             Error Code in AX:
        REM                     05h - Access Denied
        REM                     06h - Invalid Handle

;............................................................................
        REM -- DOS interrupt 21h
                DOS_FILE_MOVE_PTR       = 42h
        REM     Input(s):
        REM             BX - File Handle
        REM             AL - METHOD CODE
                                DOS_FILE_BEGIN          = 00h
                                DOS_FILE_CURRENT        = 01h
                                DOS_FILE_END            = 02h
        REM             CX - Most Significant Part of Offset
        REM             DX - Least Significant Part of Offset
        REM     Output(s):
        REM             DX:AX - New File Pointer Location
        REM             Carry Flag Clear if Successful
        REM             Carry Flag Set if Error
        REM             Error Code in AX:
        REM                     01h - Invalid Function
        REM                     06h - Invalid Handle


;............................................................................
        DOS_NORMAL_FILE         = 0000h

;............................................................................
        REM -- DOS interrupt 21h
        DOS_FILE_FIND_FIRST     = 4Eh
        REM     Input(s):
        REM             CX    - Attribute to use in search
        REM             DS:DX - pointer to ASCIIZ file specification
        REM     Output(s):
        REM             Carry Flag Clear if Successful
        REM             Carry Flag Set if Error
        REM             Error Code in AX:
                                DOS_FILE_NOT_FOUND      = 0002h
                                DOS_INVALID_PATH        = 0003h
                                DOS_NO_MORE_FILES       = 0012h


;............................................................................
        REM -- DOS interrupt 21h
                DOS_CHAR_UP_CASE        = 6520h
        REM    Input(s):
        REM             DL - character to be uppercased
        REM    Output(s):
        REM             DL - uppercased character
        REM             Carry Flag Clear if Successful
        REM             Carry Flag Set if Error
        
;............................................................................
        REM -- DOS interrupt 21h
                DOS_INTR_GET_VECT       = 35h
        REM     Input(s):
        REM             AL      - Interrupt Number
        REM     Output(s):
        REM             ES:BX   - Pointer to Handler

;............................................................................
        REM -- DOS interrupt 21h
                DOS_INTR_SET_VECT       = 25h
        REM     Input(s):
        REM             AL              - Interrupt Handler
        REM             DS:DX           - Pointer to Interrupt Handler

;............................................................................
        REM -- DOS interrupt 21h
                DOS_KEEP_TSR            = 31h
        REM     Input(s):
        REM             AL   - Return Code
        REM             DX   - Memory size to reserve in paragraph


;............................................................................
        REM -- DOS interrupt 21h
                DOS_FILE_DELETE         = 41h
        REM     Input(s):
        REM             DS:DX   - Pointer to ASCIIZ file specification
        REM     Output(s):
        REM             Carry Flag Clear if Successful
        REM             Carry Flag Set if Error
        REM             AX - Error Code
                        FILE_NOT_FOUND          = 0002h
                        ACCESS_DENIED           = 0005h



;............................................................................
        REM -- DOS interrupt 21h
                DOS_FILE_RENAME         = 56h
        REM     Input(s):
        REM             DS:DX   - Pointer to ASCIIZ current file name
        REM             ES:DI   - Pointer to ASCIIZ new filename
        REM     Output(s):
        REM             Carry Flag Clear if Successful
        REM             Carry Flag Set if Error
        REM             AX      - Error Code
                                PATH_NOT_FOUND          = 0003h
                                NO_HANDLES_AVAILABLE    = 0004h
                                ACCESS_DENIED           = 0005h
                                NOT_THE_SAME_DEVICE     = 0011h
                                
        
;............................................................................
        REM -- DOS interrupt 21h
                DOS_CREATE_DIRECTORY    = 39h
        REM     Input(s):
        REM             DS:DX   - Pointer to ASCIIZ path specification
        REM     Output(s):
        REM             Carry Flag Clear if Successful
        REM             Carry Flag Set if Error
                                PATH_NOT_FOUND          = 0003h
                                ACCESS_DENIED           = 0005h


;............................................................................
        REM -- DOS interrupt 21h
                DOS_GET_INITIAL_MCB     = 52h
        REM     Output(s):
        REM             ES:BX   - Pointer to first MCB


;............................................................................
        REM -- DOS interrupt 21h
                DOS_GET_BUSY_FLAG_ADDR  = 34h
        REM     Output(s):
        REM             ES:BX   - Pointer to InDOS Flag
        REM                     value is 0 if not busy


;............................................................................
        REM -- DOS interrupt 21h
                DOS_EXIT                = 4Ch
        REM     Input(s):
        REM             AL      - Return Code


;............................................................................
        REM -- DOS interrupt 21
                DOS_RELEASE_MEM         = 49h
        REM     Input(s):
        REM             ES      - Segment Address of Block to Freed
        REM     Output(s):
        REM             Carry Flag clear if Successful
        REM             AX      - Error Code
                        MCB_DESTROYED   = 0007h
                        MCB_INVALID     = 0009h

;............................................................................
        REM -- DOS interrupt 16h
                DOS_SET_DTA             = 1Ah
        REM     Input(s):
        REM             DS:DX - pointer to a new DTA


;............................................................................
        REM -- DOS interrupt 16h
                DOS_GET_DTA             = 1Ah
        REM     Output(s):
        REM             ES:BX - pointer to DTA


;............................................................................
        REM -- BIOS interrupt 16h
        BIOS_OLD_KEYSTAT        = 01h
        REM     Output(s):
        REM             AL - ASCII Code
        REM             AH - Scan Code
        REM             Zero Flag Clear if there is a key available
        REM             Zero Flag Set if there is no key available

;............................................................................
        REM -- BIOS interrupt 16h
                BIOS_NEW_KEYSTAT        = 11h
        REM     Output(s):
        REM             AL - ASCII Code
        REM             AH - Scan Code
        REM             Zero Flag Clear if there is a key available
        REM             Zero Flag Set if there is no key available

;............................................................................
        REM -- BIOS interrupt 16h
                BIOS_OLD_READKEY        = 00h
        REM     Output(s):
        REM             AL - ASCII Code
        REM             AH - Scan Code

;............................................................................
        REM -- BIOS interrupt 16h
                BIOS_NEW_READKEY        = 10h
        REM     Output(s):
        REM             AL - ASCII Code
        REM             AH - Scan Code

;............................................................................
        REM -- BIOS interrupt 10h
                BIOS_TELETYPE_WRITE     = 0Eh
        REM     Input(s):
        REM             AL - Character
        REM             BH - Display Page
        REM             BL - Foreground Color


;............................................................................
        REM -- BIOS interrupt 10h
                BIOS_GET_CURSOR_POS     = 03h
        REM     Input(s):
        REM             BH - page number
        REM     Output(s):
        REM             DL - column
        REM             DH - row

;............................................................................
        REM -- BIOS interrupt 10h
                BIOS_SET_CURSOR_POS     = 02h
        REM     Input(s):
        REM             BH - page number
        REM             DL - column
        REM             DH - row

;............................................................................
        REM -- BIOS interrupt 10h
                BIOS_READ_CHAR_AT_POS   = 08h
        REM     Input(s):
        REM             BH - page number
        REM     Output(s):
        REM             AL - the character


;............................................................................
        REM -- BIOS interrupt 10h
                BIOS_SCROLL_WIN_UP      = 06h
        REM     Input(s):
        REM             AL - Number of lines to scroll
        REM             BH - Attribute use for blank area
        REM             CH - Row, upper left corner
        REM             CL - Column, upper lef corner
        REM             DH - Row, lower right corner
        REM             DL - Column, lower right corner



;............................................................................
        REM -- BIOS interrupt 10h
                BIOS_READ_CHAR_ATTR     = 08h
        REM     Input(s):
        REM             BH - Display Page
        REM     Output(s):
        REM             AH - Attribute Byte
        REM             AL - Ascii Character

;............................................................................
        REM -- BIOS interrupt 10h
                BIOS_WRITE_CHAR_ATTR    = 09h
        REM     Input(s):
        REM             AL - Character
        REM             BH - Display Page
        REM             BL - Attribute bye of character in AL
        REM             CX - Number of Characters to write



;............................................................................
        REM     - COLORS -
        REM     -- dark colors
        BLACK                   = 00h
        BLUE                    = 01h
        GREEN                   = 02h
        CYAN                    = 03h
        RED                     = 04h
        MAGENTA                 = 05h
        BROWN                   = 06h
        LIGHTGRAY               = 07h
        DARKGRAY                = 08h
        REM     -- light colors
        LIGHTBLUE               = 09h
        LIGHTGREEN              = 0Ah
        LIGHTCYAN               = 0Bh
        LIGHTRED                = 0Ch
        LIGHTMAGENTA            = 0Dh
        YELLOW                  = 0Eh
        WHITE                   = 0Fh






;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        macro   ohNo    addr
                jne     addr
        endm

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        macro   ohYes   addr
                je      addr
        endm

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        macro   cle
                push    ax
                mov     ax, 0
                cmp     ax, 1
                pop     ax
        endm


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        macro   ste
                push    ax
                mov     ax, 1
                cmp     ax, 1
                pop     ax
        endm


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        macro   clz
                push    ax
                mov     ax, 0
                cmp     ax, 1
                pop     ax
        endm


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        macro   stz
                push    ax
                mov     ax, 1
                cmp     ax, 1
                pop     ax
        endm

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        macro   puts    str
                local   disp
                local   s
                jmp     disp
                s db str, '$'
        disp:
                mov     ah, 09
                mov     dx, offset s
                int     21h
        endm


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        macro   newLine
                push    ax
                push    dx
                mov     ah, DOS_DISPLAY_OUTPUT
                mov     dl, 0Dh
                int     21h
                mov     dl, 0Ah
                int     21h
                pop     dx
                pop     ax
        endm


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        REM     -- VARIOUS DATA STRUCTURES GOES HERE
;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-

        struc   mcb
                REM             -- 'M' more to follow 'Z' zero to follow
                linkIndicator   db ?
                segOfOwner      dw ?
                paragraphLen    dw ?
                db 3 dup(?)
                asciiName       db 8 dup(?)
        ends


        COMMAND_LINE_LEN_OFFSET = 80h
        COMMAND_LINE_OFFSET     = 81h

        ENVIRONMENT_SEGMENT     = 2Ch

        FALSE                   = 0
        TRUE                    = 1


        NULL                    = 0



; MICHAEL BUEN