--- /dev/null
+
+TITLE READC TELETYPE DEVICE HANDLER FOR MUDDLE
+
+RELOCATABLE
+
+.INSRT MUDDLE >
+
+.GLOBAL BUFRIN,CHRCNT,SYSCHR,ECHO,BYTPTR,ERASCH,KILLCH,BRKCH,AGC
+.GLOBAL IOIN2,READC,WRONGT,WRONGD,WRONGC,CALER1,BRFCHR,ESCAP,TTYOPE,TYI,TYO
+.GLOBAL RRESET,TTICHN,TTOCHN,CHANNO,STATUS
+
+TTYOUT==1
+TTYIN==2
+
+
+; READC IS CALLED BY PUSHJ P,READC
+; B POINTS TO A TTY FLAVOR CHANNEL
+; ONE CHARACTER IS RETURNED IN A
+; BECOMES INTERRUPTABLE IF NO CHARACTERS EXISTS
+
+READC: PUSH P,E ;SAVE E FROM DEATH
+ MOVE E,BUFRIN(B) ;GOBBLE POINTER TO BUFFER AND INFO
+ SOSGE CHRCNT(E) ;ANY CHARS LEFT?
+ PUSHJ P,INCHAR ;NO, GO READ SOME
+ ILDB A,BYTPTR(E) ;GOBBLE ONE
+ POP P,E ;RESTORE E
+ POPJ P,
+
+; HERE TO ASK SYSTEM FOR SOME CHARACTERS
+
+INCHAR: IRP A,,[0,C,D] ;SAVE ACS
+ PUSH P,A
+ TERMIN
+ CLEARM CHRCNT(E) ;NO CHARS IN BUFFER
+ MOVE D,[010700,,BYTPTR(E)] ;MAKE A BYTE POINTER TO START OF BUFFER
+ HLRE 0,E ;FIND END OF BUFFER
+ SUBM E,0
+ ANDI 0,-1 ;ISOLATE RH
+
+INCHR1: PUSHJ P,GETCH ;GET A CHARACTER
+ CAMN A,ESCAP(E) ;ESCAPE CHAR?
+ JRST DOESCP
+ CAMN A,BRFCHR(E) ;BUFFER PRINT CHAR
+ JRST CLEARQ ;MAYBE CLEAR SCREEN
+ CAMN A,BRKCH(E) ;IS THIS A BREAK?
+ JRST DONE ;YES, DONE
+ CAMN A,ERASCH(E) ;ARE IS IT ERASE?
+ JRST ERASE ;YES, GO PROCESS
+ CAMN A,KILLCH(E) ;OR KILL
+ JRST KILL
+
+INCHR2: PUSHJ P,PUTCHR ;PUT ACHAR IN BUFFER
+ JRST INCHR1
+
+DONE: IDPB A,D ;STORE
+ MOVE D,[010700,,BYTPTR(E)] ;RESET BYTER
+ MOVEM D,BYTPTR(E)
+ IRP A,,[D,C,0]
+ POP P,A
+ TERMIN
+ POPJ P,
+
+
+ERASE: SUBI A,177 ;IS THE ERASE RUBOUT
+ SKIPN CHRCNT(E) ;ANYTHING IN BUFFER?
+ JRST BARFCR ;NO, MAYBE TYPE CR
+
+ SOS CHRCNT(E) ;DELETE FROM COUNT
+ JUMPN A,NECHO ;DONT ECHO IF ERASE OTHER THAN RUBOUT
+ LDB A,D ;RE-GOBBLE LAST CHAR
+ SKIPE C,ECHO(E) ;DOES AN ECHO INS EXIST?
+ XCT C ;YES, ECHO
+
+NECHO: ADD D,[70000,,0] ;DECREMENT BYTE POINTER
+ JUMPGE D,INCHR1 ;AND GO ON, UNLESS BYTE POINTER LOST
+ SUB D,[430000,,1] ;FIX UP BYTE POINTER
+ JRST INCHR1
+
+; HERE TO KILL THE WHOLE BUFFER
+
+KILL: CLEARM CHRCNT(E) ;NONE LEFT NOW
+ MOVE D,[010700,,BYTPTR(E)] ;RESET POINTER
+
+BARFCR: MOVE A,ERASCH(E) ;GET THE ERASE CHAR
+ CAIE A,177 ;IS IT RUBOUT?
+ JRST INCHR1 ;NO, DO NOT TYPE A CR
+ MOVEI A,15 ;GET THE CR
+ SKIPE C,ECHO(E) ;ECHO INS IN C
+ XCT C
+ JRST INCHR1
+
+DOESCP: PUSHJ P,PUTCHR ;PUT INTO BUFFER
+ PUSHJ P,GETCH ;GET NEXT ONE
+ JRST INCHR2 ;INSERT IT AND GO ON
+
+CLEARQ: MOVEI A,0 ;INSERT A NULL CHAR
+ IDPB A,D ;DEPOSIT A 0 TERMINATOR
+ MOVE A,STATUS(B) ;CHECK CONSOLE KIND
+ ANDI A,77
+ CAIN A,2 ;DATAPOINT?
+ PUSHJ P,CLR ;YES, CLEAR SCREEN
+ MOVEI A,15 ;C.R.
+ MOVE C,[010700,,BYTPTR(E)] ;POINT TO START OF BUFFER
+ SKIPN ECHO(E) ;ANY ECHO INS?
+ JRST NECHO
+
+ XCT ECHO(E) ;WRITE OUT C.R.
+
+ ILDB A,C ;GOBBLE CHAR
+ JUMPE A,NECHO
+ XCT ECHO(E) ;ECHO IT
+ JRST .-3 ;DO FOR ENTIRE BUFFER
+
+CLR: SKIPN C,ECHO(E) ;ONLY IF INS EXISTS
+ POPJ P,
+ MOVEI A,20 ;ERASE SCREEN
+ XCT C
+ MOVEI A,103
+ XCT C
+ POPJ P,
+
+PUTCHR: AOS CHRCNT(E) ;COUNT THIS CHARACTER
+ IBP D ;BUMP BYTE POINTER
+ CAIG 0,@D ;DONT SKIP IF BUFFER FULL
+ PUSHJ P,BUFULL ;GROW BUFFER
+ DPB A,D ;CLOBBER BYTE POINTER IN
+ POPJ P,
+
+; BUFFER FULL, GROW THE BUFFER
+
+BUFULL: MOVEI E,1000 ;GET GROWTH SPECS
+ HRRM E,@0
+ PUSH TP,$TCHAN ;SAVE B
+ PUSH TP,B
+ PUSHJ P,AGC ;GROW THE VECTOR
+ MOVE B,(TP) ;RESTORE CHANNEL POINTER
+ SUB TP,[2,,2] ;AND REMOVE CRUFT
+ MOVE E,BUFRIN(B) ;GET AUX BUFFER POINTER
+ HLRE 0,E ;RECOMPUTE 0
+ SUBM E,0
+ ANDI 0,-1
+ POPJ P,
+
+GETCH: SOSGE C,SYSCHR(E) ;ANY CHARS IN SYSTEM?
+ JRST ENBL ;NO, ENABLE INTERRUPTS
+ XCT IOIN2(E) ;YES, GOBBLE ONE
+ POPJ P, ;AND RETURN
+
+ENBL: MOVSI A,TCHAN ;SET A'S TYPE
+ MOVEM A,BSTO(PVP)
+ ENABLE ;ENABLE INTERRUPTS
+ XCT IOIN2(E)
+ DISABLE ;GOT A CHARACTER, DISABLE INTERRUPTS
+ SETZM BSTO(PVP)
+ POPJ P,
+
+; SUBROUTINE TO FLUSH BUFFER
+
+RRESET: MOVE E,BUFRIN(B) ;GET AUX BUFFER
+ SETZM CHRCNT(E)
+ SETZM SYSCHR(E)
+ MOVE D,[010700,,BYTPTR(E)] ;RESET BYTE POINTER
+ MOVEM D,BYTPTR(E)
+ MOVE D,CHANNO(B) ;GOBBLE CHANNEL
+ LSH D,23. ;POSITION
+ IOR D,[.RESET 0]
+ XCT D ;RESET ITS CHANNEL
+ POPJ P,
+
+; SUBROUTINE TO ESTABLISH ECHO IOINS
+
+MFUNCTION ECHOPAIR,SUBR
+
+ ENTRY 2
+
+ HLRZ A,(AB) ;CHECK ARG TYPES
+ HLRZ C,(AB)
+ CAIN A,TCHAN ;IS A CHANNEL
+ CAIE C,TCHAN ;IS C ALSO
+ JRST WRONGT ;NO, ONE OF THEM LOSES
+
+ MOVE A,1(AB) ;GET CHANNEL
+ MOVE B,DIRECT(A) ;GET DIRECTION
+ CAME B,CHQUOTE READ
+ JRST WRONGD
+ LDB C,[600,,STATUS(A)] ;GET A CODE
+ CAILE C,2 ;MAKE SURE A TTY FLAVOR DEVICE
+ JRST WRONGC
+ MOVE D,3(AB) ;GET OTHER CHANNEL
+ MOVE E,DIRECT(D) ;AND ITS DIRECTION
+ CAME E,CHQUOTE PRINT
+ JRST WRONGD
+
+ MOVE B,BUFRIN(A) ;GET A'S AUX BUFFER
+ MOVE C,IOINS(D) ;AND C'S IO INS
+ MOVEM C,ECHO(B) ;CLOBBER
+ MOVE A,(AB)
+ MOVE B,1(AB) ;RETURN 1ST ARG
+ JRST FINIS
+
+TTYOPEN: .OPEN TTYIN,[SIXBIT / TTY/]
+ .VALUE
+ .OPEN TTYOUT,[SIXBIT / !TTY/] ;AND OUTPUT
+ .VALUE
+ .STATUS TTYOUT,A ;CHECK IT OUT
+ ANDI A,77 ;GET DEVICE
+ CAIE A,2 ;IF 2, CAN OPEN IN DISPLAY MODE
+ JRST SETCHN
+ .CLOSE TTYOUT,
+ .OPEN TTYOUT,[21,,(SIXBIT /TTY/)]
+ .VALUE
+
+SETCHN: MOVE B,TTICHN+1(TVP) ;GET CHANNEL
+ MOVEI A,TTYIN ;GET ITS CHAN #
+ MOVEM A,CHANNO(B)
+ .STATUS TTYIN,STATUS(B) ;CLOBBER STATUS
+
+ MOVE B,TTOCHN+1(TVP) ;GET OUT CHAN
+ MOVEI A,TTYOUT
+ MOVEM A,CHANNO(B)
+ .STATUS TTYOUT,STATUS(B)
+ POPJ P,
+
+TYI: .IOT TTYIN,A
+ POPJ P,
+
+TYO: .IOT TTYOUT,A
+ POPJ P,
+
+
+WRONGD: PUSH TP,$TATOM
+ PUSH TP,MQUOTE WROND-DIRECTION-CHANNEL
+ JRST CALER1
+
+WRONGC: PUSH TP,$TATOM
+ PUSH TP,MQUOTE NOT-A-TTY-TYPE-CHANNEL
+ JRST CALER1
+
+END
+\f\ 3\f
\ No newline at end of file