
(* MODEM SOFTWARE PACKAGE FOR CPM *)

(* PAUL L. GREENE		*)
(* MICRO ENGINEERING		*)
(* P.O. BOX 8094		*)
(* LA CRESCENTA, CA. 91214	*)

(* THIS PROGRAM IS BEING SUBMITTED TO THE PASCAL/MT USER'S   *)
(* GROUP FOR DISTRIBUTION AND USE BY INDIVIDUALS ONLY.       *)
(* MICRO ENGINEERING RESERVES ALL OTHER RIGHTS TO THIS       *)
(* PROGRAM.						     *)

(* WRITTEN FOR THE PASCAL/MT COMPILER VERSION 3.1	     *)

(* PASCAL/MT COPYRIGHT MT MICROSYSTEMS 			     *)
(* CP/M COPYRIGHT DIGITAL RESEARCH			     *)

(*************************************************************)

(* OPERATING INSTRUCTIONS *)

(* THIS PROGRAM REQUIRES THAT THE "IOBYTE" BE IMPLEMENTED,   *)
(* AND THAT THE MODEM IS CONNECTED TO ONE OF THE CONSOLE I/O *)
(* INTERFACES.  THE PROGRAM PERFORMS CONSOLE SWAPPING TO     *)
(* COMMUNICATE BETWEEN THE USER AND THE MODEM.  THE USER'S   *)
(* CONSOLE MUST BE AS FAST OR FASTER THAN THE MODEM SPEED.   *)
(* THIS PROVIDES A HARDWARE INDEPENDENT MEANS OF CONTROLLING *)
(* THE MODEM. 						     *)

(* THE PROGRAM FIRST ASKS WHICH CONSOLE NUMBER THE MODEM IS  *)
(* IS CONNECTED TO.  ANSWER WITH A SINGLE DIGIT, 0-3.  YOU   *)
(* ARE THEN ASKED TO SELECT WHETHER CONSOLE INPUT IS TO BE   *)
(* ECHOED ON THE CONSOLE OUTPUT.  ANSWER 'Y' OR 'N'.         *)

(* YOU ARE THEN ASKED TO DEFINE SEVERAL SPECIAL CONTROL      *)
(* CHARACTERS.  ONLY THE CHARACTERS FOR "QUIT" AND "MODE"    *)
(* ARE NOT SENT TO THE MODEM.  THE OTHERS ARE USED TO        *)
(* CONTROL THE REMOTE COMPUTER FROM THIS PROGRAM.  A COMMAND *)
(* PROMPT LINE IS THEN DISPLAYED, AND THE DESIRED COMMAND    *)
(* ENTERED BY TYPING THE FIRST LETTER.                       *)

(* THE "INITIAL" COMMAND REPEATS THE ABOVE INITIALIZATION.   *)

(* THE "MODEM" COMMAND CONNECTS THE USER'S CONSOLE TO THE    *)
(* MODEM FOR COMMUNICATION WITH THE REMOTE COMPUTER.  THE    *)
(* "COMMAND MODE" CONTROL CHARACTER CAN THEN BE USED ANYTIME *)
(* TO RETURN TO COMMAND LEVEL.                               *)

(* THE "QUIT" COMMAND RETURNS CONTROL TO CP/M.               *)

(* ALL COMMUNICATION FROM THE MODEM TO THE USER MAY BE       *)
(* CAPTURED ON A DISK FILE USING THE "RECEIVE" COMMAND.  THE *)
(* PROGRAM WILL ASK FOR THE FILE NAME, WHICH MAY INCLUDE A   *)
(* DISK DESIGNATOR.  A RECEIVE FILE MAY BE OPENED AT ANY     *)
(* TIME BY RETURNING TO COMMAND MODE.  WHEN ALL DESIRED      *)
(* TEXT HAS BEEN CAPTURED, THE FILE MUST BE CLOSED AND       *)
(* LOGGED ON THE DISK USING THE "CLOSE" COMMAND AFTER        *)
(* RETURNING TO COMMAND MODE.  THE AMOUNT OF TEXT THAT CAN   *)
(* BE CAPTURED IS ONLY LIMITED BY AVAILABLE DISK SPACE.      *)
(* THE TEXT IS INITIALLY BUFFERED IN MAIN MEMORY UNTIL THE   *)
(* BUFFER IS FULL.  A "SUSPEND" CHARACTER IS THEN SENT TO    *)
(* THE MODEM, AND THE BUFFER IS WRITTEN TO DISK.  THE        *)
(* PROGRAM THEN SENDS A "CONTINUE" CHARACTER TO THE MODEM    *)
(* AND RESUMES CAPTURING THE TEXT.  THEREFORE, THE REMOTE    *)
(* COMPUTER THAT YOU ARE COMMUNICATING WITH MUST HAVE BOTH   *)
(* "SUSPEND" AND "CONTINUE" CAPABILITY.                      *)

(* DISK FILES MAY BE SENT TO THE MODEM USING THE "SEND"      *)
(* COMMAND.  THE PROGRAM ASKS FOR THE NAME OF THE FILE TO    *)
(* BE SENT, WHICH MAY INCLUDE A DISK DESIGNATOR.  THE FILE   *)
(* IS THEN OPENED AND PREPARED FOR TRANSMISSION.  ACTUAL     *)
(* SENDING OF THE FILE DOES NOT BEGIN UNTIL RETURNING TO     *)
(* "MODEM" MODE.  THE SEND FILE IS ECHOED ON THE USER'S      *)
(* CONSOLE UNTIL COMPLETE OR UNTIL SUSPENDED BY RETURNING    *)
(* TO COMMAND LEVEL.  THE SEND FILE IS ALSO FULLY BUFFERED   *)
(* AND CAN BE ANY LENGTH UP TO AVAILABLE DISK CAPACITY.      *)
(* A FILE CAN BE SENT AT ANY TIME BY USING THE ABOVE         *)
(* PROCEDURE AFTER RETURNING TO COMMAND LEVEL.               *)

(* MODIFICATIONS *)

(* A DEDICATED MODEM CAN BE USED BY MODIFYING THE FOLLOWING  *)
(* PROCEDURES TO DIRECTLY ACCESS THE DEVICE:                 *)
(*	PROCEDURE WRITEMODEM                                 *)
(*	PROCEDURE READMODEM                                  *)
(*	PROCEDURE TESTMODEM                                  *)


(*************************************************************)
(*-----------------------------------------------------------*)
(*************************************************************)

PROGRAM MODEM;

CONST
	ENDFILE=$1A;		(* CP/M EOF (CONTROL-Z) *)
	BUFSIZE=4095;		(* DISK FILE BUFFERS *)
	RECSIZE=79;
	CR=$0D;
	LF=$0A;
	SPACE=$20;
	NULL=00;

	(* BDOS FUNCTIONS - SEE CP/M MANUAL *)
	IOCHK=7;IOSET=8;

	(* BIOS FUNCTIONS - SEE CP/M MANUAL *)
	WBOOT=0;CONSTS=3;CONIN=6;CONOUT=9;
	
TYPE
	BUFFER=ARRAY [0..BUFSIZE] OF CHAR;
	REC=ARRAY [0..RECSIZE] OF CHAR;
VAR
	INTITLE,OUTTITLE	:ARRAY [0..13] OF CHAR;

	INFILE,OUTFILE		:TEXT;

	INBUF,OUTBUF		:BUFFER;

	TRANSDATA,RECDATA,
	SUSPEND,CONTINUE,
	TERMINATE,MODE,QUIT,
	CIOBYTE,MIOBYTE,
	ININDEX,OUTINDEX	:INTEGER;

	DISKFULL,EOF,
	FISTAT,FOSTAT,
	CISTAT,MISTAT,
	COREADY,MOREADY,
	DONE,MWAIT,CRLF,
	CONECHO,PARITY		:BOOLEAN;

(*************************************************************)
(*************************************************************)

(*$L-*)
(*$I FILEIO*)  { CP/M FILE I/O LIBRARY }
(*$L+*)

(*************************************************************)

{ CP/M BDOS SUBROUTINE CALL }

PROCEDURE EXTERNAL [5] MON2(FUNCT:INTEGER;INFO:INTEGER);

(*************************************************************)

{ CP/M BDOS FUNCTION CALL }

FUNCTION MON1(FUNCT:INTEGER;INFO:INTEGER):INTEGER;

VAR	TEMP:INTEGER;

BEGIN

(* CALL MONITOR(BDOS), VALUE RETURNED IN REG A(LOW),B(HIGH) *)
 
MON2(FUNCT,INFO);

(* MOVE BA TO HL, SAVE AS TEMP *)

INLINE	("MOV	L,A/
	 "MOV	H,B/
	 "SHLD	/ TEMP );

MON1:=TEMP;

END;

(*************************************************************)

{ THIS PROCEDURE PERFORMS DIRECT ACCESS OF BIOS SUBROUTINES. }
{ THE ADDRESS OF THE BIOS SUBROUTINE JUMP TABLE IS ASSUMED   }
{ TO BE AVAILABLE AT LOCATION 0001.  THE PARAMETER "FUNCT"   }
{ IS AN INDEX, MODULO-3, TO THE JUMP TABLE TO SELECT THE     }
{ DESIRED FUNCTION.  THE PARAMETER "INFO" IS PASSED TO THE   }
{ BIOS SUBROUTINE IN REGISTER C.  WITH SINCERE APOLOGIES,    }
{ THIS ROUTINE IS SELF-MODIFYING, BUT IT IS ALSO SELF-       }
{ RESTORING.  THIS WAS NECESSARY SINCE FORWARD LABEL         }
{ REFERENCES ARE NOT ALLOWED IN INLINE CODE, AND NO OTHER    }
{ MEANS OF PERFORMING A COMPUTED SUBROUTINE CALL COULD BE    }
{ DEVISED.                                                   }

PROCEDURE BIOS2(FUNCT,INFO:INTEGER);

VAR
	ENTRY,ADDRESS,TEMP:INTEGER;
BEGIN
ENTRY:=$0001;	(* LOCATION OF BIOS WARM START ENTRY ADDRESS *)
ADDRESS:=ENTRY^ + FUNCT;	(* INDEX JUMP TABLE ENTRY *)
TEMP:=INFO;		(* PUT INFO AT KNOWN ADDRESS *)
INLINE(	[START]/"NOP	/		(* MODIFIED OPCODE *)
		"LDA	/TEMP/		(* GET INFO *)
		"MOV	C,A/		(* PUT IN C *)
		"LHLD	/ADDRESS/	(* LOAD BIOS ENTRY *)
		"MVI	A,/$E9/		(* 'PCHL' OPCODE *)
		"STA	/START/		(* MODIFY LOCATION *)
		"CALL	/START/		(* BRANCH TO BIOS *)
		"MVI	A,/$0/		(* 'NOP' OPCODE *)
		"STA	/START	);	(* RESTORE OPCODE *)

END;

(*************************************************************)

{ THIS FUNCTION IS SIMILAR TO PROCEDURE BIOS2 EXCEPT THAT THE }
{ CONTENT OF REGISTER A (AFTER RETURN FROM BIOS SUBROUTINE)   }
{ IS RETURNED AS THE FUNCTION VALUE.                          }

FUNCTION BIOS1(FUNCT,INFO:INTEGER):INTEGER;

VAR	ENTRY,ADDRESS,TEMP:INTEGER;

BEGIN
ENTRY:=$0001;
ADDRESS:=ENTRY^ + FUNCT;
TEMP:=INFO;
INLINE(	[START]/"NOP/
		"LDA	/TEMP/
		"MOV	C,A/
		"LHLD	/ADDRESS/
		"MVI	A,/$E9/
		"STA	/START/
		"CALL	/START/
		"MOV	L,A/		(* SAVE RETURNED DATA*)
		"MVI	H,/0/		(* CLEAR HIGH BYTE *)
		"SHLD	/TEMP/
		"MVI	A,/$0/
		"STA	/START	);

BIOS1:=TEMP;
END;

(*************************************************************)

PROCEDURE WRITECONSOLE(DATA:INTEGER);

BEGIN
IF DATA=CR THEN CRLF:=TRUE;
IF (DATA <> CR) AND (CRLF=TRUE) THEN
	BEGIN
	IF DATA <> LF THEN BIOS2(CONOUT,LF);
	CRLF:=FALSE;
	END;
BIOS2(CONOUT,DATA);
END;
	
(*************************************************************)

PROCEDURE TESTCONSOLE(VAR STAT:BOOLEAN);

VAR	DATA:INTEGER;

BEGIN
DATA:=BIOS1(CONSTS,0);
IF DATA=$00FF THEN STAT:=TRUE ELSE STAT:=FALSE;
END;

(*************************************************************)

PROCEDURE SETPARITY(VAR DATA:INTEGER);

VAR	RESULT:BOOLEAN;

(*****************************)

FUNCTION TESTPARITY(DATA:INTEGER):BOOLEAN;

VAR
RESULT:BOOLEAN;
X:INTEGER;

BEGIN
X:=DATA;
INLINE(	"LDA	/X/		(* GET CHAR *)
	"ORA	A/		(* SET PARITY FLAG *)
	"PUSH	PSW/		(* GET FLAGS TO A *)
	"POP	H/
	"MOV	A,L/
	"RRC/			(* SHIFT PARITY TO LSB *)
	"RRC/
	"ANI	/$01/		(* MASK OTHER BITS *)
	"MOV	L,A/		(* MAKE BOOLEAN *)
	"MVI	H,/0/
	"SHLD	/RESULT	);	(* TRUE IF EVEN PARITY *)

TESTPARITY:=RESULT;
END;

(******************************)

PROCEDURE CHANGEPARITY(VAR DATA:INTEGER);

VAR	X:INTEGER;

BEGIN
X:=DATA;
INLINE(	"LDA	/X/
	"XRI	/$80/		(* COMPLEMENT MSB *)
	"STA	/X	);
DATA:=X;
END;

(******************************)

BEGIN (*SETPARITY*)
RESULT:=TESTPARITY(DATA);
IF ((RESULT=TRUE) AND (PARITY=FALSE)) OR
	((RESULT=FALSE) AND (PARITY=TRUE))
		THEN CHANGEPARITY(DATA);
END; (*SETPARITY*)

(*************************************************************)

PROCEDURE WRITEMODEM(VAR DATA:INTEGER);

BEGIN
SETPARITY(DATA);
MON2(IOSET,MIOBYTE);
BIOS2(CONOUT,DATA);
MON2(IOSET,CIOBYTE);
END;

(*************************************************************)

PROCEDURE TESTMODEM(VAR STAT:BOOLEAN);

VAR	DATA:INTEGER;

BEGIN
MON2(IOSET,MIOBYTE);
DATA:=BIOS1(CONSTS,0);
IF DATA=$00FF THEN STAT:=TRUE ELSE STAT:=FALSE;
MON2(IOSET,CIOBYTE);
END;

(*************************************************************)

PROCEDURE READMODEM(VAR DATA:INTEGER);

BEGIN
REPEAT
	TESTMODEM(MISTAT)
UNTIL MISTAT=TRUE;
MON2(IOSET,MIOBYTE);
DATA:=BIOS1(CONIN,0);
DATA:=DATA & $007F;
MON2(IOSET,CIOBYTE);
END;

(*************************************************************)

PROCEDURE READFILE(VAR DATA:INTEGER);

BEGIN
IF OUTINDEX > BUFSIZE THEN
	BEGIN
	WRITEMODEM(SUSPEND);
	DATA:=ORD(GNB(OUTFILE,OUTBUF,OUTINDEX,EOF));
	WRITEMODEM(CONTINUE);
	END
ELSE
	DATA:=ORD(GNB(OUTFILE,OUTBUF,OUTINDEX,EOF));

IF EOF THEN
	BEGIN
	DATA:=0;
	WRITELN;WRITELN('* * * END OF TRANSMIT FILE * * *');
	FOSTAT:=FALSE;
	MOREADY:=FALSE;
	END;

END;

(*************************************************************)

PROCEDURE WRITEFILE(DATA:INTEGER);

VAR	
	CH:CHAR;
	TBUF:ARRAY[0..80] OF INTEGER;
	I,TDATA,TIMER,TINDEX:INTEGER;

BEGIN
IF ININDEX >= BUFSIZE THEN { WRITE BUFFER TO DISK }

	BEGIN
	WRITEMODEM(SUSPEND); 

	{ CONTINUE READING MODEM UNTIL NO CHARACTERS ARE RECEIVED    }
	{ FOR A RESPECTABLE PERIOD OF TIME.  THIS ALLOWS THE REMOTE  }
	{ TIME TO REACT TO THE "SUSPEND". THE CHARACTERS ARE PLACED  }
	{ IN A TEMPORARY BUFFER UNTIL MODEM TRANSFERS STOP.  CURRENT }
	{ TIMEOUT PERIOD IS APPROXIMATELY 2 SEC.                     }

	TINDEX:=0;
	TBUF[TINDEX]:=DATA; 
	TIMER:=0;
	WHILE TIMER < 1000 DO
		BEGIN
		TESTMODEM(MISTAT);
		IF MISTAT THEN
			BEGIN
			READMODEM(TDATA);
			TINDEX:=TINDEX+1;
			TBUF[TINDEX]:=TDATA;
			TIMER:=0;
			END
		ELSE TIMER:=TIMER+1;
		END;

	{ NOW THE CHARACTERS ARE RETRIEVED FROM THE TEMP BUFFER }
	{ AND WRITTEN TO DISK.  IF MORE THAN 1 CHARACTER IS IN }
	{ THE BUFFER, THEN WRITE TO THE CONSOLE ALSO. }

	FOR I:=0 TO TINDEX DO
		BEGIN
		TDATA:=TBUF[I];
		CH:=CHR(TDATA);
		IF (NOT DISKFULL) THEN 
			WNB(INFILE,INBUF,ININDEX,DISKFULL,CH);
		IF I <> 0 THEN WRITECONSOLE(TDATA);
		END;

	{ THE REMOTE CAN NOW BE ALLOWED TO CONTINUE. }

	WRITEMODEM(CONTINUE);
	END

ELSE

	BEGIN
	CH:=CHR(DATA);
	WNB(INFILE,INBUF,ININDEX,DISKFULL,CH);
	END;


IF DISKFULL THEN
	BEGIN
	WRITEMODEM(TERMINATE);
	WRITELN;
	WRITELN('* * * DISK FULL - TRANSFER TERMINATED * * *');
	FISTAT:=FALSE
	END;

END;

(*************************************************************)

PROCEDURE INITIAL;

VAR	
	CONSOLE:INTEGER;
	CH:CHAR;

FUNCTION CONTROL:INTEGER;

VAR	CH:CHAR;

BEGIN
READ(CH);WRITELN;
CONTROL:=ORD(CH) & $001F
END;


BEGIN
DONE:=FALSE;MWAIT:=FALSE;CRLF:=FALSE;
FOSTAT:=FALSE;FISTAT:=FALSE;
COREADY:=FALSE;MOREADY:=FALSE;
CISTAT:=FALSE;MISTAT:=FALSE;

CIOBYTE:=MON1(IOCHK,0);
MIOBYTE:=CIOBYTE & $FFFC;      (* LOGICAL AND TO CLEAR 2 LSB *)
CONSOLE:=CIOBYTE & $0003;      (* LOGICAL AND TO MASK 2 LSB *)

WRITELN;WRITELN('* * * MODEM COMMUNICATION PROGRAM * * *');
WRITELN;WRITELN('CURRENT CONSOLE NUMBER: ',CONSOLE);

REPEAT
	WRITE('ENTER MODEM CONSOLE NUMBER (0-3): ');
	READ(CONSOLE);WRITELN
UNTIL (CONSOLE >= 0) AND (CONSOLE <= 3);
MIOBYTE:=MIOBYTE + CONSOLE;

WRITE('WANT CONSOLE ECHO (Y OR N)?');
READ(CH);WRITELN;
IF CH='Y' THEN CONECHO:=TRUE ELSE CONECHO:=FALSE;

WRITE('ENTER SUSPEND CHARACTER: CONTROL-');
SUSPEND:=CONTROL;

WRITE('ENTER CONTINUE CHARACTER: CONTROL-');
CONTINUE:=CONTROL;

WRITE('ENTER TERMINATE CHARACTER: CONTROL-');
TERMINATE:=CONTROL;

WRITE('ENTER COMMAND MODE CHARACTER:CONTROL-');
MODE:=CONTROL;

WRITE('ENTER SYSTEM RETURN CHARACTER: CONTROL-');
QUIT:=CONTROL;

WRITE('WANT EVEN OR ODD PARITY (E OR O)?');
READ(CH);WRITELN;
IF CH='E' THEN PARITY:=TRUE ELSE PARITY:=FALSE;

WRITELN('* * * INITIALIZATION COMPLETE * * *');
WRITELN;

END;

(*************************************************************)

PROCEDURE COMMAND;

TYPE
	NAME=ARRAY[0..11] OF CHAR;
	STRING=RECORD
		LEN:INTEGER;
		VAL:ARRAY[1..79] OF CHAR
		END;
VAR
	CH:CHAR;
	RESULT:INTEGER;
	FILENAME:NAME;

(*$L-*)
(*$ISTRIO*)
(*$IFILESTUF*)
(*$L+*)

PROCEDURE READTITLE(VAR T:NAME);

VAR
	TITLE,FILENAME:STRING;

BEGIN
WRITE(' FILE:');
READSTR(FILENAME);WRITELN;
NAMEPARSER(FILENAME,TITLE);
MOVE(TITLE.VAL[1],T[0],12);
END;

BEGIN (* COMMAND *)
WRITEMODEM(SUSPEND);
CH:=' ';
REPEAT
	WRITELN;WRITELN;
	WRITE('COMMAND:I(NITIAL),S(END),R(ECEIVE),C(LOSE),M(ODEM),Q(UIT) ?');
	READ(CH);WRITELN;
	
	IF CH='I' THEN INITIAL;

	IF CH='S' THEN
		BEGIN
  		WRITE('SEND');
		READTITLE(FILENAME);
		OPEN(OUTFILE,FILENAME,RESULT);

		IF RESULT=255 THEN
		  WRITELN('* * * UNABLE TO OPEN FILE * * *')
		ELSE
			BEGIN
			OUTINDEX:=BUFSIZE+1;
			FOSTAT:=TRUE;
			END;
		END;

	IF CH='R' THEN
		BEGIN
		WRITE('RECEIVE');
		READTITLE(FILENAME);
		OPEN(INFILE,FILENAME,RESULT);

		IF RESULT <> 255 THEN DELETE(INFILE);

		CREATE(INFILE,FILENAME,RESULT);
		IF RESULT=255 THEN
			WRITELN('* * * DIRECTORY FULL * * *')
		ELSE
			BEGIN
			ININDEX:=0;
			FISTAT:=TRUE;
			END;
		END;

	IF CH='C' THEN
		BEGIN
		WRITEFILE(ENDFILE);
		IF ININDEX <> 0 THEN
			BEGIN
			ININDEX:=BUFSIZE;
			WRITEFILE(ENDFILE);
			END;
		CLOSE(INFILE,RESULT);
		FISTAT:=FALSE;
		END;

	IF CH='Q' THEN BIOS2(WBOOT,0);

UNTIL CH='M';
WRITELN('*** RETURNING TO MODEM ***');
WRITELN;
WRITEMODEM(CONTINUE);

END;

(*************************************************************)
(*************************************************************)

BEGIN
INITIAL;
COMMAND;
WHILE NOT DONE DO
	BEGIN

	IF NOT MOREADY THEN  (* GET NEW OUTPUT *)

		IF FOSTAT THEN  (* GET FROM FILE *)
			BEGIN
			READFILE(TRANSDATA);
			MOREADY:=TRUE;
			END
		ELSE  (* GET FROM CONSOLE *)
			BEGIN
			TESTCONSOLE(CISTAT);
			IF CISTAT THEN
				BEGIN
				TRANSDATA:=BIOS1(CONIN,0);
				MOREADY:=TRUE;
				END;
			END;

	IF MOREADY THEN
		BEGIN
		IF TRANSDATA = QUIT THEN BIOS2(WBOOT,0);
		IF TRANSDATA = MODE THEN 
			BEGIN
			MOREADY:=FALSE;
			COMMAND;
			END;
	
		END;

	IF NOT COREADY THEN (* GET NEW INPUT *)
	
		BEGIN
		TESTMODEM(MISTAT);
		IF MISTAT THEN (* GET FROM MODEM *)
			BEGIN
			READMODEM(RECDATA);
			COREADY:=TRUE;
			IF RECDATA=SUSPEND THEN
				BEGIN
				REPEAT READMODEM(RECDATA)
				UNTIL RECDATA=CONTINUE;
				COREADY:=FALSE;
				END;
			END;
		END;

	IF COREADY THEN  (* SEND TO CONSOLE *)
		BEGIN
		WRITECONSOLE(RECDATA);
		IF FISTAT THEN WRITEFILE(RECDATA);
		COREADY:=FALSE;
		END;

	IF MOREADY THEN  (* SEND TO MODEM *)
		BEGIN
		WRITEMODEM(TRANSDATA);
		IF CONECHO THEN
			BEGIN
			BIOS2(CONOUT,TRANSDATA);
			IF TRANSDATA=CR THEN BIOS2(CONOUT,LF);
			END;
		MOREADY:=FALSE;
		END;

	END;
END.  (* MODEM *)
