.TITLE DUPLEX DO COMMUNICATIONS BETWEEN TERMINALS (MACHINES?)
	.IDENT	/1.20A/
	.MCALL QIO$S,QIOW$S,CLEF$S,WTSE$S,ASTX$S,EXIT$S,SETF$S
	.MCALL	FCSMC$,CSI$,CSI$1,CSI$2,DIR$,QIO$,QIOW$
	.MCALL	MRKT$S
	FCSMC$
	.PAGE
;---------------------------------------------------------------------------
;
;	AUTHOR: MARTIN HELLER	DATE:24-OCT-79
; DUPLEX:
;	The idea here is to provide the wiring (in software)
;	so that a single terminal (unit 1, by convention) can
;	be hard wired to the PDP-11 and yet function as a terminal
;	to any computer that can be called on the telephone or
;	wired to another terminal port.
;		For dial-up use, a full-duplex modem needs to be
;	connected to another port on the PDP-11. This is useful
;	anyway, so that you can dial into the PDP-11; this program
;	lets you reverse the process and dial out. When you
;	task-build, ASG=TI:1, and explicitly assign the proper
;	port to unit 2. 
;		There is no reason that the second port couldn't be
;	hardwired as well. Therefore, if you have two computers within
;	a few hundred yards of each other, you can connect them together
;	through EIA ports and have just one terminal at your desk
;	to talk to both of them.
;		CONTROL-B CAUSES EXIT FROM THE PROGRAM
;
;** 26-OCT-79: VERSION 1.01 --	CTRL-T IS TRANSMIT A FILE
;
; TO USE: TYPE CONTROL AND T SIMULTANEOUSLY. THIS WILL NOT BE TRANSMITTED.
; THE PROGRAM WILL PROMPT FOR AN INPUT FILE SPECIFICATION, I.E.
;	DUPLEX>=SY0:[25,34]FILENAME.TYP;4
; THE USUAL RULES FOR DEFAULTS APPLY; NO WILDCARDS OR LISTS ARE ALLOWED.
; NOTICE THAT THE EQUALS SIGN IS NOT OPTIONAL--PARSING IS FOR AN INPUT FILE.
;
; ** 31-OCT-79: VERSION 1.02
;	CTRL-P TOGGLES BETWEEN FILE TRANSMISSION AND LINE TRANSMISSION
;	CTRL-N CAUSES TRANSMISSION OF NEXT LINE OF FILE IN LINE MODE
;
; 	VERSION 1.03
;	CTRL-A ABORTS A FILE TRANSMISSION
;	CTRL-F OPENS/CLOSES FILE OF RECEIVED DATA
;
; ** 26-NOV-79: VERSION 1.04
;	MESSAGES FOR CTRL-F
;	USE DIR$ FORM FOR QIO'S ISSUED FROM AST HANDLERS
;
; ** 7-DEC-79: VERSION 1.05
;	HANDLING OF CONTROL CHARACTERS FROM TERMINAL 2 CHANGED:
;	ALL BOTHERSOME NULLS AND CONTROL CHARACTERS IGNORED FROM TERM 2
;
; ** 23-MAR-83 VERSION 1.20
;	G. EVERHART
;	ADD DELAY LOGIC. CTRL-\ SETS DELAY UP... SETS UP A PREDEFINED
;	NUMBER OF TICKS DELAY AFTER EACH LINE (IF IN LINE MODE) AND
;	AUTOMATICALLY SENDS LINES OUT AFTER THAT DELAY. ANOTHER CTRL-\
;	TOGGLES THE MODE OFF AGAIN. (36 OCTAL)
;	CTRL-^ SETS CHARACTER DELAY OF (NOMINALLY) 1 TICK PER CHAR SENT
;	OR TURNS THIS DELAY OFF AGAIN. (34 OCTAL)
;	CTRL-] (OCTAL 35) ALLOWS DELAY FOR LINES TO BE SET. 1 CHARACTER
;	IS READ AND 60 OCTAL SUBTRACTED FROM IT TO GIVE DELAY.
;	IF RESULT IS NEGATIVE, DELAY IS SET TO 4.
;---------------------------------------------------------------------------
	.PAGE
	.SBTTL	DIRECTIVES
	.PSECT	DIRECT,D,REL,RW
