REM PROGRAM DIALOG
REM A PROGRAM TO AUTOMATICALLY LOG IN AND EXTRACT DATA FROM
REM A DATABASE SYSTEM
REM
REM WRITTEN BY E. VERNON BUCK, SAN LORENZO, CA. (415) 276-4607
REM VERSION 1.1, DEC. 31, 1985.
REM
REM DIALOG READS "INSTRUCTIONS" FROM AN "INSTRUCTION FILE". THIS FILE
REM IS PRODUCED BY ANY EDITOR OR WORD PROCESSOR. THE INSTRUCTION
REM FILE TELLS THE PROGRAM HOW TO CONDUCT A "DIALOG" WITH THE
REM REMOTE (OR "HOST") COMPUTER. THE INSTRUCTIONS TELL THE PROGRAM
REM WHAT MESSAGES TO SEND, AND WHAT MESSAGES TO RECEIVE FROM THE
REM HOST. THE PROGRAM CAN ALSO MAKE SOME
REM SIMPLE DECISIONS (BRANCHING) BASED ON THE CONTENT OF MESSAGES
REM RECEIVED FROM THE HOST. THE DIALOG IS DISPLAYED ON THE 
REM TERMINAL AS IT HAPPENS, AND (OPTIONALLY) RECORDED
REM IN A "LOG" FILE.
REM
REM THE INSTRUCTIONS AVAILABLE ARE AS FOLLOWS:
REM	S,ccc...c		SEND THE STRING ccc...c TO THE HOST.
REM				THE SEND COMMAND EXPECTS THE
REM				CHARACTERS TO BE ECHOED BY THE HOST.
REM
REM	T,ccc...c		TRANSMIT THE STRING ccc...c TO THE
REM				HOST. IDENTICAL TO THE S COMMAND,
REM				EXCEPT DOES NOT EXPECT AN ECHO.
REM
REM	R,ttt...t 		RECEIVE A STRING OF CHARACTERS FROM
REM				THE HOST UNTIL THE "TERMINATION" STRING
REM				ttt...t IS RECEIVED. ANY NUMBER OF
REM				CHARACTERS CAN BE RECEIVED BEFORE THE
REM				TERMINATION STRING IS ENCOUNTERED, BUT
REM				ONLY THE LAST 2000 (ABOUT 1 SCREENFULL)
REM				WILL BE RETAINED.
REM
REM	I,ddd...d,n		IF THE LAST RECEIVED CHARACTERS
REM				(INCLUDING THE TERMINATION CHARACTERS
REM				FROM THE R INSTRUCTION) WERE ddd...d,
REM				THEN JUMP TO THE INSTRUCTION LINE
REM				LABELED n. n IS A NUMBER FROM
REM				1 TO 9999.
REM
REM	G,n			GO TO (JUMP UNCONDITIONALLY) TO THE
REM				INSTRUCTION LINE LABELED n.
REM
REM	E,i,n			IF AN ERROR CONDITION OF TYPE i IS
REM				DETECTED, THEN GO TO THE
REM				INSTRUCTION LINE LABELED n.
REM				i CAN BE:
REM				 "SE" - SEND INSTRUCTION ECHO ERROR
REM				 "RE" - RECEIVE INSTRUCTION
REM				        PARITY, FRAME OR OVERRUN ERROR
REM				 "TO" - TIME OUT
REM				 "CL" - CARRIER LOST.
REM				n = 0 MEANS IGNORE THE ERROR, AND
REM				CONTINUE. THIS IS THE DEFAULT.
REM				n = -1 ("SE" ONLY) MEANS SEND A
REM				BACKSPACE AND REPEAT THE ERRONEOUS
REM				CHARACTER
REM
REM	L,lll			CONTROL THE AMOUNT OF INFORMATION TO
REM				BE SAVED IN THE LOG FILE:
REM				lll=ON -- SAVE RECEIVED DATA ONLY
REM				lll=OFF-- SAVE NOTHING
REM				lll=ALL-- SAVE BOTH TRANSMITTED AND
REM					  RECEIVED DATA (DEFAULT)
REM
REM	D,ddd			CONTROLS WHETHER THE DIALOG WILL BE
REM				DISPLAYED ON THE TERMINAL AS IT
REM				HAPPENS:
REM				ddd=OFF -- NOT DISPLAYED ON TERMINAL
REM				ddd=ON  -- DISPLAYED ON TERMINAL
REM					   (DEFAULT)
REM
REM	C,p			MAKE p THE CONTROL CHARACTER PREFACE
REM				FOR THE INSTRUCTION LINES THAT FOLLOW.
REM				I.E., ^M WOULD BE TRANSMITTED AS A
REM				CTRL-M	WHEN p="^". DEFAULT IS "^".
REM				THE C INSTRUCTION IS NON-EXECUTABLE.
REM				THAT IS, IT OPERATES ON THE INSTRUC-
REM				TIONS AS THEY ARE COMPILED, BUT NOT
REM				AS EXECUTED (SINCE INSTRUCTIONS
REM				MIGHT BE EXECUTED IN A DIFFERENT
REM				ORDER).
REM
REM	X			EXIT. CLOSE FILES AND RETURN TO CP/M.
REM
REM INSTRUCTIONS CAN BE PRECEEDED BY A "LABEL NUMBER". THIS IS LIKE A
REM LINE NUMBER IN BASIC OR FORTRAN. IT IS OPTIONAL. BUT OF COURSE, IF
REM THERE IS AN I, G OR E INSTRUCTION ANYWHERE IN THE INSTRUCTIONS,
REM THEN THERE MUST BE A LABELED INSTRUCTION WHICH MATCHES THE
REM LABELS GIVEN IN THE I, G OR E INSTRUCTION. EXAMPLE:
REM 100,S,hello^M
REM	...
REM	G,100
REM
REM NOTES: (1) ALL INSTRUCTION PARAMETERS MUST BE SEPARATED BY COMMAS.
REM	   (2) IF A COMMA IS NEEDED IN A CHARACTER STRING, THEN
REM	       SURROUND THE STRING WITH QUOTES ("...").
REM	   (3) LEADING BLANKS ARE IGNORED, UNLESS THE STRING IS QUOTED.
REM	   (4) DO NOT USE TAB CHARACTERS (CTRL-I) TO INDENT.
REM	       USE SPACES.
REM
REM TO INVOKE THE PROGRAM, TYPE
REM	DIALOG insfile.dlg logfile.log
REM WHERE insfile.dlg IS THE FILE CONTAINING THE INSTRUCTIONS, AND
REM logfile.log IS THE LOG FILE (TO BE WRITTEN BY DIALOG). THE LOG
REM FILE IS OPTIONAL AND CAN SIMPLY BE OMITTED. THERE ARE NO DEFAULT
REM FILE NAMES OR EXTENSIONS. REPLACING logfile.log WITH A QUESTION
REM MARK WILL CAUSE THE INSTRUCTION FILE TO BE COMPILED, BUT NOT
REM EXECUTED, FOR TESTING PURPOSES.
REM 
REM THE INSTRUCTIONS ARE STORED IN VARIABLES
REM 	COMM$ : THE COMMAND
REM 	ARG1$ : THE FIRST ARGUMENT (A STRING)
REM 	ARG2% : THE SECOND ARGUMENT (A NUMBER)
REM
REM PATCH AREA CONSTANTS:
REM DATA.PORT% = I/O DATA PORT FOR MODEM
REM CTRL.PORT% = I/O PORT TO READ TO TEST FOR TRANSMIT/RECEIVE
REM		 READY
REM TRDY.MASK% = THE BIT TO TEST FOR TRANSMIT READY (IS SET TO 1)
REM TRDY.POL% = SET THE SAME BIT AS TRDY.MASK% TO 1 TO REVERSE THE
REM 	POLARITY OF THE TEST (I.E. READY WHEN BIT = 0)
REM RRDY.MASK%, RRDY.POL% = SAME FOR RECEIVE READY
REM RERR.PORT% = I/O PORT TO READ TO TEST FOR READ ERRORS
REM RERR.MASK%, RERR.POL% = BITS TO TEST FOR READ ERRORS (I.E.
REM	PARITY, FRAME AND/OR OVERRUN), AND POLARITY OF SAME
REM RES.PORT% = I/O PORT TO RECEIVE THE FOLLOWING RESET BYTE. (SET TO
REM		-1 IF NO RESET BYTE IS REQUIRED)
REM RERR.RES% = BYTE TO SEND TO RESET PORT TO RESET UART AFTER
REM		RECEIPT OF PARITY, FRAME OR OVERRUN ERROR
REM CARRIER.PORT% = I/O PORT TO READ TO TEST FOR CARRIER PRESENT 
REM CARRIER.MASK% = THE BIT(S) TO TEST FOR CARRIER PRESENT (E.G. DSR)
REM CARRIER.POL% = POLARITY OF CARRIER BIT(S)
REM CC.PREF$ = THE CHARACTER THAT PRECEEDS A CHARACTER TO INDICATE
REM 	   THAT THE 2ND CHARACTER IS A CONTROL CHARACTER.
REM 	   (I.E. ^M = CTRL-M, WHEN CC.PREF$ = "^")
REM MAX.TIME = APPROX. TIME OUT TIME. FOR 4MHZ CPU, 1100 ~= 1 SECOND
REM
REM HOW TO PATCH THE .COM FILE:
REM USING DDT.COM, OR SOME OTHER PATCHING PROGRAM, FIND THE FOLLOWING
REM DATA STATEMENTS. THEY ARE PREFACED BY THE WORD "PATCH:". CHANGE
REM THE NUMBERS FOLLOWING PATCH: TO SUIT YOUR SYSTEM. THEY ARE IN
REM DECIMAL AND ASCII, AS FOLLOWS:
REM	(1) THE UART DATA I/O PORT NUMBER
REM	(2) THE UART TRANSMIT/RECEIVE READY I/O PORT NUMBER
REM	(3) THE BIT TO TEST FOR TRANSMIT READY
REM	(4) THE BIT TO BE EXCLUSIVE OR'ED BEFORE TESTING FOR TRANSMIT
REM	(5) THE BIT TO TEST FOR RECEIVE READY
REM	(6) THE BIT TO BE EXCLUSIVE OR'ED BEFORE TESTING FOR RECIEVE
REM	(7) THE I/O PORT TO READ FOR RECEIVE ERRORS
REM	(8) THE BIT(S) TO TEST FOR RECEIVE ERRORS (PARITY, FRAME
REM	    AND/OR OVERRUN, ETC.)
REM	(9) THE BIT(S) TO BE EXCLUSIVE OR'ED WITH THE ERROR BITS
REM	    BEFORE TESTING
REM    (10) THE I/O PORT TO SEND A RESET BYTE TO (-01 IF NONE)
REM    (11) BYTE TO SEND TO THE RESET PORT TO RESET UART FOLLOWING
REM	    RECEPTION OF A TRANSMISSION ERROR
REM    (12) THE I/O PORT TO READ FOR CARRIER PRESENT TEST
REM    (13) THE BIT(S) TO TEST FOR CARRIER ON (I.E., THE DSR LINE)
REM    (14) THE BIT(S) TO BE EXCLUSIVE OR'ED WITH THE CARRIER ON BIT
REM	    BEFORE TESTING
REM    (15) THE DEFAULT CHARACTER TO BE A PREFACE FOR CONTROL
REM	    CHARACTERS
REM    (16) THE TIME OUT COUNTER MAXIMUM
REM
	DATA "PATCH:",000,001,001,000,002,000,001,008,000
	DATA 001,055,001,128,000
	DATA "^",066000
