;====================================================================== ; ENVEDIT 1.00 * Copyright (c) 1992, Robert L. Hummel ; PC Magazine Assembly Language Lab Notes ; ; ENVEDIT -- an editor that allows changes to be made to the DOS master ; environment interactively. ;====================================================================== CSEG SEGMENT PARA PUBLIC 'CODE' ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG ORG 100H ;Starting offset ENTPT: JMP MAIN ;Jump over data ;---------------------------------------------------------------------- ; Some common equates. ;---------------------------------------------------------------------- CR EQU 13 ;Common equates LF EQU 10 INSKEY EQU 52H ;Extended ASCII values DEL EQU 53H F7KEY EQU 41H F9KEY EQU 43H HOME EQU 47H ENDKEY EQU 4FH RARROW EQU 4DH LARROW EQU 4BH UARROW EQU 48H DARROW EQU 50H BS EQU 0E08H ;Scan/Ascii code ;---------------------------------------------------------------------- ; Text messages. ;---------------------------------------------------------------------- COPYRIGHT$ DB CR,"ENVEDIT 1.00 ",254," Copyright (c) 1992," DB " Robert L. Hummel",CR,LF DB "PC Magazine Assembly Language Lab Notes" DB CR,LF,"$" NOENV$ DB "Can't Find The Environment",LF,"$" MEMSIZ$ DB "Not Enough Memory",LF,"$" MEMERR$ DB "Error Allocating Memory",LF,"$" BADMODE$ DB "Can't Use This Video Mode",LF,"$" SAVE$ DB "Save Changes? (Y/N) $" ;---------------------------------------------------------------------- ; Video parameters. ;---------------------------------------------------------------------- NUM_COLS DB 0 ;Number cols on screen COL_MAX DB 0 ;Rightmost column VPAGE DB 0 ;Active page ATTR DB 1FH ;Default is color BW_ATTR EQU 07H ;May switch to mono ;---------------------------------------------------------------------- ; Environment parameters. ;---------------------------------------------------------------------- ENV_SEG DW 0 ;Segment of master env STR_COUNT DW 0 ;Number strings in Env ENV_FREE DW 0 ;Free space in bytes ENV_USED DW 0 ;Len of strings in Env STR_LEN DW 0 ;Len of current str ;====================================================================== ; MAIN (Near) ; ; This procedure invokes the subroutines and defines program operation. ;---------------------------------------------------------------------- MAIN PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG CLD ;Default moves forward ;---------------------------------------------------------------------- ; Editing requires that we be in a mode with at least 80 columns on ; the screen. If the mode isn't okay, terminate with an error. ;---------------------------------------------------------------------- CALL VIDEO_SETUP ;Examine video hardware JNC M_0 MOV DX,OFFSET BADMODE$ JMP M_ERR M_0: ;---------------------------------------------------------------------- ; Relocate the stack downward in the segment. Then shrink the program's ; allocated memory to hold just the program code and the stack. ;---------------------------------------------------------------------- CLI ;Disable interrupts MOV SP,OFFSET STACK_TOP ; and Re-position stack STI ;Allow interrupts MOV AH,4AH ;Modify memory block MOV BX,(OFFSET STACK_TOP - CSEG + 15) SHR 4 INT 21H ; Thru DOS JNC M_1 MOV DX,OFFSET MEMERR$ ;Unspecified error JMP M_ERR M_1: ;---------------------------------------------------------------------- ; Locate the master environment. By our definition, the master ; environment is the environment owned by the first copy of COMMAND. ; Note: if a new, permanent copy of COMMAND has been loaded with /P, ; this technique won't work. ;---------------------------------------------------------------------- MOV AH,52H ;Undocumented function INT 21H ; returns ES:BX ASSUME ES:NOTHING MOV AX,ES:[BX-2] ;First MCB block adr ;---------------------------------------------------------------------- ; Find the first PSP block in the chain that claims to be its own ; parent. Subject to disclaimer, this is the first copy of COMMAND. ;---------------------------------------------------------------------- M_2A: MOV ES,AX ;Address MCB ASSUME ES:NOTHING INC AX ;Address of PSP CMP AX,ES:[1] ;MCB = owner? JNE M_2B ;---------------------------------------------------------------------- ; This block owns itself, and so is a PSP. ; If it is also its own parent, we have the first copy of COMMAND. ;---------------------------------------------------------------------- CMP AX,ES:[16H+10H] ;Parent = MCB? JE M_2D ;---------------------------------------------------------------------- ; Didn't own itself. Move to the next block. ;---------------------------------------------------------------------- M_2B: CMP BYTE PTR ES:[0],"Z" ;Is this last block? JE M_2C ADD AX,ES:[3] ;Go to next block JMP M_2A M_2C: MOV DX,OFFSET NOENV$ ;Couldn't find it JMP SHORT M_ERR M_2D: ;---------------------------------------------------------------------- ; We have located the first copy of COMMAND. In DOS versions 3.30+, the ; word at PSP:2C contains the segment address of the env. In earlier ; versions, the environment point is 0; use the following MCB. ;---------------------------------------------------------------------- MOV CX,WORD PTR ES:[3CH] ;Get env segment JCXZ M_3A ;If 0, use next MCB DEC CX ;Point to env's MCB JMP SHORT M_3B M_3A: MOV CX,AX ;Block adr + ADD CX,ES:[3] ; length = next MCB M_3B: MOV ES,CX ;Put env's MCB in ES ASSUME ES:NOTHING ;---------------------------------------------------------------------- ; Verify that this block is owned by COMMAND's PSP. ;---------------------------------------------------------------------- CMP AX,ES:[1] ;Proper ownership? JNE M_2C ;---------------------------------------------------------------------- ; Now, CX = ES -> MCB of what we'll call the master environment. ; From the MCB, get the length (<32k) of the block, and save it. ;---------------------------------------------------------------------- MOV BX,CX ;Put MCB in BX MOV AX,ES:[3] ;Get length in paras MOV DX,AX ;(Save for later use) MOV CL,4 ; SHL 4 = *16 SHL AX,CL ; convert to bytes MOV [ENV_FREE],AX ;Say its all free ;---------------------------------------------------------------------- ; Now scan the entire environment and count the number of strings. ;---------------------------------------------------------------------- INC BX ;Point to env adr MOV [ENV_SEG],BX ;Save for update/exit MOV DS,BX ;Point ES and DS ASSUME DS:NOTHING MOV ES,BX ; to Master env ASSUME ES:NOTHING ;---------------------------------------------------------------------- ; Initialize the counter and the pointer. Jump to middle of loop. ;---------------------------------------------------------------------- SUB DI,DI ;DI = count of strings SUB SI,SI ;SI = offset into env ;---------------------------------------------------------------------- ; A zero byte indicates the end of a string. ;---------------------------------------------------------------------- M_4A: LODSB ;Get a char OR AL,AL ;If not 0, scan again JNZ M_4A INC DI ;Increase string count ;---------------------------------------------------------------------- ; If a double-zero, that was the last string. ;---------------------------------------------------------------------- M_4B: LODSB ;Get char OR AL,AL ;If not 0, keep going JNZ M_4A ;---------------------------------------------------------------------- ; Save the string count and calculate space used and free. ;---------------------------------------------------------------------- PUSH CS ;Point DS to CSEG POP DS ASSUME DS:CSEG DEC DI ;Make count 0-based MOV [STR_COUNT],DI ; 0 = 1 empty string MOV [ENV_USED],SI ; 1 = empty SUB [ENV_FREE],SI ; = amount left ;---------------------------------------------------------------------- ; Allocate a block of memory large enough to hold a full copy of this ; environment. ;---------------------------------------------------------------------- MOV BX,DX ;Get back size in paras MOV DX,OFFSET MEMSIZ$ ;Assume an error MOV AH,48H ;Allocate memory fn INT 21H ; Thru DOS JNC M_5 ;---------------------------------------------------------------------- ; Common exit to display an error message. ;---------------------------------------------------------------------- M_ERR: MOV AH,9 ;Display string INT 21H ; Thru DOS ;---------------------------------------------------------------------- ; Common exit. ;---------------------------------------------------------------------- M_EXIT: MOV DX,OFFSET COPYRIGHT$ ;Say who we are MOV AH,9 ;Display string fn INT 21H ; Thru DOS MOV AH,4CH ;Terminate INT 21H ; Thru DOS ;---------------------------------------------------------------------- ; Make a scratch copy of this environment. Move from DS:SI to ES:DI. ;---------------------------------------------------------------------- M_5: MOV CX,[ENV_USED] ;Move just strings SUB SI,SI ;From DS:SI PUSH ES POP DS ASSUME DS:NOTHING SUB DI,DI ;To ES:DI MOV ES,AX ASSUME ES:NOTHING REP MOVSB ;Transfer PUSH CS ;Restore segment POP DS ASSUME DS:CSEG ;---------------------------------------------------------------------- ; Draw the edit window. ;---------------------------------------------------------------------- CALL CLR_BOX ;Draw the window ;---------------------------------------------------------------------- ; Invoke the string editor. Returns when F7 is pressed. ;---------------------------------------------------------------------- CALL EDIT ;String editor ;---------------------------------------------------------------------- ; Ask if changes should be copied to the environment. ;---------------------------------------------------------------------- MOV BYTE PTR [CURSOR_COL],0 ;Reposition cursor MOV BYTE PTR [CURSOR_ROW],NROW ; for message CALL CUR_SET MOV DX,OFFSET SAVE$ ;Clone the changes? MOV AH,9 ;Display string fn INT 21H ; Thru DOS M_6: MOV AH,8 ;Get a key INT 21H ; Thru DOS AND AL,NOT 20H ;Capitalize it CMP AL,"N" ;If No... JE M_7 CMP AL,"Y" ;If not Yes, try again JNE M_6 ;---------------------------------------------------------------------- ; Copy the strings down to the master block. DS:SI TO ED:DI ;---------------------------------------------------------------------- MOV CX,[ENV_USED] ;New length of block SUB SI,SI PUSH ES ;Point DS:SI to copy POP DS ASSUME DS:NOTHING SUB DI,DI MOV AX,[ENV_SEG] ;ES:DI to master MOV ES,AX ASSUME ES:NOTHING REP MOVSB ;Transfer PUSH CS ;Restore segment POP DS ASSUME DS:CSEG ;---------------------------------------------------------------------- ; Exit the program. ;---------------------------------------------------------------------- M_7: JMP M_EXIT MAIN ENDP ;====================================================================== ; VIDEO_SETUP (Near) ; ; This procedure ensures that the number of columns on the screen is 80 ; or greater and gets the current video page. It also adjusts the ; screen attribute for monochrome screens. It will allow the program to ; run in graphics mode, but won't guarantee a pretty screen. Return ; with carry set if display is in an incompatible mode. ;---------------------------------------------------------------------- ; Entry: None ; Exit: ; NC = Video mode is okay ; CY = Can't use this video mode ;---------------------------------------------------------------------- ; Changes: AX BX DX ;---------------------------------------------------------------------- VIDEO_SETUP PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG ;---------------------------------------------------------------------- ; The Get Video Mode function returns the number of columns (not the ; max col number) on screen in AH. We require at least 80 characters. ;---------------------------------------------------------------------- MOV AH,0FH ;Get video mode INT 10H ; Thru BIOS CMP AH,80 ;Enough columns? JAE VS_1 ;---------------------------------------------------------------------- ; Note: We get here if AH is below 80; the carry flag is set by CMP. ;---------------------------------------------------------------------- JMP SHORT VS_EXIT ;---------------------------------------------------------------------- ; Save the number of columns and the active video page. ; Change the display attribute for monochrome display modes. ;---------------------------------------------------------------------- VS_1: MOV [VPAGE],BH ;Save current page MOV [NUM_COLS],AH ;Save cols SUB AH,(2+1) ;Indent 2 (1-based) MOV [COL_MAX],AH ;Is rightmost column CMP AL,7 ;Normal mono JE VS_2 CMP AL,15 ;EGA mono JNE VS_3 VS_2: MOV [ATTR],BW_ATTR ;Assume mono VS_3: CLC ;Indicate success VS_EXIT: RET VIDEO_SETUP ENDP ;====================================================================== ; CLR_BOX (Near) ; ; Clear a window (box) for our information on the screen. ; Add a border for a nice touch. ;---------------------------------------------------------------------- ; Entry: None ; Exit: None ;---------------------------------------------------------------------- ; Changes: AX BX CX DX SI ;---------------------------------------------------------------------- TITLEZ DB 181,"ENVEDIT 1.00",198,0 TITLE_LEN EQU $-TITLEZ HELPZ DB "STRING: ",27,32,26," INS DEL ",24,32,25 DB " F7 = Exit/Save F9=Add String",0 BOX_CHARS DB 201,205,187 ;Describes left, middle, and DB 186, 32,186 ; right chars for each row DB 199,196,182 DB 186, 32,186 DB 199,196,182 DB 186, 32,186 DB 200,205,188 NROW EQU ($-OFFSET BOX_CHARS)/3 ;Number of rows CLR_BOX PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG ;---------------------------------------------------------------------- ; Clear the box area to set the attribute for the characters. ;---------------------------------------------------------------------- MOV AX,0700H ;Scroll window fn MOV BH,[ATTR] ; clear to this color SUB CX,CX ;Start row,col MOV DH,NROW+3 ;3 lines below box MOV DL,[NUM_COLS] ;Max column DEC DL INT 10H ;Thru BIOS ;---------------------------------------------------------------------- ; Draw the edit window row by row. ;---------------------------------------------------------------------- MOV BH,[VPAGE] ;Get active page MOV SI,OFFSET BOX_CHARS ;Edit window chars MOV CX,NROW ;Number of rows to draw SUB DH,DH ;Starting row CB_1: PUSH CX ;Save counter SUB DL,DL ;Column=0 MOV AH,2 ;Mov to DH,DL 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,[NUM_COLS] ;Width of box SUB CH,CH ; into CX DEC CX ;Minus left side DEC CX ;Minus right side INT 10H ; Thru BIOS MOV AH,2 ;Position cursor MOV DL,[NUM_COLS] ; to far right DEC DL ; column INT 10H ; Thru BIOS LODSB ;Get rightmost char MOV AH,0AH ;Write char MOV CX,1 ;1 copy INT 10H ; Thru BIOS INC DH ;Next row POP CX ;Restore counter LOOP CB_1 ;---------------------------------------------------------------------- ; Embed the program name and version in the border. ;---------------------------------------------------------------------- SUB BH,BH MOV BL,[COL_MAX] ;Rightmost column SUB BL,TITLE_LEN+2 ;Backup MOV SI,OFFSET TITLEZ ;Program name CALL WRITE_MSG ;---------------------------------------------------------------------- ; Display the help prompts. ;---------------------------------------------------------------------- MOV BX,0102H ;Row/col MOV SI,OFFSET HELPZ ;Instructions CALL WRITE_MSG RET CLR_BOX ENDP ;====================================================================== ; WRITE_MSG (Near) ; ; Write an ASCIIZ string to the screen at the indicated row and column. ;---------------------------------------------------------------------- ; Entry: ; BH = screen row ; BL = screen column ; DS:SI = Offset of ASCII string to display ;---------------------------------------------------------------------- ; Changes: AX BX ;---------------------------------------------------------------------- WRITE_MSG PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG XCHG BX,[CURSOR_POS] ;Set requested position PUSH BX ;Save old one CALL CUR_SET ;Position cursor WM_1: MOV BH,[VPAGE] LODSB OR AL,AL JZ WM_2 MOV AH,0EH INT 10H JMP WM_1 WM_2: POP [CURSOR_POS] ;Restore prev cursor CALL CUR_SET RET WRITE_MSG ENDP ;====================================================================== ; EDIT (Near) ; ; The EDIT procedure handles all the editing. It keeps track of the ; environment strings and displays them on the screen as they change. ;---------------------------------------------------------------------- ; Changes: ;---------------------------------------------------------------------- STR_NUMBER DW 0 ;Number of env string STR_START DW 0 ; and offset into seg STR_LEFT DW 0 ;Offset leftmost char ; displayed on screen CURSOR_POS LABEL WORD CURSOR_COL DB 0 ;Current cursor CURSOR_ROW DB 0 ; position on screen INS_STATE DB 0 ;0=INS FF=TYPEOVER FAR_LEFT EQU 2 ;Leftmost column DISPLAY_ROW EQU 5 ;Strings appear here EDIT PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG ;---------------------------------------------------------------------- ; Initialize the pointer to point to the current string. ; Display the selected string on the screen as read from memory. ;---------------------------------------------------------------------- MOV [STR_NUMBER],0 ;First string MOV [CURSOR_ROW],DISPLAY_ROW ; goes here ;---------------------------------------------------------------------- ; If environment is empty,insert an empty string to work on. ;---------------------------------------------------------------------- EDIT_1A: CMP [STR_COUNT],-1 ;-1=no strings JNE EDIT_1B CALL ADD_STRING ;Add an empty string EDIT_1B: CALL GET_START ;Point to it MOV [CURSOR_COL],FAR_LEFT ;Init cursor CALL UPDATE ;---------------------------------------------------------------------- ; Display the current string in the window. ;---------------------------------------------------------------------- EDIT_2: CALL DISPLAYX ;Show the string ;---------------------------------------------------------------------- ; Get a key from the keyboard and act on it. ;---------------------------------------------------------------------- EDIT_3A: SUB AH,AH ;Fetch a key INT 16H ; Thru BIOS OR AL,AL ;If AL=0, is extended JZ EDIT_5A ; which means command CMP AX,BS ;If not actual BS key JNE EDIT_4A ;Process as char ;---------------------------------------------------------------------- ; The backspace key is the only key that requires special handling. ; Treat BS as a CURSOR-LEFT/DELETE combination. ;---------------------------------------------------------------------- CALL CURSOR_LEFT JC EDIT_3A EDIT_3B: CALL STRING_DEL ;Delete char at cursor JC EDIT_3A JMP EDIT_2 ;---------------------------------------------------------------------- ; Put the character on the screen and in the string. ;---------------------------------------------------------------------- EDIT_4A: CMP [INS_STATE],0 ;If insert JE EDIT_4B ; jump ;---------------------------------------------------------------------- ; If we're at the end of the string, typeover works just like insert. ;---------------------------------------------------------------------- CALL LOCATE_SI ;If char isn't 0 CMP BYTE PTR ES:[SI],0 ; goto overwrite JNZ EDIT_4C ;---------------------------------------------------------------------- ; Move chars to the right, add the new character. ;---------------------------------------------------------------------- EDIT_4B: CALL STRING_INS ;Create hole at cursor JC EDIT_3A EDIT_4C: CALL LOCATE_SI ;Point to cursor loc MOV ES:[SI],AL ; and pop in char CALL DISPLAYX ;Show changes ;---------------------------------------------------------------------- ; -> Move the cursor to the right one space. ;---------------------------------------------------------------------- EDIT_4D: CALL CURSOR_RIGHT ;Move cursor along JMP EDIT_3A ;---------------------------------------------------------------------- ; Key is an extended key. Must be an instruction. ;---------------------------------------------------------------------- EDIT_5A: CMP AH,F9KEY ;F9=make new string JNE EDIT_5B CALL PURGE_STR ;Del string if empty CALL ADD_STRING ;Add an empty string JMP EDIT_1B EDIT_5B: CMP AH,F7KEY ;F7 is the exit key JNE EDIT_6 CALL PURGE_STR ;Del string if empty CMP [STR_COUNT],-1 ;Env totally empty? JNE EDIT_5C CALL ADD_STRING ;Min is one empty str EDIT_5C: RET ; and the only way out ;---------------------------------------------------------------------- ; All remaining key dispatch done from here. ;---------------------------------------------------------------------- EDIT_6: CMP AH,DEL ;Kill char at cursor JE EDIT_3B CMP AH,RARROW ;Move right 1 char JE EDIT_4D CMP AH,LARROW ;Move left JE EDIT_9A CMP AH,UARROW ;Move up JE EDIT_7A CMP AH,DARROW ;Move down JE EDIT_8A CMP AH,INSKEY ;Use Insert mode JE EDIT_10 CMP AH,ENDKEY ;Move to end of string JE EDIT_11 CMP AH,HOME ;Move to start of str JE EDIT_12 JMP EDIT_3A ;Didn't recognize it ;---------------------------------------------------------------------- ; ^: Move to the previous string. ;---------------------------------------------------------------------- EDIT_7A: CALL PURGE_STR ;Clean up empties DEC [STR_NUMBER] JNS EDIT_7C MOV BX,[STR_COUNT] ; reset to end EDIT_7B: MOV [STR_NUMBER],BX ;Update pointer EDIT_7C: CLC JMP EDIT_1A ;Start over ;---------------------------------------------------------------------- ; v: Move to the next string. ;---------------------------------------------------------------------- EDIT_8A: CALL PURGE_STR ;Delete if empty MOV BX,[STR_NUMBER] ;Get current string JC EDIT_8B ;If del, don't adjust INC BX ; otherwise go to next EDIT_8B: CMP BX,[STR_COUNT] ;Okay if in range JBE EDIT_7B SUB BX,BX ;Reset to zero JMP EDIT_7B ;---------------------------------------------------------------------- ; <- Move the cursor to the left one space. ;---------------------------------------------------------------------- EDIT_9A: CALL CURSOR_LEFT ;Move cursor left EDIT_9B: JMP EDIT_3A ;Failed, ignore it ;---------------------------------------------------------------------- ; Toggle the insert/typeover state. ;---------------------------------------------------------------------- EDIT_10: NOT [INS_STATE] ;Toggle the flag JMP EDIT_3A ;---------------------------------------------------------------------- ; Move to end of string. ;---------------------------------------------------------------------- EDIT_11: CALL CURSOR_RIGHT ;Move to the right JNC EDIT_11 ; as long as successful JMP EDIT_3A ;---------------------------------------------------------------------- ; Move to start of string. ;---------------------------------------------------------------------- EDIT_12: CALL CURSOR_LEFT ;Move to the left JNC EDIT_12 ; as long as successful JMP EDIT_3A EDIT ENDP ;====================================================================== ; UPDATE (Near) ; ; Update the string size and environment free numbers in the status ; line and display on the screen. ;---------------------------------------------------------------------- ; Entry: None ; Exit: None ;---------------------------------------------------------------------- ; Changes: BX CX DX SI ;---------------------------------------------------------------------- STATUSZ DB "String Size: " US_SIZE DB "00000 Env Free: " US_FREE DB "00000",0 UPDATE PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG MOV SI,OFFSET US_SIZE+4 ;Point to end of size MOV BX,[STR_LEN] ;Length in bytes CALL US_1 ;Make denary/ASCII MOV SI,OFFSET US_FREE+4 ;Repeat for free bytes MOV BX,[ENV_FREE] ;---------------------------------------------------------------------- ; Get the string size and convert to an ASCII denary number. ;---------------------------------------------------------------------- US_1: PUSH AX ;Preserve register MOV AX,BX ;Size in AX MOV BX,10 ;Base is 10 MOV CX,5 ;Digits to convert US_2: SUB DX,DX ;32-bit in DX:AX DIV BX ;Remainder in DX ADD DL,30H ;Make into ASCII MOV [SI],DL ;Store in string DEC SI ;Move to higher digit LOOP US_2 ;---------------------------------------------------------------------- ; Display the static text in the size status line. ;---------------------------------------------------------------------- MOV BX,0302H ;Row/col MOV SI,OFFSET STATUSZ ;Status line text CALL WRITE_MSG ;Put on screen POP AX ;Restore register RET UPDATE ENDP ;====================================================================== ; DISPLAYX (Near) ; ; This procedure will write the active string to the screen from the ; current cursor position forward. It is called only when a char is ; added to the string, the window is pushed, or to show a new string. ;---------------------------------------------------------------------- ; 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,[CURSOR_COL] ;Cursor column MOV CL,[COL_MAX] ;Rightmost column ;---------------------------------------------------------------------- ; Read the string and display on the screen. ;---------------------------------------------------------------------- DISPLAY_0: LODS BYTE PTR ES:[SI] ;Get string char OR AL,AL ;0 = end of string JNZ DISPLAY_1 ;---------------------------------------------------------------------- ; If at the end of the string, display a space, then keep looping until ; the entire line has been overwritten. ;---------------------------------------------------------------------- DEC SI ;Back up to zero MOV AL,20H ;Display a space ;---------------------------------------------------------------------- ; Write the char in AL to the screen. ;---------------------------------------------------------------------- DISPLAY_1: CALL CUR_SET ;Position the cursor PUSH CX ;Save cursor position MOV AH,0AH ;Write Char in AL MOV BH,[VPAGE] ; on active page MOV CX,1 ; 1 copy INT 10H ; Thru BIOS POP CX ;Restore cursor pos INC [CURSOR_COL] ;Move to next column CMP CL,[CURSOR_COL] ;Is col <= end? JAE DISPLAY_0 ;Yes, continue ;---------------------------------------------------------------------- ; Past the end of the window - done with display. ;---------------------------------------------------------------------- MOV [CURSOR_COL],CH ;Return to old spot CALL CUR_SET ; do it RET DISPLAYX ENDP ;====================================================================== ; PURGE_STR (Near) ; ; Examine the current string. If it's an empty string, remove it from ; the environment. If not, change the environment variable to all CAPS. ;---------------------------------------------------------------------- ; Entry: None ; Exit: ; NC - string is valid ; CY - string was empty and was deleted ;---------------------------------------------------------------------- ; Changes: AX SI ;---------------------------------------------------------------------- PURGE_STR PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG MOV SI,[STR_START] ;Point to start of str CMP BYTE PTR ES:[SI],0 ;Is first char 0? JNE PS_0 ;---------------------------------------------------------------------- ; Remove the string from the environment. ;---------------------------------------------------------------------- DEC [STR_COUNT] ;Reduce string count CALL CHAR_KILL ;Remove 0 STC ;CY = str empty JMP SHORT PS_EXIT ;---------------------------------------------------------------------- ; Scan the string, capitalizing all chars to the left of the "=". ;---------------------------------------------------------------------- PS_0: MOV AL,BYTE PTR ES:[SI] ;Is first char 0? CMP AL,"=" ;= ends scan JE PS_2 OR AL,AL ;0 ends scan JZ PS_2 CMP AL,"a" ;If lower case... JB PS_1 CMP AL,"z" ;...alphabetic JA PS_1 AND BYTE PTR ES:[SI],NOT(20H) ;Capitalize PS_1: INC SI ;Move to next char JMP PS_0 PS_2: CLC ;Carry off PS_EXIT: RET PURGE_STR ENDP ;====================================================================== ; ADD_STRING (Near) ; ; Adds an empty string to the environment. ;---------------------------------------------------------------------- ; Entry: None ; Exit: ; NC - string added okay ; CY - string not added, no room in environment ;---------------------------------------------------------------------- ; Changes: AX SI ;---------------------------------------------------------------------- ADD_STRING PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG CALL STRING_INS ;Add empty string JC AS_EXIT MOV [CURSOR_COL],FAR_LEFT ;Reposition cursor MOV SI,[STR_START] ;Point to 1st char MOV BYTE PTR ES:[SI],0 ;Make it null INC [STR_COUNT] ;Up string count CLC ;Success AS_EXIT: RET ADD_STRING ENDP ;====================================================================== ; GET_START (Near) ; ; Find the starting offset of the current string into the environment ; segment and the length of that string. ;---------------------------------------------------------------------- ; Entry: None ; Exit: None ;---------------------------------------------------------------------- ; Changes: AX BX SI DI ;---------------------------------------------------------------------- GET_START PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG MOV BX,[STR_NUMBER] ;String to look for SUB SI,SI ;Start at offset zero SUB DI,DI ;String counter JMP SHORT GS_3 ;---------------------------------------------------------------------- ; Scan to the end of the string. Increment the count. ;---------------------------------------------------------------------- GS_1: LODS BYTE PTR ES:[SI] ;Get char OR AL,AL ;Scan for 0 JNZ GS_1 GS_2: INC DI ;Increase string count ;---------------------------------------------------------------------- ; If this is the string we're looking for, we're done. ;---------------------------------------------------------------------- GS_3: CMP BX,DI ;Right string? JNE GS_1 ;---------------------------------------------------------------------- ; Set edit pointers and count the length of the string. ;---------------------------------------------------------------------- MOV [STR_START],SI ;Save offset MOV [STR_LEFT],SI SUB DI,DI ;String length counter GS_4: LODS BYTE PTR ES:[SI] ;Get char INC DI ;Count it OR AL,AL ;It is 0? JNZ GS_4 DEC DI ;Don't count the 0 MOV [STR_LEN],DI RET GET_START 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: None ;---------------------------------------------------------------------- ; Changes: CX SI ;---------------------------------------------------------------------- LOCATE_SI PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG MOV SI,[STR_LEFT] ;Leftmost char position SUB CH,CH ;Adjust the start of MOV CL,[CURSOR_COL] ; the string to point SUB CL,FAR_LEFT ; to the char at the ADD SI,CX ; cursor RET LOCATE_SI ENDP ;====================================================================== ; STRING_INS (Near) ; ; Create a hole in the string by moving all chars to the right of the ; current char one place to the right. ;---------------------------------------------------------------------- ; Entry: None ; Exit: ; NC - hole created okay ; CY - no room left in environment ;---------------------------------------------------------------------- ; Changes: CX SI DI ;---------------------------------------------------------------------- STRING_INS PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG CMP [ENV_FREE],0 ;Any free space? JNE SI_1 STC ;Failure JMP SHORT SI_EXIT ;---------------------------------------------------------------------- ; There's room, so perform the insertion. ;---------------------------------------------------------------------- SI_1: CALL LOCATE_SI ;Point SI=current char MOV CX,[ENV_USED] ;End of strings offset MOV DI,CX ;Is target for move SUB CX,SI ;Bytes to move MOV SI,DI ;Copy to src register DEC SI ;Copy from prev byte PUSH ES ;Both same segment POP DS ASSUME DS:NOTHING STD ;Move backwards REP MOVSB ; whole string CLD ;Restore direction PUSH CS ;Restore DS POP DS ASSUME DS:CSEG INC [ENV_USED] ;File is longer INC [STR_LEN] ; so is string DEC [ENV_FREE] ; less left CALL UPDATE ;Display new counts CLC ;Success SI_EXIT: RET STRING_INS ENDP ;====================================================================== ; STRING_DEL (Near) ; ; Delete the char at the cursor. Close up the string. ;---------------------------------------------------------------------- ; Entry: None ; Exit: ; NC = char was removed ; CY = no char to kill ;---------------------------------------------------------------------- ; Changes: SI ;---------------------------------------------------------------------- STRING_DEL PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG CALL LOCATE_SI ;Point to current char CMP BYTE PTR ES:[SI],0 ;If 0, don't delete JNE SD_1 STC ;No char to kill JMP SHORT SD_EXIT SD_1: CALL CHAR_KILL ;Always returns NC SD_EXIT: RET STRING_DEL ENDP ;====================================================================== ; CHAR_KILL (Near) ; ; Closes up the string to eliminate the current character. ;---------------------------------------------------------------------- ; Entry: ; SI = offset of char to kill ; ES = segment of char ; Exit: ; CF = NC -- always cleared ;---------------------------------------------------------------------- ; Changes: CX SI DI ;---------------------------------------------------------------------- CHAR_KILL PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG MOV CX,[ENV_USED] ;End of strings offset SUB CX,SI ;Bytes to move DEC CX ;Is one less JZ CK_1 MOV DI,SI ;ES:DI is dest INC SI ;Copy from next byte PUSH ES ;Point DS:SI to src POP DS ASSUME DS:NOTHING REP MOVSB ;Move all env PUSH CS ;Restore DS POP DS ASSUME DS:CSEG DEC [ENV_USED] ;File gets shorter DEC [STR_LEN] ; as does string INC [ENV_FREE] ; with more to spare CALL UPDATE ;Freshen counts CK_1: CLC ;Success! RET CHAR_KILL ENDP ;====================================================================== ; CURSOR_RIGHT (Near) ; ; Move the cursor right 1 char. ;---------------------------------------------------------------------- ; Entry: None ; Exit: ; NC = success ; CY = failed ;---------------------------------------------------------------------- ; Changes: CX SI ;---------------------------------------------------------------------- CURSOR_RIGHT PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG CALL LOCATE_SI ;Point to current char CMP BYTE PTR ES:[SI],0 ;Are we on last char JNE CR_0 ;of string? jmp if yes ;---------------------------------------------------------------------- ; Exit with CF=1:failure. ;---------------------------------------------------------------------- CR_A: STC ;Signal failure JMP SHORT CR_EXIT ;---------------------------------------------------------------------- ; If we're at the end of the line, we've got to scroll. ;---------------------------------------------------------------------- CR_0: MOV CL,[CURSOR_COL] ;Is cursor positioned CMP CL,[COL_MAX] ; at screen edge? JE CR_2 ;---------------------------------------------------------------------- ; Cursor can move on-screen. Scrolling isn't required. ;---------------------------------------------------------------------- INC CL ;Move to next col CR_1: MOV [CURSOR_COL],CL ;Save column... CR_1B: CALL CUR_SET ;...move cursor CLC ;Signal success CR_EXIT: RET ;---------------------------------------------------------------------- ; Slide to the right and redraw the entire string. ;---------------------------------------------------------------------- CR_2: INC [STR_LEFT] ;Move the start PUSH [CURSOR_POS] ;Save current cursor MOV [CURSOR_COL],FAR_LEFT ;Redo from left side CALL CUR_SET ;Set cursor CALL DISPLAYX ;Draw string POP [CURSOR_POS] ;Reset old cursor JMP CR_1B ;====================================================================== ; CURSOR_LEFT (Near) [NESTED PROC] ; ; Move the cursor left 1 char. ;---------------------------------------------------------------------- ; Entry: None ; Exit: ; NC = cursor was moved left ; CY = cursor was at far left of string - not moved ;---------------------------------------------------------------------- ; Changes: CX SI ;---------------------------------------------------------------------- CURSOR_LEFT PROC NEAR ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG MOV CL,[CURSOR_COL] ;Is cursor CMP CL,FAR_LEFT ; at 1st column? JE CL_1 ;Yes, jump DEC CL ;Back up cursor JMP CR_1 CL_1: MOV SI,[STR_LEFT] ;Start of window CMP SI,[STR_START] ;Past start of string? JE CR_A ;Yes, jump DEC SI MOV [STR_LEFT],SI CALL DISPLAYX CLC ;Indicate success JMP CR_EXIT CURSOR_LEFT ENDP CURSOR_RIGHT ENDP ;====================================================================== ; CUR_SET (Near) ; ; Position the cursor to the screen row and column stored in ; [CURSOR_POS]. Row, col values are not checked. ;---------------------------------------------------------------------- ; 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,[CURSOR_POS] ; new cursor position INT 10H ; Thru BIOS POP AX ;Restore register RET CUR_SET ENDP ;====================================================================== ; Allocated after program loads. ;---------------------------------------------------------------------- PC = $ PC = PC + 256 STACK_TOP = PC CSEG ENDS END ENTPT