;
AQ1:	QIO$	IO.WVB,2,2,,,,<,1>
AQ2:	QIO$	IO.WVB,1,1,,,,<,1>
OPERR:	QIOW$	IO.WVB,1,1,,,,<OPNERR,OPNERL,'0>
ROMSG:	QIOW$	IO.WVB,1,1,,,,<ROMT,ROML,'0>
RCMSG:	QIOW$	IO.WVB,1,1,,,,<RCMT,RCML,'0>
	.PAGE
	.SBTTL	INITIALIZATION
	FSRSZ$	2	;INITIALIZE FCS RECORD BUFFER
	CSI$	;DEFINE CSI CONTROL BLOCK
	.PSECT DUPLEX,I,REL,RO
DUPLEX::
	QIOW$S	#IO.ATA,#1,#1,,,,<#AST1>	;ATTACH DEVICE 1
	QIOW$S	#IO.ATA,#2,#2,,,,<#AST2>	;ATTACH DEVICE 2
	FINIT$	;INITIALIZE FCS
	MOV	#BUF1,B1PTR	;INITIALIZE BUFFERS FOR EACH TERMINAL
	MOV	#BUF2,B2PTR
1$:	CLEF$S	#3		;WAIT FOREVER: ALL WE WANT IS THE AST HANDLING
	WTSE$S	#3
	MOVB	SENTIN,R1	;SOMETHING SPECIAL CALLED FOR?
	BGT	20$	;YES
	QIOW$S	#IO.KIL,#1,#1	;NO -- EXIT
	QIOW$S	#IO.KIL,#2,#2
	QIOW$S	#IO.DET,#1,#1
	QIOW$S	#IO.DET,#2,#2
	TSTB	RCVFIL	;FILE OPEN?
	BEQ	10$	;NO
	CLOSE$	#RCVFDB	;YES - CLOSE IT
10$:	EXIT$S
;
;SPECIAL FUNCTION DESIRED: R1 HOLDS INDEX
;
20$:	CMP	#ENTRIES,R1	;CHECK FOR NON-EXISTENT CALL
	BLT	25$	;NO GOOD -- IGNORE
	DEC	R1
	ASL	R1	;CHANGE INDEX TO OFFSET
	CALL	@TABLE(R1)	; GO DO THE GOOD WORK