REM
	DIM COMM$(200),ARG1$(200),ARG2%(200),LABEL%(200)
	DIM BUFFER%(2000)
REM
REM SET THE I/O PORT PARAMETERS
	READ GARBAGE$,DATA.PORT%,CTRL.PORT%,TRDY.MASK%,_
		TRDY.POL%,RRDY.MASK%,RRDY.POL%
	READ RERR.PORT%,RERR.MASK%,RERR.POL%,RES.PORT%,RERR.RES%,_
		CARRIER.PORT%,CARRIER.MASK%,CARRIER.POL%
REM
REM SET THE CONTROL CHARACTER PREFACE
	READ CC.PREF$
REM SET THE TIME OUT COUNTER MAXIMUM
	READ MAX.TIME
REM
	TRUE% = (0 = 0)
	FALSE% = (1 = 0)
	CC.PREF% = ASC(CC.PREF$)
REM
REM GET THE FILE NAMES FROM CP/M COMMAND
REM
	INST.FILE$ = ""
	FOR ADDRESS% = 93 TO 100
	INST.FILE$ = INST.FILE$ + CHR$(PEEK(ADDRESS%))
	NEXT ADDRESS%
	IF INST.FILE$ = "        " THEN GO TO 9600
	INST.FILE$ = INST.FILE$ + "."
	DRIVE% = PEEK(92)
	IF DRIVE% > 0 THEN _
		INST.FILE$ = CHR$(DRIVE% + 64) + ":" + INST.FILE$
	FOR ADDRESS% = 101 TO 103
	INST.FILE$ = INST.FILE$ + CHR$(PEEK(ADDRESS%))
	NEXT ADDRESS%
