;======================================================================
; BLITZKEY 1.00 Copyright (c) 1992, Robert L. Hummel
; PC Magazine Assembly Language Lab Notes
;
; BlitzKey is a keyboard macro facility and type-ahead buffer enhancer.
;======================================================================
; This segment is mapped onto the BIOS data area. Do not change the
; size or position of data elements -- they are fixed by the BIOS.
;----------------------------------------------------------------------
BIOSMEM SEGMENT AT 40H ;0040:0000
ORG 01AH ;Location in the seg
KEYPTRS LABEL DWORD
KEYHEAD DW ?
KEYTAIL DW ?
KEYBUF DW 16 DUP (?)
KEYEND EQU $
BIOSMEM ENDS
;======================================================================
; Code segment.
;----------------------------------------------------------------------
CSEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:CSEG
;----------------------------------------------------------------------
; Code entry point is here, at 100h. Jump over resident routines.
;----------------------------------------------------------------------
ORG 100H ;COM file format
ENTPT:
JMP MAIN
;----------------------------------------------------------------------
; General program equates.
;----------------------------------------------------------------------
CR EQU 13 ;Common equates
LF EQU 10
BLANK EQU 32
SLASH EQU 47
INSK EQU 52H ;Extended ASCII values
DEL EQU 53H
F7KEY EQU 41H
HOME EQU 47H
ENDKEY EQU 4FH
PGUP EQU 49H
PGDN EQU 51H
RARROW EQU 4DH
LARROW EQU 4BH
UARROW EQU 48H
DARROW EQU 50H
BS EQU 0E08H ;Scan/Ascii code
JMPFAR EQU 0EAH ;Opcodes that MASM
CALLFAR EQU 09AH ; can't handle
RETFAR EQU 0CBH
U_SW EQU 1 ;Request to Uninstall
E_SW EQU 2 ;Edit macros
;----------------------------------------------------------------------
; Resident messages.
;----------------------------------------------------------------------
RES_MARKER DW 0 ;Altered when resident
COPYRIGHT$ DB CR,LF,"BlitzKey 1.00 ",254," Copyright (c) "
DB "1992, Robert L. Hummel",CR,LF,"PC Magazine "
DB "Assembly Language Lab Notes",LF,CR,LF,"$"
MARKER_LEN EQU $-RES_MARKER
;----------------------------------------------------------------------
; Enhanced buffer data. Changing ESIZE changes all dependent constants.
; Note that each char is stored as a 2-byte key code.
;----------------------------------------------------------------------
ESIZE EQU 128 ;Buffer size (keys)
EBUF DW ESIZE DUP (?) ;Our key buffer
EMAX EQU $ ;Maximum offset
EOUT DW EBUF ;Chars out here
EIN DW EBUF ; and in here
EFREE DW ESIZE ;Chars free
;----------------------------------------------------------------------
; Macro data. Note that the strings use a FAR pointer -- they can be
; located anywhere in memory. The length of the strings is stored as a
; variable and updated when a modified copy of the program is written
; to disk.
;----------------------------------------------------------------------
STR_LOC DW OFFSET STRINGS,0 ;FAR ptr to strings
STR_LEN DW OFFSET STRING_END - OFFSET STRINGS ;Str len
MOUT DW 0 ;Get macro keys here
EXPANDING DB 0 ;Non-zero if expanding
DISABLED DB 0 ;Non-zero to disable
;======================================================================
; INT_9 (ISR)
;
; The Int 9 routine in the BIOS translates keys and places them in the
; BBUF. Intercepting this interrupt gives us an opportunity to
; grab the key after it's put in the BIOS buffer and put it in EBUF.
;
; By checking before calling Int 9, we'll also get keys that were
; stuffed into the buffer by other programs and keys left there if
; EBUF overflows.
;----------------------------------------------------------------------
INT_9 PROC FAR
ASSUME CS:CSEG, DS:NOTHING, ES:NOTHING, SS:NOTHING
;----------------------------------------------------------------------
; If any chars are present in BBUF, transfer them. Call with interrupts
; disabled.
;----------------------------------------------------------------------
CALL XFERBUF
STI ;Enable interrupts
;----------------------------------------------------------------------
; To simulate an interrupt, push the flags, then make a far call to the
; original interrupt routine.
;----------------------------------------------------------------------
PUSHF ;Save w/ints on
CLI ;Disable interrupts
DB CALLFAR ;Call
OLD9 DD -1 ; old Int 9
;----------------------------------------------------------------------
; Check BBUF again, in case the Int 9 just processed put a key there.
;----------------------------------------------------------------------
CLI ;Disable interrupts
CALL XFERBUF
IRET ;Return to caller
INT_9 ENDP
;======================================================================
; INT_16 (ISR)
;
; This procedure replaces a portion of the original BIOS interrupt and
; also acts as a front end for the macro expander. Keys are fed to
; requestors from EBUF or from a macro string.
;
; If a program calls with the old 0 or 1 functions, extended scan codes
; are converted to their non-extended equivalent by changing the lower
; 8 bits to 0 from E0h.
;----------------------------------------------------------------------
; Scan codes returned for ALT+A through ALT+Z.
;----------------------------------------------------------------------
SCAN_TBL DB 1EH, 30H, 2EH, 20H, 12H, 21H, 22H, 23H
DB 17H, 24H, 25H, 26H, 32H, 31H, 18H, 19H
DB 10H, 13H, 1FH, 14H, 16H, 2FH, 11H, 2DH
DB 15H, 2CH
;----------------------------------------------------------------------
INT_16 PROC FAR
ASSUME CS:CSEG, DS:NOTHING, ES:NOTHING, SS:NOTHING
;----------------------------------------------------------------------
; If keys are available in BBUF, transfer them to EBUF. Call with
; interrupts disabled.
;----------------------------------------------------------------------
CALL XFERBUF
STI ;Enable interrupts
CLD ;String moves forward
;----------------------------------------------------------------------
; If the keyboard function requested is not 0, 10h, 1, or 11h, simply
; pass the request on to the original handler.
;----------------------------------------------------------------------
PUSH AX ;Preserve original fn
AND AH,NOT 10H ;Ignore extended bit
CMP AH,1 ;Accept 0 or 1
POP AX ;(Restore original fn)
JBE I16_1
CLI ;Disable interrupts
DB JMPFAR ;Far jump
OLD16 DD -1 ; to old handler
;----------------------------------------------------------------------
; Prepare to service the interrupt ourself.
;----------------------------------------------------------------------
I16_1:
PUSH BX ;Save used registers
PUSH CX
PUSH SI
PUSH DS
PUSH ES
PUSH CS ;Address local data
POP DS ; using DS
ASSUME DS:CSEG
MOV BX,AX ;Save original fn in BX
;----------------------------------------------------------------------
; If a macro expansion is in progress, all keys come from the macro
; expander, not EBUF.
;----------------------------------------------------------------------
CMP BYTE PTR [EXPANDING],0 ;Non-zero = use macro
JE I16_3A
;----------------------------------------------------------------------
; Macros are expanded here. Point ES:SI to macro string and load next
; key into AX.
;----------------------------------------------------------------------
I16_2A:
MOV ES,[STR_LOC][2] ;Get segment
ASSUME ES:NOTHING
MOV SI,[MOUT] ; and offset
MOV AX,ES:[SI] ;Load next key
;----------------------------------------------------------------------
; If end of macro, turn expander off and go back to normal key stream.
;---------------------------------------------------------------------
OR AX,AX ;0 = end of macro
JNZ I16_2B
MOV [EXPANDING],AL ;0 = expander off
JMP SHORT I16_3A
;----------------------------------------------------------------------
; If function 0 or 10h, move the pointer.
;----------------------------------------------------------------------
I16_2B:
TEST BH,1 ;Set rtn flag & test
JNZ I16_EXIT
ADD [MOUT],2 ;Store new pointer
JMP SHORT I16_EXIT
;----------------------------------------------------------------------
; Point SI to the (possibly) next key to read from EBUF.
;----------------------------------------------------------------------
I16_3A:
MOV SI,[EOUT] ;Get ptr to next key
;----------------------------------------------------------------------
; If no key available, then there's no need to check for a macro key.
;----------------------------------------------------------------------
MOV CX,ESIZE ;Max it holds -
SUB CX,[EFREE] ; # free = keys
JZ I16_4B
;----------------------------------------------------------------------
; Read the next key from EBUF. If not an extended char (and therefore
; not a macro key) process normally.
;----------------------------------------------------------------------
LODSW ;Get key from EBUF
OR AL,AL ;AL=0 if extended
JNZ I16_4B
;----------------------------------------------------------------------
; If macro expansion is disabled, process the key normally.
;----------------------------------------------------------------------
CMP BYTE PTR [DISABLED],0 ;0 = enabled
JNE I16_4B
;----------------------------------------------------------------------
; If extended key, check for scan code of ALT+A through ALT+Z.
;----------------------------------------------------------------------
PUSH CX ;Preserve registers
PUSH DI
PUSH CS ;Set up for scan
POP ES ;Point ES:DI
ASSUME ES:CSEG
XCHG AH,AL ;Put scan code in AL
MOV DI,OFFSET SCAN_TBL ;Match to this table
MOV CX,26 ;Bytes to scan
REPNE SCASB ;Scan for match
POP DI ;Restore register
JNE I16_4A
POP AX ;Discard old CX
;----------------------------------------------------------------------
; The key was one of our macro keys. Delete the key from EBUF.
;----------------------------------------------------------------------
INC [EFREE] ;Indicate char now free
CMP SI,OFFSET EMAX ;Past buffer end?
JB I16_3B
MOV SI,OFFSET EBUF ;Wrap to beginning
I16_3B:
MOV [EOUT],SI ;Store new pointer
;----------------------------------------------------------------------
; Load a pointer to the indicated macro, then restart with macro
; expansion on.
;----------------------------------------------------------------------
XCHG BX,CX ;CX=fn, BX=count
NEG BL ;Make BX into...
ADD BL,26-1 ;... macro number
ADD BL,BL ;Double for indexing
INC BL ;Point to macro
PUSH DS ;Save register
LDS SI,DWORD PTR [STR_LOC] ;Point DS:SI to strings
ASSUME DS:NOTHING
CALL GET_POINTER ;Get pointer
POP DS ;Restore register
ASSUME DS:CSEG
MOV [MOUT],SI ;Give to expander
MOV BYTE PTR [EXPANDING],-1 ;Expander on
MOV BX,CX ;Put function in BX
JMP I16_2A
;----------------------------------------------------------------------
; Was not a macro key, process as normal.
;----------------------------------------------------------------------
I16_4A:
XCHG AH,AL ;Restore AX
POP CX ;Restore register
;----------------------------------------------------------------------
; If 0 or 1 was called, filter out the extended keystrokes.
;----------------------------------------------------------------------
I16_4B:
CMP AL,0E0H ;E0 -> extended key
JNE I16_4C
TEST BH,10H ;NZ = extended fn
JNZ I16_4C
SUB AL,AL ;Cancel extended byte
I16_4C:
;----------------------------------------------------------------------
; If the function was 1 or 11h, we're just checking the buffer status.
; If no keys are available, return with ZF=1.
; Otherwise, return ZF=0 and the key in AX.
;----------------------------------------------------------------------
TEST BH,1 ;NZ if 1 or 11h
JZ I16_5A
OR CX,CX ;Test to set zero flag
;----------------------------------------------------------------------
; Return from the interrupt. Discard the old flags.
;----------------------------------------------------------------------
I16_EXIT:
POP ES ;Restore registers
ASSUME ES:NOTHING
POP DS
ASSUME DS:NOTHING
POP SI
POP CX
POP BX
RET 2 ;Discard old flags
;----------------------------------------------------------------------
; AH=0 or 10h is the Wait for key function. If there is a key in the
; buffer, simply continue.
;----------------------------------------------------------------------
ASSUME DS:CSEG
I16_5A:
OR CX,CX ;Nonzero if had keys
JNZ I16_5C
;----------------------------------------------------------------------
; No key was in the buffer. Enter a loop to continuosly check the EBUF
; EFREE count until it decreases from the maximum, indicating a char
; was placed in EBUF. When we get one, restart the routine.
;----------------------------------------------------------------------
I16_5B:
CALL XFERBUF ;Was buffer stuffed?
CMP [EFREE],ESIZE ;Equal means empty
JE I16_5B
JMP I16_3A
;----------------------------------------------------------------------
; Remove the key from EBUF.
;----------------------------------------------------------------------
I16_5C:
INC [EFREE] ;One more free
CMP SI,OFFSET EMAX ;Past buffer end?
JB I16_5D
MOV SI,OFFSET EBUF ;Wrap to beginning
I16_5D:
MOV [EOUT],SI ;Store new pointer
JMP I16_EXIT
INT_16 ENDP
;======================================================================
; XFERBUF (NEAR)
;
; Examine the BBUF and, if any characters are present, move to EBUF.
; Note that because this routine may be called from INT_9 while it is
; already executing a call from INT_16, the XBUSY flag must be set and
; cleared carefully to avoid any chance of re-entrancy.
;----------------------------------------------------------------------
; Entry:
; Interrupts disabled
; Exit :
; Interrupts disabled
;----------------------------------------------------------------------
; Changes: FLAGS
;----------------------------------------------------------------------
XBUSY DB 0 ;Nonzero when busy
;----------------------------------------------------------------------
XFERBUF PROC NEAR
ASSUME CS:CSEG, DS:NOTHING, ES:NOTHING, SS:NOTHING
;----------------------------------------------------------------------
; Ensure that this routine is never re-entered.
;----------------------------------------------------------------------
CMP BYTE PTR CS:[XBUSY],0 ;0=not busy
JNE X_2B
INC BYTE PTR CS:[XBUSY] ;Say it's busy now
STI
;----------------------------------------------------------------------
; Save all registers.
;----------------------------------------------------------------------
PUSH AX ;Save used registers
PUSH BX
PUSH CX
PUSH SI
PUSH DI
PUSH DS
PUSH ES
CLD ;String moves forward
;----------------------------------------------------------------------
; Point DS:SI to BBUF and ES:DI to EBUF in anticipation of the move.
;----------------------------------------------------------------------
MOV AX,BIOSMEM ;Address BIOS buffer
MOV DS,AX ; with DS
ASSUME DS:BIOSMEM
PUSH CS ;Address our buffer
POP ES ; with ES
ASSUME ES:CSEG
MOV SI,DS:[KEYHEAD] ;BIOS head pointer
MOV CX,DS:[KEYTAIL] ;BIOS tail pointer
MOV DI,CS:[EIN] ;Write chars here
MOV BX,CS:[EFREE] ;Room in our buf
;----------------------------------------------------------------------
; If EBUF is full, we can't transfer any characters.
;----------------------------------------------------------------------
OR BX,BX ;BX=chars free
X_1:
JZ X_2A
;----------------------------------------------------------------------
; If there are no characters in BBUF, we're done.
;----------------------------------------------------------------------
CMP SI,CX ;Head=tail=buffer empty
JNZ X_3A
;----------------------------------------------------------------------
; Update the pointers and exit.
;----------------------------------------------------------------------
X_2A:
MOV CS:[EIN],DI ;Save new BIN
MOV CS:[EFREE],BX ;And free space
MOV DS:[KEYHEAD],SI ;Update BIOS pointer
POP ES ;Restore registers
ASSUME ES:NOTHING
POP DS
ASSUME DS:NOTHING
POP DI
POP SI
POP CX
POP BX
POP AX
CLI ;Disable interrupts
DEC BYTE PTR CS:[XBUSY] ;Say not busy
X_2B:
RET ; after return
;----------------------------------------------------------------------
; At this point, we know there is at least one key in BBUF and room for
; at least one key in EBUF. Remove the key from BBUF.
;----------------------------------------------------------------------
ASSUME DS:BIOSMEM, ES:CSEG
X_3A:
LODSW ;Get BIOS key codes
CMP AL,0F0H ;BIOS filters these out
JNE X_3B
OR AH,AH ;Accept 00F0h
JZ X_3B
SUB AL,AL ;Make normal
X_3B:
CMP SI,OFFSET KEYEND ;If past end
JB X_3C
MOV SI,OFFSET KEYBUF ; wrap pointer
X_3C:
;----------------------------------------------------------------------
; Place the key into EBUF.
;----------------------------------------------------------------------
STOSW ;Put in EBUF
CMP DI,OFFSET EMAX ;If past end
JB X_4
MOV DI,OFFSET EBUF ; wrap pointer
X_4:
;----------------------------------------------------------------------
; Decrease the number of free chars. This operation sets the flags
; for the conditional jump at X_1.
;----------------------------------------------------------------------
DEC BX ;Decrease free chars
JMP X_1
XFERBUF ENDP
;======================================================================
; GET_POINTER (Near)
;
; Find a string in the string list. Note that when the strings are
; being edited, they are located in CSEG. When searched during macro
; expansion, however, they can be anywhere.
;----------------------------------------------------------------------
; Entry:
; CLD
; BL = number (0-based) of string to find
; DS:SI -> start of string block
; Exit :
; DS:SI -> offset of desired string
;----------------------------------------------------------------------
; Changes: AX BX SI
;----------------------------------------------------------------------
GET_POINTER PROC NEAR
ASSUME CS:CSEG, DS:NOTHING, ES:NOTHING, SS:NOTHING
SUB BH,BH ;Count strings in BH
GP_0:
CMP BH,BL ;Equal when we're done
JNE GP_1
RET ;Return to caller
GP_1:
LODSW ;Fetch word
OR AX,AX ;If not zero, continue
JNZ GP_1
INC BH ;Found end of string
JMP GP_0
GET_POINTER ENDP
;======================================================================
; SHRIVEL (NEAR)
;
; This code moves the strings lower in the segment during the first
; installation if there is no room lower in memory. It is really an
; extension of the REPLACE procedure, but has to appear here so that
; the relocated strings don't overwrite it.
;----------------------------------------------------------------------
; Entry:
; DS:SI = string source
; ES:DI = string destination
; DX = number of paragraphs to keep resident
; Exit:
; None
;----------------------------------------------------------------------
SHRIVEL PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG
REP MOVSB ;Transfer the strings
NOT WORD PTR [RES_MARKER] ;Modify for TSR
MOV AH,31H ;Keep process
INT 21H ; thru DOS
SHRIVEL ENDP
;======================================================================
; When BLITZKEY becomes resident, everything after CUTOFF is discarded
; or written over by the strings.
;----------------------------------------------------------------------
CUTOFF EQU $
;======================================================================
; Transient data -- discarded when resident.
;----------------------------------------------------------------------
ERR_MEMSIZ$ DB "There's Not Enough Memory To Execute$"
USAGE$ DB "Usage: BLITZKEY [/U|/E]$"
COM_PTR DW STRING_END ;Cannot exceed 64k-200H
RES_SEG DW -1 ;Init to none resident
STACK_TOP DW 0 ;Top of relocated stack
UMB_LINK DB -1 ;Init to not present
;======================================================================
; MAIN (Near)
;
; The MAIN procedure interprets the command line switches, checks for
; previous copies in memory, and instigates all memory management.
;----------------------------------------------------------------------
MAIN PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG
CLD ;String moves forward
;----------------------------------------------------------------------
; Relocate the stack pointer to just after the end of the program code.
;
; Note: because the program's length will change during use, the
; address of the last byte is accessed as a memory operand (COM_PTR).
;----------------------------------------------------------------------
MOV DX,OFFSET ERR_MEMSIZ$ ;Assume failure
MOV AX,[COM_PTR] ;Length of code
ADD AX,128*2 ;+ stack space
CMP AX,SP ;Past end?
JA M_3
M_1:
MOV [STACK_TOP],AX ;Save new stack top
MOV SP,AX ; and load it
;----------------------------------------------------------------------
; Release the copy of the environment allocated to this program. The
; segment address of the env block is located at offset 2Ch in the PSP.
;----------------------------------------------------------------------
MOV ES,DS:[2CH] ;Get seg of environment
ASSUME ES:NOTHING
MOV AH,49H ;Free allocated memory
INT 21H ; thru DOS
;----------------------------------------------------------------------
; Search for a copy of the program already resident in memory.
;----------------------------------------------------------------------
CALL FIND_RES
ASSUME ES:NOTHING
;----------------------------------------------------------------------
; Process the command line switches and return them bit-packed in AH.
; We can then process them by priority instead of position.
;----------------------------------------------------------------------
MOV DX,OFFSET USAGE$ ;Show correct syntax
CALL CMD_LINE ;Get switches in AH
JC M_3
;----------------------------------------------------------------------
; If the /U switch was specified, attempt to unload a resident copy.
; If no copy is resident, report the fact. Ignore all other switches.
;----------------------------------------------------------------------
TEST AH,U_SW ;NZ = unload
JZ M_4
CALL UNLOAD ;Unload if possible
ASSUME ES:NOTHING
M_3:
MOV AH,9 ;Display string
INT 21H ; thru DOS
;----------------------------------------------------------------------
; Display the program title and exit.
;----------------------------------------------------------------------
M_EXIT:
MOV AH,9 ;Display string
MOV DX,OFFSET COPYRIGHT$ ;Say who we are
INT 21H ; thru DOS
MOV AH,4CH ;Terminate with error
INT 21H ; thru DOS
;----------------------------------------------------------------------
; If the /E switch was specified, invoke the editor to edit the macro
; strings in the current copy. Jump there -- it never returns.
;----------------------------------------------------------------------
M_4:
TEST AH,E_SW ;NZ if Setup switch on
JZ M_5A
JMP SETUP ;Yes, invoke the editor
;----------------------------------------------------------------------
; No switches were specified. If not already resident, install with
; the macros in this copy. If resident, replace the resident macros.
; Jump to both these routines -- they don't return.
;----------------------------------------------------------------------
M_5A:
CMP WORD PTR [RES_SEG],-1 ;-1 = not resident
JE M_5B
JMP REPLACE ;Replace macros
M_5B:
JMP LOAD ;Load into memory
MAIN ENDP
;======================================================================
; FIND_RES (Near)
;
; Determine if a copy of BLITZKEY is already resident by searching for
; a duplicate of the copyright notice in memory.
;----------------------------------------------------------------------
; Entry: None
; Exit :
; DW [RES_SEG] = -1 if no resident copy
; code segment of resident copy, otherwise
;----------------------------------------------------------------------
; Changes: AX BX CX SI DI ES
;----------------------------------------------------------------------
FIND_RES PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; Modify marker string to avoid false matches when searching memory.
; Initialize RES_SEG and UMB_LINK. They may have been altered if this
; is a cloned copy made when BLITZKEY was resident.
;----------------------------------------------------------------------
NOT WORD PTR [RES_MARKER] ;Modify copyright
MOV WORD PTR [RES_SEG],-1 ;Say none resident
MOV BYTE PTR [UMB_LINK],-1 ;Say no UMBs
;----------------------------------------------------------------------
; If DOS 5 or later, save the current UMB state, then link them.
;----------------------------------------------------------------------
MOV AH,30H ;Get DOS version in AX
INT 21H ; thru DOS
CMP AL,5 ;Dos 5 or later?
JB FR_1
MOV AX,5802H ;Get current UMB link
INT 21H ; thru DOS
JC FR_1
MOV [UMB_LINK],AL ;Save it
MOV AX,5803H ;Set UMB to
MOV BX,1 ; linked in chain
INT 21H ; thru DOS
FR_1:
;----------------------------------------------------------------------
; Get the segment address of the first MCB using DOS IVARS function.
;----------------------------------------------------------------------
MOV AH,52H ;Get ES:BX -> IVARS
INT 21H ; thru DOS
ASSUME ES:NOTHING
MOV BX,ES:[BX-2] ;Get first MCB
;----------------------------------------------------------------------
; Point ES to the segment in BX and look for the modified copyright.
; Because ES points to the MCB header and not the block itself, the
; offset is increased (DI=SI+10) to compensate.
;----------------------------------------------------------------------
MOV AX,DS ;Current seg in AX
FR_2A:
MOV ES,BX ;Point ES to MCB
ASSUME ES:NOTHING
INC BX ;Point BX to block
MOV SI,OFFSET RES_MARKER ;Compare DS:SI
LEA DI,[SI+10H] ; to ES:DI
MOV CX,MARKER_LEN ;Compare full string
REPE CMPSB ;CMP DS:SI TO ES:DI
JNE FR_2B
;----------------------------------------------------------------------
; A match was found. If it's this copy, ignore it and continue the
; search. Otherwise, save it and we're done.
;----------------------------------------------------------------------
CMP AX,BX ;Current copy?
JE FR_2B
MOV [RES_SEG],BX ;Save resident segment
JMP SHORT FR_3A
;----------------------------------------------------------------------
; Not a match. Move to the next memory block. If no more, we're done.
;----------------------------------------------------------------------
FR_2B:
ADD BX,ES:[3] ;Add block length
CMP BYTE PTR ES:[0],"Z" ;This block the last?
JNE FR_2A
;----------------------------------------------------------------------
; Restore the UMB link to its previous state.
;----------------------------------------------------------------------
FR_3A:
MOV BL,[UMB_LINK] ;Original link state
CMP BL,-1 ;Was it recorded?
JE FR_3B
SUB BH,BH ;Link in BX
MOV AX,5803H ;Set UMB link
INT 21H ; thru DOS
FR_3B:
;----------------------------------------------------------------------
; Unmodify the copyright so we don't leave false matches in memory.
;----------------------------------------------------------------------
NOT WORD PTR [RES_MARKER] ;Modify copyright
RET
FIND_RES ENDP
;======================================================================
; CMD_LINE (Near)
;
; Reads the command line and returns switches bit-packed in AH.
;----------------------------------------------------------------------
; Entry: None
; Exit :
; CF = NC - successful
; AH = bit flags
; /U = 1
; /E = 2
;
; CF = CY - command tail contained improper syntax
;----------------------------------------------------------------------
; Changes: AX CX SI
;----------------------------------------------------------------------
CMD_LINE PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
MOV SI,80H ;Point to cmd tail len
LODSB ;Get length
CBW ;Convert to word (AH=0)
OR AL,AL ;Non-zero if switches
JNZ CMD_2A
CMD_1:
CLC ;Clear carry = no error
CMD_EXIT:
RET ;Return
;----------------------------------------------------------------------
; Something is on the line. Let's find out what.
;----------------------------------------------------------------------
CMD_2A:
MOV CX,AX ;Put count in CX
CMD_2B:
JCXZ CMD_1
CMD_2C:
LODSB ;Get character
DEC CX ;Reduce count
CMP AL,BLANK ;Skip blanks
JE CMD_2B
CMP AL,SLASH ;Is the char a slash?
JE CMD_2D ;Yes, process switch
;----------------------------------------------------------------------
; A slash must be the first non-blank character encountered. Otherwise,
; exit with the carry flag set.
;----------------------------------------------------------------------
CMD_ERR:
STC ;Carry on
JMP CMD_EXIT
;----------------------------------------------------------------------
; The switch character must immediately follow the slash. If there are
; no more characters, exit with an error.
;----------------------------------------------------------------------
CMD_2D:
JCXZ CMD_ERR
;----------------------------------------------------------------------
; Test for the legitimate options.
;----------------------------------------------------------------------
LODSB ;Get next char
DEC CX ;Decrease count
AND AL,NOT 20H ;Make switch upper case
;----------------------------------------------------------------------
; U means uninstall the resident copy.
;----------------------------------------------------------------------
CMP AL,"U" ;Uninstall switch
JNE CMD_3A
OR AH,U_SW ;Set bit flag
JMP CMD_2B
;----------------------------------------------------------------------
; E means bring up the editor.
;----------------------------------------------------------------------
CMD_3A:
CMP AL,"E" ;Edit switch
JNE CMD_ERR
OR AH,E_SW ;Set bit flag
JMP CMD_2B
CMD_LINE ENDP
;======================================================================
; UNLOAD (Near)
;
; Attempt to remove the copy of BLITZKEY already in memory.
;----------------------------------------------------------------------
; Entry:
; None
; Exit :
; CF = CY - error occurred during memory release
; NC - removed okay or not resident
; DX = offset of message reporting result
;----------------------------------------------------------------------
; Changes: AX BX CX DX ES
;----------------------------------------------------------------------
ERR_RES$ DB "There's No Resident Copy To Uninstall$"
ERR_VECT$ DB "Vectors Have Been Changed. Can't Uninstall$"
UNLOAD_OK$ DB "Uninstall Successful$"
ERR_MEM$ DB "Error Releasing Memory -- Suggest Reboot$"
;----------------------------------------------------------------------
UNLOAD PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; If there is no resident copy, return with an error message.
;----------------------------------------------------------------------
MOV DX,OFFSET ERR_RES$ ;Assume not resident
MOV CX,[RES_SEG] ;Get seg of res copy
CMP CX,-1 ;-1 = not resident
JE U_3B
MOV BYTE PTR [DISABLED],-1 ;Disable expander
;----------------------------------------------------------------------
; Determine if the hooked interrupts have been changed since the
; resident copy was installed.
;----------------------------------------------------------------------
MOV AX,3516H ;Get Int 16h vector
INT 21H ; thru DOS
ASSUME ES:NOTHING
MOV DX,OFFSET ERR_VECT$ ;Default error message
MOV AX,ES ;Get interrupt segment
CMP AX,CX ;Same as res seg?
JNE U_2B
MOV AX,3509H ;Get Int 9 vector
INT 21H ; thru DOS
ASSUME ES:NOTHING
MOV AX,ES ;Get interrupt segment
CMP AX,CX ;Same as res seg?
JNE U_2B
;----------------------------------------------------------------------
; If we get here, the interrupts were unchanged and ES points to the
; resident segment.
;----------------------------------------------------------------------
PUSH DS ;Save used register
MOV AX,2509H ;Set vector
LDS DX,DWORD PTR ES:[OLD9] ;DS:DX = old vector
ASSUME DS:NOTHING
INT 21H ; thru DOS
MOV AX,2516H ;Set vector
LDS DX,DWORD PTR ES:[OLD16] ;DS:DX = old vector
ASSUME DS:NOTHING
INT 21H ; thru DOS
POP DS ;Restore register
ASSUME DS:CSEG
;----------------------------------------------------------------------
; Release the memory block that we allocated to hold the strings.
; If an error occurs, disable the TSR and quit.
;----------------------------------------------------------------------
MOV DX,ES:[STR_LOC][2] ;Get/save str seg
MOV AH,49H ;Release segment in ES
MOV ES,DX ;ES = string seg
ASSUME ES:NOTHING
INT 21H ; thru DOS
JC U_2B
;----------------------------------------------------------------------
; If the resident code segment is different than the resident string
; segment, release the code block.
;----------------------------------------------------------------------
CMP CX,DX ;Cmp res seg, str seg
JE U_3A
MOV AH,49H ;Free memory block
MOV ES,CX ;Resident code
ASSUME ES:NOTHING
INT 21H ; thru DOS
JNC U_3A
;----------------------------------------------------------------------
; Terminate with extreme prejudice -- uninstall failed.
;----------------------------------------------------------------------
U_2A:
MOV DX,OFFSET ERR_MEM$ ;Report memory error
U_2B:
STC ;Signal failure
JMP SHORT U_EXIT
;----------------------------------------------------------------------
; Restore registers and exit.
;----------------------------------------------------------------------
U_3A:
MOV DX,OFFSET UNLOAD_OK$ ;All is okay
U_3B:
CLC ;Signal success
U_EXIT:
RET
UNLOAD ENDP
;======================================================================
; SETUP (Near)
;
; This procedure is called to edit the macro strings in this copy. Note
; that the length of the program plus the macros cannot exceed 64k.
;----------------------------------------------------------------------
; Entry: None
; Exit : Doesn't Return
;----------------------------------------------------------------------
; Changes: n/a
;----------------------------------------------------------------------
ERR_VID$ DB "Incompatible Video Mode$"
FERROR$ DB CR,LF,"File error. Try Again? (Y/N)$"
OVERWRITE$ DB CR,LF,"Overwrite existing file? (Y/N)$"
SAVE$ DB CR,LF,"Save changes (in BLITZNEW.COM)? (Y/N)$"
WERROR$ DB CR,LF,"Write error. Try Again? (Y/N)$"
FILENAME DB "BLITZNEW.COM",0
ROW_END DB 24 ;Defaults for
COL_END DB 80 ; common video
COL_MAX DB 0 ;Rightmost column
VPAGE DB 0 ;Active page
ATTR DB 0 ;Video attribute
CO_ATTR EQU 1FH ;Brite white/blue
BW_ATTR EQU 07H ;Reverse video
;----------------------------------------------------------------------
SETUP PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; Expand this copy's segment to a full 64k.
;----------------------------------------------------------------------
PUSH CS ;Point ES to this seg
POP ES
ASSUME ES:CSEG
MOV AH,4AH ;Modify memory block
MOV BX,1000H ;Ask for 64K
INT 21H ; thru DOS
JNC S_2
;----------------------------------------------------------------------
; Exit this routine with an error.
;----------------------------------------------------------------------
MOV DX,OFFSET ERR_MEMSIZ$ ;Need more room
S_1A:
MOV AH,9 ;Display string
INT 21H ; thru DOS
S_1B:
MOV AH,9 ;Display string
MOV DX,OFFSET COPYRIGHT$ ;Say who we are
INT 21H ; thru DOS
MOV AH,4CH ;Terminate program
INT 21H ; thru DOS
;----------------------------------------------------------------------
; Relocate the stack pointer to the end of the resized segment.
;----------------------------------------------------------------------
S_2:
MOV SP,0FFFEH ;Move stack to end of seg
;----------------------------------------------------------------------
; BLITZKEY requires that the video display be in a text mode to edit.
;----------------------------------------------------------------------
CALL VIDEO_SETUP ;Examine video hardware
MOV DX,OFFSET ERR_VID$ ;Assume an error
JC S_1A
;----------------------------------------------------------------------
; Draw the edit window.
;----------------------------------------------------------------------
MOV AL,[COL_END] ;Right edge of screen
SUB AL,2 ;(1 based) in one char
MOV [COL_MAX],AL ;Is rightmost column
CALL CLR_BOX ;Draw the window
;----------------------------------------------------------------------
; Invoke the string editor. Returns when F7 is pressed.
;----------------------------------------------------------------------
CALL EDIT ;String editor
MOV CX,[COM_PTR] ;Get program length
SUB CX,OFFSET STRINGS ; minus start of strings
MOV [STR_LEN],CX ; is string length
;----------------------------------------------------------------------
; Ask if changes should be written out to BLITZNEW.COM. If not, end.
;----------------------------------------------------------------------
S_3:
MOV DX,OFFSET SAVE$ ;Clone the changes?
CALL GETRESPONSE ;Yes or No
JNC S_1B
;----------------------------------------------------------------------
; Try to open the file to see if it exists.
;----------------------------------------------------------------------
MOV AX,3D02H ;Open file for r/w
MOV DX,OFFSET FILENAME ; This name
INT 21H ; thru DOS
JC S_5 ;Jump if not found
MOV BX,AX ;Move file's handle
MOV DX,OFFSET OVERWRITE$ ;Should we overwrite?
CALL GETRESPONSE
JC S_6 ;Yes
;----------------------------------------------------------------------
; Close the file handle in BX, and ask the question again. This gives
; the user the chance to save the file using some other utility.
;----------------------------------------------------------------------
S_4:
MOV AH,3EH ;Close file handle
INT 21H ; thru DOS
JMP S_3 ;Ask again
;----------------------------------------------------------------------
; File does not exist. Attempt to open as new.
;----------------------------------------------------------------------
S_5:
MOV AH,3CH ;Create file fn
SUB CX,CX ;For writing
MOV DX,OFFSET FILENAME ;This is name
INT 21H ; thru DOS
MOV BX,AX ;Save handle in BX
JNC S_6
;----------------------------------------------------------------------
; If a file error occurs, give the user a change to correct it.
;----------------------------------------------------------------------
MOV DX,OFFSET FERROR$ ;Error opening file
CALL GETRESPONSE ;Try again?
JC S_5
JMP S_3
;----------------------------------------------------------------------
; A valid file handle is in BX. Write away.
;----------------------------------------------------------------------
S_6:
MOV AH,40H ;Write to file fn
MOV CX,[COM_PTR] ;Length of file
MOV DX,100H ;Start here
SUB CX,DX ;Subtract PSP length
INT 21H ; thru DOS
JC S_7
CMP AX,CX ;EQ = All bytes written
JE S_8
;----------------------------------------------------------------------
; An error was encountered on the write.
;----------------------------------------------------------------------
S_7:
MOV DX,OFFSET WERROR$
CALL GETRESPONSE ;Try again?
JC S_6
JMP S_4
;----------------------------------------------------------------------
; File was written okay. Close and exit.
;----------------------------------------------------------------------
S_8:
MOV AH,3EH ;Close handle in BX
INT 21H ; thru DOS
JMP S_1B
SETUP ENDP
;======================================================================
; VIDEO_SETUP (Near)
;
; Determine all the paramters and info we need to handle the display.
;----------------------------------------------------------------------
; Entry: None
; Exit :
; CF = NC - video mode is okay
; CY - incompatible mode
;----------------------------------------------------------------------
; Changes: AX BX DX
;----------------------------------------------------------------------
VIDEO_SETUP PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
MOV AH,0FH ;Get video mode
INT 10H ; Thru BIOS
MOV [ATTR],CO_ATTR ;Assume color
CMP AL,3 ;CGA video modes okay
JBE VID_1
MOV [ATTR],BW_ATTR ;Force B/W
CMP AL,7 ;MDA text mode okay
JE VID_1
STC ;Else, an error
JMP SHORT VID_EXIT
;----------------------------------------------------------------------
; Save some video parameters.
;----------------------------------------------------------------------
VID_1:
MOV [COL_END],AH ;Save cols
MOV [VPAGE],BH ;Save current page
;----------------------------------------------------------------------
; Determine if an EGA/VGA adapter is installed.
;----------------------------------------------------------------------
MOV AH,12H ;EGA alternate select
MOV BL,10H ;Return EGA info
INT 10H ; thru BIOS
CMP BL,10H ;If BL unchanged
MOV DL,24 ;Set default rows
JE VID_2 ; there's no EGA/VGA
;----------------------------------------------------------------------
; Find the row count.
;----------------------------------------------------------------------
PUSH ES ;Changed by call
MOV AX,1130H ;EGA info call
SUB BH,BH ;Dummy argument
INT 10H ; thru BIOS
ASSUME ES:NOTHING
POP ES
ASSUME ES:NOTHING
;----------------------------------------------------------------------
;
;----------------------------------------------------------------------
VID_2:
MOV [ROW_END],DL ;Save rows
CLC
VID_EXIT:
RET
VIDEO_SETUP ENDP
;======================================================================
; CLR_BOX (Near)
;
; Clear the screen, the draw a window on the screen.
;----------------------------------------------------------------------
; Entry: None
; Exit : None
;----------------------------------------------------------------------
; Changes: AX BX CX DX SI
;----------------------------------------------------------------------
INSET$ DB 0B5H,"BlitzKey 1.00",0C6H,0
INSET_LEN EQU $-INSET$
HELP$ DB "STRING: ",27,32,26," INS DEL ",24,32,25
DB " MACRO: PgUp PgDn F7 = Save",0
HELP_LEN EQU $-HELP$
ALT$ DB "HOTKEY = ALT+",0
TITLE$ DB "TITLE:",0
MACRO$ DB "MACRO:",0
BOX_CHARS DB 201,205,187
DB 186, 32,186
DB 199,196,182
DB 200,205,188
NROW EQU 9
;----------------------------------------------------------------------
CLR_BOX PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; Clear the entire screen and set the colors.
;----------------------------------------------------------------------
MOV AX,0700H ;Scroll window fn
MOV BH,[ATTR] ;Clear to this color
SUB CX,CX ;Starting row & col
MOV DH,[ROW_END] ;Ending row
MOV DL,[COL_END] ;Ending column
DEC DL ; (0-based)
INT 10H ; thru BIOS
MOV BH,[VPAGE] ;Get active page
MOV SI,OFFSET BOX_CHARS ;Draw the edit window
MOV DX,CX ;Cursor from last call
MOV CX,NROW ;Number rows to draw
;----------------------------------------------------------------------
; Construct the window on the screen.
;----------------------------------------------------------------------
CB_1A:
PUSH CX ;Save row counter
MOV AH,2 ;Position cursor
SUB DL,DL ;To column 0
INT 10H ; thru BIOS
LODSB ;Get leftmost char
MOV AH,0EH ;Write char TTY
INT 10H ; thru BIOS
LODSB ;Get middle char
MOV AH,0AH ;Write repeated char
MOV CL,[COL_END] ;Width of box
SUB CH,CH
SUB CX,2 ; minus 2 sides
INT 10H ; thru BIOS
MOV AH,2 ;Position cursor
SUB DL,DL
ADD DL,[COL_END]
DEC DL ;Col = righthand edge
INT 10H ; thru BIOS
LODSB ;Get rightmost char
MOV AH,0AH ;Write repeated char
MOV CX,1 ; 1 copy
INT 10H ; thru BIOS
INC DH ;Next row
POP CX ;Restore counter
CMP CL,NROW ;Examine row we wrote
JE CB_1C ;If first row
CMP CL,2 ;or next to last
JNE CB_1B ;Don't adjust count
ADD SI,3
CB_1B:
TEST CL,1 ;If row is even
JZ CB_1C ;Don't adjust count
SUB SI,6
CB_1C:
LOOP CB_1A
;----------------------------------------------------------------------
; Fill in the title, prompt, and help lines.
;----------------------------------------------------------------------
SUB AH,AH ;Top row
MOV AL,[COL_MAX] ;Rightmost column
SUB AL,(INSET_LEN+5) ;Backup
MOV [CUR_POS],AX ; to here
MOV SI,OFFSET INSET$ ;Program name
CALL CB_2A ;Write to screen
MOV AH,7
MOV AL,(79-HELP_LEN)/2
MOV [CUR_POS],AX
MOV SI,OFFSET HELP$ ;Instructions
CALL CB_2A ;Write to screen
MOV WORD PTR [CUR_POS],0101H
MOV SI,OFFSET ALT$ ;Macro key combo
CALL CB_2A
MOV WORD PTR [CUR_POS],0301H
MOV SI,OFFSET TITLE$ ;Macro title
CALL CB_2A ;Write to screen
MOV WORD PTR [CUR_POS],0501H
MOV SI,OFFSET MACRO$ ;Macro text
CB_2A:
CALL CUR_SET ;Position cursor
CB_2B:
MOV BH,[VPAGE] ;Use active page
LODSB ;Get a char
OR AL,AL ;If zero
JZ CB_2C ; quit
MOV AH,0EH ;Else, write TTY
INT 10H ; Thru BIOS
JMP CB_2B ;Continue
CB_2C:
RET
CLR_BOX ENDP
;======================================================================
; CUR_SET (Near)
;
; Position the cursor to the stored values.
;----------------------------------------------------------------------
; Entry: None
; Exit : None
;----------------------------------------------------------------------
; Changes: BX DX
;----------------------------------------------------------------------
CUR_SET PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
PUSH AX ;Save used register
MOV AH,2 ;Position cursor fn
MOV BH,[VPAGE] ; current page
MOV DX,[CUR_POS] ; new cursor position
INT 10H ; Thru BIOS
POP AX ;Restore register
RET
CUR_SET ENDP
;======================================================================
; EDIT (Near)
;
; The EDIT procedure handles all the editing. It keeps track of the
; current macro strings and displays them on the screen as they change.
;----------------------------------------------------------------------
; Entry: None
; Exit : None
;----------------------------------------------------------------------
; Changes: AX BX CX
;----------------------------------------------------------------------
; The PTR offset points to the character that appears at the left side
; of the window
;----------------------------------------------------------------------
PTR_ARRAY LABEL WORD ;Indicates the starting
NAM_PTR DW 0 ; offset of the current
STR_PTR DW 0 ; macro name and string
ACTIVE DW 0 ;Index for array
CUR_POS LABEL WORD
CUR_COL DB 0 ;Current cursor
CUR_ROW DB 0 ; position
MACRO_PTR DB 0 ;Pointer to string set
INS_STATE DB 0 ;0=INS FF=TYPEOVER
;----------------------------------------------------------------------
EDIT PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
SUB BP,BP ;Create zero word
MOV BYTE PTR [MACRO_PTR],0 ;Choose first string
;----------------------------------------------------------------------
; Display the hotkey for this macro.
;----------------------------------------------------------------------
E_1:
MOV WORD PTR [CUR_POS],010EH ;Move past prompt
CALL CUR_SET
MOV AH,0AH ;Write character fn
MOV AL,[MACRO_PTR] ;Number of macro
ADD AL,"A" ;Convert to letter
MOV CX,1 ;Write 1 copy
INT 10H ; thru BIOS
;----------------------------------------------------------------------
; Initialize the pointers to point to the first macro set.
;----------------------------------------------------------------------
MOV BL,[MACRO_PTR] ;String # to look for
ADD BL,BL ;They come in pairs
MOV SI,OFFSET STRINGS ;DS:SI -> strings
CALL GET_POINTER ;Load pointer
MOV [NAM_PTR],SI ;Save offset
INC BL ;Next string
MOV SI,OFFSET STRINGS ;DS:SI -> strings
CALL GET_POINTER ;Load pointer
MOV [STR_PTR],SI ;Save offset
;----------------------------------------------------------------------
; Display the selected strings on the screen as read from memory.
;----------------------------------------------------------------------
MOV [ACTIVE],BP ;Change active index
MOV BYTE PTR [CUR_COL],7 ;Leftmost column
MOV BYTE PTR [CUR_ROW],3 ;Save new row
CALL DISPLAYX ;Show the string
MOV BX,2 ;Index for Macro
E_2A:
MOV [ACTIVE],BX ;Change active index
MOV BYTE PTR [CUR_COL],7 ;Leftmost column
MOV DH,3 ;Row for name
OR BX,BX ;If BX=0, we're done
JZ E_2B
ADD DH,BL ;Else row for string
E_2B:
MOV [CUR_ROW],DH ;Save new row
CALL DISPLAYX ;Show the string
;----------------------------------------------------------------------
; Get a key from the keyboard and act on it.
;----------------------------------------------------------------------
E_3:
SUB AH,AH ;Fetch the key
INT 16H ; Thru BIOS
OR AL,AL ;0=extended=command
JZ E_7
CMP AX,BS ;Actual BS key?
JNE E_5A
OR AH,AH ;If zero = char
JZ E_5A
;----------------------------------------------------------------------
; The backspace key is the only key that requires special handling.
; Treat BS as a CURSOR-LEFT/DELETE combination.
;----------------------------------------------------------------------
CALL CURSOR_LEFT ;Move cursor left
JC E_3
E_4:
CALL STRING_DEL ;Delete char at cursor
JC E_3
CALL DISPLAYX ;Display the results
;----------------------------------------------------------------------
; If the char was deleted from the TITLE string, the macro string moved
; also. Adjust the pointer.
;----------------------------------------------------------------------
CMP [ACTIVE],BP ;If TITLE is active
JNE E_3
SUB WORD PTR [PTR_ARRAY][2],2 ;Back up MACRO ptr
JMP E_3
;----------------------------------------------------------------------
; Normal chars are placed on the screen and in the string.
;----------------------------------------------------------------------
E_5A:
CMP BYTE PTR [INS_STATE],0 ;If insert state
JE E_5B
;----------------------------------------------------------------------
; If at end of string, typeover works just like insert.
; Fall through to the cursor-right routine.
;----------------------------------------------------------------------
CALL LOCATE_SI ;If current char not 0
CMP [SI],BP ; just overwrite
JNZ E_5C
E_5B:
CALL STRING_INS ;Create hole at cursor
CMP [ACTIVE],BP ;If inserting TITLE...
JNE E_5C
ADD WORD PTR [PTR_ARRAY][2],2 ;...advance MACRO
E_5C:
CALL LOCATE_SI ;Point to cursor location
MOV [SI],AX ; and pop in char
CALL DISPLAYX ;Show changes
;----------------------------------------------------------------------
; -> Move the cursor to the right one space.
;----------------------------------------------------------------------
E_6:
CALL CURSOR_RIGHT ;Move cursor along
JMP E_3
;----------------------------------------------------------------------
; Key is an extended key. Must be a command.
;----------------------------------------------------------------------
E_7:
CMP AH,F7KEY ;F7 is the exit key
JNE E_8
MOV BYTE PTR [CUR_COL],0 ;Reposition cursor
MOV BYTE PTR [CUR_ROW],NROW ; for message
CALL CUR_SET
;----------------------------------------------------------------------
; Exit the edit procedure.
;----------------------------------------------------------------------
RET ;The only way out
;----------------------------------------------------------------------
; All remaining key dispatch is performed from here.
;----------------------------------------------------------------------
E_8:
MOV BL,[MACRO_PTR] ;Number of macro set
;----------------------------------------------------------------------
; Delete kills the char at the cursor.
;----------------------------------------------------------------------
CMP AH,DEL ;Kill char at cursor
JE E_4
;----------------------------------------------------------------------
; PgUp and PgDn move between macro sets.
;----------------------------------------------------------------------
CMP AH,PGUP ;Check for PgUp
JE E_9A ; else check next
CMP AH,PGDN ;Move to next macro
JE E_9C
;----------------------------------------------------------------------
; The function of the arrow keys depend on the active string.
;----------------------------------------------------------------------
MOV BX,[ACTIVE] ;Get active index
CMP AH,RARROW ;Move right 1 char
JE E_6
CMP AH,LARROW ;Move left 1 char
JE E_10A
CMP AH,UARROW ;Move to NAME field
JE E_11
CMP AH,DARROW ;Move to MACRO field
JE E_12
;----------------------------------------------------------------------
; INS toggles the insert state.
;----------------------------------------------------------------------
CMP AH,INSK ;Use Insert mode
JE E_13
;----------------------------------------------------------------------
; HOME and END perform rapid cursor movement.
;----------------------------------------------------------------------
CMP AH,ENDKEY ;Move to end of string
JE E_14
CMP AH,HOME ;Move to start of string
JE E_15
JMP E_3 ;Didn't recongnize it
;----------------------------------------------------------------------
; PgUp key: Move to the previous macro.
;----------------------------------------------------------------------
E_9A:
DEC BL ;Back up active pointer
JNS E_9B
MOV BL,25 ;If past end, reset
E_9B:
MOV [MACRO_PTR],BL ;Update pointer
JMP E_1 ;Start over
;----------------------------------------------------------------------
; PgDn key: Move to the next macro.
;----------------------------------------------------------------------
E_9C:
INC BL ;Go forward
CMP BL,25 ;If past end
JBE E_9B
SUB BL,BL ;Reset
JMP E_9B
;----------------------------------------------------------------------
; <- Move the cursor to the left one space. If this fails, we just
; ignore it.
;----------------------------------------------------------------------
E_10A:
CALL CURSOR_LEFT ;Move cursor left
E_10B:
JMP E_3
;----------------------------------------------------------------------
; ^ Move to the NAME field.
;----------------------------------------------------------------------
E_11:
OR BX,BX ;Skip if already there
JZ E_10B
SUB BX,BX ;Else, switch
JMP E_2A
;----------------------------------------------------------------------
; v Move to the STRING field.
;----------------------------------------------------------------------
E_12:
OR BX,BX ;Skip if already there
JNZ E_10B
INC BX ;Move index to
INC BX ; second pointer
JMP E_2A
;----------------------------------------------------------------------
; Toggle the insert/typeover state.
;----------------------------------------------------------------------
E_13:
NOT BYTE PTR [INS_STATE] ;Toggle the flag
JMP E_3
;----------------------------------------------------------------------
; Move to end of string by repeatedly calling cursor right.
;----------------------------------------------------------------------
E_14:
CALL CURSOR_RIGHT ;Move to the right
JNC E_14 ; as long as successful
JMP E_3
;----------------------------------------------------------------------
; Move to start of string by repeatedly calling cursor left.
;----------------------------------------------------------------------
E_15:
CALL CURSOR_LEFT ;Move to the left
JNC E_15 ; as long as successful
JMP E_3
EDIT ENDP
;======================================================================
; DISPLAYX (Near)
;
; Write the active string to the screen from the current cursor
; position forward. Called only when a char is typed or the window is
; pushed.
;----------------------------------------------------------------------
; Entry: None
; Exit : None
;----------------------------------------------------------------------
; Changes: AX BX CX SI
;----------------------------------------------------------------------
DISPLAYX PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
CALL CUR_SET ;Position the cursor
CALL LOCATE_SI ;Point SI to string
MOV CH,[CUR_COL] ;From current cursor
MOV CL,[COL_MAX] ; to rightmost column
;----------------------------------------------------------------------
; Display each character until the end of the visible window.
;----------------------------------------------------------------------
D_0:
LODSW ;Get character
OR AX,AX ;0=end of string
JNZ D_1
;----------------------------------------------------------------------
; If we've reached the end of the string, just display blanks until the
; window is filled.
;----------------------------------------------------------------------
DEC SI ;Back up to the zero
DEC SI
MOV AL,BLANK ;Display a blank
;----------------------------------------------------------------------
; Display the char in AL. Use Fn 0Ah to print control characters.
;----------------------------------------------------------------------
D_1:
CALL CUR_SET ;Position the cursor
PUSH CX ;Save register
MOV AH,0AH ;Write Repeated Char
MOV BH,[VPAGE] ;Active page
MOV CX,1 ;# copies to write
INT 10H ; thru BIOS
POP CX ;Restore register
INC BYTE PTR [CUR_COL] ;Change position
CMP CL,[CUR_COL] ;Is col <= end?
JAE D_0
;----------------------------------------------------------------------
; Past the end of the window - done with display.
;----------------------------------------------------------------------
MOV [CUR_COL],CH ;Return to old spot
CALL CUR_SET ; do it
RET
DISPLAYX ENDP
;======================================================================
; LOCATE_SI (Near)
;
; Point SI to the same char in the string that is currently above the
; cursor on the screen.
;----------------------------------------------------------------------
; Entry: None
; Exit:
; SI = offset of char currently above the cursor
;----------------------------------------------------------------------
; Changes: BX CX SI
;----------------------------------------------------------------------
LOCATE_SI PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; Point SI to the offset of the first visible character.
;----------------------------------------------------------------------
MOV BX,[ACTIVE] ;Get active index
MOV SI,[PTR_ARRAY][BX] ;Get start of string
;----------------------------------------------------------------------
; Adjust SI forward based on the current cursor position.
;----------------------------------------------------------------------
MOV CL,[CUR_COL] ;Current column
SUB CL,7 ; - leftmost
SUB CH,CH ;Make into word
ADD CX,CX ;Double offset
ADD SI,CX ;Add to pointer
RET
LOCATE_SI ENDP
;======================================================================
; CURSOR_RIGHT (Near, nested)
;
; Move the cursor right 1 char.
;----------------------------------------------------------------------
; Entry:
; BX = String index into pointer array
; Exit:
; CF = NC - cursor was moved
; CY - cursor could not be moved
;----------------------------------------------------------------------
; Changes: CX SI
;----------------------------------------------------------------------
CURSOR_RIGHT PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; If already on last char, return failure.
;----------------------------------------------------------------------
CALL LOCATE_SI ;Get pointers
CMP WORD PTR [SI],0 ;0=last char of string
JNE CR_1
CR_A:
STC ;Signal failure
JMP SHORT CR_EXIT
;----------------------------------------------------------------------
; Common exit.
;----------------------------------------------------------------------
CR_0:
CLC ;Signal success
CR_EXIT:
RET
;----------------------------------------------------------------------
; Move the cursor within the visible window.
;----------------------------------------------------------------------
CR_1:
MOV CL,[CUR_COL] ;Get current column
CMP CL,[COL_MAX] ;At screen edge?
JE CR_3
INC CL ;Move to next col
CR_2:
MOV [CUR_COL],CL ;Save column
CALL CUR_SET ;Move cursor
JMP CR_0
;----------------------------------------------------------------------
; This cursor movement is pushing the visible window.
;----------------------------------------------------------------------
CR_3:
ADD WORD PTR [PTR_ARRAY][BX],2 ;Move the start
PUSH WORD PTR [CUR_POS] ;Save current cursor
MOV BYTE PTR [CUR_COL],7 ;Redisplay from left side
CALL CUR_SET ;Set cursor
CALL DISPLAYX ;Draw string
POP WORD PTR [CUR_POS] ;Reset old cursor
CALL CUR_SET
JMP CR_0
;======================================================================
; CURSOR_LEFT (Near, nested)
;
; Move the cursor left 1 char.
;----------------------------------------------------------------------
; Entry:
; BX = String index into pointer array
; Exit:
; CF = NC - cursor was moved
; CY - cursor could not be moved
;----------------------------------------------------------------------
; Changes: CX SI
;----------------------------------------------------------------------
CURSOR_LEFT PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; If not in first column, simply move cursor within window.
;----------------------------------------------------------------------
MOV CL,[CUR_COL] ;Get cursor position
CMP CL,7 ;At 1st column?
JE CL_1
DEC CL ;Back up cursor
JMP CR_2
;----------------------------------------------------------------------
; Push the window.
;----------------------------------------------------------------------
CL_1:
MOV SI,[PTR_ARRAY][BX] ;Start of window
DEC SI ;Back one char
DEC SI
CMP WORD PTR [SI],0 ;Stop if past start
JE CR_A
MOV [PTR_ARRAY][BX],SI ;Update the pointer
CALL DISPLAYX ;Display the string
JMP CR_0
CURSOR_LEFT ENDP
CURSOR_RIGHT ENDP
;======================================================================
; STRING_DEL (Near)
;
; Delete the char at the cursor and close up the string.
;----------------------------------------------------------------------
; Entry: None
; Exit :
; CF = NC - char deleted successfully
; CY - char could not be deleted
;----------------------------------------------------------------------
; Changes: CX SI DI
;----------------------------------------------------------------------
STRING_DEL PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
CALL LOCATE_SI ;Point to current char
CMP WORD PTR [SI],0 ;Can't backup too far
JNZ SD_1
STC ;Return failure
SD_EXIT:
RET
;----------------------------------------------------------------------
; Delete the char and adjust the COM file length.
;----------------------------------------------------------------------
SD_1:
MOV CX,[COM_PTR] ;End of strings offset
SUB CX,SI ;# bytes to move is 2
DEC CX ; less than length
DEC CX
MOV DI,SI ;Dest is DI
INC SI ;Src is previous
INC SI ; word
REP MOVSB ;Move the strings
SUB WORD PTR [COM_PTR],2 ;File gets shorter
CLC ;Say succcess
JMP SD_EXIT
STRING_DEL ENDP
;======================================================================
; STRING_INS (Near)
;
; Create a hole in the string by moving everything to the right.
;----------------------------------------------------------------------
; Entry: None
; Exit : None
;----------------------------------------------------------------------
; Changes: CX SI DI
;----------------------------------------------------------------------
STRING_INS PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
CALL LOCATE_SI ;SI = current word
MOV CX,[COM_PTR] ;End of strings offset
MOV DI,CX ; also target for move
SUB CX,SI ;Bytes to move
MOV SI,DI ;Copy to src register
DEC SI ;Copy from prev word
DEC SI
STD ;Move backwards
REP MOVSB ; whole string
CLD ;Strings forward again
ADD WORD PTR [COM_PTR],2 ;File is longer
RET
STRING_INS ENDP
;======================================================================
; GETRESPONSE (Near)
;
; Accept only a Y or N answer from the console.
;----------------------------------------------------------------------
; Entry:
; DX = offset of $-terminated prompt to display
; Exit:
; CF = CY if answer was yes
; NC if answer was no
;----------------------------------------------------------------------
; CHANGES: AX
;----------------------------------------------------------------------
GETRESPONSE PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
MOV AH,9 ;Display string fn
INT 21H ; thru DOS
GETR_0:
SUB AH,AH ;Wait for key
INT 16H ; thru BIOS
AND AL,NOT 20H ;Capitalize
CMP AL,"N" ;Was it N?
JNE GETR_1
;----------------------------------------------------------------------
; Note that if the comparison was equal, CF is off.
;----------------------------------------------------------------------
GETR_EXIT:
RET ; just end
GETR_1:
CMP AL,"Y" ;If not YES,
JNE GETR_0 ; try again
STC ;Carry on
JMP GETR_EXIT
GETRESPONSE ENDP
;======================================================================
; REPLACE (Near)
;
; The program has been loaded previously, and is already resident.
; Just replace the old strings with the new strings.
;----------------------------------------------------------------------
; Entry: None
; Exit : Doesn't Return
;----------------------------------------------------------------------
; Changes: n/a
;----------------------------------------------------------------------
BADREPLACE$ DB CR,LF,"BlitzKey Failed. Suggest Reboot.",CR,LF,"$"
NEWSEGLEN DW 0
REPLACE PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; Get the PSP of the resident program. Save in BP for easy access.
;----------------------------------------------------------------------
MOV BX,[RES_SEG] ;Get resident PSP
MOV BP,BX ;Save in BP
MOV ES,BX ; and load into ES
ASSUME ES:NOTHING
;----------------------------------------------------------------------
; Make the PSP of the resident segment (in BX) the active PSP for
; subsequent memory management calls.
;----------------------------------------------------------------------
MOV AH,50H ;Set active PSP
INT 21H ; thru DOS
;----------------------------------------------------------------------
; Turn off any current macro expansion and prevent more from starting.
;----------------------------------------------------------------------
MOV BYTE PTR ES:[EXPANDING],0 ;Expander off
MOV BYTE PTR ES:[DISABLED],-1 ;Disable macros
;----------------------------------------------------------------------
; Compare the resident string segment (as recorded in the pointer
; located in the resident copy) to the resident code segment.
;----------------------------------------------------------------------
MOV AX,ES:[STR_LOC][2] ;Get string segment
CMP AX,BX ;Same as old prog seg?
JE R_1
;----------------------------------------------------------------------
; If the segments don't match, the strings are in a separate block, so
; we can simply release the entire string block.
;----------------------------------------------------------------------
MOV ES,AX ;Point ES to segment
ASSUME ES:NOTHING
MOV AH,49H ;Free allocated memory
INT 21H ; thru DOS
JNC R_2
JMP SHORT R_4
;----------------------------------------------------------------------
; The strings are still attached to the original BLITZKEY.COM file.
; (This occurs only on the first load.) Shrink the old code segment
; to hold just the program code. The current PSP must be set to the
; owner of this block (the resident copy) before making this call.
;----------------------------------------------------------------------
R_1:
MOV AH,4AH ;Modify block size
MOV BX,(OFFSET CUTOFF - CSEG + 15) SHR 4
INT 21H ; thru DOS
JC R_4
;----------------------------------------------------------------------
; Try to locate a block in lower memory that is large enough to contain
; the strings. We want the allocated block must belong to the resident
; copy. It must be the active PSP when this call is made.
;----------------------------------------------------------------------
R_2:
CALL FIND_LOW ;Look for memory block
ASSUME ES:NOTHING
JC R_3 ;Jump if not found
;----------------------------------------------------------------------
; Room was found. The new segment was returned in AX. Copy the strings
; from this copy of the program to the new block. MOVE_STRINGS
; updates the pointers in the resident copy.
;----------------------------------------------------------------------
MOV ES,BP ;Point to res copy
ASSUME ES:NOTHING
CALL MOVE_STRINGS ;Copy strings to block
ASSUME ES:NOTHING
;----------------------------------------------------------------------
; Before terminating, we must set the active PSP back to this copy of
; the program.
;----------------------------------------------------------------------
MOV AH,50H ;Set active PSP
MOV BX,CS ; to this program
INT 21H ; thru DOS
;----------------------------------------------------------------------
; Enable macro expansion and terminate.
;----------------------------------------------------------------------
MOV ES,BP ;Point to res copy
ASSUME ES:NOTHING
MOV BYTE PTR ES:[DISABLED],0 ;Enable macros
R_EXIT:
MOV AH,9 ;Display string
MOV DX,OFFSET COPYRIGHT$ ;Say who we are
INT 21H ; thru DOS
MOV AH,4CH ;All done! Terminate.
INT 21H ; thru DOS
;----------------------------------------------------------------------
; There is no room in low memory, but we still want to relocate these
; string to the lowest possible address. We currently own all memory.
; Shrink this PSP block to hold just the program, strings, and stack.
;----------------------------------------------------------------------
ASSUME ES:NOTHING
R_3:
MOV AH,50H ;Set active PSP
MOV BX,CS ; back to us
INT 21H ; thru DOS
MOV AH,4AH ;Modify block size
MOV BX,[STACK_TOP] ; to hold prog+stack
ADD BX,15 ;Round up
MOV CL,4
SHR BX,CL ;Convert to paras
MOV [NEWSEGLEN],BX ;Save this size
PUSH CS ;Put seg to modify (CS)
POP ES ; into ES
ASSUME ES:NOTHING
INT 21H ; thru DOS
JNC R_5
;----------------------------------------------------------------------
; A memory error occurred. Display a message and exit.
;----------------------------------------------------------------------
R_4:
MOV DX,OFFSET BADREPLACE$ ;Indicate an error
MOV AH,9 ;Display string
INT 21H ; thru DOS
JMP R_EXIT
;----------------------------------------------------------------------
; Allocate all memory above us as a single block. If there's not
; enough to hold a new copy of this program, terminate.
;----------------------------------------------------------------------
R_5:
MOV AH,48H ;Allocate memory
MOV BX,0FFFFH ;Ask for 640K
INT 21H ; thru DOS
CMP BX,[NEWSEGLEN] ;Enough mem available?
JB R_4
MOV AH,48H ;Allocate BX paras
INT 21H ; thru DOS
JC R_4
;----------------------------------------------------------------------
; The segment of the new block is returned in AX. Duplicate the program
; at the new address. Copy from DS:SI to ES:DI.
;----------------------------------------------------------------------
SUB SI,SI ;SI = 0
MOV DI,SI ;DI = 0
MOV ES,AX ;New block segment
ASSUME ES:NOTHING
MOV CX,[STACK_TOP] ;Bytes to move
REP MOVSB ;Copy to new address
;----------------------------------------------------------------------
; Now, hop up to our new home by using a far return to change segments.
;----------------------------------------------------------------------
PUSH AX ;Put new CS on stack
MOV DX,OFFSET TARGET ;And address of the
PUSH DX ; next instruction
DB RETFAR ;Jump to new segment
;----------------------------------------------------------------------
; Now we're at AX:TARGET, in the new copy of the program. Note that
; although the segment is not really CSEG, it seems that way to the
; assembler for the purpose of calculating offsets.
; CS = new CSEG
; DS = old CSEG
; ES = nothing
; SS = old CSEG
; Move the stack to the new copy.
;----------------------------------------------------------------------
ASSUME CS:CSEG
TARGET:
CLI ;Disable interrupts
MOV SS,AX ;Change segment
ASSUME SS:CSEG
MOV SP,[STACK_TOP] ; and offset
STI ;Enable interrupts
;----------------------------------------------------------------------
; Release the memory held by the old copy of the program. Then point
; DS to this segment.
;----------------------------------------------------------------------
PUSH DS ;Put old PSP segment
POP ES ; into ES
ASSUME ES:NOTHING
MOV DS,AX ;Point DS to this seg
ASSUME DS:CSEG
MOV AH,49H ;Free block in ES
INT 21H ; thru DOS
JC R_4
;----------------------------------------------------------------------
; Now allocate a block for the strings. The block must belong to the
; resident copy, so make it the active process.
;
; The block we released was big enough to hold the entire program. The
; allocation call must now succeed. The new segment is returned in AX.
;----------------------------------------------------------------------
MOV AH,50H ;Set active PSP
MOV BX,BP ; to resident copy
INT 21H ; thru DOS
CALL FIND_LOW ;Allocate block
ASSUME ES:NOTHING
MOV ES,BP ;Point ES to res PSP
ASSUME ES:NOTHING
;----------------------------------------------------------------------
; Copy the strings from this copy to the new block. MOVE_STRINGS
; updates the pointers in the resident copy.
;----------------------------------------------------------------------
CALL MOVE_STRINGS ;Copy strings to block
ASSUME ES:NOTHING
;----------------------------------------------------------------------
; Release the current PSP segment.
;----------------------------------------------------------------------
PUSH CS ;Point ES to the
POP ES ; current segment
ASSUME ES:NOTHING
MOV AH,49H ;Release block
INT 21H ; thru DOS
JC R_4
;----------------------------------------------------------------------
; Re-enable macro expansion.
;----------------------------------------------------------------------
MOV ES,BP ;Point to res copy
ASSUME ES:NOTHING
MOV BYTE PTR ES:[DISABLED],0 ;Enable macros
;----------------------------------------------------------------------
; Now terminate by using the TSR call with the already resident PSP.
; If we don't, DOS tries to access this program's original PSP to close
; the file handles and crashes.
;----------------------------------------------------------------------
MOV AH,31H ;Keep TSR seg
MOV BX,(OFFSET CUTOFF-CSEG+15) SHR 4 ;code length
INT 21H ; thru DOS
REPLACE ENDP
;======================================================================
; LOAD (Near)
;
; This procedure will cause the load copy of the program to become
; resident. The new copy will try to locate the strings as low in
; memory as possible. Hook the interrupt vectors, load the strings,
; and TSR.
;----------------------------------------------------------------------
; Entry: None
; Exit : Doesn't Return
;----------------------------------------------------------------------
; Changes: n/a
;----------------------------------------------------------------------
LOAD PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; Display our copyright notice.
;----------------------------------------------------------------------
MOV AH,9 ;Display string
MOV DX,OFFSET COPYRIGHT$ ;Say who we are
INT 21H ; thru DOS
;----------------------------------------------------------------------
; Hook into the interrupt chains for Int 9 and Int 16h.
;----------------------------------------------------------------------
MOV AX,3509H ;Get Int 9 vector
INT 21H ; thru DOS
ASSUME ES:NOTHING
MOV WORD PTR [OLD9][0],BX ;Save in resident
MOV WORD PTR [OLD9][2],ES ; portion
MOV AX,2509H ;Set vector for 9
MOV DX,OFFSET INT_9 ;Point it here
INT 21H ; thru DOS
MOV AX,3516H ;Get Int 16h vector
INT 21H ; thru DOS
ASSUME ES:NOTHING
MOV WORD PTR [OLD16][0],BX ;Save in resident
MOV WORD PTR [OLD16][2],ES ; portion
MOV AX,2516H ;Set vector for 16h
MOV DX,OFFSET INT_16 ;Point it here
INT 21H ; thru DOS
PUSH CS ;Point ES back to
POP ES ; this seg
ASSUME ES:CSEG
;----------------------------------------------------------------------
; As loaded, BLITZKEY owns all memory from its PSP to the end of
; conventional memory. If UMBs are not linked, the only memory an
; allocation call will find will be below us. Try to find a low memory
; block to contain the strings. If success, AX contains segment of
; allocated block. If no room is found, discard excess code and
; relocate strings downward.
;----------------------------------------------------------------------
CALL FIND_LOW ;Look for lower block
JNC L_1 ;No Carry if found
;----------------------------------------------------------------------
; No memory was found. Relocate the strings downward in the segment
; until they appear just after the last resident procedure.
;----------------------------------------------------------------------
MOV SI,OFFSET STRINGS ;Source of strings
MOV DI,OFFSET CUTOFF ;Destination
MOV CX,[STR_LEN] ;Number bytes to move
MOV STR_LOC[2],ES ;New segment of strings
MOV STR_LOC[0],DI ;New offset
MOV DX,CX ;Length of strings
ADD DX,DI ; plus program length
ADD DX,15 ;Round up
PUSH CX ;(save count for REP)
MOV CL,4
SHR DX,CL ;Convert to paras
POP CX ;(restore count)
JMP SHRIVEL
;----------------------------------------------------------------------
; A chunk of memory of suitable size was found below this program at
; segment in AX. Relocate the strings to the new area. DS:SI to AX:DI
; Then TSR,leaving only the macro expander resident in this segment.
;----------------------------------------------------------------------
L_1:
CALL MOVE_STRINGS ;Move the strings
ASSUME ES:NOTHING
MOV DX,(OFFSET CUTOFF - CSEG + 15) SHR 4
;----------------------------------------------------------------------
; This copy is going to become resident. Modify RES_MARKER.
;----------------------------------------------------------------------
L_2:
NOT WORD PTR [RES_MARKER] ;Modify for matching
MOV AH,31H ;Keep process resident
INT 21H ; thru DOS
LOAD ENDP
;======================================================================
; FIND_LOW (Near)
;
; Look for a piece of memory large enough to hold DS:STR_LEN bytes.
; Will find low memory in standard systems or use high memory if
; it was linked when program was started.
;----------------------------------------------------------------------
; Entry: None
; Exit :
; CF = NC - Memory block found
; AX = segment of block
;
; CF = CY - Memory block not found
;----------------------------------------------------------------------
; Changes: AX BX CX
;----------------------------------------------------------------------
FIND_LOW PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
MOV AH,48H ;Allocate memory
MOV BX,[STR_LEN] ;Change length in bytes
ADD BX,15
MOV CL,4
SHR BX,CL ; to paras
INT 21H ; thru DOS
RET
FIND_LOW ENDP
;======================================================================
; MOVE_STRINGS (Near)
;
; Copies the string block from the current copy (located at
; DS:[STRINGS]) to AX:0. The string pointer and block length is updated
; in the resident copy.
;----------------------------------------------------------------------
; Entry:
; DS = segment of current program copy
; ES = segment of resident program copy
; AX = destination segment for string copy
; Exit:
; None
;----------------------------------------------------------------------
; Changes: CX SI DI ES
;----------------------------------------------------------------------
MOVE_STRINGS PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
CLD ;String moves forward
;----------------------------------------------------------------------
; Save the string length of the current strings in the resident copy.
;----------------------------------------------------------------------
MOV CX,[STR_LEN] ;Bytes to move
MOV ES:[STR_LEN],CX ;Update resident copy
;----------------------------------------------------------------------
; Copy the strings into the indicated block.
;----------------------------------------------------------------------
SUB DI,DI ;Copy to offset 0
MOV ES:[STR_LOC][0],DI ;New offset
MOV ES:[STR_LOC][2],AX ;New segment
MOV ES,AX ;Destination is ES:DI
ASSUME ES:NOTHING
MOV SI,OFFSET STRINGS ;Source is DS:SI
REP MOVSB ;Move 'em
RET
MOVE_STRINGS ENDP
;======================================================================
; The strings are stored here, after the program code. The macro titles
; are stored in ASCIIZ form. The macros are stored as words.
; During execution, the strings can be anywhere in memory.
;----------------------------------------------------------------------
DW 0 ;Prevents backing up too far
;Used when editing ONLY
STRINGS DW 26 DUP(0,0) ;Strings start empty
DW 0 ;End of string block
STRING_END EQU $
CSEG ENDS
END ENTPT