25$:	CLRB	SENTIN	;SET UP FOR ANOTHER REQUEST
	BR	1$	;GO WAIT (PROCESS AST'S)
	.PAGE
	.SBTTL	BUFFERS AND POINTERS
	.PSECT	DATA,D,REL,RW
CIRSIZ = 256.
BUFSIZ = 136.
B1PTR:	.WORD	0
B2PTR:	.WORD	0
SAVE:	.WORD	0
RPTR:	.WORD	0
IOSB:	.BLKW	2
;
XMTFDB:	FDBDF$
	FDRC$A	,XMTBUF,BUFSIZ
	FDOP$A	3,CSIBLK+C.DSDS	;FILENAME WILL BE PARSED BY CSI
XMTBUF:	.BLKB	BUFSIZ
FILENAME:	.BLKB	30.
	.EVEN
CSIBLK:	.BLKB	C.SIZE
	.EVEN
RCVFDB:	FDBDF$
	FDRC$A	,RCVBUF,BUFSIZ
	FDOP$A	4,,RCVNAM
RCVBUF:	.BLKB	BUFSIZ
	.EVEN
RCVNAM:	NMBLK$	RECEIVED,DAT
;
BUF1:	.BLKB	CIRSIZ	;THESE CIRCULAR BUFFERS JUST HAVE TO BE 
BUF2:	.BLKB	CIRSIZ	;BIG ENOUGH TO PROVIDE A REASONABLE TYPE-AHEAD
SENTIN:	.BYTE	0	;CAPABILITY WHEN THE TERMINALS GO AT DIFFERENT SPEEDS
XFMODE:	.BYTE	0
XABORT:	.BYTE	0
RCVFIL:	.BYTE	0
XACTIV:	.BYTE	0
	.EVEN
LDLYV:	.WORD	6	;NUMBER OF TICKS TO WAIT BETWEEN LINES
CHDLV:	.WORD	1	;NUMBER OF TICKS BETWEEN CHARACTERS (IF WAITING)
LWF:	.WORD	0	;FLAG 1 IF LINEWAIT ACTIVE
CWF:	.WORD	0	;FLAG 1 IF CHAR WAIT ACTIVE
;
FASK:	.ASCII	/DUPLEX>/
FASKL = .-FASK
OPNERR:	.ASCII	/ERROR OPENING FILE/
OPNERL = .-OPNERR
LMSG:	.ASCII	/TYPE CTRL-N TO TRANSMIT A LINE/
LMSGL = .-LMSG
ROMT:	.ASCII	/RECEIVED DATA FILE HAS BEEN OPENED/
ROML = .-ROMT
RCMT:	.ASCII	/RECEIVED DATA FILE HAS BEEN CLOSED/
RCML = .-RCMT
LDMT:	.ASCII	/LINE DELAY SET TO /
DSMB:
LDMC:	.ASCII	/4 TICKS/
LDML=.-LDMT
CDMT:	.ASCII	/CHARACTER DELAY SET TO 1 TICK/
CDML=.-CDMT
LUMT:	.ASCII	/LINE DELAY CLEARED/
LUML=.-LUMT
CUMT:	.ASCII	/CHAR DELAY CLEARED/
CUML=.-CUMT
XCMT:	.ASCII	/Transmission Complete/
XCML=.-XCMT
DSMT:	.ASCII	/Enter value (1 char) for delay:/
DSML=.-DSMT
	.EVEN
	.SBTTL	TABLES
	.PSECT	TABLE,D,REL,RO
;
; SUBROUTINES FOR SPECIAL FUNCTIONS
;
TABLE:	XMIT	;#1 -- CTRL-T
	RECV	;#2 -- CTRL-F
	LDM	;#3 -- CTRL-\ (LINE DELAY ACTIVE MSG)
	CDM	;#4 -- CTRL-^ (CHARACTER DELAY ACTIVE MSG)
	LDS	;#5 -- OCTAL 35 (SET UP DELAYS) CTRL-]
ENTRIES = .-TABLE/2
	.PAGE
	.SBTTL	ASYNCHRONOUS TRAP HANDLERS
	.PSECT	AST,I,REL,RO
;
; AST SERVICE ROUTINE FOR TERMINAL 1
;
AST1:	MOV	R0,SAVE	;ON GENERAL PRINCIPLES
	MOV	(SP)+,R0	;CHARACTER RECIEVED AND PARAMETER2
	BIC	#177400,R0	;LOW BYTE FOR KEY PRESSED
;NOTE THE TEST FOR EXIT IS NOW CONTROL B. THIS ALLOWS CONTROL C TO BE
;USED TO CONTROL THE REMOTE COMPUTER.
	CMP	#2,R0	;CTRL-B WANTS IMMEDIATE EXIT FROM PROGRAM
	BEQ	XIT	;EXIT TASK
	CMP	#24,R0	;CTRL-T
	BEQ	10$	;TRANSMIT FILE
	CMP	#20,R0	;CTRL-P
	BEQ	20$	;TOGGLE XMIT MODE
	CMP	#16,R0	;CTRL-N
	BEQ	30$	;XMIT NEXT LINE
	CMP	#1,R0	;CTRL-A
	BEQ	15$	;ABORT TRANSMISSION
	CMP	#6,R0	;CTRL-F
	BEQ	25$	;RECEIVE FILE OPEN/CLOSE
	CMPB	#36,R0	;CTRL-\
	BEQ	507$	;TOGGLE LINE DELAY MODE
	CMPB	#34,R0	;CTRL-^
	BEQ	508$	;TOGGLE CHARACTER DELAY MODE
	CMPB	#35,R0	;CTRL-]
	BEQ	509$	;SET UP DELAY MAGNITUDE FOR LINES
	MOVB	R0,@B1PTR	;TRANSMIT BYTE
	MOV	B1PTR,AQ1+Q.IOPL
	DIR$	#AQ1
	INC	B1PTR	;MAINTAIN CIRCULAR BUFFER
	CMP	B1PTR,#BUF1+CIRSIZ
	BLE	5$
	MOV	#BUF1,B1PTR
5$:	MOV	SAVE,R0	;GENERAL PRINCIPLES AGAIN
	ASTX$S		;RETURN FROM AST SERVICE
;
10$:	MOVB	#1,SENTIN
11$:	SETF$S	#3
	BR	5$
; CTRL-A: ABORT TRANSMISSION
15$:	INCB	XABORT
	BR	5$
; CTRL-P: TOGGLE TRANSMISSION MODE
20$:	COMB	XFMODE
	CLEF$S	#4
	BR	5$
; CTRL-F: OPEN/CLOSE FILE FOR DATA RECEIVED
25$:	MOVB	#2,SENTIN
	BR	11$
; CTRL-N: TRANSMIT NEXT LINE
30$:	SETF$S	#4
	BR	5$
; CTRL-\: TOGGLE LINE DELAY
507$:	COM	LWF	;SET FLAG OR CLEAR IT
	MOVB	#3,SENTIN
	BR	11$
; OCTAL 35 : SET UP DELAY MAGNITUDE
509$:	MOVB	#5,SENTIN
	BR	11$	;35 - SET UP DELAY
;
; CTRL-^: TOGGLE CHARACTER DELAY
508$:	COM	CWF
	MOVB	#4,SENTIN
	BR	11$
;
; CONTROL-B PRESSED: IMMEDIATE EXIT
;
XIT:	CLRB	SENTIN
	SETF$S	#3
	MOV	SAVE,R0
	ASTX$S
;
; AST HANDLER FOR 2ND TERMINAL
;
AST2:	MOV	R0,SAVE
	MOV	(SP)+,R0
	BIC	#177400,R0	;1 BYTE FOR KEY PRESSED
	CMP	#3,R0	;CTRL-C 
	BEQ	5$	;IGNORE CTRL-C ( THIS IS A CHANGE )
	CMP	#24,R0	;CTRL-T (DC4)
	BEQ	5$	;IGNORE DC4 (GENERATED BY 6800 EDITOR)
	CMP	#25,R0	;CTRL-U (NAK)
	BEQ	5$	;IGNORE NAK (GENERATED BY 6800 BASIC)
	CMP	#177,R0	;DEL
	BEQ	5$	;IGNORE DEL (GENERATED BY 6800 BASIC)
	MOVB	R0,@B2PTR	;TRANSMIT BYTE
	MOV	B2PTR,AQ2+Q.IOPL
	DIR$	#AQ2
	TSTB	RCVFIL	;FILE OPEN TO RECEIVE DATA?
	BEQ	2$	;NO
	CALL	RCVCHR	;YES: PUT CHARACTER INTO FILE
2$:	INC	B2PTR
	CMP	B2PTR,#BUF2+CIRSIZ
	BLE	5$
	MOV	#BUF2,B2PTR
5$:	MOV	SAVE,R0
	ASTX$S		;RETURN FROM AST SERVICE
	.PAGE
	.SBTTL	XMIT - TRANSMIT A FILE
	.PSECT	XMIT,I,REL,RO
;
;	GET FILE DESCRIPTER, PARSE IT, OPEN INPUT FILE
;
XMIT:	QIOW$S	#IO.RPR,#1,#1,,#IOSB,,<#FILENAME,#30.,#6,#FASK,#FASKL,#'$>
	BR	522$
521$:	JMP	10$
520$:	JMP	20$
522$:
	CSI$1	#CSIBLK,#FILENAME,IOSB+2	;COMPRESS LINE
	BCS	520$	;ERROR HANDLING
	CSI$2	#CSIBLK,INPUT	;PARSE FILE DESCRIPTOR
	BCS	520$	;ERROR HANDLING
	OPEN$R	#XMTFDB	;OPEN FILE FOR READING
	BCS	520$	;ERROR HANDLING
	CLRB	XABORT	;INITIALIZE
	INCB	XACTIV	;FLAG FILE ACTIVE
	TSTB	XFMODE
	BEQ	5$
	QIOW$S	#IO.WVB,#1,#1,,,,<#LMSG,#LMSGL,#'0> ;SAY LINE MODE ACTIVE
;
;	READ A RECORD FROM FILE AND WRITE IT TO TERMINAL 2
;
5$:	TSTB	XABORT	;STOP NOW?
	BNE	521$	;YES
	TSTB	XFMODE	;TRANSMIT WHOLE FILE INTACT?
	BEQ	8$	;YES
;DELAY ROUTINE HERE IF ACTIVE...
	TST	LWF	;ARE WE DELAYING PER LINE?
	BEQ	501$	;IF EQ, NO, JUST AWAIT CTRL-N
	MRKT$S	#15,LDLYV,#1	;SET FLAG 15 AFTER 'LDLYV' TICKS
	WTSE$S	#15	;AWAIT TIMEOUT
	BR	8$	;AND SKIP THE WAITS FOR CONTROL-N...
501$:
	WTSE$S	#4	;NO- WAIT FOR PERMISSION FOR THIS LINE
	CLEF$S	#4	;INHIBIT NEXT LINE
8$:	GET$	#XMTFDB
	BCS	10$	; END AND ERROR HANDLING
	TST	CWF	;ARE WE INSERTING DELAYS BETWEEN CHARACTERS?
	BEQ	502$	;IF EQ NO...(THANK HEAVEN...DELAY IS COSTLY)
;CHARACTER DELAY. THIS MUST BE HANDLED BY SINGLE-CHARACTER QIO$'S
;SO LET'S GET AT IT.
	TST	XMTFDB+F.NRBD	;ZERO BYTES? NORMAL IF SO
	BLE	502$
	MOV	R0,-(SP)
	MOV	R1,-(SP)	;NEED SOME REGISTERS
	MOV	#XMTFDB+F.NRBD+2,R0	;ADDRESS TO SHOOT OUT
	MOV	XMTFDB+F.NRBD,R1	;BYTE COUNT
	QIOW$S	#IO.WVB,#2,#2,,,,<R0,#1,#'+>	;EMIT 1ST CHAR WITH CR FIRST
	INC	R0	;PASS CHARACTER EMITTED
	DEC	R1
	BLE	503$
504$:
	MRKT$S	#15,CHDLV,#1
	WTSE$S	#15		;WAIT FOR 'CHDLV' TICKS
	QIOW$S	#IO.WVB,#2,#2,,,,<R0,#1,#0>
	INC	R0
	DEC	R1
	BGT	504$
503$:
	MOV	(SP)+,R1	;RESTORE REGS USED...
	MOV	(SP)+,R0
	JMP	5$
502$:
	QIOW$S	#IO.WVB,#2,#2,,,,<XMTFDB+F.NRBD+2,XMTFDB+F.NRBD,#'+>
	JMP	5$
;
;	DONE WITH FILE (EITHER END OR ERROR) -- CLOSE IT
;
10$:	CLOSE$	#XMTFDB
	CLRB	XACTIV	;FLAG FILE CLOSED
	QIOW$S	#IO.WVB,#1,#1,,,,<#XCMT,#XCML,#'0> ;SAY TRANSMISSION DONE
	RETURN
;
; ERROR OPENING FILE -- SAY SO AND WAIT
;
20$:	DIR$	#OPERR
	RETURN
	.PAGE
	.SBTTL	RECV - HANDLE DATA RECEIVED
	.PSECT	RECV,I,REL,RO
RECV:
	TSTB	RCVFIL	;IS A FILE OPEN
	BNE	10$	;YES -- CLOSE IT
;OPEN FILE
	FDAT$R	#RCVFDB,#R.VAR,#FD.CR,,#-5,#-5
	OPEN$W	#RCVFDB
	BCS	20$	; ERROR HANDLING
	MOVB	#1,RCVFIL	;MARK FILE OPEN
	MOV	#RCVBUF,RPTR
	DIR$	#ROMSG	;SAY FILE OPENED
	RETURN
;CLOSE FILE
10$:	CLOSE$	#RCVFDB
	CLRB	RCVFIL	;MARK FILE CLOSED
	DIR$	#RCMSG	;SAY FILE CLOSED
	RETURN
;
; ERROR OPENING FILE -- SAY SO AND WAIT
;
20$:	DIR$	#OPERR
	RETURN
;
; LDM - LINE DISPLAY MESSAGE DISPLAY
;
LDM:	TST	LWF	;NOW IN LINEWAITING MODE?
	BNE	510$	;IF NE NO, WAITING CLEARED
	QIOW$S	#IO.WVB,#14,#14,,,,<#LDMT,#LDML,#'+>
	RETURN
510$:	QIOW$S	#IO.WVB,#14,#14,,,,<#LUMT,#LUML,#'+>
	RETURN
;
; CDM - CHAR DISPLAY MESSAGE DISPLAY
;
CDM:	TST	CWF	;NOW IN CHAR WAITING MODE?
	BNE	511$	;IF NE NO, WAITING CLEARED
	QIOW$S	#IO.WVB,#14,#14,,,,<#CDMT,#CDML,#'+>
	RETURN
511$:	QIOW$S	#IO.WVB,#14,#14,,,,<#CUMT,#CUML,#'+>
	RETURN
;
;
; LDS - LINE DELAY MAGNITUDE SETUP
LDS:	QIOW$S	#IO.WVB,#14,#14,,,,<#DSMT,#DSML,#'+>
	QIOW$S	#IO.RVB,#14,#14,,,,<#DSMB,#1>
;READ 1 CHARACTER INTO BUFFER. THEN SET DELAY FROM IT.
; (SUBTRACT ASCII 0)
	MOVB	DSMB,-(SP)
	BIC	#177400,(SP)	;ZERO HIGH BYTE
	SUB	#60,(SP)	;ADJUST THE ASCII
	MOV	(SP)+,LDLYV	;STORE AS NEW DELAY VALUE
	BGT	512$		;AS LONG AS >0 ALL'S WELL
	MOV	#4,LDLYV	;IF NEGATIVE JUST PUT IN 4
512$:	RETURN
;
;
; RECIEVE A CHAR: BYTE IN R0 IS TO BE STUFFED INTO BUFFER
; WHEN BUFFER IS FULL OR CR DETECTED, FLAG BUFFER FOR WRITING
RCVCHR:	CMPB	#15,R0	;CR?
	BEQ	20$	;YES - PUT BUFFER
	CMPB	#12,R0	;LF?
	BEQ	10$	;IGNORE LF
	TSTB	R0	;NUL?
	BEQ	10$	;IGNORE NUL
	MOVB	R0,@RPTR	;STUFF CHAR INTO BUFFER
	INC RPTR	;MAINTAIN POINTER
	CMP	RPTR,#RCVBUF+BUFSIZ	;CHECK FOR BUFFER FULL
	BGT	20$	;NEED TO PUT BUFFER
10$:	RETURN	;ALL DONE
20$:	MOV	RPTR,R0	;COMPUTE RECORD LENGTH
	SUB	#RCVBUF,R0
	BLT	25$	;NEGATIVE CHARS WOULD BE AN ERROR
	MOV	R0,RCVFDB+F.NRBD	;SET LENGTH
	PUT$	#RCVFDB	;SHIP IT OUT
25$:	MOV	#RCVBUF,RPTR	;SET UP FOR NEW BUFFER
	RETURN
	.END	DUPLEX