REM
	LOG.FILE$ = ""
	FOR ADDRESS% = 109 TO 116
	LOG.FILE$ = LOG.FILE$ + CHR$(PEEK(ADDRESS%))
	NEXT ADDRESS%
	NO.LOG.FILE% = (LOG.FILE$ = "        " )
	COMPILE.ONLY% = (LOG.FILE$ = "?       " )
	IF NO.LOG.FILE% THEN GO TO 100
	LOG.FILE$ = LOG.FILE$ + "."
	DRIVE% = PEEK(108)
	IF DRIVE% > 0 THEN _
		LOG.FILE$ = CHR$(DRIVE% + 64) + ":" + LOG.FILE$
	FOR ADDRESS% = 117 TO 119
	LOG.FILE$ = LOG.FILE$ + CHR$(PEEK(ADDRESS%))
	NEXT ADDRESS%
REM
100	REM
REM OPEN THE INSTRUCTION FILE
	OPEN "I",#1,INST.FILE$
REM
REM
REM READ THE INSTRUCTIONS INTO MEMORY AND CHECK FOR OBVIOUS
REM ERRORS
	FOR NO.INSTR% = 1 TO 200
REM	SET TRAP FOR SYNTAX ERROR & INITIALIZE LABEL% ARRAY
	LABEL.PRESENT% = FALSE%
	LABEL%(NO.INSTR%) = 0
REM	EXIT LOOP IF NO MORE COMMANDS
	IF EOF(1) THEN GO TO 500
200	REM
	INPUT #1, COMMAND$
REM TAKE FIRST CHARACTER AND CONVERT TO UPPER CASE IF NECESSARY
	ASCII.VAL% = ASC(COMMAND$)
	IF ASCII.VAL% > 96 AND ASCII.VAL% <= 122 THEN _
		FIRST.CHAR$ = CHR$(ASCII.VAL% - 32) ELSE _
		FIRST.CHAR$ = CHR$(ASCII.VAL%)
REM GO TO APPROPRIATE SECTION TO INTERPRET DIFFERENT INSTRUCTIONS
	IF FIRST.CHAR$ = "L" THEN GO TO 300
	IF FIRST.CHAR$ = "D" THEN GO TO 320
	IF FIRST.CHAR$ = "S" OR FIRST.CHAR$ = "R" _
		OR FIRST.CHAR$ = "T" THEN GO TO 350
	IF FIRST.CHAR$ = "I" THEN GO TO 400
	IF FIRST.CHAR$ = "E" THEN GO TO 430
	IF FIRST.CHAR$ = "G" THEN GO TO 440
	IF FIRST.CHAR$ = "C" THEN GO TO 450
	IF FIRST.CHAR$ = "X" THEN GO TO 490
REM
REM COMMAND$ IS NONE OF THE INTRINSIC COMMANDS. ASSUME IT IS
REM A STATEMENT LABEL
REM TEST FOR ILLEGAL COMMAND
	IF LABEL.PRESENT% OR ASCII.VAL% < 48 OR _
		ASCII.VAL% > 57 THEN GO TO 9010
	LABEL.VALUE% = VAL(COMMAND$)
REM CHECK RANGE OF LABEL
	IF LABEL.VALUE% < 1 OR LABEL.VALUE% > 9999 THEN GO TO 9015
REM CHECK FOR DUPLICATE LABEL ON A PREVIOUS INSTRUCTION
	IF NO.INSTR% = 1 THEN GO TO 280
	FOR LABEL.NO% = 1 TO NO.INSTR% - 1
	IF LABEL%(LABEL.NO%) = LABEL.VALUE% THEN GO TO 9020
	NEXT LABEL.NO%
280	REM
REM SAVE THIS LABEL 
	LABEL%(NO.INSTR%) = LABEL.VALUE%
	LABEL.PRESENT% = TRUE%
	GO TO 200
REM
300	REM
REM COMPILE A LOG INSTRUCTION
REM READ THE ARGUMENT
	IF EOF(1) THEN GO TO 9000
	INPUT #1, ARGUMENT1$
