( This is not used in 4th/86 as shelling to a DOS editor is available The source is provided for interest -- it DOES work in 4th/86 if FLOADed -- 8 July 88 -- MFB ) ( File: EDIT) ( ========================================================= = = = 4TH EDITOR = = = ========================================================= Written 24 May 1982 by Tom Thompson This package lets you edit text files without leaving 4th. The intent is to provide the capability to make minor changes to the source files of programs being debugged without having to exit 4th to load the system editor. The edit maintains a buffer between (HEAD) and (LUM). The actual distances from these two points are in the constants DEFSPACE and DATSPACE. After invoking EDIT, you can return to 4th, execute words, build definitions, etc. and then re-enter the editor as long as the new definitions or whatever have not crept into the edit buffer (Of course, you can always use the primary entry again if the buffer has been clobbered). The primary entry is: " dsk:fname.ext" EDIT The re-entry is: REDIT The edit commands are listed below. The editor has two modes: "edit" and "insert". In edit mode, you are prompted for a command. The prompt contains the line number that an "edit cursor" is pointing at. Terminate commands with carriage return. No spaces are needed between a command and its parameters. In insert mode, you are prompted for the next line to be inserted. Enter ^A (control A) followed by carriage return to terminate insert. Insertion starts after the line addressed by the edit cursor. carriage ret. Next line. nn Line nn ( nn is number from 0 to 9999). +nn Forward nn lines. -nn Backward nn lines. ? Display status. A[string] Append string to end of current line or repeat prev append. C[/str1/str2/] Repeat prev change or change str1 to str2. D[n1] [n2] Delete current, delete n1 lines beginning with current, or delete lines n1 thru n2. H[nn] Move pointer to previous window and display [set window size]. I[string] Insert mode or insert 1 line. J[nn] Display current window [set window size]. K[nn] Move pointer to next window and display [set window size]. L[string] Locate string or repeat prev locate. P Toggle print line numbers. Q Exit edit without save. X Exit with save. **** WORDS USEABLE FROM FORTH **** ESTATUS Prints status of edit buffer (room left, etc.) **** MISCELLANEOUS HINTS **** 1. Enter a very large line number to move to the end of the file. 2. Enter a line number of "0", then "I", to insert text at the top of the file. 3. You can re-enter the editor (REDIT) after either X or Q. 4. Turn on line numbers (P) so that text entered via insert command is "aligned" with text displayed with window commands. 5. Use "FORGET" so that edited files can be reLOADed without "already defined" messages. 6. The command table in the code below can easily be modified to add new commands or change the names of existing ones. 7. This works: { Q 3 3 + . REDIT } **** RESTRICTIONS **** All of the restictions listed below are because of size/features tradeoffs. Additional code removes restrictions. 1. File must fit in edit buffer (usually around 9-10k in "fully loaded" 48K system). 2. No backup made when file is saved. 3. Locate string restricted to 30 characters. 4. Change strings restricted to 31 characters. 5. When you delete all lines in a file and then "X", a disk file with one sector of 1A'S is left. ) ( Note: CONBUF is used for line buffer and CONBUF pointers are reset so 4th never sees rest of line after EDIT ) DATA[ BYTE 12 ] WDIM ( display window dimensions) DATA[ BYTE 1 ] PLNFLAG ( print line numbers flag) DATA[ BYTE 0 ] ROK ( re-edit ok flag) 300 CONST DEFSPACE ( space to reserve for defs during edit. In other words, you can define up to DEFSPACE bytes of code without clobbering buffer) 100 CONST DATSPACE ( space to reserve for data alloc during edit) 2 BLOCK EBBEG ( edit buffer begin) 2 BLOCK EBEND ( edit buffer end) 2 BLOCK EBMAX ( max edit buffer end) 2 BLOCK EBCUR ( edit buffer current) 2 BLOCK ELNUM ( current line number) DATA[ BYTE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] EFNAME ( filename buffer) DATA[ BYTE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] CHGOSTR ( change string buffer) DATA[ BYTE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] CHGNSTR ( change string buffer) DATA[ BYTE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] LOCSTR ( locate string buffer) DATA[ BYTE 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ] APPSTR ( append string buffer) 2 BLOCK EFCB ( file control block) 1 BLOCK EXFLAG ( edit exit flag) 1 BLOCK EEOL ( input string end encountered flag) : WSIZE WDIM B@ ; : ELINE ( get input string) 127 CONBUF ! ( max len = 127) 0 CONIP ! ( reset pointer) 0 EEOL B! ( clr eol flag) CONBUF GETLIN ( get line ) CRLF ; : E. ( print 4 digit number with leading 0's) 1 SWAP 0 SWAP 0 1000 DOUT 0 100 DOUT 0 10 DOUT 0 1 DOUT 3 KILL ; : ENXCHR ( get next character from input line) CONBUF 1+ B@ CONIP @ > IF ( more chars in line) CONBUF CONIP @ + 2+ B@ ( got char) 1 CONIP +! ( inc ptr) ELSE ( line exhausted) 1 EEOL B! 13 ( return a cr) THEN ; : EBKCHR ( make enxchr return same char again) EEOL B@ 0= IF ( eol not encountered) CONIP @ IF -1 CONIP +! THEN THEN ; : EUCASE ( convert tos to uppercase) "z" OVER >= OVER "a" >= AND IF 20H - THEN ; 0 CONST FREFELOOP : EERR ( print error and abort command) " ** ERROR ** " ." ." CRLF FREFELOOP EXEC ( return to ELOOP) ; : EMEMER " Edit Buffer Full" EERR ; : EDIGCHK ( return 1 if next char in input line is digit) ENXCHR "9" OVER >= SWAP "0" >= AND EBKCHR ; : EINUM ( convert string to number <= 9999) BEGIN ENXCHR 32 - END EBKCHR ( skip leading spaces) 0 REPEAT EDIGCHK WHILE ( is digit) DUP 1000 >= IF ( getting too big) " Number > 9999" EERR THEN 10 * ENXCHR "0" - + ( add digit) ENDWHILE ENXCHR DUP 13 = SWAP 32 = OR 0= IF ( not proper delimiter) " Bad Number" EERR THEN ; : EINUMNZ ( input number, convert 0 to 1) EINUM DUP 0= IF DROP 1 THEN ; : EOFCHK ( return 1 if positioned at end of file) EBCUR @ EBEND @ = ; CODE ENXLA ( find addr of next line from addr on tos) ( exit: tos = 1 if eof nos = addr of line) D POP, '' EBEND LHLD, A E MOV, L CMP, IFZ A D MOV, H CMP, THEN IFZ ( end of file) H 1 LXI, ELSE ( not eof) D DCX, BEGIN D INX, D LDAX, 13 CPI, ENDZ ( find cr) D INX, D LDAX, 10 CPI, IFZ D INX, THEN ( extra bump if lf follows cr) H 0 LXI, ( not eof) THEN D PUSH, ;PUSH : EMPNL ( move edit pointer to next line) ELNUM @ IF ( not on line 0) EBCUR @ ENXLA 0= IF ( not eof) 1 ELNUM +! ( bump current line number) THEN EBCUR ! ELSE ( move to line 1) 1 ELNUM ! THEN ; : EABLA ( get addr and number of abs line) ( exit: tos = addr, nos=line number) EBBEG @ OVER DUP 0= SWAP 1 = OR 0= IF ( not line 0 or 1) OVER 1- 1 DO ENXLA IF ( eof) SWAP DROP I SWAP EXIT ( return eof line number) THEN LOOP ( til desired or eof) THEN ; : EMABS ( move to line: tos = line number; fix EBCUR and ELNUM) EABLA EBCUR ! ELNUM ! ; : EPL ( print line: tos = addr ) REPEAT DUPB@ DUP 13 - WHILE ( not cr) .C 1+ ENDWHILE DROP DROP ; : EPCL1 ( print current line) .C PLNFLAG B@ IF ELNUM @ E. THEN " | " ." ELNUM @ 0= IF ( top of file) " --- TOP ---" ." ELSE EOFCHK IF ( end of file) " --- BOT ---" ." ELSE EBCUR @ EPL THEN THEN CRLF ; : EPCL 32 EPCL1 ; : ERELPOS ( Move pointer tos lines (plus or minus)) * ELNUM @ + DUP 0 < IF DROP 0 THEN EMABS ; : EMAKHOL ( tos = hole addr, nos = size) OVER 1+ EBMAX @ EBEND @ - U> IF ( not enough space) EMEMER THEN EBEND @ OVER - DUP IF ( do move unless hole is at end) EBEND @ DUP 5 PICK + ( tos: dest, src, count, hole addr, size) :EXIT L C MOV, H B MOV, B POP, ( b = dest) D POP, ( d = src) XTHL, ( h = cnt) BEGIN D DCX, D LDAX, B DCX, B STAX, H DCX, A L MOV, H ORA, ENDZ B POP, :ENTER ELSE DROP ( count) THEN DROP ( hole addr) EBEND +! ( bump end) ; : EAPPEND ( tos = address of string to insert) APPSTR DUPB@ DUP IF ( stuff to append) EMPNL EBCUR @ 2 - EMAKHOL ( make hole at end of current line) 1+ EBCUR @ 2 - OVER 1- B@ MOVE ( append buffer to hole) ELNUM @ 1- EMABS EPCL ( redisplay current line) ELSE " Append buffer empty" EERR THEN ; : EACMD ( append line command) CONBUF 1+ B@ CONIP @ > IF ( not null command line) CONBUF 1+ DUPB@ 1- DUP 49 < IF ( room in append buffer) OVER 1+ B! 1+ DUPB@ 1+ APPSTR SWAP MOVE ( move string to append buffer) ELSE " Append string > 48 characters" EERR THEN THEN EAPPEND ; : EINSERT ( tos = address of string to insert) DUPB@ 2+ OVER B! 0A0DH OVER DUPB@ 1- + ! ( added crlf to line) DUPB@ ( space needed) EMPNL ( insert after current) EBCUR @ EMAKHOL ( make room) 1+ ( src) EBCUR @ ( dest) OVER 1- B@ ( cnt) MOVE ; : EICMD ( insert lines command) CONBUF 1+ B@ CONIP @ > IF ( Istring option) CONBUF 1+ DUPB@ 1- OVER 1+ B! 1+ EINSERT ( insert the line) ELSE BEGIN 32 .C PLNFLAG B@ IF ( display line numbers) ELNUM @ 1+ EOFCHK IF ( eof) 1- DUP 0= + ( if eof, subtract 1 unles that makes top) THEN E. THEN " i>" ." ( prompt) ELINE ( get input) CONBUF 1+ DUPB@ 1 = SWAP 1+ B@ 1 = AND IF ( single ^A) 1 ( exit insert loop) ELSE CONBUF 1+ EINSERT 0 ( insert and continue) THEN END THEN ; : EDCMD ( delete lines command) EINUM ( 1st number) EINUM ( 2nd) OVER OVER OR IF ( not two 0's (nulls)) DUP IF ( second number not 0 (not null)) ( case of delete range option) OVER OVER > IF ( 1st > 2nd) " Bad Range" EERR THEN OVER EMABS ( set new current line) SWAP - 1+ ( number to delete) ELSE ( case of delete n lines beginning with current) DROP ( null n2) THEN ELSE ( case of delete current line) DROP DROP ( nulls) 1 ( number to delete) THEN ELNUM @ 0= IF " Can't delete line 0" EERR THEN ELNUM @ SWAP EBCUR @ SWAP ( save current position) 1 DO ( delete tos lines or until eof) EMPNL EOFCHK IF EXIT THEN LOOP EBCUR @ OVER = 0= IF ( something actually deleted) DUP ( dest is on tos) EBCUR @ ( src) SWAP EBEND @ EBCUR @ - ( cnt) MOVE EBCUR @ SWAP - ( newcur - oldcur) -1* EBEND +! ( end corrected) 1- EMABS ( position to line before delete) ELSE ( nothing deleted) DROP DROP THEN EPCL ( print new current line) ; : E+CMD ( command to move fwd nn lines and print new current line) 1 EINUMNZ ERELPOS EPCL ; : E-CMD ( Move back nn lines and print new current line command) -1 EINUMNZ ERELPOS EPCL ; : EJCMD ( print current window) EINUM DUP IF DUP WDIM B! THEN DROP ( update window size if number) ELNUM @ DUP WSIZE 2 / - ( current line - wsize/2) DUP 0 < IF DROP 0 THEN ( tos: start line nos: old current line) EMABS ( set pointer to start line) WSIZE 1 DO DUP ELNUM @ = IF ">" ELSE 32 THEN ( blank or "cursor") EPCL1 ( print current) EOFCHK IF EXIT THEN ( quit on eof) EMPNL ( point to next line) LOOP EMABS ( restore current line pointer) ; : EKCMD ( Move forwards one window ) 1 WSIZE ERELPOS EJCMD ; : EHCMD ( Move backwards one window ) -1 WSIZE ERELPOS EJCMD ; : EQCMD ( Quit Command) 1 EXFLAG B! 13 CONBUF 1+ DUPB@ 1+ DUP 3 PICK B! + B! ( append cr to command line) ; : EXCMD ( exit with save command) EQCMD EFNAME 2 SYSOPEN EBEND @ EBBEG @ - DUP IF ( something to write) EBBEG @ 3 PICK SYSWRITE ( write it) ELSE DROP ( size) THEN SYSCLOSE ; : EPCMD ( toggle print line numbers flag) PLNFLAG B@ 1 XOR PLNFLAG B! ; : E.16 ( print unsigned 16 bits) DUP 0 < IF DROP " >32K" ." ELSE . THEN ; : ESTATUS ( print status) " Edit buffer " ." ROK B@ 0= IF " is undefined" ." RETURN THEN HEAD @ 1+ EBBEG @ U> EBMAX @ 1+ LUM @ U> OR IF " has been overwritten" ." RETURN THEN " - Size:" ." EBMAX @ EBBEG @ - DUP E.16 ( size) " Used:" ." EBEND @ EBBEG @ - DUP E.16 ( used) " Remaining:" ." - E.16 ( free) " \13 10\Forth Space - Code:" ." EBBEG @ HEAD @ - 2 - E.16 ( def space) " Data" ." LUM @ EBMAX @ - E.16 ( data) CRLF ; ( de = string addr, hl = test string addr, c = test string count. Search string for match. Return when match found: de -> next char after match string and cy set. Return when cr hit: de -> past cr and cy clear) CODE ESRCH# BEGIN D LDAX, M CMP, IFZ ( first char matches) B 0 MVI, BEGIN H INX, D INX, B INR, C DCR, ifz ret, then ( RZ, ( exit if matched)) D LDAX, M CMP, ENDNZ A C MOV, B ADD, C A MOV, ( restore count) A E MOV, B SUB, E A MOV, IFC D DCR, THEN ( and pointers) A L MOV, B SUB, L A MOV, IFC H DCR, THEN A XRA, ( not cr) THEN D INX, 13 CPI, ENDZ ( quit if cr encountered) STC, ( not found sts) RET, ;NOTHREAD ASM : EGETSTR ( get string: tos=buffer, nos=delimiter, 3os=limit) 0 OVER B! BEGIN ENXCHR DUP 13 = OVER 5 PICK = OR IF ( end or delimiter) 1 ( exit loop) ELSE OVER DUPB@ 1+ 6 PICK OVER > IF ( limit not reached) DUP 5 PICK B! ( count updated) + B! ( character stored) ELSE ( limit reached so dump) 3 KILL THEN 0 ( cont loop) THEN END 4 KILL ; : ECCMD ( change string command) CONBUF 1+ B@ 1- IF ( not null) 32 ( limit) ENXCHR ( delimiter) OVER OVER CHGOSTR EGETSTR ( get search string) CHGNSTR EGETSTR ( get replace string) THEN EOFCHK ELNUM @ 0= OR IF " Can't Change Here" EERR THEN EBCUR @ CHGOSTR B@ IF ( search string not null) :EXIT D POP, B PUSH, H '' CHGOSTR LXI, C M MOV, ( length of search string) H INX, ' ESRCH# CALL, B POP, D PUSH, H 0 LXI, IFC ( no find) H INX, THEN H PUSH, :ENTER IF ( no find) " Not Found" EERR THEN CHGOSTR B@ - THEN ( at this point tos is addr of old string to delete) CHGNSTR B@ CHGOSTR B@ - ( net change) DUP 0> IF ( bigger) OVER OVER SWAP EMAKHOL ELSE ( make smaller or same) DUP IF ( smaller) OVER CHGOSTR B@ + ( src) OVER OVER + ( dest) OVER EBEND @ SWAP - ( count) MOVE DUP EBEND +! THEN THEN DROP CHGNSTR 1+ SWAP CHGNSTR B@ MOVE EPCL ; : ELCMD ( locate [string] command ) EBEND @ 32 + EBMAX @ U> IF EMEMER THEN CONBUF 1+ B@ 1- ( length of string ) IF ( not null string ) 31 ( limit) 13 ( delimiter) LOCSTR EGETSTR ( move to locate buffer ) 0 LOCSTR B@ 1+ DUP LOCSTR B! LOCSTR + B! ( append 0) THEN EMPNL ( move pointer to next line (locate after current)) LOCSTR B@ IF ( not null) LOCSTR 1+ EBEND @ LOCSTR B@ MOVE ( move locate string to end of buffer ) EBEND @ EBCUR @ OVER BEGIN DUPB@ 0= IF 3 KILL 1 ELSE OVER B@ OVER B@ = IF 1+ SWAP 1+ SWAP 0 ELSE OVER B@ 0DH = IF 1 ELNUM +! THEN DROP 1+ OVER 0 THEN THEN END ELNUM @ EABLA EBEND @ = IF ( no match ) DROP 0 ( position to line 0 ) THEN THEN EMABS EPCL ; 15 BLOCK EFNSTR : EGETFN ( get file name) 14 13 EFNSTR EGETSTR EFNSTR B@ IF EFNSTR ELSE " EDTMP.$$$" THEN ; : EWCMD ( write range of lines) EINUM DUP 0= IF DROP 1 THEN EINUM OVER OVER > IF " Bad Range" EERR THEN OVER EMABS SWAP - EBCUR @ SWAP 0 DO EMPNL EOFCHK IF EXIT THEN LOOP EBCUR @ OVER = 0= IF EGETFN 2 OPEN IF " Write Open" EERR THEN SWAP EBCUR @ OVER - SWAP 3 PICK WRITE SWAP CLOSE DROP IF " Write" EERR THEN THEN ; : EREERR OVER EBCUR @ + EBCUR @ OVER EBEND @ SWAP - MOVE SWAP -1* EBEND +! EERR ; : ERCMD ( read in block ) EMPNL EBMAX @ EBEND @ - 1- DUP EBCUR @ EMAKHOL EGETFN 1 OPEN IF DROP " Read Open" EREERR THEN OVER EBCUR @ 3 PICK READ DROP SWAP CLOSE DROP OVER OVER = IF DROP " Buffer Full" EREERR THEN EBCUR @ OVER + OVER 1 DO 1- DUPB@ 1AH - IF EXIT THEN SWAP 1- SWAP LOOP DROP OVER OVER EBCUR @ + SWAP EBCUR @ + SWAP OVER EBEND @ SWAP - MOVE - -1* EBEND +! EPCL ; DATA[ BYTE "Q" WORD ' EQCMD BYTE "+" WORD ' E+CMD BYTE "-" WORD ' E-CMD BYTE "J" WORD ' EJCMD BYTE "K" WORD ' EKCMD BYTE "H" WORD ' EHCMD BYTE "D" WORD ' EDCMD BYTE "A" WORD ' EACMD BYTE "I" WORD ' EICMD BYTE "P" WORD ' EPCMD BYTE "L" WORD ' ELCMD BYTE "C" WORD ' ECCMD BYTE "?" WORD ' ESTATUS BYTE "X" WORD ' EXCMD BYTE "W" WORD ' EWCMD BYTE "R" WORD ' ERCMD BYTE 0 ] ECTAB CODE ELOOP ( fix up sp's for error control) '' CLISP LHLD, SPHL, '' CLICSP LHLD, '' CSP SHLD, :ENTER CRLF BEGIN ELNUM @ E. " e>" ." ELINE ( get input line) EDIGCHK IF ( 1st digit is numeric) EINUM EMABS ( move to abs line) EPCL ( and print it) ELSE ( not numeric) ENXCHR ( line's 1st character) DUP 13 = IF ( null input) DROP EMPNL EPCL ( move ptr to next line and print it) ELSE ( not number or cr; search command table) EUCASE ( convert to upper case) ECTAB BEGIN OVER OVER B@ DUP IF ( not zero entry) = IF ( found match) SWAP DROP 1+ @ EXEC ( do cmd) 1 ( exit loop) ELSE ( didn't match, check next) 3 + 0 ( cont loop) THEN ELSE ( end of table; no match) DROP DROP " Invalid Command" ." CRLF 1 ( exit loop) THEN END THEN THEN EXFLAG B@ ( exit or quit command?) END CLI ; ' ELOOP PATCH FREFELOOP ( fix fwd ref for error control) : EDIT HEAD @ DEFSPACE + 0A0DH OVER ! 2+ DUP EBBEG ! EBCUR ! LUM @ DATSPACE - EBMAX ! 0 ELNUM ! DUPB@ 15 > IF " Bad Filename" 1 ERMSG THEN EFNAME $! EFNAME 1 OPEN DUP IF ( some error) 1- IF ( error is not no file) " Open Error" 1 ERMSG THEN ( creating new file) EBBEG @ EBEND ! ( set empty) ELSE ( edit of existing file) DROP ( error code) EBMAX @ 2 - EBBEG @ - DUP ( num bytes max) EBBEG @ ( buffer addr) 4 PICK ( fcb addr) SYSREAD 3 PICK SYSCLOSE ( dont need file anymore) SWAP OVER = IF ( buffer filled) " File Too Big" 1 ERMSG THEN EBBEG @ + BEGIN ( discard 1a's) 1- DUPB@ 1AH - END 1- DUP@ 0A0DH - IF ( make sure crlf at end) 2+ 0A0DH OVER ! THEN 2+ EBEND ! ( end of buffer fixed) THEN DROP ( fcb addr) 0 EXFLAG B! ( make ELOOP loop until exit cmd) 1 ROK B! ( set reedit ok) ELOOP ( ELOOP won't return, it goes to CLI) ; : REDIT ( allow re-edit of buffer contents unless ROK not true or buffer clobbered) HEAD @ 1+ EBBEG @ U> EBMAX @ 1+ LUM @ U> ROK B@ 0= OR OR IF " Can't Re-edit" 1 ERMSG THEN 0 EXFLAG B! ELOOP ;