home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
norge.freeshell.org (192.94.73.8)
/
192.94.73.8.tar
/
192.94.73.8
/
pub
/
computers
/
cpm
/
alphatronic
/
DRIPAK.ZIP
/
CPM_3-0
/
SOURCES
/
ED.PLM
< prev
next >
Wrap
Text File
|
1982-12-31
|
82KB
|
2,648 lines
$ TITLE(' CP/M-80 3.0 --- ED')
ED:
DO;
/* MODIFIED FOR .PRL OPERATION MAY, 1979 */
/* MODIFIED FOR OPERATION WITH CP/M 2.0 AUGUST 1979 */
/* modified for MP/M 2.0 June 1981 */
/* modified for CP/M 1.1 Oct 1981 */
/* modified for CONCURRENT CP/M 1.0 Jul 1982 */
/* modified for CP/M 3.0 July 1982 */
/* modified for CP/M 3.0 SEPT 1982 */
/* MODIFICATION LOG:
* July 1982 whf: some code cleanup (grouped logicals, declared BOOL);
* fixed disk full error handling; fixed read from null files;
* fixed (some) of the dirty fcb handling (shouldn't use settype
* function on open fcbs!).
* July 1982 dh: installed patches to change macro abort command from
* ^C to ^Y and to not print error message when trying to delete
* a file that doesn't exist. Added PERROR: PROCEDURE to print
* error messages in a consistant format and modified error
* message handler at RESET: entry point. Also corrected Invalid
* filename error to not abort ED if parsing a R or X command.
* Modified start (at PLM:) and SETDEST: to prompt for missing
* filenames. Modified parse$fcb & parse$lib to set a global
* flag and break if it got an invalid filename for X or R commands.
* Start sets page size from the system control block (SCB) if
* ED is running under CP/M-80 (high(ver)=0).
* The H command now works with new files. (sets newfile=false)
* Sept 82
* Corrected bug in which ED file b: didn't work. Changed PLM:
* and SETDEST: routines.
* Nov 82
* Corrected bug in parse$fcb where filenames of 9 characters and
* types of 4 characters where accepted as valid and truncated.
*/
$include (copyrt.lit)
declare
mpmproduct literally '01h', /* requires mp/m */
cpm3 literally '30h'; /* requires 3.0 cp/m */
declare plm label public; /* entry point for plm86 interface */
/* THE FOLLOWING COMMANDS CREATE ED.COM AND ED.CMD:
wm $1.plm
attach b 5
b:seteof $1.plm
vax $1.plm $$san\batch smpmcmd $1 date($2 Oct 81)\
b:is14
ERA $1.MOD
era $1
era $1.obj
:f1:PLM80 $1.PLM debug PAGEWIDTH(132) $3
:f1:link $101.obj,$1.obj,:f1:plm80.lib to $1.mod
:f1:locate $1.mod code(0100H) stacksize(100) map print($1.tra)
:f1:cpm
b:objcpm $1
attach b 1
the following VAX commands were used to create ED.CMD
$ asm86 scd1.a86 debug xref
! scd1 does a jump to the plm code
$ plm86 'p1'.plm 'p2' 'p3' 'p4' optimize(3) debug
$ link86 scd1.obj,'p1'.obj to 'p1'.lnk
$ loc86 'p1'.lnk od(sm(dats,code,data,stack,const)) ad(sm(code(0))) ss(stack(+16))
$ h86 'p1'
followed by the gencmd command
gencmd ed data[b1E3,m80,xFFF]
where 1E2 is the start of the constant area / 16 from ED.MP2
*/
/* DECLARE 8080 Interface
JMP EDCOMMAND - 3 (TO ADDRESS LXI SP)
EDJMP BYTE DATA(0C3H),
EDADR ADDRESS DATA(.EDCOMMAND-3); */
/**************************************
* *
* B D O S INTERFACE *
* *
**************************************/
mon1:
procedure (func,info) external;
declare func byte;
declare info address;
end mon1;
mon2:
procedure (func,info) byte external;
declare func byte;
declare info address;
end mon2;
mon3:
procedure (func,info) address external;
declare func byte;
declare info address;
end mon3;
declare fcb (1) byte external; /* 1st default fcb */
declare fcb16 (1) byte external; /* 2nd default fcb */
declare tbuff (1) byte external; /* default dma buffer */
DECLARE
MAXB ADDRESS EXTERNAL, /* MAX BASE 0006H */
BUFF (128)BYTE EXTERNAL, /* BUFFER 0080H */
SECTSHF LITERALLY '7', /* SHL(1,SECTSHF) = SECTSIZE */
SECTSIZE LITERALLY '80H'; /* SECTOR SIZE */
BOOT: PROCEDURE ;
call mon1(0,0); /* changed for MP/M-86 version */
/* SYSTEM REBOOT */
END BOOT;
$ eject
/* E D : T H E C P / M C O N T E X T E D I T O R */
/* COPYRIGHT (C) 1976, 1977, 1978, 1979, 1980, 1981, 1982
DIGITAL RESEARCH
BOX 579 PACIFIC GROVE
CALIFORNIA 93950
Revised:
07 April 81 by Thomas Rolander
21 July 81 by Doug Huskey
29 Oct 81 by Doug Huskey
10 Nov 81 by Doug Huskey
08 July 82 by Bill Fitler
26 July 82 by Doug Huskey
*/
/* DECLARE COPYRIGHT(*) BYTE DATA
(' COPYRIGHT (C) 1982, DIGITAL RESEARCH ');
**** this message should be in the header ***
*/
declare date(*) byte data ('8/82');
/* COMMAND FUNCTION
------- --------
A APPEND LINES OF TEXT TO BUFFER
B MOVE TO BEGINNING OR END OF TEXT
C SKIP CHARACTERS
D DELETE CHARACTERS
E END OF EDIT
F FIND STRING IN CURRENT BUFFER
H MOVE TO TOP OF FILE (HEAD)
I INSERT CHARACTERS FROM KEYBOARD
UP TO NEXT <ENDFILE>
J JUXTAPOSITION OPERATION - SEARCH FOR FIRST STRING,
INSERT SECOND STRING, DELETE UNTIL THIRD STRING
K DELETE LINES
L SKIP LINES
M MACRO DEFINITION (SEE COMMENT BELOW)
N FIND NEXT OCCURRENCE OF STRING
WITH AUTO SCAN THROUGH FILE
O RE-EDIT OLD FILE
P PAGE AND DISPLAY (MOVES UP OR DOWN 24 LINES AND
DISPLAYS 24 LINES)
Q QUIT EDIT WITHOUT UPDATING THE FILE
R<FILENAME> READ FROM FILE <FILENAME> UNTIL <ENDFILE> AND
INSERT INTO TEXT
S SEARCH FOR FIRST STRING, REPLACE BY SECOND STRING
T TYPE LINES
U TRANSLATE TO UPPER CASE (-U CHANGES TO NO TRANSLATE)
W WRITE LINES OF TEXT TO FILE
X<FILENAME> TRANSFER (XFER) LINES TO FILE <FILENAME>
Z SLEEP FOR 1/2 SECOND (USED IN MACROS TO STOP DISPLAY)
<CR> MOVE UP OR DOWN AND PRINT ONE LINE
IN GENERAL, THE EDITOR ACCEPTS SINGLE LETTER COMMANDS WITH OPTIONAL
INTEGER VALUES PRECEDING THE COMMAND. THE EDITOR ACCEPTS BOTH UPPER AND LOWER
CASE COMMANDS AND VALUES, AND PERFORMS TRANSLATION TO UPPER CASE UNDER THE FOL-
LOWING CONDITIONS. IF THE COMMAND IS TYPED IN UPPER CASE, THEN THE DATA WHICH
FOLLOWS IS TRANSLATED TO UPPER CASE. THUS, IF THE "I" COMMAND IS TYPED IN
UPPER CASE, THEN ALL INPUT IS AUTOMATICALLY TRANSLATED (ALTHOUGH ECHOED IN
LOWER CASE, AS TYPED). IF THE "A" COMMAND IS TYPED IN UPPER CASE, THEN ALL
INPUT IS TRANSLATED AS READ FROM THE DISK. GLOBAL TRANSLATION TO UPPER CASE
CAN BE CONTROLLED BY THE "U" COMMAND (-U TO NEGATE ITS EFFECT). IF YOU ARE
OPERATING WITH AN UPPER CASE ONLY TERMINAL, THEN OPERATION IS AUTOMATIC.
SIMILARLY, IF YOU ARE OPERATING WITH A LOWER CASE TERMINAL, AND TRANSLATION
TO UPPER CASE IS NOT SPECIFIED, THEN LOWER CASE CHARACTERS CAN BE ENTERED.
A NUMBER OF COMMANDS CAN BE PRECEDED BY A POSITIVE OR
NEGATIVE INTEGER BETWEEN 0 AND 65535 (1 IS DEFAULT IF NO VALUE
IS SPECIFIED). THIS VALUE DETERMINES THE NUMBER OF TIMES THE
COMMAND IS APPLIED BEFORE RETURNING FOR ANOTHER COMMAND.
THE COMMANDS
C D K L T P U <CR>
CAN BE PRECEDED BY AN UNSIGNED, POSITIVE, OR NEGATIVE NUMBER,
THE COMMANDS
A F J N W Z
CAN BE PRECEDED BY AN UNSIGNED OR POSITIVE NUMBER,
THE COMMANDS
E H O Q
CANNOT BE PRECEDED BY A NUMBER. THE COMMANDS
F I J M R S
ARE ALL FOLLOWED BY ONE OR MORE STRINGS OF CHARACTERS WHICH CAN
BE OPTIONALLY SEPARATED OR TERMINATED BY EITHER <ENDFILE> OR <CR>.
THE <ENDFILE> IS GENERALLY USED TO SEPARATE THE SEARCH STRINGS
IN THE S AND J COMMANDS, AND IS USED AT THE END OF THE COMMANDS IF
ADDITIONAL COMMANDS FOLLOW. FOR EXAMPLE, THE FOLLOWING COMMAND
SEQUENCE SEARCHES FOR THE STRING 'GAMMA', SUBSTITUTES THE STRING
'DELTA', AND THEN TYPES THE FIRST PART OF THE LINE WHERE THE
CHANGE OCCURRED, FOLLOWED BY THE REMAINDER OF THE LINE WHICH WAS
CHANGED:
SGAMMA<ENDFILE>DELTA<ENDFILE>0TT<CR>
THE CONTROL-L CHARACTER IN SEARCH AND SUBSTITUTE STRINGS IS
REPLACED ON INPUT BY <CR><LF> CHARACTERS. THE CONTROL-I KEY
IS TAKEN AS A TAB CHARACTER.
THE COMMANDS R & X MUST BE FOLLOWED BY A FILE NAME (WITH default
FILE TYPE OF 'LIB') WITH A TRAILING <CR> OR <ENDFILE>. THE COMMAND
I IS FOLLOWED BY A STRING OF SYMBOLS TO INSERT, TERMINATED BY
A <CR> OR <ENDFILE>. IF SEVERAL LINES OF TEXT ARE TO BE INSERTED,
THE I CAN BE DIRECTLY FOLLOWED BY AN <ENDFILE> OR <CR> IN WHICH
CASE THE EDITOR ACCEPTS LINES OF INPUT TO THE NEXT <ENDFILE>.
THE COMMAND 0T PRINTS THE FIRST PART OF THE CURRENT LINE,
AND THE COMMAND 0L MOVES THE REFERENCE TO THE BEGINNING OF THE
CURRENT LINE. THE COMMAND 0P PRINTS THE CURRENT PAGE ONLY, WHILE
THE COMMAND 0Z READS THE CONSOLE RATHER THAN WAITING (THIS IS USED
AGAIN WITHIN MACROS TO STOP THE DISPLAY - THE MACRO EXPANSION
STOPS UNTIL A CHARACTER IS READ. IF THE CHARACTER IS NOT A BREAK
THEN THE MACRO EXPANSION CONTINUES NORMALLY).
NOTE THAT A POUND SIGN IS TAKEN AS THE NUMBER 65535, ALL
UNSIGNED NUMBERS ARE ASSUMED POSITIVE, AND A SINGLE - IS ASSUMED -1
A NUMBER OF COMMANDS CAN BE GROUPED TOGETHER AND EXECUTED
REPETITIVELY USING THE MACRO COMMAND WHICH TAKES THE FORM
<NUMBER>MC1C2...CN<DELIMITER>
WHERE <NUMBER> IS A NON-NEGATIVE INTEGER N, AND <DELIMITER> IS
<ENDFILE> OR <CR>. THE COMMANDS C1 ... CN FOLLOWING THE M ARE
EXECUTED N TIMES, STARTING AT THE CURRENT POSITION IN THE BUFFER.
IF N IS 0, 1, OR OMITTED, THE COMMANDS ARE EXECUTED UNTIL THE END
IF THE BUFFER IS ENCOUNTERED.
THE FOLLOWING MACRO, FOR EXAMPLE, CHANGES ALL OCCURRENCES OF
THE NAME 'GAMMA' TO 'DELTA', AND PRINTS THE LINES WHICH
WERE CHANGED:
MFGAMMA<ENDFILE>-5DIDELTA<ENDFILE>0LT<CR>
(NOTE: AN <ENDFILE> IS THE CP/M END OF FILE MARK - CONTROL-Z)
IF ANY KEY IS DEPRESSED DURING TYPING OR MACRO EXPANSION, THE
FUNCTION IS CONSIDERED TERMINATED, AND CONTROL RETURNS TO THE
OPERATOR.
ERROR CONDITIONS ARE INDICATED BY PRINTING ONE OF THE CHARACTERS:
SYMBOL ERROR CONDITION
------ ----------------------------------------------------
GREATER FREE MEMORY IS EXHAUSTED - ANY COMMAND CAN BE ISSUED
WHICH DOES NOT INCREASE MEMORY REQUIREMENTS.
QUESTION UNRECOGNIZED COMMAND OR ILLEGAL NUMERIC FIELD
POUND CANNOT APPLY THE COMMAND THE NUMBER OF TIMES SPECFIED
(OCCURS IF SEARCH STRING CANNOT BE FOUND)
LETTER O CANNOT OPEN <FILENAME>.LIB IN R COMMAND
THE ERROR CHARACTER IS ALSO ACCOMPANIED BY THE LAST CHARACTER
SCANNED WHEN THE ERROR OCCURRED. */
$ eject
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * GLOBAL VARIABLES * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
DECLARE LIT LITERALLY 'LITERALLY',
DCL LIT 'DECLARE',
PROC LIT 'PROCEDURE',
ADDR LIT 'ADDRESS',
BOOLEAN LIT 'BYTE',
CTLL LIT '0CH',
CTLR LIT '12H', /* REPEAT LINE IN INSERT MODE */
CTLU LIT '15H', /* LINE DELETE IN INSERT MODE */
CTLX LIT '18H', /* EQUIVALENT TO CTLU */
CTLH LIT '08H', /* BACKSPACE */
TAB LIT '09H', /* TAB CHARACTER */
LCA LIT '110$0001B', /* LOWER CASE A */
LCZ LIT '111$1010B', /* LOWER CASE Z */
ESC LIT '1BH', /* ESCAPE CHARACTER */
ENDFILE LIT '1AH'; /* CP/M END OF FILE */
DECLARE
TRUE LITERALLY '1',
FALSE LITERALLY '0',
FOREVER LITERALLY 'WHILE TRUE',
CTRL$Y LITERALLY '19h',
CR LITERALLY '13',
LF LITERALLY '10',
WHAT LITERALLY '63';
DECLARE
MAX ADDRESS, /* .MEMORY(MAX)=0 (END) */
MAXM ADDRESS, /* MINUS 1 */
HMAX ADDRESS; /* = MAX/2 */
declare
i byte; /* used by command parsing */
DECLARE
us literally '8', /* file from user 0 */
RO LITERALLY '9', /* R/O FILE INDICATOR */
SY LITERALLY '10', /* SYSTEM FILE ATTRIBUTE */
EX LITERALLY '12', /* EXTENT NUMBER POSITION */
UB LITERALLY '13', /* UNFILLED BYTES */
ck LITERALLY '13', /* checksum */
MD LITERALLY '14', /* MODULE NUMBER POSITION */
NR LITERALLY '32', /* NEXT RECORD FIELD */
FS LITERALLY '33', /* FCB SIZE */
RFCB (FS) BYTE /* READER FILE CONTROL BLOCK */
INITIAL(0, /* FILE NAME */ ' ',
/* FILE TYPE */ 'LIB',0,0,0),
RBP BYTE, /* READ BUFFER POINTER */
XFCB (FS) BYTE /* XFER FILE CONTROL BLOCK */
INITIAL(0, 'X$$$$$$$','LIB',0,0,0,0,0,0,0),
XFCBE BYTE AT(.XFCB(EX)), /* XFCB EXTENT */
XFCBR BYTE AT(.XFCB(NR)), /* XFCB RECORD # */
xfcbext byte initial(0), /* save xfcb extent for appends */
xfcbrec byte initial(0), /* save xfcb record for appends */
XBUFF (SECTSIZE) BYTE, /* XFER BUFFER */
XBP BYTE, /* XFER POINTER */
NBUF BYTE, /* NUMBER OF BUFFERS */
BUFFLENGTH ADDRESS, /* NBUF * SECTSIZE */
SFCB (FS) BYTE AT(.FCB), /* SOURCE FCB = DEFAULT FCB */
SDISK BYTE AT (.FCB), /* SOURCE DISK */
SBUFFADR ADDRESS, /* SOURCE BUFFER ADDRESS */
SBUFF BASED SBUFFADR (128) BYTE, /* SOURCE BUFFER */
password (16) byte initial(0), /* source password */
DFCB (FS) BYTE, /* DEST FILE CONTROL BLOCK */
DDISK BYTE AT (.DFCB), /* DESTINATION DISK */
DBUFFADR ADDRESS, /* DESTINATION BUFFER ADDRESS */
DBUFF BASED DBUFFADR (128) BYTE, /* DEST BUFFER */
NSOURCE ADDRESS, /* NEXT SOURCE CHARACTER */
NDEST ADDRESS, /* NEXT DESTINATION CHAR */
tmpfcb (FS) BYTE; /* temporary fcb for rename & deletes */
DECLARE /**** some of the logicals *****/
newfile BOOLEAN initial (false), /* true if no source file */
onefile BOOLEAN initial (true), /* true if output file=input file */
XFERON BOOLEAN initial (false), /* TRUE IF XFER ACTIVE */
reading BOOLEAN initial (false), /* TRUE IF reading RFCB */
PRINTSUPPRESS BOOLEAN initial (false),/* TRUE IF PRINT SUPPRESSED */
sys BOOLEAN initial (false), /* true if system file */
protection BOOLEAN initial (false), /* password protection mode */
INSERTING BOOLEAN, /* TRUE IF INSERTING CHARACTERS */
READBUFF BOOLEAN, /* TRUE IF END OF READ BUFFER */
TRANSLATE BOOLEAN initial (false), /* TRUE IF XLATION TO UPPER CASE */
UPPER BOOLEAN initial (false), /* TRUE IF GLOBALLY XLATING TO UC */
LINESET BOOLEAN initial (true), /* TRUE IF LINE #'S PRINTED */
has$bdos3 BOOLEAN initial (false), /* true if BDOS version >= 3.0 */
tail BOOLEAN initial (true), /* true if readiing from cmd tail */
dot$found BOOLEAN initial (false); /* true if dot found in fname parse*/
DECLARE
dtype (3) byte, /* destination file type */
libfcb (12) byte initial(0,'X$$$$$$$LIB'),/* default lib name */
tempfl (3) byte initial('$$$'), /* temporary file type */
backup (3) byte initial('BAK'); /* backup file type */
declare
error$code address;
DECLARE
COLUMN BYTE initial(0), /* CONSOLE COLUMN POSITION */
SCOLUMN BYTE INITIAL(8), /* STARTING COLUMN IN "I" MODE */
TCOLUMN BYTE, /* TEMP DURING BACKSPACE */
QCOLUMN BYTE; /* TEMP DURING BACKSPACE */
DECLARE DCNT BYTE; /* RETURN CODE FROM MON? CALLS */
/* COMMAND BUFFER */
DECLARE (MAXLEN,COMLEN) BYTE, COMBUFF(128) BYTE,
CBP BYTE initial(0);
DECLARE /* LINE COUNTERS */
BASELINE ADDRESS, /* CURRENT LINE */
RELLINE ADDRESS; /* RELATIVE LINE IN TYPEOUT */
DECLARE
FORWARD LIT '1',
BACKWARD LIT '0',
RUBOUT LIT '07FH',
POUND LIT '23H',
MACSIZE LIT '128', /* MAX MACRO SIZE */
SCRSIZE LIT '100', /* SCRATCH BUFFER SIZE */
COMSIZE LIT 'ADDRESS'; /* DETERMINES MAX COMMAND NUMBER*/
DCL MACRO(MACSIZE) BYTE,
SCRATCH(SCRSIZE) BYTE, /* SCRATCH BUFFER FOR F,N,S */
(WBP, WBE, WBJ) BYTE, /* END OF F STRING, S STRING, J STRING */
(FLAG, MP, MI, XP) BYTE,
MT COMSIZE;
DCL (START, RESTART, OVERCOUNT, OVERFLOW,
disk$err, dir$err, RESET, BADCOM) LABEL;
/* global variables used by file parsing routines */
dcl ncmd byte initial(0);
DCL (DISTANCE, TDIST) COMSIZE,
(DIRECTION, CHAR) BYTE,
( FRONT, BACK, FIRST, LASTC) ADDR;
dcl LPP byte initial(23); /* LINES PER PAGE */
/* the following stucture is used near plm: to set
the lines per page from the BDOS 3 SCB */
declare
pb (2) byte data (28,0);
declare
ver address; /* VERSION NUMBER */
declare
err$msg address initial(0),
invalid (*) byte data ('Invalid Filename$'),
dirfull (*) byte data ('DIRECTORY FULL$'),
diskfull (*) byte data ('DISK FULL$'),
password$err(*) byte data ('Creating Password$'),
not$found (*) byte data ('File not found$'),
notavail (*) byte data ('File not available$');
$ eject
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * CP/M INTERFACE ROUTINES * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* IO SECTION */
READCHAR: PROCEDURE BYTE; RETURN MON2(1,0);
END READCHAR;
conin:
procedure byte;
return mon2(6,0fdh);
end conin;
PRINTCHAR: PROCEDURE(CHAR);
DECLARE CHAR BYTE;
IF PRINTSUPPRESS THEN RETURN;
CALL MON1(2,CHAR);
END PRINTCHAR;
TTYCHAR: PROCEDURE(CHAR);
DECLARE CHAR BYTE;
IF CHAR >= ' ' THEN COLUMN = COLUMN + 1;
IF CHAR = LF THEN COLUMN = 0;
CALL PRINTCHAR(CHAR);
END TTYCHAR;
BACKSPACE: PROCEDURE;
/* MOVE BACK ONE POSITION */
IF COLUMN = 0 THEN RETURN;
CALL TTYCHAR(CTLH); /* COLUMN = COLUMN - 1 */
CALL TTYCHAR(' ' ); /* COLUMN = COLUMN + 1 */
CALL TTYCHAR(CTLH); /* COLUMN = COLUMN - 1 */
COLUMN = COLUMN - 2;
END BACKSPACE;
PRINTABS: PROCEDURE(CHAR);
DECLARE (CHAR,I,J) BYTE;
I = CHAR = TAB AND 7 - (COLUMN AND 7);
IF CHAR = TAB THEN CHAR = ' ';
DO J = 0 TO I;
CALL TTYCHAR(CHAR);
END;
END PRINTABS;
GRAPHIC: PROCEDURE(C) BOOLEAN;
DECLARE C BYTE;
/* RETURN TRUE IF GRAPHIC CHARACTER */
IF C >= ' ' THEN RETURN TRUE;
RETURN C = CR OR C = LF OR C = TAB;
END GRAPHIC;
PRINTC: PROCEDURE(C);
DECLARE C BYTE;
IF NOT GRAPHIC(C) THEN
DO; CALL PRINTABS('^');
C = C + '@';
END;
CALL PRINTABS(C);
END PRINTC;
CRLF: PROCEDURE;
CALL PRINTC(CR); CALL PRINTC(LF);
END CRLF;
PRINTM: PROCEDURE(A);
DECLARE A ADDRESS;
CALL MON1(9,A);
END PRINTM;
PRINT: PROCEDURE(A);
DECLARE A ADDRESS;
CALL CRLF;
CALL PRINTM(A);
END PRINT;
perror: procedure(a);
declare a address;
call print(.(tab,'ERROR - $'));
call printm(A);
call crlf;
end perror;
READ: PROCEDURE(A);
DECLARE A ADDRESS;
CALL MON1(10,A);
END READ;
/* used for library files */
OPEN: PROCEDURE(FCB);
DECLARE FCB ADDRESS;
if MON2(15,FCB) = 255 then do;
flag = 'O';
err$msg = .not$found;
go to reset;
end;
END OPEN;
/* used for main source file */
OPEN$FILE: PROCEDURE(FCB) ADDRESS;
DECLARE FCB ADDRESS;
RETURN MON3(15,FCB);
END OPEN$FILE;
CLOSE: PROCEDURE(FCB);
DECLARE FCB ADDRESS;
DCNT = MON2(16,FCB);
END CLOSE;
DELETE: PROCEDURE(FCB);
DECLARE FCB ADDRESS;
DCNT = MON2(19,FCB);
END DELETE;
DISKREAD: PROCEDURE(FCB) BYTE;
DECLARE FCB ADDRESS;
RETURN MON2(20,FCB);
END DISKREAD;
DISKWRITE: PROCEDURE(FCB) BYTE;
DECLARE FCB ADDRESS;
RETURN MON2(21,FCB);
END DISKWRITE;
RENAME: PROCEDURE(FCB);
DECLARE FCB ADDRESS;
CALL MON1(23,FCB);
END RENAME;
READCOM: PROCEDURE;
MAXLEN = 128; CALL READ(.MAXLEN);
END READCOM;
BREAK$KEY: PROCEDURE BOOLEAN;
IF MON2(11,0) THEN
DO; /* CLEAR CHAR */
IF MON2(1,0) = CTRL$Y THEN
RETURN TRUE;
END;
RETURN FALSE;
END BREAK$KEY;
CSELECT: PROCEDURE BYTE;
/* RETURN CURRENT DRIVE NUMBER */
RETURN MON2(25,0);
END CSELECT;
SETDMA: PROCEDURE(A);
DECLARE A ADDRESS;
/* SET DMA ADDRESS */
CALL MON1(26,A);
END SETDMA;
set$attribute: procedure(FCB);
declare fcb address;
call MON1(30,FCB);
end set$attribute;
/* The PL/M built-in procedure "MOVE" can be used to move storage,
its definition is:
MOVE: PROCEDURE(COUNT,SOURCE,DEST);
DECLARE (COUNT,SOURCE,DEST) ADDRESS;
/ MOVE DATA FROM SOURCE TO DEST ADDRESSES, FOR COUNT BYTES /
END MOVE;
*/
/* this routine is included solely for
enconomy of space over the use of the
equivalent (in-line) code generated by
the built-in function */
move: proc(c,s,d);
dcl (s,d) addr, c byte;
dcl a based s byte, b based d byte;
do while (c:=c-1)<>255;
b=a; s=s+1; d=d+1;
end;
end move;
write$xfcb: PROCEDURE(FCB);
DECLARE FCB ADDRESS;
call move(8,.password,.password(8));
if MON2(103,FCB)= 0ffh then
call perror(.password$err);
END write$xfcb;
read$xfcb: PROCEDURE(FCB);
DECLARE FCB ADDRESS;
call MON1(102,FCB);
END read$xfcb;
/* 0ff => return BDOS errors */
return$errors:
procedure(mode);
declare mode byte;
call mon1 (45,mode);
end return$errors;
REBOOT: PROCEDURE;
IF XFERON THEN
CALL DELETE(.libfcb);
CALL BOOT;
END REBOOT;
version: procedure address;
/* returns current cp/m version # */
return mon3(12,0);
end version;
$ eject
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * SUBROUTINES * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* INPUT / OUTPUT BUFFERING ROUTINES */
/* abort ED and print error message */
ABORT: PROCEDURE(A);
DECLARE A ADDRESS;
CALL perror(A);
CALL REBOOT;
END ABORT;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* fatal file error */
FERR: PROCEDURE;
CALL CLOSE(.DFCB); /* ATTEMPT TO CLOSE FILE FOR LATER RECOVERY */
CALL ABORT (.dirfull);
END FERR;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set password if cpm 3*/
setpassword: procedure;
if has$bdos3 then
call setdma(.password);
end setpassword;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* delete file at afcb */
delete$file: procedure(afcb);
declare afcb address;
call setpassword;
call delete(afcb);
end delete$file;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* rename file at afcb */
rename$file: procedure(afcb);
declare afcb address;
call delete$file(afcb+16); /* delete new file */
call setpassword;
call rename(afcb);
end rename$file;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* make file at afcb */
make$file: procedure(afcb);
declare afcb address;
call delete$file(afcb); /* delete file */
call setpassword;
DCNT = MON2(22,afcb); /* create file */
end make$file;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* fill string @ s for c bytes with f */
fill: proc(s,f,c);
dcl s addr,
(f,c) byte,
a based s byte;
do while (c:=c-1)<>255;
a = f;
s = s+1;
end;
end fill;
$ eject
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * FILE HANDLING ROUTINES * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* set destination file type to type at A */
SETTYPE: PROCEDURE(afcb,A);
DECLARE (afcb, A) ADDRESS;
CALL MOVE(3,A,aFCB+9);
END SETTYPE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set dma to xfer buffer */
SETXDMA: PROCEDURE;
CALL SETDMA(.XBUFF);
END SETXDMA;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* fill primary source buffer */
FILLSOURCE: PROCEDURE;
DECLARE I BYTE;
ZN: PROCEDURE;
NSOURCE = 0;
END ZN;
CALL ZN;
DO I = 0 TO NBUF;
CALL SETDMA(SBUFFADR+NSOURCE);
IF (DCNT := DISKREAD(.FCB)) <> 0 THEN
DO; IF DCNT > 1 THEN CALL FERR;
SBUFF(NSOURCE) = ENDFILE;
I = NBUF;
END;
ELSE
NSOURCE = NSOURCE + SECTSIZE;
END;
CALL ZN;
END FILLSOURCE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* get next character in source file */
GETSOURCE: PROCEDURE BYTE;
DECLARE B BYTE;
if newfile then return endfile; /* in case they try to #a */
IF NSOURCE >= BUFFLENGTH THEN CALL FILLSOURCE;
IF (B := SBUFF(NSOURCE)) <> ENDFILE THEN
NSOURCE = NSOURCE + 1;
RETURN B;
END GETSOURCE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* try to free space by erasing backup */
erase$bak: PROCEDURE BOOLEAN;
if onefile then
if newfile then do;
call move(fs,.dfcb,.tmpfcb); /* can't diddle with open fcb */
CALL SETTYPE(.tmpfcb,.BACKUP);
CALL DELETE$file(.tmpfcb);
if dcnt <> 255 then
return true;
end;
return false;
end erase$bak;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* write output buffer up to (not including)
ndest (low 7 bits of ndest are 0 */
WRITEDEST: PROCEDURE;
DECLARE (I,N,save$ndest) BYTE;
n = shr(ndest,sectshf); /* calculate number sectors to write */
if n=0 then return; /* no need to write if we haven't filled sector*/
save$ndest = ndest; /* save for error recovery */
ndest = 0;
DO I = 1 TO N;
retry:
CALL SETDMA(DBUFFADR+NDEST);
IF DISKWRITE(.DFCB) <> 0 THEN
if erase$bak then
go to retry;
else do; /* reset buffer, let them take action (delete files) */
if ndest <> 0 then
call move(save$ndest-ndest, dbuffadr+ndest, dbuffadr);
ndest = save$ndest-ndest;
go to disk$err;
end;
NDEST = NDEST + SECTSIZE;
END;
ndest = 0;
END WRITEDEST;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* put a character in output buffer */
PUTDEST: PROCEDURE(B);
DECLARE B BYTE;
IF NDEST >= BUFFLENGTH THEN CALL WRITEDEST;
DBUFF(NDEST) = B;
NDEST = NDEST + 1;
END PUTDEST;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* put a character in the xfer buffer */
PUTXFER: PROCEDURE(C);
DECLARE C BYTE;
IF XBP >= SECTSIZE THEN /* BUFFER OVERFLOW */
DO;
retry:
CALL SETXDMA;
xfcbext = xfcbe; /* save for appends */
xfcbrec = xfcbr;
IF DISKWRITE(.XFCB) <> 0 THEN
if erase$bak then
go to retry;
else do;
/******** call close(.xfcb); *** commented out whf 8/82 !!!! ********/
go to disk$err;
end;
XBP = 0;
END;
XBUFF(XBP) = C; XBP = XBP + 1;
END PUTXFER;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* empty xfer buffer and close file.
This routine is added to allow saving lib
files for future edits - DH 10/18/81 */
close$xfer: procedure;
dcl i byte;
do i = xbp to sectsize;
call putxfer(ENDFILE);
end;
call close(.xfcb);
end close$xfer;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* compare xfcb and rfcb to see if same */
compare$xfer: procedure BOOLEAN;
dcl i byte;
i = 12;
do while (i:=i-1) <> -1;
if xfcb(i) <> rfcb(i) then
return false;
end;
return true;
end compare$xfer;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* restore xfer file extent and current
record, read record and set xfer pointer
to first ENDFILE */
append$xfer: procedure;
xfcbe = xfcbext;
call open(.xfcb);
xfcbr = xfcbrec;
call setxdma;
if diskread(.xfcb) = 0 then do;
xfcbr = xfcbrec; /* write same record */
do xbp = 0 to sectsize;
if xbuff(xbp) = ENDFILE then
return;
end;
end;
end append$xfer;
$ eject
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * END EDIT ROUTINE * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* finish edit, close files, rename */
FINIS: PROCEDURE;
MOVEUP: PROCEDURE(afcb);
dcl afcb address;
/* set second filename (new name) for rename function */
CALL MOVE(16,aFCB,aFCB+16);
END MOVEUP;
/* * * * * * * * WRITE OUTPUT BUFFER * * * * * * * * */
/* SET UNFILLED BYTES - USED FOR ISIS-II COMPATIBILITY */
/* DFUB = 0 ; <<<< REMOVE FOR MP/M 2 , CP/M 3 */
DO WHILE (LOW(NDEST) AND 7FH) <> 0;
/* COUNTS UNFILLED BYTES IN LAST RECORD */
/* DFUB = DFUB + 1; */
CALL PUTDEST(ENDFILE);
END;
CALL WRITEDEST;
if not newfile then
call close(.sfcb); /* close this to clean up for mp/m environs */
/* * * * * * CLOSE TEMPORARY DESTINATION FILE * * * * * */
CALL CLOSE(.DFCB);
IF DCNT = 255 THEN CALL FERR;
if sys then do;
dfcb(sy)=dfcb(sy) or 80h;
call setpassword;
call set$attribute(.dfcb);
end;
/* * * * * * RENAME SOURCE TO BACKUP IF ONE FILE * * * * * */
if onefile then do;
call moveup(.sfcb);
CALL SETTYPE(.sfcb+16,.BACKUP); /* set new type to BAK */
CALL RENAME$FILE(.SFCB);
end;
/* * * * * * RENAME TEMPORARY DESTINATION FILE * * * * * */
CALL MOVEUP(.DFCB);
CALL SETTYPE(.DFCB+16,.DTYPE);
CALL RENAME$FILE(.DFCB);
END FINIS;
$ eject
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * COMMAND ROUTINES * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* print a character if not macro expansion */
PRINTNMAC: PROCEDURE(CHAR);
DECLARE CHAR BYTE;
IF MP <> 0 THEN RETURN;
CALL PRINTC(CHAR);
END PRINTNMAC;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* return true if lower case character */
LOWERCASE: PROCEDURE(C) BOOLEAN;
DECLARE C BYTE;
RETURN C >= LCA AND C <= LCZ;
END LOWERCASE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* translate character to upper case */
UCASE: PROCEDURE(C) BYTE;
DECLARE C BYTE;
IF LOWERCASE(C) THEN RETURN C AND 5FH;
RETURN C;
END UCASE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* get password and place at fcb + 16 */
getpasswd: proc;
dcl (i,c) byte;
call crlf;
call print(.('Password ? ','$'));
retry:
call fill(.password,' ',8);
do i = 0 to 7;
nxtchr:
if (c:=ucase(conin)) >= ' ' then
password(i)=c;
if c = cr then
go to exit;
if c = CTLX then
goto retry;
if c = CTLH then do;
if i<1 then
goto retry;
else do;
password(i:=i-1)=' ';
goto nxtchr;
end;
end;
if c = 3 then
call reboot;
end;
exit:
c = break$key; /* clear raw I/O mode */
end getpasswd;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* translate to upercase if translate flag
is on (also translate ESC to ENDFILE) */
UTRAN: PROCEDURE(C) BYTE;
DECLARE C BYTE;
IF C = ESC THEN C = ENDFILE;
IF TRANSLATE THEN RETURN UCASE(C);
RETURN C;
END UTRAN;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* print the line number */
PRINTVALUE: PROCEDURE(V);
/* PRINT THE LINE VALUE V */
DECLARE D BYTE,
ZERO BOOLEAN,
(K,V) ADDRESS;
K = 10000;
ZERO = FALSE;
DO WHILE K <> 0;
D = LOW(V/K); V = V MOD K;
K = K / 10;
IF ZERO OR D <> 0 THEN
DO; ZERO = TRUE;
CALL PRINTC('0'+D);
END;
ELSE
CALL PRINTC(' ');
END;
END PRINTVALUE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* print line with number V */
PRINTLINE: PROCEDURE(V);
DECLARE V ADDRESS;
IF NOT LINESET THEN RETURN;
CALL PRINTVALUE(V);
CALL PRINTC(':');
CALL PRINTC(' ');
IF INSERTING THEN
CALL PRINTC(' ');
ELSE
CALL PRINTC('*');
END PRINTLINE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* print current line (baseline) */
PRINTBASE: PROCEDURE;
CALL PRINTLINE(BASELINE);
END PRINTBASE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* print current line if not in a macro */
PRINTNMBASE: PROCEDURE;
IF MP <> 0 THEN RETURN;
CALL PRINTBASE;
END PRINTNMBASE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* get next character from command tail */
getcmd: proc byte;
if buff(ncmd+1) <> 0 then
return buff(ncmd := ncmd + 1);
return cr;
end getcmd;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* read next char from command buffer */
READC: PROCEDURE BYTE;
/* MAY BE MACRO EXPANSION */
IF MP > 0 THEN
DO;
IF BREAK$KEY THEN GO TO OVERCOUNT;
IF XP >= MP THEN
DO; /* START AGAIN */
IF MT <> 0 THEN
DO; IF (MT:=MT-1) = 0 THEN
GO TO OVERCOUNT;
END;
XP = 0;
END;
RETURN UTRAN(MACRO((XP := XP + 1) - 1));
END;
IF INSERTING THEN RETURN UTRAN(READCHAR);
/* GET COMMAND LINE */
IF READBUFF THEN
DO; READBUFF = FALSE;
IF LINESET AND COLUMN = 0 THEN
DO;
IF BACK >= MAXM THEN
CALL PRINTLINE(0);
ELSE
CALL PRINTBASE;
END;
ELSE
CALL PRINTC('*');
CALL READCOM; CBP = 0;
CALL PRINTC(LF);
COLUMN = 0;
END;
IF (READBUFF := CBP = COMLEN ) THEN
COMBUFF(CBP) = CR;
RETURN UTRAN(COMBUFF((CBP := CBP +1) -1));
END READC;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* get upper case character from command
buffer or command line */
get$uc: proc;
if tail then
char = ucase(getcmd);
else
char = ucase(readc);
end get$uc;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* parse file name
this routine requires a routine to get
the next character and put it in a byte
variable */
parse$fcb: proc(fcbadr) byte;
dcl fcbadr addr;
dcl afcb based fcbadr (33) byte;
dcl drive lit 'afcb(0)';
dcl (i,delimiter) byte;
dcl pflag boolean;
putc: proc;
afcb(i := i + 1) = char;
pflag = true;
end putc;
delim: proc boolean;
dcl del(*) byte data (CR,ENDFILE,' ,.;=:<>_[]*?');
/* 0 1 2345678901234 */
do delimiter = 0 to last(del);
if char = del(delimiter) then do;
if delimiter > 12 then /* * or ? */
call perror(.('Cannot Edit Wildcard Filename$'));
return (true);
end;
end;
return (false);
end delim;
pflag = false;
flag = true; /* global flag set to false if invalid filename */
dot$found = false; /* allow null extensions in 'parse$lib' */
call get$uc;
if char <> CR then
if char <> ENDFILE then do;
/* initialize fcb to srce fcb type & drive */
call fill(fcbadr+12,0,21);
call fill(fcbadr+1,' ',11);
/* clear leading blanks */
do while char = ' ';
call get$uc;
end;
/* parse loop */
do while not delim;
i = 0;
/* get name */
do while not delim;
if i > 7 then
go to err; /* too long */
call putc;
call get$uc;
end;
if char = ':' then do;
/* get drive from afcb(1) */
if i <> 1 then
go to err; /* invalid : */
if (drive := afcb(1) - 'A' + 1) > 16 then
go to err; /* invalid drive */
afcb(1) = ' ';
call get$uc;
end;
if char = '.' then do;
/* get file type */
i = 8;
dot$found = true; /* .ext specified (may be null)*/
call get$uc;
do while not delim;
if i > 10 then
go to err; /* too long */
call putc;
call get$uc;
end;
end;
if char = ';' then do;
/* get password */
call fill(fcbadr+16,' ',8); /* where fn #152 puts passwd */
i = 15; /* passwd is last field */
call get$uc;
do while not delim;
if i > 23 then
go to err;
call putc;
call get$uc;
end;
call move(8,fcbadr+16,.password); /* where ed wants it */
end;
end; /* parse loop */
/* delimiter must be a comma or space */
if delimiter > 3 then /* not a CR,ENDFILE,SPACE,COMMA */
go to err;
if not pflag then
go to err;
end;
return (pflag);
err:
call perror(.invalid);
return (flag:=false);
end parse$fcb;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set up destination FCB */
setdest: PROCEDURE;
dcl i byte;
/* onefile = true; (initialized) */
if not tail then do;
call print(.('Enter Output file: $'));
call readcom;
cbp,readbuff = 0;
call crlf;
call crlf;
end;
if parse$fcb(.dfcb) then do;
onefile = false;
if dfcb(1) = ' ' then
call move(15,.sfcb+1,.dfcb+1);
end;
else
CALL MOVE(16,.SFCB,.DFCB);
call move(3,.dfcb(9),.dtype); /* save destination type */
end setdest;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set read lib file DMA address */
SETRDMA: PROCEDURE;
CALL SETDMA(.BUFF);
END SETRDMA;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* read lib file routine */
READFILE: PROCEDURE BYTE;
IF RBP >= SECTSIZE THEN
DO; CALL SETRDMA;
IF DISKREAD(.RFCB) <> 0 THEN RETURN ENDFILE;
RBP = 0;
END;
RETURN UTRAN(BUFF((RBP := RBP + 1) - 1));
END READFILE;
$ eject
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * INITIALIZATION * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
SETUP: PROCEDURE;
/* * * * * * * * * OPEN SOURCE FILE * * * * * * * * */
sfcb(ex), sfcb(md), sfcb(nr) = 0;
if has$bdos3 then do;
call return$errors(0FEh); /* set error mode */
call setpassword;
end;
error$code = open$file (.SFCB);
if has$bdos3 then do; /* extended bdos errors */
call return$errors(0); /* reset error mode */
if low(error$code) = 0FFh and high(error$code) = 7 then do;
call getpasswd; /* let them enter password */
call crlf;
call crlf;
call setpassword; /* set dma to password */
error$code = open$file(.fcb); /* reset error$code */
end;
if low(error$code)=0FFh and high(error$code)<>0 then
call abort(.notavail); /* abort anything but not found */
end;
dcnt=low(error$code);
if onefile then do;
IF ROL(FCB(RO),1) THEN
CALL abort(.('FILE IS READ/ONLY$'));
else IF ROL(FCB(SY),1) THEN /* system attribute */
do;
if rol(FCB(us),1) then
dcnt = 255; /* user 0 file so create */
else
sys = true;
end;
end;
/* * * * * * NEW FILE IF NO SOURCE FILE * * * * * */
IF DCNT = 255 THEN do;
if not onefile then
call abort(.not$found);
newfile = true;
CALL PRINT(.('NEW FILE$'));
CALL CRLF;
END;
/* * * * * * MAKE TEMPORARY DESTINATION FILE * * * * * */
CALL SETTYPE(.dfcb,.tempfl);
DFCB(EX)=0;
CALL MAKE$file(.DFCB);
if dcnt = 255 then
call ferr;
/* THE TEMP FILE IS NOW CREATED */
/* now create the password if any */
if protection <> 0 then do;
dfcb(ex) = protection or 1; /* set password */
call setpassword;
call write$xfcb(.dfcb);
end;
dfcb(ex),DFCB(32) = 0; /* NEXT RECORD IS ZERO */
/* * * * * * * * * RESET BUFFER * * * * * * * * */
NSOURCE = BUFFLENGTH;
NDEST = 0;
BASELINE = 1; /* START WITH LINE 1 */
END SETUP;
$ eject
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * BUFFER MANAGEMENT * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* DISTANCE is the number of lines prefix
to a command */
/* set maximum distance (0FFFFH) */
SETFF: PROCEDURE;
DISTANCE = 0FFFFH;
END SETFF;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* return true if distance is zero */
DISTZERO: PROCEDURE BOOLEAN;
RETURN DISTANCE = 0;
END DISTZERO;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set distance to zero */
ZERODIST: PROCEDURE;
DISTANCE = 0;
END ZERODIST;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* check for zero distance and decrement */
DISTNZERO: PROCEDURE BOOLEAN;
IF NOT DISTZERO THEN
DO; DISTANCE = DISTANCE - 1;
RETURN TRUE;
END;
RETURN FALSE;
END DISTNZERO;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set memory limits of command from
distance and direction */
SETLIMITS: PROC;
DCL (I,K,L,M) ADDR, (MIDDLE,LOOPING) BYTE;
RELLINE = 1; /* RELATIVE LINE COUNT */
IF DIRECTION = BACKWARD THEN
DO; DISTANCE = DISTANCE+1; I = FRONT; L = 0; K = 0FFFFH;
END;
ELSE
DO; I = BACK; L = MAXM; K = 1;
END;
LOOPING = TRUE;
DO WHILE LOOPING;
DO WHILE (MIDDLE := I <> L) AND
MEMORY(M:=I+K) <> LF;
I = M;
END;
LOOPING = (DISTANCE := DISTANCE - 1) <> 0;
IF NOT MIDDLE THEN
DO; LOOPING = FALSE;
I = I - K;
END;
ELSE do;
RELLINE = RELLINE - 1;
IF LOOPING THEN
I = M;
end;
END;
IF DIRECTION = BACKWARD THEN
DO; FIRST = I; LASTC = FRONT - 1;
END;
ELSE
DO; FIRST = BACK + 1; LASTC = I + 1;
END;
END SETLIMITS;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* increment current position */
INCBASE: PROCEDURE;
BASELINE = BASELINE + 1;
END INCBASE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* decrement current position */
DECBASE: PROCEDURE;
BASELINE = BASELINE - 1;
END DECBASE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* increment limits */
INCFRONT: PROC; FRONT = FRONT + 1;
END INCFRONT;
INCBACK: PROCEDURE; BACK = BACK + 1;
END INCBACK;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* decrement limits */
DECFRONT: PROC; FRONT = FRONT - 1;
IF MEMORY(FRONT) = LF THEN
CALL DECBASE;
END DECFRONT;
DECBACK: PROC; BACK = BACK - 1;
END DECBACK;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* move current page in memory if move flag
true otherwise delete it */
MEM$MOVE: PROC(MOVEFLAG);
DECLARE (MOVEFLAG,C) BYTE;
/* MOVE IF MOVEFLAG IS TRUE */
IF DIRECTION = FORWARD THEN
DO WHILE BACK < LASTC; CALL INCBACK;
IF MOVEFLAG THEN
DO;
IF (C := MEMORY(BACK)) = LF THEN CALL INCBASE;
MEMORY(FRONT) = C; CALL INCFRONT;
END;
END;
ELSE
DO WHILE FRONT > FIRST; CALL DECFRONT;
IF MOVEFLAG THEN
DO; MEMORY(BACK) = memory(front); CALL DECBACK;
END;
END;
END MEM$MOVE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* force a memory move */
MOVER: PROC;
CALL MEM$MOVE(TRUE);
END MOVER;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* reset memory limit pointers, deleting
characters (used by D command) */
SETPTRS: PROC;
CALL MEM$MOVE(FALSE);
END SETPTRS;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set limits and force a move */
MOVELINES: PROC;
CALL SETLIMITS;
CALL MOVER;
END MOVELINES;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set front to lower value deleteing
characters (used by S and J commands) */
setfront: proc(newfront);
dcl newfront addr;
do while front <> newfront;
call decfront;
end;
end setfront;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set limits for memory move */
SETCLIMITS: PROC;
IF DIRECTION = BACKWARD THEN
DO; LASTC = BACK;
IF DISTANCE > FRONT THEN
FIRST = 1;
ELSE
FIRST = FRONT - DISTANCE;
END;
ELSE
DO; FIRST = FRONT;
IF DISTANCE >= MAX - BACK THEN
LASTC = MAXM;
ELSE
LASTC = BACK + DISTANCE;
END;
END SETCLIMITS;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* read another line of input */
READLINE: PROCEDURE;
DECLARE B BYTE;
/* READ ANOTHER LINE OF INPUT */
CTRAN: PROCEDURE(B) BYTE;
DECLARE B BYTE;
/* CONDITIONALLY TRANSLATE TO UPPER CASE ON INPUT */
IF UPPER THEN RETURN UTRAN(B);
RETURN B;
END CTRAN;
DO FOREVER;
IF FRONT >= BACK THEN GO TO OVERFLOW;
IF (B := CTRAN(GETSOURCE)) = ENDFILE THEN
DO; CALL ZERODIST; RETURN;
END;
MEMORY(FRONT) = B;
CALL INCFRONT;
IF B = LF THEN
DO; CALL INCBASE;
RETURN;
END;
END;
END READLINE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* write one line out */
WRITELINE: PROCEDURE;
DECLARE B BYTE;
DO FOREVER;
IF BACK >= MAXM THEN /* EMPTY */
DO; CALL ZERODIST; RETURN;
END;
CALL INCBACK;
CALL PUTDEST(B:=MEMORY(BACK));
IF B = LF THEN
DO; CALL INCBASE;
RETURN;
END;
END;
END WRITELINE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* write lines until at least half the
the buffer is empty */
WRHALF: PROCEDURE;
CALL SETFF;
DO WHILE DISTNZERO;
IF HMAX >= (MAXM - BACK) THEN
CALL ZERODIST;
ELSE
CALL WRITELINE;
END;
END WRHALF;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* write lines determined by distance
called from W and E commands */
WRITEOUT: PROCEDURE;
DIRECTION = BACKWARD; FIRST = 1; LASTC = BACK;
CALL MOVER;
IF DISTZERO THEN CALL WRHALF;
/* DISTANCE = 0 IF CALL WRHALF */
DO WHILE DISTNZERO;
CALL WRITELINE;
END;
IF BACK < LASTC THEN
DO; DIRECTION = FORWARD; CALL MOVER;
END;
END WRITEOUT;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* clear memory buffer */
CLEARMEM: PROCEDURE;
CALL SETFF;
CALL WRITEOUT;
END CLEARMEM;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* clear buffers, terminate edit */
TERMINATE: PROCEDURE;
CALL CLEARMEM;
if not newfile then
DO WHILE (CHAR := GETSOURCE) <> ENDFILE;
CALL PUTDEST(CHAR);
END;
CALL FINIS;
END TERMINATE;
$ eject
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * COMMAND PRIMITIVES * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* insert char into memory buffer */
INSERT: PROCEDURE;
IF FRONT = BACK THEN GO TO OVERFLOW;
MEMORY(FRONT) = CHAR; CALL INCFRONT;
IF CHAR = LF THEN CALL INCBASE;
END INSERT;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* read a character and check for endfile
or CR */
SCANNING: PROCEDURE BYTE;
RETURN NOT ((CHAR := READC) = ENDFILE OR
(CHAR = CR AND NOT INSERTING));
END SCANNING;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* read command buffer and insert characters
into scratch 'til next endfile or CR for
find, next, juxt, or substitute commands
fill at WBE and increment WBE so it
addresses the next empty position of scratch */
COLLECT: PROCEDURE;
SETSCR: PROCEDURE;
SCRATCH(WBE) = CHAR;
IF (WBE := WBE + 1) >= SCRSIZE THEN GO TO OVERFLOW;
END SETSCR;
DO WHILE SCANNING;
IF CHAR = CTLL THEN
DO; CHAR = CR; CALL SETSCR;
CHAR = LF;
END;
IF CHAR = 0 THEN GO TO BADCOM;
CALL SETSCR;
END;
END COLLECT;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* find the string in scratch starting at
PA and ending at PB */
FIND: PROCEDURE(PA,PB) BYTE;
DECLARE (PA,PB) BYTE;
DECLARE J ADDRESS,
(K, MATCH) BYTE;
J = BACK ;
MATCH = FALSE;
DO WHILE NOT MATCH AND (MAXM > J);
LASTC,J = J + 1; /* START SCAN AT J */
K = PA ; /* ATTEMPT STRING MATCH AT K */
DO WHILE SCRATCH(K) = MEMORY(LASTC) AND
NOT (MATCH := K = PB);
/* MATCHED ONE MORE CHARACTER */
K = K + 1; LASTC = LASTC + 1;
END;
END;
IF MATCH THEN /* MOVE STORAGE */
DO; LASTC = LASTC - 1; CALL MOVER;
END;
RETURN MATCH;
END FIND;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set up the search string for F, N, and
S commands */
SETFIND: PROCEDURE;
WBE = 0; CALL COLLECT; WBP = WBE;
END SETFIND;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* check for found string in F and S commands */
CHKFOUND: PROCEDURE;
IF NOT FIND(0,WBP) THEN /* NO MATCH */ GO TO OVERCOUNT;
END CHKFOUND;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* parse read / xfer lib FCB */
parse$lib: procedure(fcbadr) byte;
dcl fcbadr address;
dcl afcb based fcbadr (33) byte;
dcl b byte;
b = parse$fcb(fcbadr);
/* flag = false if invalid */
if not flag then do;
flag = 'O';
goto reset;
end;
if afcb(9) = ' ' and not dot$found then
call move(3,.libfcb(9),fcbadr+9);
if afcb(1) = ' ' then
call move(8,.libfcb(1),fcbadr+1);
return b;
end parse$lib;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* print relative position */
PRINTREL: PROCEDURE;
CALL PRINTLINE(BASELINE+RELLINE);
END PRINTREL;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* type lines command */
TYPELINES: PROCEDURE;
DCL I ADDR;
DCL C BYTE;
CALL SETLIMITS;
/* DISABLE THE * PROMPT */
INSERTING = TRUE;
IF DIRECTION = FORWARD THEN
DO; RELLINE = 0; I = FRONT;
END;
ELSE
I = FIRST;
IF (C := MEMORY(I-1)) = LF then do;
if COLUMN <> 0 THEN
CALL CRLF;
end;
else
relline = relline + 1;
DO I = FIRST TO LASTC;
IF C = LF THEN
DO;
CALL PRINTREL;
RELLINE = RELLINE + 1;
IF BREAK$KEY THEN GO TO OVERCOUNT;
END;
CALL PRINTC(C:=MEMORY(I));
END;
END TYPELINES;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set distance to lines per page (LPP) */
SETLPP: PROCEDURE;
DISTANCE = LPP;
END SETLPP;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* save distance in TDIST */
SAVEDIST: PROCEDURE;
TDIST = DISTANCE;
END SAVEDIST;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Restore distance from TDIST */
RESTDIST: PROCEDURE;
DISTANCE = TDIST;
END RESTDIST;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* page command (move n pages and print) */
PAGE: PROCEDURE;
DECLARE I BYTE;
CALL SAVEDIST;
CALL SETLPP;
CALL MOVELINES;
I = DIRECTION;
DIRECTION = FORWARD;
CALL SETLPP;
CALL TYPELINES;
DIRECTION = I;
IF LASTC = MAXM OR FIRST = 1 THEN
CALL ZERODIST;
ELSE
CALL RESTDIST;
END PAGE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* wait command (1/2 second time-out) */
WAIT: PROCEDURE;
DECLARE I BYTE;
DO I = 0 TO 19;
IF BREAK$KEY THEN GO TO RESET;
CALL TIME(250);
END;
END WAIT;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set direction to forward */
SETFORWARD: PROCEDURE;
DIRECTION = FORWARD;
DISTANCE = 1;
END SETFORWARD;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* append 'til buffer is at least half full */
APPHALF: PROCEDURE;
CALL SETFF; /* DISTANCE = 0FFFFH */
DO WHILE DISTNZERO;
IF FRONT >= HMAX THEN
CALL ZERODIST;
ELSE
CALL READLINE;
END;
END APPHALF;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* insert CR LF characters */
INSCRLF: PROCEDURE;
/* INSERT CR LF CHARACTERS */
CHAR = CR; CALL INSERT;
CHAR = LF; CALL INSERT;
END INSCRLF;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* test if invalid delete or
backspace at beginning of inserting */
ins$error$chk: procedure;
if (tcolumn = 255) or (front = 1) then
go to reset;
end ins$error$chk;
$ eject
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * COMMAND PARSING * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* test for upper or lower case command
set translate flag (used to determine
if following characters should be translated
to upper case */
TESTCASE: PROCEDURE;
DECLARE T BYTE;
TRANSLATE = TRUE;
T = LOWERCASE(CHAR);
CHAR = UTRAN(CHAR);
TRANSLATE = UPPER OR NOT T;
END TESTCASE;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set translate to false and read next
character */
READCTRAN: PROCEDURE;
TRANSLATE = FALSE;
CHAR = READC;
CALL TESTCASE;
END READCTRAN;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* return true if command is only character
not in macro or combination on a line */
SINGLECOM: PROCEDURE(C) BOOLEAN;
DECLARE C BYTE;
RETURN CHAR = C AND COMLEN = 1 AND MP = 0;
END SINGLECOM;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* return true if command is only character
not in macro or combination on a line, and
the operator has responded with a 'Y' to a
Y/N request */
SINGLERCOM: PROCEDURE(C) BOOLEAN;
DECLARE (C,i) BYTE;
IF SINGLECOM(C) THEN
DO forever;
CALL CRLF; CALL PRINTCHAR(C);
CALL MON1(9,.('-(Y/N)',WHAT,'$'));
i = UCASE(READCHAR); CALL CRLF;
IF i = 'N' THEN GO TO START;
if i = 'Y' then
RETURN TRUE;
END;
RETURN FALSE;
END SINGLERCOM;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* return true if char is a digit */
DIGIT: PROCEDURE BOOLEAN;
RETURN (I := CHAR - '0') <= 9;
END DIGIT;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* return with distance = number char =
next command */
NUMBER: PROCEDURE;
DISTANCE = 0;
DO WHILE DIGIT;
DISTANCE = SHL(DISTANCE,3) +
SHL(DISTANCE,1) + I;
CALL READCTRAN;
END;
END NUMBER;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* set distance to distance relative to
the current line */
RELDISTANCE: PROCEDURE;
IF DISTANCE > BASELINE THEN
DO; DIRECTION = FORWARD;
DISTANCE = DISTANCE - BASELINE;
END;
ELSE
DO; DIRECTION = BACKWARD;
DISTANCE = BASELINE - DISTANCE;
END;
END RELDISTANCE;
$ eject
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * MAIN PROGRAM * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
plm: /* entry of MP/M-86 Interface */
/* INITIALIZE THE SYSTEM */
ver = version;
if low(ver) >= cpm3 then
has$bdos3 = true; /* handles passwords & xfcbs */
/* * * * * * * SET UP MEMORY BUFFER * * * * * * * * * */
/* I/O BUFFER REGION IS 1/8 AVAILABLE MEMORY */
NBUF = SHR(MAX := MAXB - .MEMORY,SECTSHF+3) - 1;
/* NBUF IS NUMBER OF BUFFERS - 1 */
BUFFLENGTH = SHL(DOUBLE(NBUF+1),SECTSHF+1);
/* NOW SET MAX AS REMAINDER OF FREE MEMORY */
IF BUFFLENGTH + 1024 > MAX THEN
DO; CALL perror(.('Insufficient memory$'));
CALL BOOT;
END;
/* REMOVE BUFFER SPACE AND 00 AT END OF MEMORY VECTOR */
MAX = MAX - BUFFLENGTH - 1;
/* RESET BUFFER LENGTH FOR I AND O */
BUFFLENGTH = SHR(BUFFLENGTH,1);
SBUFFADR = MAXB - BUFFLENGTH;
DBUFFADR = SBUFFADR - BUFFLENGTH;
MEMORY(MAX) = 0; /* STOPS MATCH AT END OF BUFFER */
MAXM = MAX - 1;
HMAX = SHR(MAXM,1);
/* * * * * * SET UP SOURCE & DESTINATION FILES * * * * * */
if fcb(1)=' ' then do;
call print(.('Enter Input file: $'));
call readcom;
call crlf;
tail = false;
end;
if not parse$fcb(.SFCB) then /* parse source fcb */
call reboot;
if has$bdos3 then do;
call read$xfcb(.sfcb); /* get prot from source */
protection = sfcb(ex); /* password protection mode */
sfcb(ex) = 0;
if high(ver) = 0 then /* CP/M-80 */
if (lpp:=mon2(49,.pb)) = 0 then
lpp = 23; /* get lines per page from SCB */
end;
call setdest; /* parse destination file */
tail = false; /* parse$fcb from ED command */
/* SOURCE AND DESTINATION DISKS SET */
/* IF SOURCE AND DESTINATION DISKS DIFFER, CHECK FOR
AN EXISTING SOURCE FILE ON THE DESTINATION DISK - THERE
COULD BE A FATAL ERROR CONDITION WHICH COULD DESTROY A
FILE IF THE USER HAPPENED TO BE ADDRESSING THE WRONG
DISK */
IF (SDISK <> DDISK) or not onefile THEN
IF mon2(15,.dfcb) <> 255 THEN /* try to open */
/* SOURCE FILE PRESENT ON DEST DISK */
CALL ABORT(.('Output File Exists, Erase It$'));
RESTART:
CALL SETUP;
MEMORY(0) = LF;
FRONT = 1; BACK = MAXM;
COLUMN = 0;
GO TO START;
OVERCOUNT: FLAG = POUND; GO TO RESET;
BADCOM: FLAG = WHAT; GO TO RESET;
OVERFLOW: /* ARRIVE HERE ON OVERFLOW CONDITION (I,F,S COMMAND) */
FLAG = '>'; go to reset;
disk$err:
flag = 'F';
err$msg = .diskfull;
go to reset;
dir$err:
flag = 'F';
err$msg = .dirfull;
RESET: /* ARRIVE HERE ON ERROR CONDITION */
PRINTSUPPRESS = FALSE;
CALL PRINT(.(tab,'BREAK "$'));
CALL PRINTC(FLAG);
CALL PRINTM(.('" AT $'));
if char = CR or char = LF then
call printm(.('END OF LINE$'));
else
CALL PRINTC(CHAR);
if err$msg <> 0 then do;
call perror(err$msg);
err$msg = 0;
end;
CALL CRLF;
START:
READBUFF = TRUE;
MP = 0;
$ eject
DO FOREVER; /* OR UNTIL THE POWER IS TURNED OFF */
/* **************************************************************
SIMPLE COMMANDS (CANNOT BE PRECEDED BY DIRECTION/DISTANCE):
E END THE EDIT NORMALLY
H MOVE TO HEAD OF EDITED FILE
I INSERT CHARACTERS
O RETURN TO THE ORIGINAL FILE
R READ FROM LIBRARY FILE
Q QUIT EDIT WITHOUT CHANGES TO ORIGINAL FILE
************************************************************** */
INSERTING = FALSE;
CALL READCTRAN;
FLAG = 'E';
MI = CBP; /* SAVE STARTING ADDRESS FOR <CR> COMMAND */
IF SINGLECOM('E') THEN
DO; CALL TERMINATE;
CALL REBOOT;
END;
ELSE IF SINGLECOM('H') THEN /* GO TO TOP */
DO; CALL TERMINATE;
newfile = false;
if onefile then do;
/* PING - PONG DISKS */
CHAR = DDISK;
DDISK = SDISK;
SDISK = CHAR;
end;
else do;
call settype(.dfcb,.dtype);
call move (16,.dfcb,.sfcb); /* source = destination */
onefile = true;
end;
GO TO RESTART;
END;
ELSE IF CHAR = 'I' THEN /* INSERT CHARACTERS */
DO;
IF (INSERTING := (CBP = COMLEN) AND (MP = 0)) THEN do;
tcolumn = 255; /* tested in ins$error$chk routine */
distance = 0;
direction = backward;
if memory(front-1) = LF then
call printbase;
else
call typelines;
end;
DO WHILE SCANNING;
DO WHILE CHAR <> 0;
IF CHAR=CTLU OR CHAR=CTLX OR CHAR=CTLR THEN
/* LINE DELETE OR RETYPE */
DO;
/* ELIMINATE OR REPEAT THE LINE */
IF CHAR = CTLR THEN
DO; CALL CRLF;
CALL TYPELINES;
END;
ELSE
/* LINE DELETE */
DO; CALL SETLIMITS; CALL SETPTRS;
IF CHAR = CTLU THEN
DO; CALL CRLF; CALL PRINTNMBASE;
END;
ELSE
/* MUST BE CTLX */
DO WHILE COLUMN > SCOLUMN;
CALL BACKSPACE;
END;
END;
END;
ELSE IF CHAR = CTLH THEN
DO;
call ins$error$chk;
IF (TCOLUMN := COLUMN) > 0 THEN
CALL PRINTNMAC(' '); /* RESTORE AFT BACKSP */
call decfront;
if tcolumn > scolumn then
DO; /* CHARACTER CAN BE ELIMINATED */
PRINTSUPPRESS = TRUE;
/* BACKSPACE CHARACTER ACCEPTED */
COLUMN = 0;
CALL TYPELINES;
PRINTSUPPRESS = FALSE;
/* COLUMN POSITION NOW RESET */
IF (QCOLUMN := COLUMN) < SCOLUMN THEN
QCOLUMN = SCOLUMN;
COLUMN = TCOLUMN; /* ORIGINAL VALUE */
DO WHILE COLUMN > QCOLUMN;
CALL BACKSPACE;
END;
END;
else
do;
if memory(front-1) = CR then
call decfront;
call crlf;
call typelines;
end;
CHAR = 0;
END;
ELSE IF CHAR = RUBOUT THEN
DO; call ins$error$chk;
CALL DECFRONT; CALL PRINTC(CHAR:=MEMORY(FRONT));
CHAR = 0;
END;
else if char = LF and memory(front-1) <> CR then
do;
call printc(CR);
call inscrlf;
end;
ELSE
/* NOT A SPECIAL CASE */
DO;
IF NOT GRAPHIC(CHAR) THEN
DO;
CALL PRINTNMAC('^');
CALL PRINTNMAC(CHAR + '@');
end;
/* COLUMN COUNT GOES UP IF GRAPHIC */
/* COMPUTE OUTPUT COLUMN POSITION */
if char = CTLL and not inserting then
call inscrlf;
else do;
IF MP = 0 THEN
DO;
IF CHAR >= ' ' THEN
COLUMN = COLUMN + 1;
ELSE IF CHAR = TAB THEN
COLUMN = COLUMN + (8 - (COLUMN AND 111B));
END;
CALL INSERT;
END;
end;
IF CHAR = LF THEN CALL PRINTNMBASE;
IF CHAR = CR THEN
CALL PRINTNMAC(CHAR:=LF);
ELSE
CHAR = 0;
tcolumn = 0;
END; /* of while char <> 0 */
END; /* of while scanning */
IF CHAR <> ENDFILE THEN do; /* MUST HAVE STOPPED ON CR */
CALL INSCRLF;
column = 0;
end;
IF INSERTING AND LINESET THEN CALL CRLF;
END;
ELSE IF SINGLERCOM('O') THEN /* FORGET THIS EDIT */
do;
call close(.sfcb);
GO TO RESTART;
end;
ELSE IF CHAR = 'R' THEN
DO; DECLARE I BYTE;
/* READ FROM LIB FILE */
CALL SETRDMA;
IF (FLAG := parse$lib(.rfcb)) THEN
reading = false;
if not reading then do;
if not flag then
/* READ FROM XFER FILE */
CALL MOVE(12,.XFCB,.RFCB);
RFCB(12), RFCB(32) = 0; /* zero extent, next record */
rbp = sectsize;
CALL open(.RFCB);
reading = true;
end;
DO WHILE (CHAR := READFILE) <> ENDFILE;
CALL INSERT;
END;
reading = false;
call close (.rfcb);
END;
ELSE IF SINGLERCOM('Q') THEN
DO;
CALL DELETE$file(.DFCB);
if newfile or not onefile then do;
call settype(.dfcb,.dtype);
call delete$file(.dfcb);
end;
CALL REBOOT;
END;
ELSE
/* MAY BE A COMMAND WHICH HAS AN OPTIONAL DIRECTION AND DISTANCE */
DO; /* SCAN A SIGNED INTEGER VALUE (IF ANY) */
DCL I BYTE;
CALL SETFORWARD;
IF CHAR = '-' THEN
DO; CALL READCTRAN; DIRECTION = BACKWARD;
END;
IF CHAR = POUND THEN
DO; CALL SETFF; CALL READCTRAN;
END;
ELSE IF DIGIT THEN
DO; CALL NUMBER;
/* MAY BE ABSOLUTE LINE REFERENCE */
IF CHAR = ':' THEN
DO; CHAR = 'L';
CALL RELDISTANCE;
END;
END;
ELSE IF CHAR = ':' THEN /* LEADING COLON */
DO; CALL READCTRAN; /* CLEAR THE COLON */
CALL NUMBER;
CALL RELDISTANCE;
IF DIRECTION = FORWARD THEN
DISTANCE = DISTANCE + 1;
END;
$ eject
IF DISTZERO THEN
DIRECTION = BACKWARD;
/* DIRECTION AND DISTANCE ARE NOW SET */
/* **************************************************************
MAY BE A COMMAND WHICH HAS DIRECTION AND DISTANCE SPECIFIED:
B BEGINNING/BOTTOM OF BUFFER
C MOVE CHARACTER POSITIONS
D DELETE CHARACTERS
K KILL LINES
L MOVE LINE POSITION
P PAGE UP OR DOWN (LPP LINES AND PRINT)
T TYPE LINES
U UPPER CASE TRANSLATE
V VERIFY LINE NUMBERS
<CR> MOVE UP OR DOWN LINES AND PRINT LINE
************************************************************** */
IF CHAR = 'B' THEN
DO; DIRECTION = 1 - DIRECTION;
FIRST = 1; LASTC = MAXM; CALL MOVER;
END;
ELSE IF CHAR = 'C' THEN
DO; CALL SETCLIMITS; CALL MOVER;
END;
ELSE IF CHAR = 'D' THEN
DO; CALL SETCLIMITS;
CALL SETPTRS; /* SETS BACK/FRONT */
END;
ELSE IF CHAR = 'K' THEN
DO; CALL SETLIMITS;
CALL SETPTRS;
END;
ELSE IF CHAR = 'L' THEN
CALL MOVELINES;
ELSE IF CHAR = 'P' THEN /* PAGE MODE PRINT */
DO;
IF DISTZERO THEN
DO; DIRECTION = FORWARD;
CALL SETLPP; CALL TYPELINES;
END;
ELSE
DO WHILE DISTNZERO; CALL PAGE;
CALL WAIT;
END;
END;
ELSE IF CHAR = 'T' THEN
CALL TYPELINES;
ELSE IF CHAR = 'U' THEN
UPPER = DIRECTION = FORWARD;
ELSE IF CHAR = 'V' THEN
DO; /* 0V DISPLAYS BUFFER STATE */
IF DISTZERO THEN
DO; CALL PRINTVALUE(BACK-FRONT);
CALL PRINTC('/');
CALL PRINTVALUE(MAXM);
CALL CRLF;
END;
ELSE if (LINESET := DIRECTION = FORWARD) then
scolumn = 8;
else
scolumn = 0;
END;
ELSE IF CHAR = CR THEN /* MAY BE MOVE/TYPE COMMAND */
DO;
IF MI = 1 AND MP = 0 THEN /* FIRST COMMAND */
DO; CALL MOVELINES; CALL SETFORWARD; CALL TYPELINES;
END;
END;
$ eject
ELSE IF DIRECTION = FORWARD OR DISTZERO THEN
DO;
/* **************************************************************
COMMANDS WHICH ALLOW ONLY A PRECEDING NUMBER:
A APPEND LINES
F FIND NTH OCCURRENCE
M APPLY MACRO
N SAME AS F WITH AUTOSCAN THROUGH FILE
S PERFORM N SUBSTITUTIONS
W WRITE LINES TO OUTPUT FILE
X TRANSFER (XFER) LINES TO TEMP FILE
Z SLEEP
************************************************************** */
IF CHAR = 'A' THEN
DO; DIRECTION = FORWARD;
FIRST = FRONT; LASTC = MAXM; CALL MOVER;
/* ALL STORAGE FORWARD */
IF DISTZERO THEN CALL APPHALF;
/* DISTANCE = 0 IF APPHALF CALLED */
DO WHILE DISTNZERO;
CALL READLINE;
END;
DIRECTION = BACKWARD; CALL MOVER;
/* POINTERS REPOSITIONED */
END;
ELSE IF CHAR = 'F' THEN
DO; CALL SETFIND; /* SEARCH STRING SCANNED
AND SETUP BETWEEN 0 AND WBP-1 IN SCRATCH */
DO WHILE DISTNZERO; CALL CHKFOUND;
END;
END;
ELSE IF CHAR = 'J' THEN /* JUXTAPOSITION OPERATION */
DO; DECLARE T ADDRESS;
CALL SETFIND; CALL COLLECT;
WBJ = WBE; CALL COLLECT;
/* SEARCH FOR STRING 0 - WBP-1, INSERT STRING WBP TO WBJ-1,
AND THEN DELETE UP TO STRING WBJ TO WBE-1 */
DO WHILE DISTNZERO; CALL CHKFOUND;
/* INSERT STRING */ MI = WBP - 1;
DO WHILE (MI := MI + 1) < WBJ;
CHAR = SCRATCH(MI); CALL INSERT;
END;
T = FRONT; /* SAVE POSITION FOR DELETE */
IF NOT FIND(WBJ,WBE) THEN GO TO OVERCOUNT;
/* STRING FOUND, SO MOVE IT BACK */
FIRST = FRONT - (WBE - WBJ);
DIRECTION = BACKWARD; CALL MOVER;
/* NOW REMOVE THE INTERMEDIATE STRING */
call setfront(t);
END;
END;
ELSE IF CHAR = 'M' AND MP = 0 THEN /* MACRO DEFINITION */
DO; XP = 255;
IF DISTANCE = 1 THEN CALL ZERODIST;
DO WHILE (MACRO(XP := XP + 1) := READC) <> CR;
END;
MP = XP; XP = 0; MT = DISTANCE;
END;
ELSE IF CHAR = 'N' THEN
DO; /* SEARCH FOR STRING WITH AUTOSCAN */
CALL SETFIND; /* SEARCH STRING SCANNED */
DO WHILE DISTNZERO;
/* FIND ANOTHER OCCURRENCE OF STRING */
DO WHILE NOT FIND(0,WBP); /* NOT IN BUFFER */
IF BREAK$KEY THEN GO TO RESET;
CALL SAVEDIST; CALL CLEARMEM;
/* MEMORY BUFFER WRITTEN */
CALL APPHALF;
DIRECTION = BACKWARD; FIRST = 1; CALL MOVER;
CALL RESTDIST; DIRECTION = FORWARD;
/* MAY BE END OF FILE */
IF BACK >= MAXM THEN GO TO OVERCOUNT;
END;
END;
END;
ELSE IF CHAR = 'S' THEN /* SUBSTITUTE COMMAND */
DO; CALL SETFIND;
CALL COLLECT;
/* FIND STRING FROM 0 TO WBP-1, SUBSTITUTE STRING
BETWEEN WBP AND WBE-1 IN SCRATCH */
DO WHILE DISTNZERO;
CALL CHKFOUND;
/* FRONT AND BACK NOW POSITIONED AT FOUND
STRING - REPLACE IT */
call setfront(FRONT - (MI := WBP)); /* BACKED UP */
DO WHILE MI < WBE;
CHAR = SCRATCH(MI);
MI = MI + 1; CALL INSERT;
END;
END;
END;
ELSE IF CHAR = 'W' THEN
CALL WRITEOUT;
ELSE IF CHAR = 'X' THEN /* TRANSFER LINES */
DO;
flag = parse$lib(.rfcb);
xbp = 0;
IF DISTZERO THEN
DO; /* delete the file */
xferon = false;
CALL DELETE(.rfcb);
if dcnt = 255 then
call perror(.not$found);
END;
ELSE
do; /* transfer lines */
declare i address;
if xferon and compare$xfer then
call append$xfer;
else
DO;
XFERON = TRUE;
call move(12,.rfcb,.xfcb);
xfcbext, xfcbrec, xfcbe, xfcbr = 0;
CALL MAKE$file(.XFCB);
IF DCNT = 255 THEN
goto dir$err;
END;
CALL SETLIMITS;
DO I = FIRST TO LASTC;
CALL PUTXFER(MEMORY(I));
END;
call close$xfer;
END;
END;
ELSE IF CHAR = 'Z' THEN /* SLEEP */
DO;
IF DISTZERO THEN
DO; IF READCHAR = ENDFILE THEN GO TO RESET;
END;
DO WHILE DISTNZERO; CALL WAIT;
END;
END;
ELSE IF CHAR <> 0 THEN /* NOT BREAK LEFT OVER FROM STOP */
/* DIRECTION FORWARD, BUT NOT ONE OF THE ABOVE */
GO TO BADCOM;
END;
ELSE /* DIRECTION NOT FORWARD */
GO TO BADCOM;
END;
END;
END;