REM TEST FOR LEGALITY OF ARGUMENT
	IF ARGUMENT1$ <> "IN" AND ARGUMENT1$ <> "OFF" AND _
		ARGUMENT1$ <> "ALL" THEN GO TO 9025
	IF ARGUMENT1$ <> "OFF" AND NO.LOG.FILE% THEN GO TO 9005 
	IF ARGUMENT1$ = "OFF" THEN ARG2%(NO.INSTR%) = 0
	IF ARGUMENT1$ = "IN" THEN ARG2%(NO.INSTR%) = 1
	IF ARGUMENT1$ = "ALL" THEN ARG2%(NO.INSTR%) = 2
	ARG1$(NO.INSTR%) = ARGUMENT1$
	GO TO 490
REM
320	REM
REM COMPILE D (DISPLAY ON/OFF) COMMAND
	IF EOF(1) THEN GO TO 9000
	INPUT #1, ARGUMENT1$
	IF ARGUMENT1$ <> "ON" AND ARGUMENT1$ <> "OFF" _
		THEN GO TO 9025
	ARG2%(NO.INSTR%) = ( ARGUMENT1$ = "ON" )
	ARG1$(NO.INSTR%) = ARGUMENT1$
	GO TO 490
REM
350	REM
REM COMPILE A SEND OR RECEIVE INSTRUCTION
REM READ THE ARGUMENT
	IF EOF(1) THEN GO TO 9000
	INPUT #1, IN.STRING$
REM TEST FOR LEGALITY
	IF LEN(IN.STRING$) < 1 THEN GO TO 9025
REM CONVERT CONTROL CHARACTERS
	GO SUB 10000
	IF LEN(OUT.STRING$) > 2000 THEN GO TO 9025
	ARG1$(NO.INSTR%) = OUT.STRING$
REM SAVE THE CONTROL CHARACTER PREFACE (NEEDED FOR LOGGING)
	ARG2%(NO.INSTR%) = CC.PREF%
	GO TO 490
REM
400	REM
REM COMPILE AN IF INSTRUCTION
REM READ THE 1ST ARGUMENT
	IF EOF(1) THEN GO TO 9000
	INPUT #1, IN.STRING$
REM TEST FOR LEGALITY
	IF LEN(IN.STRING$) < 1 THEN GO TO 9025
REM CONVERT CONTROL CHARACTERS
	GO SUB 10000
	ARG1$(NO.INSTR%) = OUT.STRING$
REM READ THE 2ND ARGUMENT
	IF EOF(1) THEN GO TO 9000
	INPUT #1, ARGUMENT2%
REM TEST FOR REASONABLENESS
	IF ARGUMENT2% < 1 OR ARGUMENT2% > 9999 THEN GO TO 9030
	ARG2%(NO.INSTR%) = ARGUMENT2%
	GO TO 490
REM
430	REM
REM COMPILE AN ON ERROR JUMP
	IF EOF(1) THEN GO TO 9000
	INPUT #1, ARGUMENT1$
	IF ARGUMENT1$ <> "RE" AND ARGUMENT1$ <> "SE" _
		AND ARGUMENT1$ <> "TO" AND ARGUMENT1$ <> "CL" _
		THEN GO TO 9025
	ARG1$(NO.INSTR%) = ARGUMENT1$
	IF EOF(1) THEN GO TO 9000
	INPUT #1, ARGUMENT2%
REM TEST FOR REASONABLENESS
	IF ARGUMENT2% < -1 OR ARGUMENT2% > 9999 THEN GO TO 9030
	ARG2%(NO.INSTR%) = ARGUMENT2%
	GO TO 490
REM
REM
440	REM
REM COMPILE A JUMP (GOTO) INSTRUCTION
	IF EOF(1) THEN GO TO 9000
	INPUT #1, ARGUMENT2%
REM TEST FOR REASONABLENESS
	IF ARGUMENT2% < 1 OR ARGUMENT2% > 9999 THEN GO TO 9030
	ARG2%(NO.INSTR%) = ARGUMENT2%
	GO TO 490
REM
450	REM
REM COMPILE A CHANGE CONTROL CHARACTER PREFACE
	IF EOF(1) THEN GO TO 9000
	INPUT #1, ARGUMENT1$
	IF LEN(ARGUMENT1$) <> 1 THEN GO TO 9025
	CC.PREF% = ASC(ARGUMENT1$)
	ARG1$(NO.INSTR%) = ARGUMENT1$
	ARG2%(NO.INSTR%) = CC.PREF%
	GO TO 490
REM
490	REM
REM STORE THE COMMAND
	COMM$(NO.INSTR%) = FIRST.CHAR$
	NEXT NO.INSTR%
500	REM
	NO.INSTR% = NO.INSTR% - 1
REM
	CLOSE #1
	IF NO.INSTR% <= 0 THEN GO TO 9040
REM
REM REPLACE THE STATEMENT LABELS IN THE I, E, AND G COMMANDS WITH THE
REM ACTUAL LINE NUMBERS REPRESENTED BY THE LABELS
	FOR I% = 1 TO NO.INSTR%
	COMMAND$ = COMM$(I%)
	IF COMMAND$ <> "I" AND COMMAND$ <> "G" _
		AND COMMAND$ <> "E" THEN GO TO 800
	TARGET.LABEL% = ARG2%(I%)
REM IF TARGET IS <= 0, THEN IT'S NOT A LINE NUMBER, SO SKIP
	IF TARGET.LABEL% <= 0 THEN GO TO 800
REM SEARCH THROUGH THE LIST OF LABELS
	FOR K% = 1 TO NO.INSTR%
	IF LABEL%(K%) = TARGET.LABEL% _
		THEN GO TO 750
	NEXT K%
REM LABEL NOT FOUND IN LABEL LIST
	GO TO 9035
750	REM
REM SET ARG2% TO THE LINE NUMBER OF THE LABELED INSTRUCTION
	ARG2%(I%) = K%
