;====================================================================== ; 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