.TITLE DUPLEX DO COMMUNICATIONS BETWEEN TERMINALS (MACHINES?)
	.IDENT	/1.05A/
	.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$
	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
;
;---------------------------------------------------------------------------
	.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
RCVFDB:	FDBDF$
	FDRC$A	,RCVBUF,BUFSIZ
	FDOP$A	4,,RCVNAM
RCVBUF:	.BLKB	BUFSIZ
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
;
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
	.SBTTL	TABLES
	.PSECT	TABLE,D,REL,RO
;
; SUBROUTINES FOR SPECIAL FUNCTIONS
;
TABLE:	XMIT	;#1 -- CTRL-T
	RECV	;#2 -- CTRL-F
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
	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$
;
; 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,#'$>
	CSI$1	#CSIBLK,#FILENAME,IOSB+2	;COMPRESS LINE
	BCS	20$	;ERROR HANDLING
	CSI$2	#CSIBLK,INPUT	;PARSE FILE DESCRIPTOR
	BCS	20$	;ERROR HANDLING
	OPEN$R	#XMTFDB	;OPEN FILE FOR READING
	BCS	20$	;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	10$	;YES
	TSTB	XFMODE	;TRANSMIT WHOLE FILE INTACT?
	BEQ	8$	;YES
	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
	QIOW$S	#IO.WVB,#2,#2,,,,<XMTFDB+F.NRBD+2,XMTFDB+F.NRBD,#'+>
	BR	5$
;
;	DONE WITH FILE (EITHER END OR ERROR) -- CLOSE IT
;
10$:	CLOSE$	#XMTFDB
	CLRB	XACTIV	;FLAG FILE CLOSED
	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
; 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