800	REM
	NEXT I%
REM
REM
REM EXIT IF THIS WAS A COMPILE ONLY RUN
	IF NOT COMPILE.ONLY% THEN GO TO 900
	PRINT "No instruction errors detected in ";INST.FILE$
	END
REM
900	REM
REM EXECUTE THE INSTRUCTIONS
REM
REM
REM OPEN THE LOG FILE
	IF NOT NO.LOG.FILE% THEN OPEN "O",#2,LOG.FILE$
REM INITIALIZE INSTRUCTION COUNTER
	INST.LIN% = 0
REM INITIALIZE LOGGING FLAG
	IF NO.LOG.FILE% THEN LOG.FLAG% = 0 ELSE LOG.FLAG% = 2
REM INITIALIZE TERMINAL DISPLAY ON FLAG
	DISPLAY% = TRUE%
REM INITIALIZE ON ERROR TARGET LINE NUMBER
	RERR.LIN% = 0
	SERR.LIN% = 0
	TERR.LIN% = 0
	CERR.LIN% = 0
REM INITIALIZE BUFFER POINTER
	CHAR.NO% = 0
REM
REM
REM TOP OF EXECUTION LOOP
1000	REM
	INST.LIN% = INST.LIN% + 1
	IF INST.LIN% > NO.INSTR% OR INST.LIN% < 1 _
		THEN GO TO 9550
	COMMAND$ = COMM$(INST.LIN%)
	IF COMMAND$ = "S" OR COMMAND$ = "T" THEN GO TO 2000
	IF COMMAND$ = "R" THEN GO TO 3000
	IF COMMAND$ = "I" THEN GO TO 4000
	IF COMMAND$ = "G" THEN GO TO 4400
	IF COMMAND$ = "E" THEN GO TO 4500
	IF COMMAND$ = "L" THEN GO TO 5000
	IF COMMAND$ = "D" THEN GO TO 5100
	IF COMMAND$ = "C" THEN GO TO 1000
	IF COMMAND$ = "X" THEN GO TO 9550
REM
REM
2000	REM
REM EXECUTE THE SEND COMMAND
REM
REM GET THE STRING
	CHARS.OUT$ = ARG1$(INST.LIN%)
REM GET THE NUMBER OF CHARACTERS TO SEND
	NO.OUT% = LEN(CHARS.OUT$)
REM COPY THE CHARACTERS INTO THE OUTPUT BUFFER
	FOR CHAR.NO% = 1 TO NO.OUT%
	BUFFER%(CHAR.NO%) = ASC(MID$(CHARS.OUT$,CHAR.NO%,1))
	NEXT CHAR.NO%
REM
REM IF LOG.FLAG% IS 2, WRITE CHARACTERS TO LOG FILE.
	IF NOT DISPLAY% AND LOG.FLAG% < 2 THEN GO TO 2400
REM MUST REINSERT THE CONTROL CHARACTER PREFACE.
	PRNT.PREF$ = CHR$(ARG2%(INST.LIN%))
	FOR CHAR.NO% = 1 TO NO.OUT%
	CHAR% = BUFFER%(CHAR.NO%)
	IF CHAR% < 32 THEN GO TO 2200
	PRNT.CHAR$ = CHR$(CHAR%)
	IF DISPLAY% THEN PRINT PRNT.CHAR$;
	IF LOG.FLAG% = 2 THEN PRINT #2, PRNT.CHAR$;
	GO TO 2300
2200	REM
REM WRITE PRINTABLE CONTROL CHARACTER
	IF DISPLAY% THEN PRINT PRNT.PREF$;
	PRNT.CHAR$ = CHR$(CHAR% + 64)
	IF DISPLAY% THEN PRINT PRNT.CHAR$;
	IF LOG.FLAG% < 2 THEN GO TO 2300
	PRINT #2, PRNT.PREF$;
	PRINT #2, PRNT.CHAR$;
2300	REM
	NEXT CHAR.NO%
REM
REM WRITE <CR>,<LF> TO LOG FILE
	IF DISPLAY% THEN PRINT ""
	IF LOG.FLAG% = 2 THEN PRINT #2, ""
2400	REM 
REM
REM CLEAR THE MODEM RECEIVER REGISTER
	IF COMMAND$ = "S" THEN GARBAGE% = INP(DATA.PORT%)
REM
REM NOW SEND THE CHARACTERS TO THE OUTPUT PORT
	FOR CHAR.NO% = 1 TO NO.OUT%
	CHAR.OUT% = BUFFER%(CHAR.NO%)
2420	REM
REM
REM WAIT FOR READY SIGNAL FROM CONTROL PORT
	TIME.COUNT = 0.0
2425	REM
REM IF ENABLED, TEST FOR TIME OUT
	IF TERR.LIN% > 0 AND TIME.COUNT > MAX.TIME THEN _
		GO TO 2440 ELSE TIME.COUNT = TIME.COUNT + 1.0
REM IF ENABLED, TEST FOR CARRIER STILL PRESENT
	IF (CERR.LIN% > 0) AND _
		((INP(CARRIER.PORT%) XOR CARRIER.POL%) AND _
		CARRIER.MASK%) = 0 THEN GO TO 2445
REM SEE IF TRANSMITTER READY FOR NEW CHARACTER ELSE LOOP
	IF ((INP(CTRL.PORT%) XOR TRDY.POL%) AND TRDY.MASK%) = 0 _
		THEN GO TO 2425
REM
REM SEND CHARACTER
	OUT DATA.PORT%,CHAR.OUT%
REM
REM SKIP ECHO IF T COMMAND
	IF COMMAND$ = "T" THEN GO TO 2500
