#TITLE "FANLED - FANcy Line EDitor for TurboDOS 1.4x" #PAGE 132,66 MODULE "FanLEdit" ; ; THIS IS THE RELEASE VERSION OF FANLED ; v 1.10 as of 4-5-85 ; v 1.11 as of 4-5-85 fixes ^Q problem ; v 1.30 as of 5-4-85 fixes ^Y problem, vers# in sync with 8 bit ; added ^U ; v 1.40 as of 7-13-85 now using COMSUB routines, fixed obscure bug ; ; ANOMALY: ; The labels GN1, GN2, GN3 all point to GETNXT and are used ; so save a few bytes. ; FANLED is somewhat optimized, a more experienced programmer ; may be able to squeeze a few more bytes out. It shrunk over ; 250 bytes from the raw translation... ; ; +-------------------------------+ ; | COMMAND CHARACTER DEFINITIONS | ; +-------------------------------+ ; BEGLIN == 'A'-0X40 ; ^A - BEGINNING OF LINE BACKCH == 'B'-0X40 ; ^B - CURSOR BACKWARDS DELFOR == 'D'-0X40 ; ^D - DELETE CHAR FORWARD ENDLIN == 'E'-0X40 ; ^E - END OF LINE FORWCH == 'F'-0X40 ; ^F - CURSOR FORWARD DELBAK == 'H'-0X40 ; ^H - DELETE CHAR BACKWARD KILFOR == 'K'-0X40 ; ^K - KILL FORWARD QUOTE == 'Q'-0X40 ; ^Q - QUOTE NEXT CHAR RECALL == 'R'-0X40 ; ^R - RECALL PREVIOUS COMMAND LINE ABORT == 'U'-0X40 ; ^U - ABORT LINE KILBAK == 'X'-0X40 ; ^X - KILL BACKWARD YANK == 'Y'-0X40 ; ^Y - YANK (UNKILL) DELETE == 0X7F ; DEL- SAME AS ^H ; ; EXPERIMENTAL EQUATES ; PUSHK == 'G'-0X40 ; PUSH KILL LINE POPK == 'T'-0X40 ; ; +---------------+ ; | OTHER EQUATES | ; +---------------+ ; CR == 0X0D ; CARRIAGE RETURN BELL == 7 ; BELL CHARACTER LITC == '^' ; CARET (PREFIX FOR CONTROL CHAR DISPLAY) ; ; +-------------------+ ; | DATA STORAGE AREA | ; +-------------------+ ; LOC Data# ; FORSPC::BYTE 'L'-0X40 ; NONDESTRUCTIVE FORWARD SPACE CHARACTER QFLAG: BYTE 0XFF ; QUOTE NEXT FLAG CLSAVE: WORD 0 ; COMMAND LINE STRUCTURE POINTER SAVE CLEN: BYTE 0 ; COMMAND LINE LENGTH INSY: BYTE 0 ; INSERT YANK FLAG KILLEN: BYTE 0 ; LENGTH OF KILL BUFFER KILBUF: RES 162 ; 162 BYTE KILL BUFFER OLDLIN: RES 163 ; OLD COMMAND LINE ; EXPERIMENTAL STORAGE: KILLEV: BYTE 0 ; KILL (STACK) LEVEL KLEVAD: WORD 0 ; LEVEL POINTER ADDRESS ; ; +---------------------------------------+ ; | COMMAND TABLE (PATCHABLE IN PAR FILE) | ; +---------------------------------------+ ; ; Each command occupies 3 bytes, the command character ; followed by the address of the command routine. ; CMDTBL: FANBG:: BYTE BEGLIN WORD CBEGLI FANBK:: BYTE BACKCH WORD CBACKC FANEL:: BYTE ENDLIN WORD CENDLI FANFC:: BYTE FORWCH WORD CFORWC FANDF:: BYTE DELFOR WORD CDELFO FANDB:: BYTE DELBAK WORD CDELBA FANKF:: BYTE KILFOR WORD CKILFO FANQU:: BYTE QUOTE WORD CQUOTE FANRC:: BYTE RECALL WORD CRECAL FANKB:: BYTE KILBAK WORD CKILBA FANYA:: BYTE YANK WORD CYANK FANAB:: BYTE ABORT WORD CABORT BYTE DELETE WORD CDELBA BYTE CR WORD EXIT ; EXPERIMENTAL: FANPU:: BYTE PUSHK WORD CPUSHK FANPO:: BYTE POPK WORD CPOPK NCMDS == ($-CMDTBL)/3 ; NUMBER OF COMMANDS ; ; +--------------+ ; | CODE SECTION | ; +--------------+ ; LOC Code# ; ; REGISTER USAGE: ; ES:BX normally points to the current character in the command line ; unless used otherwise ; DX normally points to the kill buffer or the old line buffer ; CH always holds the current byte count for the main command line ; CL always holds the current cursor position in the command line ; AL usually holds a byte received from the keyboard and/or echoed ; +--------------------+ ; | COMMON SUBROUTINES | ; +--------------------+ ; ; GETCH - get character in AL, preserve all regs ; GETCH: PUSH CX PUSH DX PUSH BX PUSH ES CALL CONIN# JMPS POPR ; ; ERROR - ring the bell ; ERROR: MOV AL,=BELL JMPS PUTCH ; ; BKSPC - backspace ; BKSPC: MOV AL,=8 ; ; PUTCH - display character in AL, preserve all regs ; PUTCH: PUSH CX PUSH DX PUSH BX PUSH ES MOV CL,AL PUSH AX CALL CONOUT# POP AX POPR: POP ES POP BX POP DX POP CX RET ; ; DELLFT - delete left character with pointer movement ; assume: ES:BX points into command line, CH holds ; current byte count ; DELLFT: DEC CH ; DECREMENT COUNT DEC CL ; DECREMENT CURSOR POINTER DEC BX ; DECREMENT POINTER ES CMP [BX],=' ' JNC DELCHL ; DELETE ONE CHAR IF NO CONTROL INC DH ; INCREMENT CONTROL CHAR COUNTER CALL DELCHL ; ; DELCHL - delete left character (bs,sp,bs) ; DELCHL: PUSH CX PUSH DX PUSH BX PUSH ES CALL DMS# BYTE 8,' ',8,0 JMPS POPR ; ; GETCMD - AL=character, determine if it's an edit command ; if edit command, DX=address, Z=reset ; GETCMD: PUSH BX PUSH CX CMP AL,=CR ; IS IT A RETURN? JZ ISCR ; YUP! TEST BYTE QFLAG,=0XFF ; SEE IF QUOTE FLAG SET MOV BYTE QFLAG,=0XFF ; RESET QUOTE FLAG ANYWAY JZ GETCMX ; EXIT IF QUOTE FLAG ISCR: MOV CH,=NCMDS MOV BX,&CMDTBL ; POINT TO COMMAND TABLE GETCML: CMP AL,[BX] JZ GOTCMD ; GOT A COMMAND INC BX INC BX INC BX DEC CH ; KEEP SEARCHING JNZ GETCML MOV CH,AL XOR AL,AL ; SET ZERO FLAG MOV AL,CH JMPS GETCMX ; EXIT ; GOTCMD: INC BX MOV DX,[BX] OR AL,AL ; IF NOT ^@ TYPED JNZ GETCMX ; THEN SKIP INC AL ; SPECIAL CASE WHERE ^@ IS NOT ATTENTION MOV AL,=0 GETCMX: POP CX POP BX RET ; ; Two external move routines are used, LDIR# and LDDR#. ; Both are contained in COMSUB and work as follows: ; Move CX bytes from AX:BX to BP:DX ; --------------------------------- ; LDIR moves from bottom to top, LDDR moves from top to bottom ; +--------------------------+ ; | MAIN PROGRAM ENTRY POINT | ; +--------------------------+ ; INPUT: ; ES:BX points to a data structure: ; = CLBLEN (command line buffer length ; = actual command line length ; = first byte of command line ; OUTPUT: ; BX = pointer to actual command line length ; A = actual command line length ; INPLN:: XOR CX,CX ; ZERO COUNT AND CURSOR MOV BYTE QFLAG,=0XFF ;CLEAR QUOTE FLAG ES MOV AL,[BX] ; GET MAX BYTE COUNT INC BX ; POINT TO CURRENT BYTE COUNT MOV CLSAVE,BX ; SAVE POINTER ES MOV BYTE [BX],=0 ; BE SURE COMMAND LINE IS EMPTY INC BX ; POINT TO FIRST CHAR OF COMMAND LINE MOV CLEN,AL ; STORE MAX COMMAND LINE LENGTH ; ; MAIN ROUTINE LOOP ; GETNXT: CALL GETCH ; GET A CHARACTER CALL GETCMD ; CHECK IF COMMAND, RETURN ZERO IF NOT JZ ASCII ; NO COMMAND, ENTER IT INTO CMD LINE JMPI DX ; JUMP TO DE ; ; enter AL into command line, echo character ; ASCII: CMP CH,CLEN ; END OF LINE REACHED? JNZ NOLOV ; NOPE CALL ERROR ; ELSE BEEP JMPS GETNXT ; AND TRY AGAIN ; NOLOV: CMP CH,CL ; SEE IF AT END OF LINE JNZ INSERT ; INSIDE LINE, SO DO INSERT MODE ES MOV [BX],AL INC BX ; INCREMENT INC CH ; POINTERS INC CL CALL OUTCH JMPS GETNXT ; GO GET ANOTHER ; ; INSERT - insert a character, move all others right ; INSERT: CMP CH,CLEN JNZ __X CALL ERROR ; COMPLAIN JMPS GETNXT ; AND EXIT IF TOO LONG ; __X: PUSH CX ; SAVE POINTERS SUB CH,CL ; SUBTRACT CURSOR POSITION MOV CL,CH ; SET COUNTER MOV CH,=0 ; CX=NUMBER OF BYTES RIGHT OF CURSOR ADD BX,CX ; POINT TO END OF LINE MOV DH,BH ; DUPLICATE IN DX MOV DL,BL INC DX ; DX POINTS PAST LINE INC CX INC CX PUSH AX MOV AX,ES MOV BP,ES CALL LDDR# ; MOVE LINE UP ONE CHAR POP AX INC BX POP CX ; GET COUNTERS BACK INC CH ; INCREMENT CURSOR AND COUNT INC CL INC BX ; INCREMENT POINTER ES MOV [BX],AL ; SAVE IT IN COMMAND LINE PUSH BX ; SAVE POINTER PUSH CX ; SAVE COUNTERS SUB CH,CL ; COUNT AGAIN INC CH MOV CL,CH ES CMP [BX],=' ' ; COMPENSATE FOR INSERTED CTL CHAR JNC INSL DEC CL INSL: ES MOV AL,[BX] CMP AL,=' ' JNC __X INC CL __X: CALL OUTCH ; DISPLAY THE CHAR INC BX DEC CH JNZ INSL ; INSD: DEC CL INSDL: CALL BKSPC DEC CL JNZ INSDL ; INSE: POP CX POP BX INC BX JMP GETNXT ; ; OUTCH - output a character, display control char if necessary ; OUTCH: CMP AL,=' ' ; CHECK IF CTL CHARACTER JNC NOCTL ; SKIP IF NOT PUSH AX ; SAVE CHAR MOV AL,=LITC ; PREFIX CHAR CALL PUTCH ; DISPLAY IT POP AX ; GET CHAR BACK ADD AL,=0X40 ; MAKE IT ALPHA NOCTL: JMP PUTCH ; DISPLAY CHAR ; ; +----------------------------+ ; | COMMAND PROCESSOR ROUTINES | ; +----------------------------+ ; ; CR typed -- exit back to turbodos ; EXIT: CMP CH,CL ; SEE IF WE'RE AT END OF LINE JZ CNTXIT INC BX ; ELSE UP POINTERS INC CL JMPS EXIT ; CNTXIT: PUSH CX MOV AL,=CR CALL ECHO# ; ECHO THE RETURN POP CX MOV BX,CLSAVE ; GET COMMAND LINE POINTER ES MOV [BX],CH ; SAVE ACTUAL BYTE COUNT PUSH BX ; SAVE PUSH CX ; REGISTERS MOV CL,CH ; SET UP COUNT MOV CH,=0 INC CL MOV DX,&OLDLIN MOV AX,ES MOV BP,DS CALL LDIR# POP CX ; GET REGS BACK POP BX MOV AL,CH ; BYTE COUNT IN A RET ; ; CBEGLI - go to beginning of line ; CBEGLI: INC CL ; SET UP CURSOR POINTER CBEGL1: DEC CL ; DECREMENT CURSOR PTR JNZ __X ; UNTIL ZERO JMPS GN1 ; __X: CALL BKSPC DEC BX ES CMP [BX],=' ' JNC CBEGL1 CALL BKSPC JMPS CBEGL1 ; ; CENDLI - go to end of line ; CENDLI: CMP CL,CH ; IF CURSOR AT END OF LINE JNZ __X ; THEN GET NEXT CHAR JMPS GN1 ; __X: INC CL ; POINT TO NEXT CHAR ES MOV AL,[BX] INC BX CMP AL,=' ' MOV AL,FORSPC JNC __Y CALL PUTCH __Y: CALL PUTCH JMP CENDLI ; AND SO ON ; ; CBACKC - back 1 char ; CBACKC: OR CL,CL ; IF AT LEFT JZ GN1 ; DO NOTHING CALL BKSPC ; ELSE BACKSPACE DEC CL ; DECREMENT COUNTER DEC BX ; POINTER ES CMP [BX],=' ' JNC GN1 CALL BKSPC JMPS GN1 ; ; CFORWC - forward 1 char ; CFORWC: CMP CL,CH ; IF AT END JZ GN1 ; DO NOTHING INC CL ; INCREMENT COUNTER ES CMP [BX],=' ' MOV AL,FORSPC JNC __X CALL PUTCH __X: CALL PUTCH ; MOVE CURSOR INC BX ; POINTER GN1: JMP GETNXT ; ; CQUOTE - do next char literally ; CQUOTE: MOV BYTE QFLAG,=0 JMPS GN1 ; ; CRECAL - recall previous command line ; CRECAL: OR CH,CH JZ __X ; COUNT MUST BE 0 JMPS GN1 ; __X: CMP OLDLIN,=BYTE 0 ; ANYTHING IN? JNZ __Y ; NO, EMPTY JMPS GN1 ; __Y: PUSH BX MOV DX,&OLDLIN ; GET OLD LINE XCHG DX,BX ; SET UP FOR MOVE DEC DX MOV CL,[BX] MOV CH,=0 PUSH CX INC CL MOV AX,DS MOV BP,ES CALL LDIR# POP CX MOV CH,CL ; GET COUNT INTO CH POP BX ; GET POINTER DSLP: ES MOV AL,[BX] ; GET BYTE CALL OUTCH ; DISPLAY IT WITH CTL CHAR EXPANSION INC BX ; POINT TO NEXT DEC CH ; UNTIL ALL DONE JNZ DSLP MOV CH,CL ; COUNT = CURSOR JMPS GN1 ; EXIT ; ; CDELBA - delete previous character, move following chars to left ; CDELBA: OR CL,CL ; SEE IF AT START OF LINE JZ GN1 MOV DX,=0 CALL DELLFT ; DELETE CHAR TO THE LEFT CMP CL,CH JZ GN2 JMPS MOVDEL ; MOVE AFTER DELETE ; ; CDELFO - delete following character, move remainder left ; CDELFO: CMP CH,CL ; IF CURSOR AT RIGHT END JZ GN2 ; THEN DO NOTHING MOV DX,=0 DEC CH MOVDEL: MOV AL,CH ; SEE IF END OF LINE PUSH CX ; SAVE COUNTERS PUSH BX ; SAVE LINE PTR SUB AL,CL ; AL=NUMBER OF CHARS PUSH AX INC AL MOV CL,AL MOV CH,=0 PUSH DX ; SAVE CONTROL CHAR COUNT MOV DH,BH MOV DL,BL INC BX PUSH AX MOV AX,ES MOV BP,AX CALL LDIR# POP AX POP DX ; GET CONTROL CHAR COUNT (0/1) POP AX ; GET BYTE COUNT POP BX ; GET LINE POINTER PUSH BX ; SAVE IT AGAIN MOV CH,AL OR AL,AL JNZ MVDELL MOV AL,=' ' CALL PUTCH CALL PUTCH CALL BKSPC CALL BKSPC JMPS MVDELQ ; MVDELL: ES MOV AL,[BX] CMP AL,=' ' JNC MVD1 INC DL MVD1: CALL OUTCH INC DL ; INCREMENT CHAR COUNT INC BX DEC CH JNZ MVDELL MOV AL,=' ' CALL PUTCH ; WIPE OUT LAST CHAR CALL PUTCH CALL BKSPC MVDEL1: MOV CH,DL CALL BKSPC MVDEL2: CALL BKSPC DEC CH JNZ MVDEL2 MVDELQ: POP BX POP CX GN2: JMP GETNXT ; ; CKILFO - kill all chars forward ; CKILFO: CMP CL,CH JZ GN2 ; NOTHING TO KILL MOV AL,CH SUB AL,CL ; GET BYTE COUNT MOV CH,CL PUSH CX ; SAVE COUNT, CURSOR MOV KILLEN,AL ; STORE KILL BUFFER LENGTH MOV CL,AL MOV CH,=0 PUSH CX PUSH BX ; SAVE LINE POINTER MOV DX,&KILBUF MOV BP,DS MOV AX,ES CALL LDIR# ; MOVE INTO KILL BUFFER (ES TO DS) POP BX POP CX PUSH BX MOV CH,CL CKILF1: ES MOV AL,[BX] INC BX CMP AL,=' ' MOV AL,=' ' JNC CKILF2 CALL PUTCH INC CL CKILF2: CALL PUTCH DEC CH JNZ CKILF1 CKILF3: CALL BKSPC DEC CL JNZ CKILF3 POP BX POP CX JMPS GN2 ; ; CKILBA - kill all chars backward ; CKILBA: OR CL,CL JZ GN2 ; NOTHING TO KILL PUSH CX ; SAVE COUNTERS PUSH BX ; SAVE POINTER MOV DX,CLSAVE ; GET POINTER TO START INC DX SUB BX,DX ; BX IS NOW LENGTH MOV CH,BH MOV CL,BL MOV BX,&KILLEN ; POINT TO KILL BUFFER MOV [BX],CL INC BX XCHG DX,BX MOV BP,DS MOV AX,ES CALL LDIR# ; MOVE STUFF INTO KILL BUFFER (ES TO DS) POP BX ; GET OLD PTR POP CX PUSH CX PUSH BX CKILB1: DEC BX ES CMP [BX],=' ' JNC __X CALL BKSPC __X: CALL BKSPC DEC CL JNZ CKILB1 MOV DL,=0 CKILB2: ES MOV AL,[BX] INC BX CMP AL,=' ' MOV AL,=' ' JNC CKILB3 CALL PUTCH INC DL CKILB3: CALL PUTCH INC DL DEC CH JNZ CKILB2 MOV CH,DL CKILB4: CALL BKSPC DEC CH JNZ CKILB4 POP BX ; GET LINE PTR POP CX ; GET COUNTERS SUB CH,CL ; A NOW HAS BYTE COUNT MOV CL,CH MOV CH,=0 PUSH CX INC CX MOV DX,CLSAVE INC DX PUSH DX MOV AX,DS MOV BP,ES CALL LDIR# ; (DS TO ES) POP BX POP CX MOV CH,CL MOV CL,=0 OR CH,CH JZ GN3 PUSH BX PUSH CX MOV DL,=0 CKILB5: ES MOV AL,[BX] CMP AL,=' ' JNC CKILB6 INC DL PUSH AX MOV AL,=LITC CALL PUTCH POP AX ADD AL,=0X40 CKILB6: CALL PUTCH INC DL INC BX DEC CH JNZ CKILB5 MOV CH,DL CKILB7: CALL BKSPC DEC CH JNZ CKILB7 POP CX POP BX GN3: JMP GETNXT ; ; CYANK - yank kill buffer to current cursor ; CYANK: MOV AL,KILLEN ; GET KILL BUFFER LENGTH OR AL,AL JZ GN3 ; KILL BUFFER EMPTY ADD AL,CH ; CHECK TOTAL SIZE JC __X ; OVER 256 BYTES... CMP CLEN,AL JNC YNTL ; GO AHEAD IF NOT TOO LONG __X: CALL ERROR ; BEEP IF TOO LONG JMPS GN3 ; YNTL: MOV INSY,=0 CMP CH,CL ; CHECK IF CURSOR AT END OF LINE MOV AL,KILLEN ; GET LENGTH OF KILL BUFFER JZ NOMOVE ; SKIP IF AT END OF LINE PUSH CX ; SAVE COUNT PUSH BX ; SAVE POINTER MOV DL,AL MOV DH,=0 ; DX = NUMBER OF BYTES TO BE FREED SUB CH,CL MOV CL,CH MOV CH,=0 ; CX HAS BYTES TO BE MOVED ADD BX,CX ; POINT TO END OF LINE PUSH BX ADD BX,DX ; POINT TO NEW END OF LINE XCHG DX,BX POP BX INC CX MOV AX,ES MOV BP,AX CALL LDDR# POP BX POP CX MOV INSY,=0XFF MOV AL,KILLEN ; GET LENGTH DELETE CHAR FORWARD NOMOVE: PUSH CX PUSH BX XCHG BX,DX ; DESTINATION TO DX MOV BX,&KILBUF MOV CL,AL MOV CH,=0 PUSH CX MOV AX,DS MOV BP,ES CALL LDIR# POP DX POP BX POP CX YDSLP: ES MOV AL,[BX] CALL OUTCH INC CH INC CL INC BX DEC DL JNZ YDSLP MOV AL,INSY INC AL JNZ GN4 ; ; now redisplay remainder of line, then step back ; PUSH BX ; SAVE LINE PTR MOV DL,=0 ; CHARACTER COUNT PUSH CX YTL1: CMP CH,CL JZ YTL25 ES MOV AL,[BX] INC CL CMP AL,=' ' JNC YTL2 INC DL YTL2: INC DL CALL OUTCH INC BX JMP YTL1 ; YTL25: POP CX YTL3: CALL BKSPC DEC DL JNZ YTL3 POP BX GN4: JMP GN3 ; ; ABORT LINE ; CABORT: MOV CX,=0 MOV BX,CLSAVE INC BX JMP EXIT ; ; EXPERIMENTAL STUFF ; ; PUSH KILLED LINE ; CPUSHK: CMP KILLEV,=0 ; IF NOTHING STACKED JZ __CPK ; THEN CONTINUE __CPUE: CALL ERROR ; ELSE BEEP __GNXT: JMP GETNXT ; AND CONTINUE ; __CPK: PUSH BX PUSH CX MOV BX,=170 ; WE NEED 163 BYTES CALL ALLOC# ; GET THEM TEST AL,AL ; IF WE CAN JZ __CP ; OK, SO WE DON'T GET IT CALL ERROR __GX: POP CX POP BX JMPS __GNXT ; __CP: INC KILLEV ; INCREMENT KILL KEVEL MOV KLEVAD,BX ; SAVE POINTER MOV DX,BX MOV BX,&KILLEN MOV AX,DS MOV BP,AX MOV CX,=163 ; 163 BYTES CALL LDIR# JMPS __GX ; ; POP KILLED LINE ; CPOPK: CMP KILLEV,=1 ; IF STACK ACTIVE JZ __CPK ; THEN CONTINUE CALL ERROR ; ELSE BEEP __GNXT: JMP GETNXT ; __CPK: PUSH BX PUSH CX MOV KILLEV,=0 ; SET KILL LEVEL TO 0 MOV BX,KLEVAD ; GET STACK POINTER MOV DX,&KILLEN ; GET DESTINATION ADDR MOV AX,DS MOV BP,AX MOV CX,=163 ; 163 BYTES CALL LDIR# ; MOVE IT MOV BX,KLEVAD CALL DEALOC# ; DEALLOCATE MEMORY POP CX POP BX JMPS __GNXT ; QUIT ; END  .