REM WAIT FOR ECHO IN RECEIVER
REM TIME.COUNT IS NOT REINITIALIZED UNTIL ECHO IS RECEIVED
2430	REM
REM IF ENABLED, TEST FOR TIME OUT
	IF TERR.LIN% > 0 AND TIME.COUNT > MAX.TIME THEN _
		GO TO 2440 ELSE TIME.COUNT = TIME.COUNT + 1.0
REM IF ENABLED, TEST FOR CARRIER PRESENT
	IF (CERR.LIN% > 0) AND _
		((INP(CARRIER.PORT%) XOR CARRIER.POL%) AND _
		CARRIER.MASK%) = 0 THEN GO TO 2445
REM SEE IF ECHOED CHARACTER HAS BEEN RECEIVED, ELSE LOOP
	IF ((INP(CTRL.PORT%) XOR RRDY.POL%) AND RRDY.MASK%) = 0 _
		THEN GO TO 2430
REM
REM GET ECHO AND STRIP PARITY BIT
	ECHO% = INP(DATA.PORT%) AND 127
REM
REM TEST ECHO IF ON ERROR TESTING IS ENABLED
	IF SERR.LIN% = 0 OR ECHO% = CHAR.OUT% THEN GO TO 2500
	IF SERR.LIN% = -1 THEN GO TO 2450
	INST.LIN% = SERR.LIN% - 1
	IF DISPLAY% THEN PRINT "<<<-Send echo error"
	IF LOG.FLAG% > 0 THEN PRINT #2, _
		"<<<-Send echo error"
	GO TO 2510
REM
2440	REM
REM TIME OUT DURING SEND PROCEDURE
	INST.LIN% = TERR.LIN% - 1
	IF DISPLAY% THEN PRINT "<<<-Send time out"
	IF LOG.FLAG% > 0 THEN PRINT #2, "<<<-Send time out"
	GO TO 2510
REM
2445	REM
REM CARRIER LOST WHILE ATTEMPTING TO SEND A CHARACTER
	INST.LIN% = CERR.LIN% - 1
REM RESET I/O ERROR SO WON'T GIVE FALSE ALARM ON NEXT CHARACTER
	IF RES.PORT% >= 0 THEN OUT RES.PORT%,RERR.RES%
REM CLEAR THE RECEIVER BUFFER
	GARBAGE% = INP(DATA.PORT%)
	IF DISPLAY% THEN PRINT "<<<-Carrier lost"
	IF LOG.FLAG% > 0 THEN PRINT #2, "<<<-Carrier lost"
	GO TO 2510
REM
2450	REM
REM
REM AUTOMATIC RE-TRY OPTION (E INSTRUCTION E,SE,-1)
REM WAIT FOR READY SIGNAL FROM CONTROL PORT
2455	REM
REM IF ENABLED, TEST FOR TIME OUT
	IF TERR.LIN% > 0 AND TIME.COUNT > MAX.TIME THEN _
		GO TO 2440 ELSE TIME.COUNT = TIME.COUNT + 1.0
REM IF ENABLED, TEST FOR CARRIER STILL PRESENT
	IF (CERR.LIN% > 0) AND _
		((INP(CARRIER.PORT%) XOR CARRIER.POL%) AND _
		CARRIER.MASK%) = 0 THEN GO TO 2445
REM SEE IF TRANSMITTER READY FOR NEW CHARACTER ELSE LOOP
	IF ((INP(CTRL.PORT%) XOR TRDY.POL%) AND TRDY.MASK%) = 0 _
		THEN GO TO 2455
REM
REM SEND BACKSPACE CHARACTER
	OUT DATA.PORT%,8
REM
REM GET BACKSPACE ECHO
REM WAIT FOR ECHO IN RECEIVER
2460	REM
REM IF ENABLED, TEST FOR TIME OUT
	IF TERR.LIN% > 0 AND TIME.COUNT > MAX.TIME THEN _
		GO TO 2440 ELSE TIME.COUNT = TIME.COUNT + 1.0
REM IF ENABLED, TEST FOR CARRIER STILL PRESENT
	IF (CERR.LIN% > 0) AND _
		((INP(CARRIER.PORT%) XOR CARRIER.POL%) AND _
		CARRIER.MASK%) = 0 THEN GO TO 2445
REM GET I/O STATUS BYTE AND LOOP UNTIL CHARACTER READY
	IF ((INP(CTRL.PORT%) XOR RRDY.POL%) AND RRDY.MASK%) = 0 _
		THEN GO TO 2460
REM
REM GET ECHOED BACKSPACE
	GARBAGE% = INP(DATA.PORT%)
REM GO BACK AND TRY TO SEND CHARACTER AGAIN
	GO TO 2425
REM
2500	REM
REM
	NEXT CHAR.NO%
2510	REM
REM
REM SET CHAR.NO% TO 0. USED AS FLAG BY "I" COMMAND.
	CHAR.NO% = 0
	GO TO 1000
REM
3000	REM
REM RECEIVE A STRING. HALT RECEPTION WHEN A TERMINATION
REM STRING IS FOUND.
REM
REM GET TERMINATION STRING
	TERM.STRING$ = ARG1$(INST.LIN%)
REM GET LENGTH OF TERMINATION STRING MINUS 1.
	NO.TERM.M1% = LEN(TERM.STRING$) - 1
REM INITIALIZE THE TEST STRING AND BUFFER COUNTER
	TEST.STRING$ = ""
	CHAR.NO% = 0
	WRAPPED% = FALSE%
REM
3010	REM
REM WAIT FOR CHARACTER READY SIGNAL FROM CONTROL PORT
	TIME.COUNT = 0.0
3020	REM
REM IF ENABLED, TEST FOR TIME OUT
	IF TERR.LIN% > 0 AND TIME.COUNT > MAX.TIME THEN _
		GO TO 3070 ELSE TIME.COUNT = TIME.COUNT + 1.0
REM IF ENABLED, TEST FOR CARRIER STILL PRESENT
	IF (CERR.LIN% > 0) AND _
		((INP(CARRIER.PORT%) XOR CARRIER.POL%) AND _
		CARRIER.MASK%) = 0 THEN GO TO 3060
REM GET I/O STATUS BYTE AND LOOP UNTIL CHARACTER READY
	IF ((INP(CTRL.PORT%) XOR RRDY.POL%) AND RRDY.MASK%) = 0 _
		THEN GO TO 3020
REM
REM GET CHARACTER FROM DATA PORT AND STRIP OFF PARITY BIT
	CHAR% = INP(DATA.PORT%) AND 127
REM ADD THE CHARACTER TO THE BUFFER
	CHAR.NO% = CHAR.NO% + 1
REM SEE IF BUFFER IS FULL
	IF CHAR.NO% <= 2000 THEN GO TO 3050
REM WRAP THE BUFFER POINTER BACK TO BEGINNING AND SET FLAG
	CHAR.NO% = 1
	WRAPPED% = TRUE%
3050	REM
	BUFFER%(CHAR.NO%) = CHAR%
REM
REM IF ENABLED, CHECK FOR DATA (PARITY, ETC.) ERROR
	IF (RERR.LIN% <= 0) OR ((INP(RERR.PORT%) XOR RERR.POL%) AND _
		RERR.MASK%) = 0	THEN GO TO 3080
REM ERROR FOUND
	INST.LIN% = RERR.LIN% - 1
REM RESET I/O ERROR SO WONT GIVE FALSE ALARM ON NEXT CHARACTER
	IF RES.PORT% >= 0 THEN OUT RES.PORT%,RERR.RES%
REM PRINT ALERT MESSAGE
	IF DISPLAY% THEN PRINT "Receive data error->>>"
	IF LOG.FLAG% >= 1 THEN PRINT #2, _
		"Receive data error->>>"
REM SKIP THE TERMINATION STRING TEST, BUT LOG AND PRINT THE
REM DATA RECEIVED SO FAR
	GO TO 3090
REM
3060	REM
REM CARRIER LOST WHILE TRYING TO RECEIVE
	INST.LIN% = CERR.LIN% - 1
REM RESET I/O ERROR SO WON'T GIVE FALSE ALARM ON NEXT CHARACTER
	IF RES.PORT% >= 0 THEN OUT RES.PORT%,RERR.RES%
REM CLEAR THE RECEIVER BUFFER
	GARBAGE% = INP(DATA.PORT%)
	IF DISPLAY% THEN PRINT "Carrier lost->>>"
	IF LOG.FLAG% >= 1 THEN PRINT #2, _
		"Carrier lost->>>"
	GO TO 3090
REM
3070	REM
REM TIME OUT ERROR EXIT FOR RECEIVE
	INST.LIN% = TERR.LIN% - 1
	IF DISPLAY% THEN PRINT "Receive time out->>>";
	IF LOG.FLAG% >= 1 THEN PRINT #2, _
		"Receive time out->>>";
	GO TO 3090
REM
3080	REM
REM DELETE THE LEFT MOST CHARACTER FROM TEST STRING AND ADD
REM THE NEW CHARACTER TO THE RIGHT END
	TEST.STRING$ = RIGHT$(TEST.STRING$,NO.TERM.M1%) + CHR$(CHAR%)
REM
REM SEE IF TEST STRING IS TERMINATION STRING
REM ELSE GO BACK FOR NEXT CHARACTER
	IF TEST.STRING$ <> TERM.STRING$ THEN GO TO 3010
REM
REM TERMINATION STRING FOUND. IF LOGGING IS ON, SAVE TO DISK
3090	REM
	IF NOT WRAPPED% THEN GO TO 3200
	FOR I% = CHAR.NO% + 1 TO 2000
	PRNT.CHAR$ = CHR$(BUFFER%(I%))
	IF DISPLAY% THEN PRINT PRNT.CHAR$;
	IF LOG.FLAG% >= 1 THEN PRINT #2, PRNT.CHAR$;
	NEXT I%
3200	REM
	FOR I% = 1 TO CHAR.NO%
	PRNT.CHAR$ = CHR$(BUFFER%(I%))
	IF DISPLAY% THEN PRINT PRNT.CHAR$;
	IF LOG.FLAG% >= 1 THEN PRINT #2, PRNT.CHAR$;
	NEXT I%
REM
	GO TO 1000
REM
4000	REM
REM EXECUTE AN IF MATCH THEN BRANCH INSTRUCTION.
REM RIGHT- MOST CHARACTER OF TEST STRING IS ALIGNED WITH THE
REM MOST RECENTLY RECEIVED CHARACTER (INCLUDING THE TERMINATION
REM CHARACTER(S)) IN THE R COMMAND.
REM VARIABLE CHAR.NO% WILL BE > 0 ONLY IF PRECEEDING INSTRUCTION
REM WAS A RECEIVE. THE I INSTRUCTION CAN ONLY BE EXECUTED IF
REM CHARACTERS HAVE BEEN RECEIVED.
	IF CHAR.NO% <= 0 THEN GO TO 1000
REM GET THE STRING WE ARE LOOKING FOR
	TEST.STRING$ = ARG1$(INST.LIN%)
	NO.TEST% = LEN(TEST.STRING$)
REM SEE IF BUFFER HAS AT LEAST AS MANY CHARACTERS AS STRING
	IF NO.TEST% > CHAR.NO% AND NOT WRAPPED% THEN GO TO 1000
REM VARIABLE TEST.CHAR.NO% POINTS TO A CHARACTER IN TEST STRING
	TEST.CHAR.NO% = NO.TEST% + 1
REM COMPARE THE CHARACTERS, FROM RIGHT TO LEFT
	FOR I% = CHAR.NO% TO 1 STEP -1
	TEST.CHAR.NO% = TEST.CHAR.NO%  - 1
REM IF NO MORE CHARACTERS TO COMPARE IN STRING, THEN IT MATCHED
	IF TEST.CHAR.NO% = 0 THEN GO TO 4100
	TEST.CHAR% = ASC(MID$(TEST.STRING$,TEST.CHAR.NO%,1))
REM END TEST AT FIRST MISMATCH
	IF BUFFER%(I%) <> TEST.CHAR% THEN _
		GO TO 1000
	NEXT I%
REM
REM IN CASE BUFFER WAS WRAPPED, THEN CONTINUE COMPARING CHARS
	IF NOT WRAPPED% THEN GO TO 4100
REM IF WRAPPED, THEN CONTINUE FROM END OF BUFFER%
	FOR I% = 2000 TO CHAR.NO% + 1 STEP -1
	TEST.CHAR.NO% = TEST.CHAR.NO% - 1
	IF TEST.CHAR.NO% = 0 THEN GO TO 4100
	TEST.CHAR% = ASC(MID$(TEST.STRING$,TEST.CHAR.NO%,1))
	IF BUFFER%(I%) <> TEST.CHAR% THEN _
		GO TO 1000
	NEXT I%
4100	REM
REM IF WE HAVE GOTTEN THIS FAR (I.E. WE HAVE NOT BEEN SENT
REM BACK TO LINE 1000) THEN WE DEFINITELY HAVE A MATCH.	
	INST.LIN% = ARG2%(INST.LIN%) - 1
	GO TO 1000
REM
4400	REM
REM EXECUTE A JUMP (GO TO) INSTRUCTION
	INST.LIN% = ARG2%(INST.LIN%) - 1
	GO TO 1000
REM
4500	REM
REM EXECUTE AN ON ERROR GO TO INSTRUCTION
	ARGUMENT1$ = ARG1$(INST.LIN%)
	IF ARGUMENT1$ = "RE" THEN _
		RERR.LIN% = ARG2%(INST.LIN%)
	IF ARGUMENT1$ = "SE" THEN _
		SERR.LIN% = ARG2%(INST.LIN%)
	IF ARGUMENT1$ = "TO" THEN _
		TERR.LIN% = ARG2%(INST.LIN%)
	IF ARGUMENT1$ = "CL" THEN _
		CERR.LIN% = ARG2%(INST.LIN%)
	GO TO 1000
REM
5000	REM
REM CHANGE THE LOG FLAG
	LOG.FLAG% = ARG2%(INST.LIN%)
	GO TO 1000
REM
5100	REM
REM CHANGE THE DISPLAY (PRINT TO TERMINAL) FLAG
	DISPLAY% = ARG2%(INST.LIN%)
	GO TO 1000
REM
6000	REM
REM
REM
REM ERROR MESSAGES
9000	REM
	PRINT "Error: end of file at line #";NO.INSTR%; _
		", command = ";FIRST.CHAR$
	GO TO 9500
9005	REM
	PRINT "Error: L instruction without log file name"; _
		" in CP/M command"
	GO TO 9500
9010	REM
	PRINT "Error: illegal instruction:";FIRST.CHAR$; _
		", at line #";NO.INSTR%
	GO TO 9500
9015	REM
	PRINT "Error: illegal label for line #";NO.INSTR%
	GO TO 9500
9020	REM
	PRINT "Error: duplicate label ";LABEL.VALUE%; _
		" at line #";NO.INSTR%
	GO TO 9500
9025	REM
	PRINT "Error: illegal parameter for ";FIRST.CHAR$; _
		" command at line #";NO.INSTR%
	GO TO 9500
9030	REM
	PRINT "Error: illegal label argument in ";FIRST.CHAR$; _
		" command at line #";NO.INSTR%
	GO TO 9500
9035	REM
	PRINT "Error: undefined label in line #";I%
	GO TO 9500
9040	REM
	PRINT "Error: no instructions found."
	GO TO 9500
9050	REM
	PRINT "Error: too many characters in Send string at line #";_
		INST.LIN%
9500	REM
	PRINT "Instruction file ";INST.FILE$
9550	REM
	CLOSE
	END
REM
9600	REM
	PRINT "To run DIALOG, type"
	PRINT "   DIALOG INSTFILE.DLG LOGFILE.LOG"
	PRINT "where INSTFILE.DLG contains the instructions"; _
		" (the input to DIALOG),"
	PRINT "and LOGFILE.LOG (optional) will receive the log"; _
		" (the output from DIALOG)."
	PRINT "To check for syntax errors only, type"
	PRINT "   DIALOG INSTFILE.DLG ?
	END
REM
10000	REM
REM THIS SUBROUTINE CONVERTS ANY CHARACTERS PRECEEDED BY THE
REM CONTROL CHARACTER PREFACE TO CONTROL CHARACTERS
	NO.CHARACTERS% = LEN(IN.STRING$)
	OUT.STRING$ = ""
	PREFACE% = FALSE%
REM
	FOR CHAR.NO% = 1 TO NO.CHARACTERS%
	CHAR% = ASC(MID$(IN.STRING$,CHAR.NO%,1))
REM BRANCH IF PREVIOUS CHARACTER WAS THE CONTROL PREFACE
	IF PREFACE% THEN GO TO 10050
REM CHECK EACH CHARACTER FOR THE CONTROL PREFACE
	IF CHAR% <> CC.PREF% THEN GO TO 10100
	PREFACE% = TRUE%
	GO TO 10200
10050	REM
REM MAKE CHARACTER INTO CORRESPONDING CONTROL CHARACTER
	CHAR% = CHAR% - 64
	PREFACE% = FALSE%
10100	REM
REM COPY THE CHARACTER INTO THE OUTPUT BUFFER
	OUT.STRING$ = OUT.STRING$ + CHR$(CHAR%)
10200	REM
	NEXT CHAR.NO%
	RETURN
REM
