.MCALL .MODULE .MODULE VTCOM,VERSION=26,COMMENT= ; Copyright (c) 1998 by Mentec, Inc., Nashua, NH. ; All rights reserved ; ; This software is furnished under a license for use only on a ; single computer system and may be copied only with the ; inclusion of the above copyright notice. This software, or ; any other copies thereof, may not be provided or otherwise ; made available to any other person except for use on such ; system and to one who agrees to these license terms. Title ; to and ownership of the software shall at all times remain ; in Mentec, Inc. ; ; The information in this document is subject to change without ; notice and should not be construed as a commitment by Digital ; Equipment Corporation, or Mentec, Inc. ; ; Digital and Mentec assume no responsibility for the use or ; reliability of its software on equipment which is not supplied ; by Digital or Mentec, and listed in the Software Product ; Description. .SBTTL Edit history .ENABL LC ;+ ; ; Virtual Terminal Communications Program. ; ; Author: Janice Kelso ; ; Updates: ; JLB 14-APR-81 Fixed bug in error printer. ; DG 09-SEP-82 Added DIAL support for DF03. ; GLA 22-SEP-82 General Cleanup. ; GLA 23-NOV-82 Added .FETCH MD if background job. ; ; X07 (013) 14-Sep-83 Code clean-up, added status command, standardized ; MBG and centralized error messages. ; ; Y01 (000) 06-Oct-83 Add check for protocol message from TRANSF. ; JRW ; ; Y01 (001) 20-Oct-83 Added support for running virtual on XM, out of ; MBG address space fetch for XM, PAUSE vs. EXIT commands, ; conditional assembly for expert version, fetching ; and use of XC if XL not available. ; ; Y01 (002) 11-Nov-83 XC is for use on PRO and XL is for use on other ; MBG PDP-11's. If you wish to use XL on a PRO, assign ; it the logical name XC. ; ; Y01 (003) 28-Nov-83 Changed FETCH error message to bring in line with ; MBG RT "standard" ; ; Y01 (004) 30-Nov-83 'Internal error' reported after failed .ENTER or ; MBG .LOOKUP which was caught by .SERR. ; ; Y01 (005) 01-Dec-83 Correction to previous fix. Works correctly now. ; MBG .PURGE was reseting ERRBYT ; ; Y01 (006) 09-Jan-84 Expanded .SERR error table to catch invalid ; MBG directory errors ; ; V01 (000) 22-Jan-84 Ready for release. Added check to give warning ; MBG message if running REL version under XM ; ; V05 (001) 23-Jan-84 Ready for release ; ; V05 (002) 23-Jan-84 Modified transfer code so that RESET now will ; MBG really abort a protocol-style transfer rather ; than just cause it to get confused. Also added ; audit information. ; ; V05 (003) 29-Jan-84 IOCMPL was doing .RSUM's for which no matching ; MBG .SPND had been performed. ; ; V05 (004) 30-Jan-84 .SPND counter should start at 1 rather than 0 ; MBG (as per suggestion by GLA) ; ; V05 (007) 20-Oct-84 Added SELECT command for use with mini-exchange ; MBG and HANGUP command for general phone control. ; ; V05 (008) 29-OCT-84 Changed time delay between dropping and asserting ; MBG DTR to conform with 'local disconnect' section ; of DEC STD 52. ; ; V05 (009) 12-Nov-84 More complete (less kludgy) code for SELECT. ; MBG Documentation was found to be incorrect, slash ; does not work as an idle character. <001> may ; be used. Timing between characters removed as ; no longer needed. ; ; V05 (010) 21-Dec-84 INDFIL slow send code re-written, nulls were not ; MBG being discarded, so each one took 1/10 second ; delay. ; ; V05 (011) 03-Jan-85 Slight modification to "send slow", delay check of ; MBG SLOW vs. FAST until block boundary. ; ; V05 (012) 25-Jan-85 Corrected definition for ..FAST, added space for ; MBG larger autodial prefixes (for other modem types) ; and added support for characters in dial string ; which will be excluded when sent to modem. ; ; V05 (013) 03-Mar-85 Reducing size of virtual VTCOM by moving area for ; MBG extra queue elements into root. ; ; V05 (014) 06-Mar-85 Changing space for prefix and dial string, and ; MBG adding space for a suffix. ; ; V05 (015) 07-Mar-85 EXIT now closes an open log file. ; MBG ; ; V05 (016) 12-Mar-85 Fix of suffix support. ; MBG ; ; V05 (017) 01-Oct-85 Addition of check for running clock ; MBG ; ; V05 (019) 02-Jun-86 Added check for send of XON using ^Q command ; MBG mode construct. Uses driver clear spfun now. ; ; V05 (020) 08-Jul-86 Changed 'connection established/lost' messages ; MBG to be based on carrier detect, not clear-to-send. ; ; V05 (022) 07-Nov-86 Added heuristics to bump packet size during ; MBG transfer based on error rate on serial line. ; ; V05 (023) 21-Sep-87 Added global symbol for patching command mode ; MBG entry character (defaults to ). ; ; V05 (024) 28-Jul-88 Changed prefix and suffix characters for proper ; MBG auto-dialing on DF224. ; ; V05 (025) 03-Sep-90 Added code such that on EXIT, all I/O to the ; MBG communications handler is aborted and the handler ; is released. This ensures that any handler RELEASE ; code is entered. In addition, added macros to ; define replacements for Bxx instructions with ; out-of-range displacements. ; ; V05 (026) 01-Nov-1990 bracket error messages with ;+/;ERROR/.../;- ; JFW ; ; This program has been designed to be run as a background or foreground ; job as well as a system job (VTCOM.REL). In addition, it has been designed ; to run as a virtual job under XM (VTCOM.SAV). ; ;- .SBTTL PSECT definitions ;+ ; ; The Psect usage of this program is as follows: ; ; PSECT usage ; ; Init This contains code just needed for initialization ; of this program. Once initialization is done, the ; code space is reused for buffers for those buffers ; that cannot be swapped over by the USR. ; ; NonSwp This Psect contains all code and data that is ; needed while the USR is swapped in ; ; UsrSwp This Psect contains all code and data that the USR ; can swap over when needed. ; ; TmoCnt This Psect contains two words for every timeout ; declared with the "DclTmo" macro. The first word is ; the number of seconds that the timeout has left to ; run. The second word is the address of the task ; control block of the task to "UnSuspend" when the ; timeout goes to zero. ; ; The Psects are opened by using the following macros: ; ; Init Code ; opens the "Init" Psect ; Init Data ; opens the "Init" Psect ; Swapable Code ; opens the "NonSwp" Psect ; Swapable Data ; opens the "NonSwp" Psect ; NonSwapable Code ; opens the "UsrSwp" Psect ; NonSwapable Data ; opens the "UsrSwp" Psect ; NonSwapable Buffer ; Only buffer space may be assembled in after ; ; this one. It opens the "Init" Psect and ; ; assigns the buffer space over the init code. ; ;- .PSECT Init IniBuf = . IniCod = . .PSECT NonSwp .PSECT UsrSwp SwArea: .PSECT TmoCnt TmoLst: .PSECT Init Psect = 0 .MACRO Initialize Type .IIF EQ Psect, IniCod = . .IIF EQ Psect-1, IniBuf = . .PSECT Init . = IniCod Psect = 0 .ENDM .MACRO Swapable Type .IIF EQ Psect, IniCod = . .IIF EQ Psect-1, IniBuf = . .PSECT UsrSwp Psect = 3 .ENDM .MACRO NonSwapable Type .DSABL LC .IIF EQ Psect, IniCod = . .IIF EQ Psect-1, IniBuf = . .PSECT NonSwp Psect = 2 .IF IDN ,Buffer .PSECT Init . = IniBuf Psect = 1 .ENDC .ENABL LC .ENDM .SBTTL System Macros and Definitions .MCALL .SERR, .TLOCK, .LOCK .MCALL .UNLOC, .SETTO, .DSTAT .MCALL .GTJB, .FETCH, .CSISP .MCALL .LOOKU, .ENTER, .CLOSE .MCALL .PURGE, .TTINR, .TTOUT .MCALL .PRINT, .RCTRL, .READC .MCALL .WRITC, .SPFUN, .SPND .MCALL .RSUM, .MRKT, .SCCA .MCALL .QSET, .HRESE, .EXIT .MCALL .GVAL, .GTIM, .RELEA .MCALL .ABTIO ; GLOBAL ROUTINES .GLOBL XMCHEK, PRTMES ; PROTOCOL AND HANDLER VERSION INFORMATION $$$VER == <^R1.0> ; Requires TRANSF at this version .XL == 18. ; Requires this version of XL ; Audit trail information .WEAK .VTROO, .VTCHE .AUDIT .VTROO, .VTCOM, .VTCHE ; RT-11 System communication area JSW = 44 ;Job Status Word TTLC$ = 040000 ;Lower case bit TTSPC$ = 010000 ;Terminal special mode bit VIRT$ = 002000 ;Virtual program TCBIT$ = 000100 ;Inhibit terminal I/O wait bit EDIT$ = 000020 ;Disable SL UFLOAT = 46 ;USR load address ERRBYT = 52 ;EMT error reporting byte USERRB = 53 ;User program error reporting byte ; RMON fixed offset area CONFG2 = 370 ;Configuration word 2 PROS$ = 020000 ;PRO-3xx series processor ; Channel numbers for various files Modem = 1 ;Channel number of modem used for ; asynchronous I/O calls ModemW = 2 ;Channel number of modem used for ; synchronous I/O calls File = 3 ;Channel used for file transfer file Log = 4 ;Channel used for logging SndFil = 5 ;Channel used for sending text file ; Sizes of important inforamtion StkSiz = 10. ;Size of task stack (words) MesSiz = 256. ;Max number of words in a message XLSiz = 64. ;Max number of bytes read from ; XL driver in one I/O operation TrnMax =: 256. ;Maximum data words in a packet TrnMin =: 8. ;Minimum data words in a packet ; Define names for various control characters Bell = 7 ;Beep Tab = 11 ;Tab LinFed = 12 ;Line Feed CarRet = 15 ;Carriage Return CtrlO = 17 ;Control/O CtrlP = 20 ;Control/P Xon = 21 ;Control/Q, DC1, Xon Xoff = 23 ;Control/S, DC3, Xoff Escape = 33 ;Escape Space = 40 ;Space ; Define the codes for the various .SPFUN calls to XL: device ClrDrv = 201 ;Clear recieved CTRL/S from host ; flag and send CTRL/Q to the Host BrkDrv = 202 ;Transmit a break if word count is ; non-zero, stop break if zero. SrdDrv = 203 ;Special read, word count is maximum ; number of bytes to read. The read ; will be terminated once the number ; of bytes have been read or the input ; buffer is empty. The read will, ; however, transfer at least one char ; even if the input buffer is empty ; when the read is issued. StsDrv = 204 ;Request driver status including ; XON/XOFF status, modem signals and ; driver support level ST.XFH = 000001 ;XOFF sent to host ST.XOF = 000002 ;XOFF received from HOST ST.CTS = 000004 ;Dataset: Clear-to-send ST.CD = 000010 ;Dataset: Carrier Detect ST.RI = 000020 ;Dataset: Ring Indicator OFFDRV = 205 ;Shut down driver by having it ; turn off interrupts on program exit DTRDRV = 206 ;Set/Reset DTR ; Significant other definitions .IIF NDF FT.MIN FT.MIN = 0 ;Minimal error messages (default=no) .WEAK ROOTOP ;Only used in virtual VTCOM .SBTTL Macros for Tasking .IIF NDF .Pass, .Pass = 0 .IIF NDF TskLst, TskLst = 0 .IIF NDF TskCur, TskCur = 0 .IIF NDF TskCnt, TskCnt = 0 .Pass = .Pass+1 .MACRO Task TskNam Swapable Data TskBgn = . NonSwapable Data .IIF EQ .Pass-1, TskCnt = TskCnt+1 TskLst = TskCur ; save addr of previous task control block TskCmp = . ; save addr of I/O completion entry for task JSR R5,IOcmpl ; This wart allows The addr of the task ; control block to be gotten into R5 easily. ; No return is ever made to this JSR. TskCur = . .IIF NB TskNam, TskNam: TskNxt = .-TskCur .WORD TskLst ; Holds address of previously declared task. ; tasks are linked into a circular list. TskFlg = .-TskCur TskSus = 1 ; when bit set, task is suspended TskWt = 2 ; when bit set, task waiting for I/O completion .WORD 0 ; Tasks start out running TskBuf = .-TskCur .WORD .+2 ; address of buffer for task single char I/O .WORD 0 ; task char buffer TskRsm = .-TskCur .WORD TskCmp ; address of I/O completion address for task TskIOS = .-TskCur .WORD 0 ; holds I/O status saved by I/O completion ; routine TskArg = .-TskCur .WORD .+2 ; address of area to use for RT system calls .BLKW 12. ; argument block TskSnm = .-TskCur .WORD 1 ; holds number of words that are saved on the ; task's stack TskReg = .-TskCur .BLKW 5 ; this area holds task registers. Only R0 ; through R4 are saved. R5 always contains ; the address of the currently running task's ; control block so it does not need to be ; saved TskStk = .-TskCur .WORD TskBgn ; this area is where the tasks stack is copied .BLKW StkSiz-1 ; to when the task is not running. The stack ; is initially loaded with the start address ; of the task. TskSiz = .-TskCur Swapable Code .ENDM ; suspend current task .MACRO Suspend CALL Spnd .ENDM ; resume execution of a suspeneded task. If no task name is given in the call ; then all suspended tasks are resumed. .MACRO Resume TskNam .IF NB TskNam JSR R0,UnSpnd .WORD TskNam .IFF CALL Rsum .ENDC .ENDM ; pauses a task's execution for the specified amount of time. .MACRO Wait Minutes=0,Seconds=0 JSR R1,Wait .WORD 0,<60.*60.*Minutes>+<60.*Seconds> .ENDM ; declare timeout counter. This declares the timeouts name and the task to be ; woken up when the timeout happens .MACRO DclTmo TmoNam,TskNam .PSECT TmoCnt TmoNam: .WORD 0,TskNam .IIF LE Psect-1, .PSECT Init .IIF EQ Psect-2, .PSECT NonSwp .IIF EQ Psect-3, .PSECT UsrSwp .ENDM ; sets a time to wait into a timeout counter. Timeouts can only be in whole ; seconds, nothing smaller. Note that any task can reset a timeout count ; no matter what task will actually be UnSuspended when the timeout happens. ; Note that a timeout can be reset to a new value at anytime regardless of ; whether a timeout is in progress or not. .MACRO SetTmo TmoNam,Minutes=0,Seconds=0 MOV #<60.*Minutes>+Seconds,TmoNam .ENDM ; print text on TTY. "Text" is the address of text string to print. Text ; string must conform to .PRINT type text string. If "text" address is left ; out then it is assumed the address of the text string is in R1. .MACRO Print Text .IF NB Text JSR R0,Print .WORD Text .IFF CALL TTYprt .ENDC .ENDM ; Print error message at terminal in the standard form ; '?facility-severity-text' .MACRO ERROR. MESADR JSR R0,PRTMES .WORD MESADR .ENDM ; Define an error message .MACRO ERRTXT SEVER,EXPERT,TEXT,CR=YES ..MASK = 0 ..FLAG = 0 .IRPC XXX, .IF EQ ..FLAG .IF IDN SEVER,XXX ..FLAG = 1 .IFF .IIF EQ ..MASK ..MASK = 1 ..MASK = ..MASK*2 .ENDC ;IDN SEVER,XX .ENDC ;EQ ..FLAG .ENDR .IF NE ..FLAG .NLIST BEX .BYTE ..MASK .BYTE ''SEVER .IF EQ FT.MIN .ASCII |'TEXT' |<200> .IFF .ASCII |'EXPERT' |<200> .ENDC ;EQ FT.MIN .IF IDN CR,YES . = .-2 .BYTE 0 .ENDC ;IDN CR,YES .LIST BEX .IFF .ERROR ;INVALID SEVERITY ''SEVER'' .ENDC ;NE ..FLAG .ENDM ; print a carriage return/line feed on TTY .MACRO NewLin CALL NL .ENDM ; Macro to define replacements for Bxx instructions for ; use with long displacements .MACRO BRDEF BARG1,BARG2 .MACRO 'BARG1'. DEST,?LOCAL BARG2 LOCAL JMP DEST LOCAL: .ENDM ;'BARG1'. .MACRO 'BARG2'. DEST,?LOCAL BARG1 LOCAL JMP DEST LOCAL: .ENDM ;'BARG2'. .ENDM ;BRDEF BRDEF BEQ,BNE ;Define BEQ. and BNE. .SBTTL Program Initialization Code Initialize Code ; this is all overwritten once init done Start:: BIT #VIRT$,@#JSW ;Are we virtually running? BNE 10$ ;Yes, then bypass some code MOV #SwArea,@#UFLOAT ;Tell RT where USR may swap 10$: .QSET #QELEM,#10. ;Allocate more queue elements for I/O BIS #,@#JSW ;Set terminal lower case, ; TT special mode, ; inhibit terminal I/O wait, ; no SL .SERR ;Tell RT to report all errors MOV FromTT+TskArg,R2 ;R2->area for use with EMT's JSR PC,SJTMCK ;Check for SJ without timer support .GTIM R2,#OLDTIM ;Get the current time MOV #20000.,R0 ;Set for programmed delay 12$: DEC R0 ;Delayed long enough? BGT 12$ ;Nope, procrastinate a little longer .GTIM R2,#NEWTIM ;Get the current time MOV NEWTIM+2,R1 ;R1 = new lo order time MOV NEWTIM,R0 ;R0 = new hi order time SUB OLDTIM+2,R1 ;Subract the old low-order time SBC R0 ; (handle borrow) SUB OLDTIM,R0 ; double-precision subtract BIS R1,R0 ;Did any time seem to transpire? BNE 14$ ;Yes, clock is running... JMP 68$ ;Nope, report it 14$: .GVAL R2,#CONFG2 ;Get configuration word 2 contents BIT #PROS$,R0 ;Running on a PRO? BEQ 15$ ;Nope... MOV #<^RXC >,XL ;Yes, change the device to use MOV #"XC,XLMSG ; and report on 15$: .GTJB R2,#JobDat ;Get some info about this job .DSTAT #DevDat,#XL ; and about the XL handler BCS 120$ TST DevDat+4 ;Is XL already loaded? BNE 50$ ;Yes, then no need to do a .FETCH TST JobDat ;Nope, are we the background? BNE 110$ ;Nope, then we can't .FETCH it BIT #VIRT$,@#JSW ;Virtually running? BNE 30$ ;Yes... .SETTOP #-2 ;Try for all the memory we can get MOV R0,R1 ;Determine where to .FETCH XL to SUB DevDat+2,R1 ; put it high in memory CMP R1,FETLOC+2 ;Would we be fetching it over ourself? BLO 100$ ;Yes, so not enough memory .FETCH R1,#XL ;Now go fetch it BR 40$ 30$: .FETCH #ROOTOP,#XL 40$: BCS 70$ ; .FETCH error?! MOV SP,BGFET ;Set the flag to indicate that ; we had to fetch the handler for ; the background job 50$: .LOOKUP R2,#ModemW,#XL ;Open a channel to the modem for ; synchronous I/O calls BCS 80$ .LOOKUP R2,#Modem,#XL ;Open another channel to the modem for ; asynchronous I/O calls BCS 80$ 60$: .SCCA R2,#AbrtFg ;Trap ^C's so we can pass them .SPFUN R2,#ModemW,#ClrDrv,#0,#0,#0 ;Reset the driver XON/XOFF and ; force an XON to the host .SPFUN R2,#ModemW,#StsDrv,#Status,#0,#1 ;Get the driver status CMPB Status+1,#.XL ;Can we live with this version of XL? BNE 90$ ;Nope... BIT #VIRT$,@#JSW ;Are we virtually running? BNE 65$ ;Yes, then bypass the next check JSR PC,XMCHEK ;If not virtual version, check for XM 65$: JMP Sched ;Yes, so lets start things up ;+ ;ERROR 68$: ERROR. NOCLOK ;Clock isn't running BR 130$ 70$: ERROR. BadFet ;fetch error BR 125$ 80$: ERROR. BadLok ;lookup error BR 125$ 90$: ERROR. WrngXL ;Wrong version of XL BR 125$ 100$: ERROR. NoMem ;Not enough memory BR 130$ 110$: ERROR. NoXL ;XL handler is not loaded BR 125$ 120$: ERROR. InsXL ;Unknown device XL ;- 125$: PRINT XLMSG 130$: CLR R0 ; Now terminate the program and do .EXIT ; a reset also XL: .RAD50 /XL/ ;Communications handler device name .WORD 0,0,0 ; in Rad50 (for FETCH/LOOKUP, etc) XLMSG: .ASCIZ /XL:/ ;Communications handler device name ; IN ASCII (for printing text) ;+ ;ERROR NoXL: ERRTXT F,HNL,,CR=NO WrngXL: ERRTXT F,VER,,CR=NO InsXL: ERRTXT F,IDV,,CR=NO NoMem: ERRTXT F,MEM, BadFet: ERRTXT F,FET,,CR=NO BadLok: ERRTXT F,LKP,,CR=NO NOCLOK: ERRTXT F,CLK, ;- .EVEN JobDat: .BLKW 1. ; Job parameter area. *keep together* DevDat: .BLKW 11. ; Device parameter. *keep together* FetLoc: .LIMIT ; Oh linker tell us where we went OLDTIM: .BLKW 2 NEWTIM: .BLKW 2 NonSwapable Data ; This exists even after init done AbrtFg: .BLKW 1 ; : Control/C flag word BGFET: .WORD 0 ; : Background fetch flag .SBTTL SCHED - Task Scheduler ;+ ; ; This is a very simple task scheduler. All tasks are at the same priority. ; A task does not lose control of the CPU until it does a call to "SPND" ; or "PASS". Suspend (SPND) is used when a task voluntarily stops itself ; from executing. "PASS" is used when a task is waiting for I/O to complete. ; At the time a task does "PASS" or "SPND" it must have no more than "StkSiz" ; words on the stack because there is only room for that many words of stack ; in the task's control block. Note that the contents of a task's stack is ; copied into the task control block, you cannot assume something will remain ; on the stack at the same place when a task loses control. ; ; A task's registers are saved when it looses control and are restored when ; a task is restarted. Note that R5 must always contain the address of the ; current task. ; ;- Swapable Code .ENABL LSB Sched: MOV RunLst,R5 ;Get address of next task MOV TskNxt(R5),RunLst ;Record addr of task to do after this DEC RunCnt ;1 less task to run TST TskFlg(R5) ;Is this task runnable? BNE 30$ ;Nope... MOV SP,StkBas ;Yes, record stack base for later MOV R5,R0 ;Copy address of control block ADD #TskStk,R0 ; to get pointer to top of stack ADD TskSnm(R5),R0 ; save area ADD TskSnm(R5),R0 10$: MOV -(R0),-(SP) ;Restore a word to the task's stack DEC TskSnm(R5) ;More to do? BNE 10$ ;Yes... MOV -(R0),R4 ;Now that we've unwound the stack, MOV -(R0),R3 ; let's restore the task's MOV -(R0),R2 ; registers (R5 always points to MOV -(R0),R1 ; current task's control block) MOV -(R0),R0 RETURN ;return to task where we left off .SBTTL SPND - Suspends a task ;+ ; ; SPND ; Suspends a task. It will not run again until a RESUME ; is done to restart it. ; ; CALL: ; SUSPEND ; or ; CALL SPND ; ; INPUT: ; R5 -> Current task control block ; ; IMPLICIT OUTPUT: ; SUSPEND bit is set in the task's flag word ; ;- Spnd: BIS #TskSus,TskFlg(R5) ;Flag task as being SUSPENDED BR Pass ; Now go allow another task to run .SBTTL PASS - Have task give up control of the CPU ;+ ; ; PASS ; Passes control to another task. ; ; CALL: ; CALL PASS ; ; INPUT: ; R5 -> Current task's control block ; ; IMPLICIT OUTPUT: ; Current task info (registers and stack) are saved in the ; task's control block. ; ; NOTE: ; This routine expects that the current task is either suspended ; or is waiting for I/O. ; ;- Pass: MOV R0,TskReg(R5) ;Save task's R0 MOV R5,R0 ;Copy address of control block ADD #TskReg+2,R0 ; so we can start saving the MOV R1,(R0)+ ; task's registers (R5 need MOV R2,(R0)+ ; not be saved as it always MOV R3,(R0)+ ; points to the control block MOV R4,(R0)+ ; of the current task) 20$: MOV (SP)+,(R0)+ ;Now save a word of the task's stack INC TskSnm(R5) ;Bump count of stack words saved CMP TskSnm(R5),#StkSiz ;Have we run out of stack save area? BHI 40$ ;Yep, serious... CMP SP,StkBas ;Nope, anything left on stack to save? BLO 20$ ;Yes, go save another word 30$: TST RunCnt ;Any tasks waiting to run? BPL Sched ;Yes, then go start the next one INC SPNDLV ;Nope, flag that we've gone and .SPND ; SUSPENDed ourself BR 30$ ;We were .RSUM'd, go check for ; runnable tasks again ; We get here if there wasn't enough space in the task control block ; to save all the words the task had placed on the stack. If this ; occurs, try enlarging 'StkSiz' ;+ ;ERROR 40$: ERROR. StkOvf ;'Stack overflow...' ;- .HRESET CLR R0 .EXIT .DSABL LSB Swapable Data SPNDLV: .WORD 1 ; : SUSPEND level counter RunLst: .WORD TskLst ; : Address of next task to run RunCnt: .WORD TskCnt-1 ; : Runnable task counter ; -1 indicates no tasks to run ; >=0 indicates tasks to run StkBas: .WORD 0 ;Stack address when task started ;+ ;ERROR StkOvf: ERRTXT F,PDL, ;- .EVEN .SBTTL RSUM - Resume all Suspended Tasks .ENABL LSB Swapable Code ;+ ; ; RSUM ; Resume all suspended tasks. This is an indiscriminate resume. All ; tasks must assume they could be inadvertently resumed and must ; check to see if the conditions that caused them to suspend ; themselves have changed. Note that this does not effect tasks ; that are waiting for I/O. ; ; CALL: ; CALL RSUM ; ; IMPLICIT OUTPUT: ; SUSPEND bit in each suspended task's flag word is reset ; RUNCNT bumped for each runnable task ; RUNLST loaded with address of control block for task at ; head of run queue ; ;- Rsum: MOV R0,-(SP) ;Save R0 for awhile MOV R5,-(SP) ;Save address of current task MOV RunLst,R0 ;Get address of start of task ring MOV R0,R5 ;Copy address of the control block ; loop to RESUME all suspended tasks 10$: CALL 20$ ;RESUME the task MOV TskNxt(R5),R5 ;Get address of next task in ring CMP R0,R5 ;Have we looped back to first task? BNE 10$ ;Nope, go resume this one MOV (SP)+,R5 ;All done, restore registers MOV (SP)+,R0 RETURN .SBTTL UNSPND - Resume a specific suspended task ;+ ; ; UNSPND ; Resumes a particular task. ; ; CALL: ; RESUME task_name ; or ; JSR R0,UNSPND ; .WORD task_name ; ; IMPLICIT OUTPUT: ; SUSPEND bit in task's flag word is reset ; RUNCNT bumped to account for resumed task ; task is added to end of task ring ; ;- UnSpnd: MOV R5,-(SP) ;Save address of current control block MOV (R0)+,R5 ;Get address of task to resume CALL 20$ ;Go resume it MOV (SP)+,R5 ;Restore pointer to control block RTS R0 ; Here we resume a process whose task control block is pointed to by R5 20$: BIT #TskSus,TskFlg(R5) ;Is task suspended? BEQ 30$ ;Nope, so don't do anything BIC #TskSus,TskFlg(R5) ;Yes, reset the suspend bit CALL 50$ ;And make sure the task will run 30$: RETURN .SBTTL TIMSLC - Allows other tasks to run ;+ ; ; TIMSLC ; Called when a task wishes to relinquish control of the CPU ; and allow other tasks to run. Does not suspend the task ; issuing the request. ; ; CALL: ; CALL TIMSLC ; ; INPUT: ; R5 -> current task control block ; ;- TimSlc: CALL 50$ ;Make sure current task is runnable CALL Pass ;Let other tasks run RETURN ;Return to task which issued request .SBTTL IOCMPL - I/O completion routine ;+ ; ; IOCMPL ; This is the I/O completion routine. It is NEVER called by the ; user. The address of a task's inidividaul completion routine is ; kept in each task control block. They are unique to each task. ; These completion routines call this routine to actually record ; the I/O completion. ; ; Before making an RT I/O call, the waiting for I/O bit must be ; set in the task control block. This MUST be set before the I/O ; is started. The following instruction can be used to set the ; bit: ; ; BIS #TskWt,TskFlg(R5) ; ; Only RT I/O with completion routines should be used. If other ; forms are used then tasking stops while the I/O is in progress. ; Specify TskRsm(R5) as the completion address in the RT I/O ; call. The call should be followed by a release of control of ; the CPU with: ; ; CALL Pass ; ; CALL: ; JSR R5,IOCMPL ; ; where the word following the call is the first word of the ; task control block of the task for which I/O is completing. ; ; INPUT: ; R0 = I/O status codes ; ; OUTPUT: ; R5 restored from stack and RETURN is made from completion routine ; ;- NonSwapable Code IOcmpl: MOV R0,TskIOS(R5) ;Save the status of the I/O BIC #TskWt,TskFlg(R5) ;Reset the 'waiting for I/O' bit CALL 50$ ;Make the task runnable BEQ 40$ ;Tasks are running, just return TST SPNDLV ;Any outstanding .SPNDs? BEQ 40$ ;Nope... DEC SPNDLV ;Yes, we're about to do the matching .RSUM ; RESUME 40$: MOV (SP)+,R5 ;Restore R5 saved before entry RETURN ; exit I/O completion handling ; Task to run is in R5, make it runnable 50$: INC RunCnt ;Bump the count of runnable tasks BNE 60$ ;Others are running... MOV R5,RunLst ;This is the only one, make it the ; beginning of the task ring RETURN ; We have more than one task to run, so make them all runnable ; so that we are sure to run this one. 60$: MOV #TskCnt-1,RunCnt ;Set count of tasks to run to MAX SEZ ;Indicate no resume is needed RETURN .DSABL LSB .SBTTL WATCHER - State Change and Time-out Watcher Task ;+ ; ; WATCHER ; This task watches the modem port to detect changes of state. ; The two state changes that it watches for are the loss/gain ; of carrier and whether a XOFF was recieved from the Host. It ; also decrements the timeout counts and RESUMEs any tasks that ; have timed-out. ; ;- .ENABL LSB Task Watcher MOV Status,R3 ;R3=previous driver status BIC #^C,R3 ;Discard all but Carrier Detect 10$: Wait Seconds=1 ;Now wait a second before .SPFUN TskArg(R5),#ModemW,#StsDrv,#Status,#0,#1 ; checking again MOV Status,R4 ;R4 = current driver status BIT #ST.XOF,R4 ;Have we received XOFF from host? BEQ 20$ ;Nope... Resume FromTT ;Yes, wake up TT input task for beep 20$: BIC #^C,R4 ;Discard all but Carrier Detect CMP R3,R4 ;Is it the same as last time? BEQ 60$ ;Yes... CALL GrbTTY ;Nope, grab control of the terminal .RCTRLO ;Reset any in affect Print Bellbf ;Get the user's attention MOV R4,R3 ;Save the new driver state BNE 40$ ;We just got 'carrier detect' CLR XLTO ;We lost 'carrier detect', cause CLR MesTmo ; a few things to timeout, CLR Code ; reset 'message in progress' flag, INC MesAbt ; set 'abort file transfer' and INC SndAbt ; 'abort send' flags Resume ;Now resume all tasks so they can ; abort .SPFUN TskArg(R5),#ModemW,#ClrDrv,#0,#0,#1 ;Do a driver clear ;+ ;ERROR ERROR. CarLos ;Inform the user of loss of connection ;- MOV #4,R2 ; and make sure it gets his 30$: Wait Seconds=1/4 ; attention by adding 4 bells Print Bells ; separated by 1/4 second. DEC R2 BGT 30$ BR 50$ ;+ ;ERROR 40$: ERROR. CarDet ;Inform user of connection ;- 50$: NewLin ;Add a new line CALL RlsTTY ;Release the terminal Wait Seconds=5 ;Now wait awhile to ride out 'bounce' 60$: MOV #TmoLst,R1 ;R1->list of timeout counts 70$: TST (R1)+ ;Is this count zero? BEQ 90$ ;Yes, then it isn't in use DEC -2(R1) ;Nope, decrement the timeout counter BNE 90$ ;Didn't time out, on to next counter MOV (R1),80$ ;Timed-out, get address of task JSR R0,UnSpnd ; and resume it's execution 80$: .WORD 0 90$: TST (R1)+ ;Skip the task address BNE 70$ ;Do it again for another entry BR 10$ Status: .WORD 0 ; : modem driver/line status ; low byte contains: ; bit 0 if host has been XOFF'd ; bit 1 if driver has been XOFF'd ; bit 2 if 'clear to send' asserted ; bit 3 if 'carrier detect' asserted ; bit 4 if 'ring indicate' asserted ; high byte contains: ; driver version number Bells: .BYTE Bell,200 BellBf: .BYTE Bell,0 ;+ ;ERROR CarDet: ERRTXT I,CON, CarLos: ERRTXT I,COF, ;- .EVEN .DSABL LSB .SBTTL TOHOST - Simple Output to Host task ;+ ; ; TOHOST ; This task is responsible for sending user input to the host. ; This task is necessary so that input from the user can be ; processed even when output to the host is halted due to an ; XOFF recieved from the Host. ; ;- .ENABL LSB Task ToHost 10$: MOV ToChar,R0 ;Are there any characters waiting? BNE 20$ ;Yes... Suspend ;No, so wait to be RESUMEd BR 10$ ;Now check again 20$: CLR ToChar ;Indicate that we've got them Resume FromTT ;Wake up the user input task CALL WrModm ;Send the character(s) to the host BR 10$ ;Go check for more to send ToChar: .WORD 0 ; : Hybrid data ; when zero, indicates we are ; waiting for characters to send ; when non-zero, contains 1 or 2 ; characters to send to the host .DSABL LSB .SBTTL FROMTT - Input from Terminal Handler Task ;+ ; ; FROMTT ; Processes input from the user. Entry to command mode is made ; on receipt of a . Otherwise, characters are passed ; to the TOHOST routine to have them shipped to the host. If ; we have been XOFF'd by the host, all characters (with the ; exception of are ignored and echoed as . ; ;- Task FromTT Main: CALL TTYin ;Get a character from the user CMPB R0,#CtrlP ;Is it a ? ..LCCH == .-2 ;'Local' character, enter command mode BEQ CmdMod ;Yes, go to command mode 10$: BIT #2,Status ;Nope, have we been XOFF'd? BNE 40$ ;Yes... TSTB ToChar ;Nope, space for the character? BNE 20$ ;Not in the low byte... MOVB R0,ToChar ;Yes, so save it Resume ToHost ;And resume TOHOST so it can be sent BR Main 20$: TSTB ToChar+1 ;Is there room in the high byte? BNE 30$ ;Nope... MOVB R0,ToChar+1 ;Yes, so save it there Resume ToHost ;Wake up the TOHOST task BR Main 30$: Suspend ;No room for this character, wait ; until we are resumed by TOHOST BR 10$ ; and then try to send it again 40$: MOV R0,ToChar ;Store the character for output ; (overwrite any previous characters) Resume ToHost ;Resume execution of TOHOST task MOV #Bell,R0 ;Echo character as a CALL TTYout BR Main .SBTTL Command Mode Processing ;+ ; ; CMDMOD ; Entered on receipt of in the routine MAIN. Prompts ; the user for a command and then processes it. ; ;- .ENABL LSB CmdMod: CALL GrbTTY ;Get control of the terminal BIC #,@#JSW ;Disable terminal special modes MOV LogOn,SvLgOn ;Save current logging state flag AskCmd: CLR LogOn ; and then disable logging .RCTRLO ;Reset effects of a CTRL-O NewLin ;Advance to a new line Print M.PROM ; and prompt the user for a command CALL GetCmd ;Get Command line from user BCS AskCmd ;Ask again if error TSTB CMDLIN ;Any response? BEQ 150$ ;Nope, handle as implicit HELP 10$: CMPB CmdLin,#'^ ;Yes, is first character a '^'? BEQ 130$ ;Yes, then send second character ; as a control character MOV #ComLst-2,R1 ;Nope, R1->command list 20$: TST (R1)+ ;Bump past command action address MOV (R1)+,R0 ;Get start of command text string BEQ 80$ ;If no more, invalid command... MOV #CmdLin,R3 ;R3->command entered by user 30$: CMPB (R0),#SPACE ;Is this character a SPACE? BEQ 60$ ;Yes, ignore spaces in commands CMPB (R0),#'[ ;Nope, are we at the optional part? BEQ 40$ ; command then branch else CMP R3,R2 ; if we have run out of user command then BHIS 20$ ; this is not correct command so branch else CMPB (R0)+,(R3)+ ; compare command char, if they are same BEQ 30$ ; then continue comparing this command else BR 20$ ; go try another command ; check if optional part of command name is correct 40$: INC R0 ; inc past optional part designator 50$: CMPB (R0),#'] ; if we have reached the end of the command BEQ 60$ ; then branch else CMP R3,R2 ; if we have run out of user command then BHIS 70$ ; this is correct command so branch else CMPB (R0)+,(R3)+ ; compare command char, if they are same BEQ 50$ ; then continue comparing this command else BR 20$ ; go try another command ; command matched to end of command string, see if user command done to 60$: CMP R3,R2 ; if user typed command not done to then BNE 80$ ; command is wrong, go try another else ; Found the command handler, go handle the command 70$: JMP @(R1)+ ; print illegal command error and go ask for another command ;+ ;ERROR 80$: ERROR. IllCmd ;- BR AskCmd ; LOG command, resume logging host output to file 90$: TST LgOpnd ;Is there an open log file? BEQ 100$ ;Nope... MOV #1,SvLgOn ;Yes, then re-enable logging BR 230$ ;+ ;ERROR 100$: ERROR. NoLog ;'Log file not open' ;- BR AskCmd ; NOLOG command, suspend logging host output to file 110$: TST LgOpnd ;Is there an open log file BEQ 100$ ;Nope... CLR SvLgOn ;Yes, then suspend logging BR 230$ ; CTRL/P command, send a control/P to the HOST 120$: MOV #CtrlP,R0 ;R0= BR 140$ ; UPARROW command, send control character to HOST 130$: CMP R2,#CmdLin+2 ;Is command only 2 characters long? BNE 80$ ;Nope, invalid command... MOVB CmdLin+1,R0 ;Get the second character BIC #^C37,R0 ;Make it a control character CMPB R0,#XON ;Trying to send XON? BEQ. 260$ ;Yes, make use of the CLEAR code 140$: CALL WrModm ;Send it to the host BR 230$ ; HELP command, print the help text 150$: NewLin ;Output a new line, Print Id ; the program identification, NewLin ; and another new line MOV #ComLst,R2 ;R2->start of command list 160$: MOV (R2)+,R1 ;R1->command help string BEQ AskCmd ;If zero, all done... Print ;Print the help text TST (R2)+ ;Skip past the command action address BR 160$ ;Go do another line of help ; OPENLOG command, Open a log file for host output. 170$: CALL LogOpn ;Open a log file for output (causes ; a previously open file to be closed) BCS AskCmd ;If any errors, reprompt BR 230$ ; CLOSELOG command, Close an open log file. 180$: TST LgOpnd ;Is the an open log file? BEQ 100$ ;Nope... CALL LogCls ;Yes, then close the file BCS AskCmd ;If any errors, reprompt BR 230$ ; SEND command, sends file to host as if typed. 190$: CALL SndOpn ;Start up the send BCS AskCmd ;If any errors, reprompt BR 230$ ; PAUSE command, leave driver running, clean exit to RT. 210$: TST BGFET ;Running in background with fetched ; handler? BEQ 220$ ;Nope, so leave XL alone. ; Otherwise, handle like EXIT ; EXIT command, shut down driver. 200$: .ABTIO #Modem ;Abort all asynchronous and .ABTIO #ModemW ; synchronous I/O to comm handler .SPFUN TskArg(R5),#ModemW,#OFFDrv,#0,#0,#1 ;Tell it to shut down .RELEA #XL ; then release it... 220$: TST LGOPND ;Is there an open log file? BEQ 225$ ;Nope... CALL LOGCLS ;Yes, then close it 225$: .HRESET ;Make sure XL aborts all I/O CLR R0 ;Make it a hard exit .EXIT ; CONTINUE command, exit command mode and return to terminal mode. 230$: BIS #,@#JSW ;Reset the terminal special modes MOV SvLgOn,LogOn ;Restore the logging state flag CALL RlsTTY ;Release the terminal JMP Main ; BREAK command, Issue a Break for 3/10 of a second 240$: .SPFUN TskArg(R5),#ModemW,#BrkDrv,#0,#1,#1 ;Assert BREAK Wait Seconds=3/10. ;Wait 3/10 second .SPFUN TskArg(R5),#ModemW,#BrkDrv,#0,#0,#1 ;De-assert BREAK BR 230$ ; RESET command, Clear sent and received XON/XOFF status ; Cause the message handler and file transfer stuff to timeout 250$: CLR XLTO ; Make sure message handling stuff in file CLR MesTmo ; transfer times-out and set that CLR Code ; we are not in the middle of a message CLR SELFLG ; And clear echo inhibit flag (SELECT) INC MesAbt ; Set file transfer abort flag and INC SndAbt ; SEND abort flag Resume ; resume all tasks so this can be cleared up ; CLEAR command, do a modem driver clear 260$: .SPFUN TskArg(R5),#ModemW,#ClrDrv,#0,#0,#1 ;Do a driver clear, resets ; received XOFF from host flag, sends ; XON to host. BR 230$ ; Exit from command processing ; SLOW command, set flag to signal host is stupid 270$: CLR WtToFg BR 230$ ; FAST command, sets to setting of smart Host (HOST does XOFF/XON) 280$: MOV #1,WtToFg BR 230$ ; SHOW command, displays information about what's going on 290$: TST TRANSF ;Is there an active transfer? BNE 300$ ;Yes, display the status PRINT M.NOTR ;Nope, let user know BR 320$ 300$: PRINT M.PACS ;'Packets sent' MOV PACSEN,R0 ;R0 = count of packets sent CALL DECOUT ;Print it NEWLIN PRINT M.PACR ;'Packets received' MOV PACREC,R0 ;R0 = count of packets received CALL DECOUT ;Print it NEWLIN PRINT M.PSIZ ;'Packet size' MOV TRNSIZ,R0 ;R0 = current packet size (bytes) BNE 310$ MOV RCVSIZ,R0 310$: CALL DECOUT ;Print it NEWLIN PRINT M.NEXB ;'Next active block' MOV BLKNUM,R0 ;R0 = next block to be read or written CALL DECOUT ;Print it NEWLIN 320$: PRINT M.LOG ;Set to report logging status MOV #M.OFF,R1 ;Assume it is off TST LGOPND ;Is it? BEQ 330$ ;Yes (log file is not open) MOV #M.ON,R1 ;Assume it is running TST SVLGON ;Is it? BNE 330$ ;Yes... MOV #M.SUS,R1 ;Nope, it's been suspended 330$: PRINT PRINT M.SEND ;Start to report SEND speed setting MOV #M.SLOW,R1 ;Nope, assume it's set SLOW TST WTTOFG ;Correct assumption? BEQ 340$ ;Yes... MOV #M.FAST,R1 ;Nope, it's set FAST 340$: PRINT PRINT M.DIAL ;Set to report dial string MOV #M.NODI,R1 ;Assume it hasn't been set TSTB DI.STR ;Has it? BEQ 350$ ;Nope... MOVB #'=,R0 ;Yes, delimit with ' = ' CALL TTYOUT MOVB #SPACE,R0 CALL TTYOUT MOV #DI.STR,R1 ;Then print the dial string 350$: PRINT 360$: JMP 230$ ; DIAL command, auto-originate a call via a modem 370$: PRINT M.PROM ;Prompt for the dial string PRINT M.DIAQ CALL GETCMD ;Get a response BCS 420$ ;Line too long... TSTB CMDLIN ;Any response? BEQ 390$ ;Nope, take the default MOV #CMDLIN,R0 ;Yes, R0->response MOV #DI.STR,R1 ;R1->Number part of dial string 380$: MOVB (R0)+,(R1)+ ;Store a character of the new string BNE 380$ ;If not done, continue... 390$: TSTB DI.STR ;Has the dial string been set? BEQ 410$ ;Nope... MOV #DI.PFX,R1 ;R1 -> Autodial prefix string 400$: MOVB (R1)+,R0 BEQ 401$ CALL WRMODM BR 400$ 401$: MOV #DI.STR,R1 ;R1 -> String to dial 402$: MOVB (R1)+,R0 ;Get a character of the string BEQ 405$ ;Done if null byte MOV #DI.EXC,R2 ;R2 -> Character exclusion table 403$: TSTB (R2) ;End of exclusion table? BEQ 404$ ;Yes, character is okay to send CMPB R0,(R2)+ ;No, is it this exclusion character? BNE 403$ ;Nope, try another BR 402$ ;Yes, so exclude it 404$: CALL WRMODM ;Send the character BR 402$ ;And check for another 405$: MOV #DI.SFX,R1 ;R1 -> Autodial suffix string 406$: MOVB (R1)+,R0 BEQ 360$ CALL WRMODM BR 406$ ;+ ;ERROR 410$: ERROR. E.NODI ;No dial string to dial ;- 420$: JMP ASKCMD ; SELECT command, select a port on the mini-exchange 430$: PRINT M.PROM ;Prompt for the port number PRINT M.PORT CALL GETCMD ;Get a response BCS 450$ ;Line too long... TSTB CMDLIN ;Any response? BEQ 440$ ;Nope, berate the user MOV #CMDLIN,R2 ;R2->User's response CMPB (R2),#'M ;Was it an 'M'? BNE 432$ ;Nope... MOVB #'R,(R2) ;Yes, replace it with what we ; have to pass to mini-exchange 432$: MOV #PORTNM,R1 ;r1->Port number area MOVB (R2),(R1)+ ;Store the port selection CMPB (R2),#'R ;Selecting port 8 as modem? BEQ 435$ ;Yes...then nothing else used CMPB (R2),#'1 ;Nope, then is it a valid port number? BLT 438$ ;Nope... CMPB (R2),#'8 ;Maybe, check upper limit BGT 438$ ;Definately not... CMPB 1(R2),#'P ;It's okay, is it followed by 'P'? BNE 435$ ;Nope... MOVB #'P,(R1)+ ;Yes, port is configured such that ; after connection, DTR may drop ; without causing problems 435$: MOVB #15,(R1)+ ;Final character is CLRB (R1) ;Null byte to end string JSR PC,HANGER ;Use the HANGUP code to toggle DTR WAIT SECONDS=<2/10.> ;Now wait awhile for DSR to assert MOV #PORTST,R1 MOV SP,SELFLG ;Inhibit echoed select string chars 436$: MOVB (R1)+,R0 ;Get a character to send BEQ 360$ ;Done on null byte CALL WRMODM ;Send the character BR 436$ ;Go do another ;+ ;ERROR 438$: ERROR. E.INVP ;'Invalid port selection' JMP ASKCMD 440$: ERROR. E.NOPO ;'No port selected' 450$: JMP ASKCMD ;- ; HANGUP command, toggles DTR to hangup phone 460$: JSR PC,HANGER ;Toggle DTR to hangup phone JMP 230$ ; and continue HANGER: .SPFUN TskArg(R5),#ModemW,#DTRDRV,#0,#0,#1 ;De-assert DTR WAIT SECONDS=2 .SPFUN TskArg(R5),#ModemW,#DTRDRV,#0,#1,#1 ;Assert DTR RTS PC ; Command List, consists of two word per entry. first word is address of text ; string which specifies both the command plus help text for command. second ; is the address of the routine for handling the command. ComLst: .WORD Break,240$ ;BREAK .WORD Clear,260$ ;CLEAR .WORD ClsLog,180$ ;CLOSE .WORD Cont,230$ ;CONTINUE .WORD CtrP,120$ ;CTRLP .WORD Dial,370$ ;DIAL .WORD Exit,200$ ;EXIT .WORD OffWt,280$ ;FAST .WORD HANGUP,460$ ;HANGUP .WORD Help,150$ ;HELP .WORD OnLog,90$ ;LOG .WORD OffLog,110$ ;NOLOG .WORD OpnLog,170$ ;OPENLOG .WORD PAUSE,210$ ;PAUSE .WORD Rst,250$ ;RESET .WORD Select,430$ ;SELECT .WORD Send,190$ ;SEND .WORD Show,290$ ;SHOW .WORD OnWt,270$ ;SLOW .WORD Uparrw,0 ; this is here just for HELP .WORD 0 ; end of table .DSABL LSB ; Autodial information .NLIST BEX DI.EXC: .ASCIZ / -()[]/ ;Dial string excluded characters ..DPFX == . DI.PFX: .BYTE 'A-100 ;DF224 autodial prefix character .REPT 15.-<.-DI.PFX> ;Space for 15 prefix characters .BYTE 0 .ENDR .BYTE 0 ;Prefix fence ..DIAL == . DI.STR: ;Number to autodial .REPT 39.-<.-DI.STR> ;Space for 39 characters .BYTE 0 .ENDR .BYTE 0 ;Dial string fence ..DSFX == . DI.SFX: .BYTE '! ;Initiates dialing .REPT 15.-<.-DI.SFX> ;Space for 15 suffix characters .BYTE 0 .ENDR .BYTE 0 ;Suffix fence ; Mini-Exchange port selection information PORTST: .ASCII "//" ;Mini-exchange autobaud characters .BYTE 001,001,001 ;A few idle characters PORTNM: .BLKB 4 ;The port identifier , ; [

],,<0> .EVEN .LIST BEX SvLgOn: .WORD 0 ; When non-zero logging on when not in command mode LogOn: .WORD 0 ; When Non-zero input from Host is being logged LgOpnd: .WORD 0 ; When Non-zero log file is open PACSEN: .WORD 0 ; : count of packets sent PACREC: .WORD 0 ; : count of packets received .NLIST BEX ; Program identification Id: .NLCSI TYPE=I,PART=ALL .ASCIZ / - COMMAND SUMMARY/ ; Command Strings/Help text Break: .ASCII /BR[EAK]/ .IF EQ FT.MIN .ASCII / Transmit a BREAK to the Host./ .ENDC ;EQ FT.MIN .BYTE 0 Clear: .ASCII /CLE[AR]/ .IF EQ FT.MIN .ASCII / Restart terminal display./ .ENDC ;EQ FT.MIN .BYTE 0 ClsLog: .ASCII /CLO[SELOG]/ .IF EQ FT.MIN .ASCII / Close the log file./ .ENDC ;EQ FT.MIN .BYTE 0 Cont: .ASCII /CO[NTINUE]/ .IF EQ FT.MIN .ASCII / Return to terminal mode./ .ENDC ;EQ FT.MIN .BYTE 0 CtrP: .ASCII "CT[RL/P]" .IF EQ FT.MIN .ASCII " Send a CTRL/P to the Host." .ENDC ;EQ FT.MIN .BYTE 0 DIAL: .ASCII /DI[AL]/ .IF EQ FT.MIN .ASCII / Have the modem dial a number./ .ENDC ;EQ FT.MIN .BYTE 0 Exit: .ASCII /EX[IT]/ .IF EQ FT.MIN .ASCII / Terminate this program./ .ENDC; EQ FT.MIN .BYTE 0 OffWt: .ASCII /FA[ST]/ .IF EQ FT.MIN .ASCII / No Delay between characters during SENDs./ .ENDC ;EQ FT.MIN .BYTE 0 HANGUP: .ASCII /HA[NGUP]/ .IF EQ FT.MIN .ASCII / Hang up phone (drop connection)./ .ENDC ;EQ FT.MIN .BYTE 0 Help: .ASCII /HE[LP]/ .IF EQ FT.MIN .ASCII / Type this help message./ .ENDC ;EQ FT.MIN .BYTE 0 OnLog: .ASCII /L[OG]/ .IF EQ FT.MIN .ASCII / Resume logging./ .ENDC ;EQ FT.MIN .BYTE 0 OffLog: .ASCII /NOL[OG]/ .IF EQ FT.MIN .ASCII / Suspend logging./ .ENDC ;EQ FT.MIN .BYTE 0 OpnLog: .ASCII /OP[ENLOG]/ .IF EQ FT.MIN .ASCII / Open a file and begin logging Host output./ .ENDC ;EQ FT.MIN .BYTE 0 PAUSE: .ASCII /PA[USE]/ .IF EQ FT.MIN .ASCII / Exit program, leave handler running./ .ENDC ;EQ FT.MIN .BYTE 0 Rst: .ASCII /RE[SET]/ .IF EQ FT.MIN .ASCII / Terminate any file transfer./ .ENDC ;EQ FT.MIN .BYTE 0 Send: .ASCII /SEN[D]/ .IF EQ FT.MIN .ASCII / Transmit a file to the Host as if it were typed./ .ENDC ;EQ FT.MIN .BYTE 0 SELECT: .ASCII /SEL[ECT]/ .IF EQ FT.MIN .ASCII / Select connection to a port of the mini-exchange./ .ENDC ;EQ FT.MIN .BYTE 0 Show: .ASCII /SH[OW]/ .IF EQ FT.MIN .ASCII / Show status./ .ENDC ;EQ FT.MIN .BYTE 0 OnWt: .ASCII /SL[OW]/ .IF EQ FT.MIN .ASCII / Delay between characters during SENDs./ .ENDC ;EQ FT.MIN .BYTE 0 Uparrw: .ASCII /^x/ .IF EQ FT.MIN .ASCII / Send control character "x" to Host./ .ENDC ;EQ FT.MIN .BYTE 0 ; Prompt strings M.PROM: .ASCII /TT::VTCOM> /<200> M.LOGQ: .ASCII /Log file? /<200> M.SENQ: .ASCII /Send file? /<200> M.DIAQ: .ASCII /Dial string? /<200> M.PORT: .ASCII /Port? /<200> ; Information from SHOW command M.NOTR: .ASCIZ / No transfer in progress/ M.PACS: .ASCII / Packets sent = /<200> M.PACR: .ASCII / Packets received = /<200> M.PSIZ: .ASCII / Packet size = /<200> M.NEXB: .ASCII / Next active block = /<200> M.LOG: .ASCII / Logging is /<200> M.OFF: .ASCIZ /OFF/ M.ON: .ASCIZ /ON/ M.SUS: .ASCIZ /SUSPENDED/ M.SEND: .ASCII / SEND is set /<200> M.SLOW: .ASCIZ /SLOW/ M.FAST: .ASCIZ /FAST/ M.DIAL: .ASCII / Dial string /<200> M.NODI: .ASCIZ /is not set/ .EVEN ; Error messages ;+ ;ERROR E.INVP: ERRTXT E,IVP, E.NOPO: ERRTXT E,NPS, E.NODI: ERRTXT E,NDS, NoLog: ERRTXT W,FNO, IllCmd: ERRTXT F,ICM, LogOff: ERRTXT W,LFC, OpFrSn: ERRTXT W,FNS, ;- .LIST BEX .EVEN .SBTTL DECOUT - Decimal output routine ;+ ; ; DECOUT ; Prints at the terminal the decimal representation of ; the binary number passed in R0. ; ; CALL: ; JSR PC,DECOUT ; ; INPUT: ; R0 = number ; ;- DECOUT: MOV R0,-(SP) ;Make a copy of the number CLR R0 ;Reset loop counter for crude divide 10$: INC R0 ;Bump times through loop SUB #10.,(SP) ;Divide done by multiple subtracts BHIS 10$ ;If result has not changed sign... ADD #10.+60,(SP) ;Correct digit and make it printable DEC R0 ;Any more to do? BEQ 20$ ;Nope... JSR PC,DECOUT ;Yes, then do it recursively 20$: MOVB (SP)+,R0 ;R0=digit to print CALL TTYOUT ;Print it RTS PC ;Now return recursively .SBTTL LOGOPN - Open Logging File ;+ ; ; LOGOPN ; Opens for output a file to be used in logging host output. If ; a file is already open, it is closed. ; ; CALL: ; CALL LOGOPN ; ; RETURN: ; c-bit = 0 if successful, log file is open ; c-bit = 1 if unsuccessful, log file is not open ; ;- Swapable Code LogOpn: CALL LogCls ;Close any open file PRINT M.PROM ;Prompt for the file name PRINT M.LOGQ CALL GetCmd ; and go get it BCS 20$ ;Bad response (too long)... MOV #CmdLin,R3 ;R3->response string ; (R2->null byte at end of string) JSR R1,OpnFil ;Go open the file .WORD 1 ; for output .WORD Log ; on channel 'LOG' .RAD50 \LOG\ ; with default file type of '.LOG' BCS 20$ ;Unsuccessful... MOV #LogBuf,LogPnt ;File opened, reset buffer pointer CLR LogNum ;Start with block zero INC LgOpnd ;Indicate LOG file is open INC SvLgOn ;And make sure logging starts up ; when we exit command mode CLC ;Indicate success 20$: RETURN .SBTTL LOGCLS - Close Logging File ;+ ; ; LOGCLS ; Closes the logging file. ; ; CALL: ; CALL LOGCLS ; ; REGISTER USAGE: ; R0 munged ; ;- Swapable Code LogCls: TST LgOpnd ;Is a LOG file already open? BEQ 30$ ;Nope, then we're done... CLR LgOpnd ;Reset the 'log file opened' and CLR LogOn ; 'logging in progress' flags CLR SvLgOn 10$: CMP LogPnt,#LogBuf ;Do we have a partially full buffer? BEQ 20$ ;Nope... CLR R0 ;Yes, so do zero-fill by forcing null CALL FrcLog ; bytes to the buffer until it gets BR 10$ ; written out. 20$: .CLOSE #Log ;Close the file BCC 30$ ;No problems... ;+ ;ERROR CALL PrtErr ;Close error?! ... report it .WORD WrErr ;- 30$: RETURN .SBTTL LOGCHR - Log character from Host ;+ ; ; LOGCHR ; Places a character into the logging buffer if a log file is open ; and logging is enabled. When the buffer is full, it is written ; out to the LOG file. If an error occurs when writing to the file, ; logging is disabled, the log file is closed and the user is ; informed. ; ; CALL: ; CALL LOGCHR to log only when logging is enabled ; or ; CALL FRCLOG to log regardless ; ; INPUT: ; R0 = character to be logged ; ;- Swapable Code .ENABL LSB LogChr: TST LogOn ;Is logging enabled? BEQ 10$ ;Nope... FrcLog: MOVB R0,@LogPnt ;Place the character in the buffer INC LogPnt ;Bump the buffer pointer CMP LogPnt,#LogEbf ;Is the buffer now full? BLO 10$ ;Nope... MOV #LogBuf,LogPnt ;Yes, reset buffer pointer BIS #TskWt,TskFlg(R5) ;Tell scheduler we're waiting for I/O .WRITC TskArg(R5),#Log,#LogBuf,#256.,TskRsm(R5),LogNum ; start I/O CALL IOerr ;Check for errors BCS 20$ ;In case of any... INC LogNum ;Successfull, bump to next block 10$: RETURN 20$: CLR LogOn ;Reset the 'logging enabled' flags CLR SvLgOn ;+ ;ERROR CALL PrtErr ;Inform the user of the error .WORD WrErr ;- CALL LogCls ;Close the logging file ;+ ;ERROR ERROR. LogOff ; and inform user of the fact ;- RETURN .DSABL LSB NonSwapable Data LogPnt: .WORD LogBuf ; : logging buffer pointer LogNum: .WORD 0 ;Block number this buffer will be ; written to NonSwapable Buffer LogBuf: .BLKW 256. ;Logging buffer LogEbf: .SBTTL GETCMD - Get command line from user ;+ ; ; GETCMD ; Gets a user response and places it in the buffer 'CMDLIN'. ; ; CALL: ; CALL GETCMD ; ; OUTPUT: ; c-bit = 0, response in buffer, R2->null byte at end of response ; c-bit = 1, response was too long ; ; NOTE: ; Spaces and control characters are ignored. ; ;- Swapable Code GetCmd: MOV #CmdLin,R2 ;R2->Response buffer 10$: CALL TTYin ;Get a character CMPB R0,#LinFed ;Is it a ? BEQ 30$ ;Yes... CMPB R0,#SPACE ;Is it a control character? BLOS 10$ ;Yes, then ignore it MOVB R0,(R2)+ ;Place the character in the buffer CMP R2,#EndCmd-2 ;Response buffer full? BLOS 10$ ;Not yet... 20$: CALL TTYin ;Yes, get a character CMPB R0,#LinFed ;Is it a ? BNE 20$ ;Nope, ignore it ;+ ;ERROR ERROR. ToLong ;Inform user that line was too long ;- RETURN 30$: CLRB (R2) ;Terminate response with a null byte RETURN NonSwapable Buffer CmdLin: .BLKB 40. ; : user response buffer ;The last two bytes are reserved ; for use when the buffer contains ; a file name to be passed to CSI EndCmd: .EVEN Swapable Data ;+ ;ERROR ToLong: ERRTXT F,LTL, ;- .EVEN .SBTTL SNDOPN - Open a file for SEND ;+ ; ; SNDOPN ; Opens a file for sending to the host. ; ; CALL: ; CALL SNDOPN ; ; RETURN: ; c-bit = 0, send file open ; c-bit = 1, open error or send in progress ; ;- NonSwapable Code SndOpn: TST SndFlg ;Send already in progress? BNE 30$ ;Yes... PRINT M.PROM ;Nope, prompt for the file name PRINT M.SENQ CALL GetCmd ; and then get it BCS 10$ ;Response was too long... MOV #CmdLin,R3 ;R3->file name in response buffer ; (R2->null byte following response) JSR R1,OpnFil ;Open the file .WORD 0 ; for input .WORD SndFil ; on channel 'sndfil' .WORD 0 ; file has no default type BCS 10$ ;Open failed... CLR SndNum ;Success, start with block zero INC SndFlg ;Set the 'SEND in progress' flag Resume IndFil ;Wake up the send task CLC ;Indicate success 10$: RETURN ;+ ;ERROR 30$: ERROR. OpFrSn ;Send already in progress ;- RETURN .SBTTL INDFIL - Send a file to the host ;+ ; ; INDFIL ; This task sends a file to the host as if it were being typed ; by the user. Only useful for ASCII text files. Send speed is ; affected by the SLOW and FAST commands. ; ;- .ENABL LSB Task IndFil 10$: CLR SndAbt ;Reset 'abort SEND' flag Suspend ;Wait until resumed by SNDOPN TST SndFlg ;Are we to send a file yet? BEQ 10$ ;Nope, indiscriminate resume 20$: TST SndAbt ;Should we abort? BNE 60$ ;Yes, then go handle it BIS #TskWt,TskFlg(R5) ;Nope, tell scheduler we're waiting .READC TskArg(R5),#SndFil,#SndBuf,#256.,TskRsm(R5),SndNum ;for I/O CALL IOerr ;Any errors? BCS 50$ ;Yes... INC SndNum ;Bump block number for next read MOV #SndBuf,R1 ;R1->buffer of text to send to host 30$: TST SndAbt ;Should we abort? BNE 60$ ;Yes, then go handle it TST WTTOFG ;Set SLOW or FAST? BEQ 40$ ;Slow... CMP R1,#SNDBUF ;Are we at a block boundary? BNE 40$ ;Nope, then continue SLOW until next ; block MOV #SNDEBF,HOSTPT ;Set pointer to end of buffer to write CALL SNDMDM ;Force the buffer BR 20$ ;Now do another block 40$: CMP R1,#SNDEBF ;Past end of buffer? BHIS 20$ ;Yes, go do another block MOVB (R1)+,R0 ;Get a character to send BEQ 40$ ;Nulls are not to be suffered CALL WRMODM ;Send the single character WAIT SECONDS=1/10. ;Delay before sending next character BR 30$ 50$: TSTB @#ERRBYT ;Was read error due to EOF? BEQ 60$ ;Yes, then it wasn't really an error ;+ ;ERROR CALL PrtErr ;Nope, report to problem .WORD WrErr ;- 60$: .PURGE #SndFil ;Close out the file CLR SndFlg ;Reset the 'SEND in progress' flag BR 10$ .DSABL LSB SndAbt: .WORD 0 ; : 'abort SEND' flag SndFlg: .WORD 0 ; : 'SEND in progress' flag ..FAST == . WtToFg: .WORD 0 ; : FAST send is in effect NonSwapable Data SndNum: .WORD 0 ;Block number to read from SEND file NonSwapable Buffer SndBuf: .BLKW 256. ;Buffer for use when sending SndEbf: ; ; Use the same buffer for binary transmission as for ascii sends ; HostBf = SndBuf ; buffer used for output messages HostX = SndEbf ; end of buffer HostPt: .WORD HostBf ; pointer to free space in HostBf .SBTTL FROMXL - Task to process input from Host ;+ ; ; FROMXL ; This task processes input from the Host and normally outputs ; it all on the users terminal. It also looks for the escape ; sequence that signals the start of a message block in file ; transfers. Once the start of message is detected all further ; input from the host is handled specially by the "PutMes" routine. ; ; The start of message escape sequence is the APC (Application ; Code) escape sequences. This escape seuences is: ; ; APC = Escape + "_" ; start of message ; ;- .ENABL LSB Task FromXL 10$: CALL RdModm ;Get a character from the host TST R0 ;Null character? BEQ 10$ ;Yes, ignore it CMPB R0,#15 ;Is character a ? BNE 15$ ;Nope... CLR SELFLG ;Yes, reset the select flag 15$: TST SELFLG ;Is select flag set? BNE 10$ ;Yes, don't echo received character CMPB R0,#Escape ;Possible start of APC? BNE 20$ ;Nope, output to terminal as is CALL RdModm ;Maybe, get second character CMPB R0,#'_ ;Is it an APC final character? BEQ 30$ ;Yes... MOV R0,R1 ;Nope, so save it for awhile BEQ 10$ ; unless it's null MOV #Escape,R0 ;Send an to the terminal CALL TTYout MOV R1,R0 ; followed by the other character 20$: CALL TTYout BR 10$ 30$: CALL PutMes ;Go process message BR 10$ .DSABL LSB NonSwapable Data SELFLG: .WORD 0 ; : Inhibit character echo ; Used to inhibit echo of characters ; comprising the port selection ; string during SELECT .SBTTL PUTMES - Message from Host handler ;+ ; ; PUTMES ; This routine is called upon receipt of the start of a ; message from the host. Messages are framed by the APC ; (application Code) and ST (String Terminator) escape ; sequences. ; ; CALL: ; CALL PUTMES ; ; REGISTER USAGE: ; all registers munged ; ; NOTES: ; APC = + "_" ; start of message ; ST = + "\" ; end of message ; ; Messages are of the form: ; ; ; where the codes are defined as follows: ; ; Code "s" = VTCOM is requested to send a file to the Host ; "r" = VTCOM is requested to recieve a file from the Host ; "0" = data message zero ; "1" = data message one ; "x0" = recieved data message zero from VTCOM incorrectly ; "x1" = recieved data message one from VTCOM incorrectly ; "y0" = recieved data message zero from VTCOM correctly ; "y1" = recieved data message one from VTCOM correctly ; "zy" = tells VTCOM that EOF reached on file being received ; "zx" = tells VTCOM that file transfer is aborted by HOST ; "yz" = received end of file message correctly from VTCOM ; "yz" = received end of file message incorrectly from VTCOM ; ; The words which make up the message are encoded as three printable ; ascii characters in the range 40-137. The three characters are ; generated by taking bits <15:12>, <11:06>, and <05:00> and ; adding 40(8) to each (to make them printable). ; ; The checksum is generated by adding the message code plus all ; the data words together. The checksum is the negation of that ; addition. ; ; Only the "s", "r", "0", and "1" messages contain data. For the ; "s" and "r" type messages, the data is the name of the file to ; be opened/created by VTCOM. ; ;- Swapable Code PutMes: INC MesFlg ;Indicate we're processing a message 10$: SetTmo MesTmo,Minutes=1 ;Set time-out for receipt of message CLR NewMes ;Reset 'new message' flag CALL GetChr ;Get the single-character message code BCS 150$ ;On end of message... CLR AbtFlg ;Reset the 'message is bad' flag MOV R0,Code ;Store the message code CMPB R0,#'x ;Is it a two character message? BLO 20$ ;Nope... CALL GetChr ;Yes, so get second character BCS 70$ ;On end of message... MOVB R0,Code+1 ;Store the second character 20$: MOV Code,R4 ;Start the checksum accumulation MOV #Mess,R1 ;R1->incoming message buffer 30$: CALL GetChr ;Get a character of the message BCS 80$ ;On end of message... TST SquFlg ;Is the compression table active? BEQ 40$ ;Nope... CMPB R0,#140 ;Yes, is this a compression byte? BLO 40$ ;Nope... ADD R0,R4 ;Yes, accumulate the checksum SUB #140,R0 ;Determine table offset for this word ASL R0 ;Convert byte to word offset MOV SquTab(R0),(R1)+ ;Place decoded word in message buffer BR 30$ ; and get another message character 40$: SUB #40,R0 ;Strip off the excess bits MOV R0,R3 ;Save decoded bits MOV #002020,R2 ;Load up decoding mask 50$: CALL GetChr ;Get next character BCS 70$ ;End of message?! SUB #40,R0 ;Strip off the excess bits 60$: ASL R3 ;Loop to shift what we have of word ASL R2 ; up to make room for new bits BCC 60$ ADD R0,R3 ;Merge with current decoded word TST R2 ;Have we done all three characters? BNE 50$ ;Nope, go do another MOV R3,(R1)+ ;Place decoded word in message buffer ADD R3,R4 ;Accumulate the checksum CMP R1,#EndMes ;Is the message buffer full? BLO 30$ ;Nope, go get more of the message 70$: INC AbtFlg ;Flag message as bad BR 90$ ;Check if we should NAK it 80$: TST R4 ;Did the checksum work out? BNE 70$ ;Nope, flag message as bad 90$: MOV GetFlg,R4 ;Is message handler task waiting? BNE 100$ ;Yes... Suspend ;Nope, se we'll wait for it BR 90$ 100$: CMPB Code,(R4)+ ;Is this message code one of those ; being waited for? BEQ 110$ ;Yes... TSTB (R4) ;Not this one, are there more? BNE 100$ ;Yes, so check again TST AbtFlg ;Was message flagged as bad? BEQ 120$ ;Nope, give it to the message ; handler anyway BR 150$ ;Yes, so we'll just forget it 110$: TST AbtFlg ;Was expected message flagged bad? BNE 140$ ;Yes... 120$: SUB #Mess,R1 ;Nope, compute message size ASR R1 ; in words MOV R1,RcvSiz ; and save it DEC RcvSiz ; (minus the checksum word) MOV #Code,R2 ;And move the whole message (including MOV #RcvCod,R3 ; the code into the RCVCOD buffer 130$: MOV (R2)+,(R3)+ DEC R1 BNE 130$ CLR GetFlg ;Indicate we have a message Resume MesHnd ; and wake up the message handler task BR 150$ ; Here if an expected message was flagged as bad 140$: CMPB Code,#'x ;Was message a NAK? BEQ 150$ ;Yes, so don't NAK it CMPB Code,#'y ;Nope, was it an ACK? BEQ 150$ ;Yes, don't NAK it, either. MOVB Code,R1 ;Get the message code SWAB R1 ; in the high byte ADD #'x,R1 ; with NAK indicator in low byte CLR R3 ;No block of data CALL OutMes ;Send the message 150$: TST NewMes ;Message ended by new message? BNE 10$ ;Yes, go process the new message CLR MesFlg ;Indicate we're no longer processing ; a message INC PACREC ;Bump message packets received count RETURN Code: .WORD 0 ; : message code Mess: .BLKW MesSiz+2 ;Message buffer (plus room for ; checksum) EndMes: NonSwapable Buffer RcvCod: .BLKW ; : message code RcvMes: .BLKW MesSiz+2 ;Decoded message buffer RcvSiz: .BLKW 1 ; : message size (in words) Swapable Data MesFlg: .WORD 0 ; : 'message in progress' flag ;set non-zero when processing ;a message NewMes: .WORD 0 ; : 'new message' flag ;set non-zero when end of message ;is APC starting a new one AbtFlg: .WORD 0 ; : 'bad message' flag ;set non-zero when message was ;received in error .SBTTL GETCHR - Get a message character ;+ ; ; GETCHR ; This routine returns a character contained in a message. ; Embedded host messages (such as mail and operator messages) ; are processed by this routine and output to the terminal. ; ; CALL: ; CALL GETCHR ; ; RETURNS: ; c-bit = 0, R0 contains character from message ; c-bit = 1, Indicates end of message, message error, ; embedded operator, mail or system message. ; ;- Swapable Code GetChr: MOV R1,-(SP) ;Save some registers for awhile MOV R2,-(SP) ; get a char from the Host and check if it is special. Note any control ; character is special cause they cannot normally appear in a message (except ; for the escape sequence that terminates a message. 10$: CALL TrModm ;Get a character from the host ; (with time-out) 20$: TST R0 ;Did we time-out? BEQ 100$ ;Yes, then abort message CMPB R0,#CarRet ;Is character a ? BEQ 40$ ;Yes, then go process embedded text CMPB R0,#LinFed ;Is it a ? BEQ 40$ ;Yes, also indicates embedded text CMPB R0,#Escape ;Is it an ? BEQ 110$ ;Yes, go check for end of message CMPB R0,#SPACE ;Any other control character... BLO 10$ ; just ignore 30$: MOV (SP)+,R2 ;Restore the work registers MOV (SP)+,R1 CLC ;Indicate success RETURN ; On a or , start storing text in the embedded text buffer 40$: CALL TTYout ;Print the character INC AbtFlg ;Reject the block of data 50$: MOV #Line,R1 ;R1->embedded text buffer 60$: CALL TrModm ;Get a character from host ; (with time-out) MOVB R0,(R1)+ ;Store in text buffer CMPB R0,#Escape ;Is it an ? BEQ 110$ ;Yes, go check for message end TST R0 ;Did we time out? BEQ 120$ ;Yes, forget line CMPB R0,#CarRet ;If it is a BEQ 70$ CMPB R0,#LinFed ; or , BEQ 70$ ; print the text on the terminal CMP R1,#Line+80. ;None of the above, line buffer full? BHIS 30$ ;Yes, then not really text, forget it CMPB R0,#SPACE ;Is it a control character? BHIS 60$ ;Nope, so go get another character CMPB R0,#Bell ;Yes, is it a or BEQ 60$ CMPB R0,#Tab ; a ? BEQ 60$ ;Yes, they can be in the line also DEC R1 ;Ignore anything else BR 60$ ; On the matching or , print the text buffer to the ; user's terminal 70$: MOV #Line,R2 ;R2->embedded text buffer 80$: MOVB (R2)+,R0 ;Get a character of text BEQ 120$ ;If null, done CALL TTYout ;Otherwise, print it CMP R2,R1 ;Are we done? BLO 80$ ;Nope... BR 50$ ;Yes, look for another line to print ; Encountered illegal escape sequence, abort message but continue to look ; for message termination 90$: INC AbtFlg ;Flag message as bad BR 20$ ;Go check for message end ; Time-out occured in waiting for char, set that message is aborted (bad) ; and that message processing is done 100$: INC AbtFlg ;Flag message as bad BR 130$ ;Tell caller no more to come ; recieved, check to see if it is due to start or end of a message 110$: CALL TrModm ;Get another character CMPB R0,#'\ ;Is it the sequence? BEQ 120$ ;Yes... CMPB R0,#'_ ;No, is it another ? BNE 90$ ;No... INC NewMes ;Yes, set 'new message' flag INC AbtFlg ;Flag current message as bad ; Message terminated, return zero just to have something there 120$: CLR R0 ; Message block terminated due to something, carry flags message termination 130$: MOV (SP)+,R2 ;Restore work registers MOV (SP)+,R1 SEC ;Indicate bad message RETURN ; buffer used to store what might be system text that occured in the middle of ; a file transfer message block from the Host Line: .BLKB 80. .SBTTL TRMODM - Timed read of character from host ;+ ; ; TRMODM ; This routine tries to get a character from the Host. If none ; is gotten within a certain time, then any XOFFs are cleared, ; and another attempt is made to get a char from the Host. ; Whatever way this one goes, its status is returned to the caller. ; ; CALL: ; CALL TRMODM ; ; RETURNS: ; R0 = character from host, zero if time-out ; ;- TrModm: CALL RdModm ;Get a character from the host TST R0 ;Did time-out occur? BNE 10$ ;Nope, return character .SPFUN TskArg(R5),#ModemW,#ClrDrv,#0,#0,#1 ;Yes, do a driver clear CALL RdModm ;And try the read again 10$: RETURN .SBTTL RDMODM - Get char from Host with Time-out detect ;+ ; ; RDMODM ; ; CALL: ; CALL RDMODM ; ; RETURNS: ; R0 = character from host, zero if time-out occured ; ;- RdModm: SetTmo XLTO,Seconds=5 ;Set 5 second time-out 10$: MOVB @XLPnt,R0 ;Get a character from XL buffer BNE 20$ ;If non-zero... TST XLTO ;None available, time-out yet? BEQ 30$ ;Yes, then return zero Suspend ;Not yet, wait a while BR 10$ ;Check again 20$: BIC #<^C177>,R0 ;Strip to seven bits INC XLPnt ;Bump the buffer pointer TSTB @XLPnt ;Any more characters? BNE 30$ ;Yes, then just return character Resume XLRdr ;Nope, resume XLRDR first 30$: RETURN DclTmo XLTO,FromXL ;Declare host read time-out counter XLPnt: .WORD XLZero ;Host character buffer pointer. Points ; to a zero byte if buffer is empty NonSwapable Data XLBuf: .BLKB XLSiz ;Host input character buffer XLZero: .BYTE 0 ;This is here so that there is always ; a null byte at the end of the buffer .EVEN .SBTTL XLRDR - Read from host task ;+ ; ; XLRDR ; This task does the actual read of characters from the host. ; ; NOTES: ; Characters read are placed into buffer 'XLBUF'. ; ;- .ENABL LSB Task XLRdr 10$: TST MesFlg ;Are we processing a message? BEQ 20$ ;Nope... CMP XLPnt,#XLZero ; if we read a whole buffer full of char BHIS 20$ ; on the last read then branch else Wait Seconds=<3./60.> ;Wait for characters to be received 20$: MOV #XLZero,XLPnt ;Indicate no characters available yet BIS #TskWt,TskFlg(R5) ;Tell scheduler that we're waiting for .SPFUN TskArg(R5),#Modem,#SrdDrv,#XLBuf,#XLSiz,#1,TskRsm(R5) ; I/O ; (reads at least 1 character, but ; no more than 6) CALL Pass ;Pass the CPU around the table MOV #XLBuf,XLPnt ;Indicate there are characters now Resume FromXL ;Resume the handler task 30$: TSTB @XLPnt ;Has it used the ones available? BEQ 10$ ;Yes, then it's time to read some more Suspend ;Otherwise, suspend until they've BR 30$ ; been used up .DSABL LSB .SBTTL GETMES - Get and process a message ;+ ; ; GETMES ; This routine waits for a message to be accumulated by the ; FromXL task. It informs the FromXL task what messages can ; be NAKed if they are recieved incorrectly by telling it ; what are the expected message codes. Note that "x" and "y" ; message are never NAKed or ACKed cause they are the ACK/NAK ; messages. This routine uses the jump table given in the ; call to the routine to branch to the handler for the code ; that actually came from the Host. Note that the jump table ; must have and can take jumps in the jump table for message ; codes that were not expected. The expected codes just have ; an effect on what is NAKed on an incorrect message. ; ; CALL: ; JSR R0,GetMes ; .WORD address of .ASCIZ string containing message ; codes for messages that the caller expects to ; recieve. Only expected messages are NAKed if ; they are recieved incorrectly. ; .WORD address of "s" message handler ; .WORD address of "r" message handler ; .WORD address of "z" message handler ; .WORD address of "0" message handler ; .WORD address of "1" message handler ; .WORD address of "x" message handler ; .WORD address of "y" message handler ; .WORD address of time-out on message handler ; .WORD address of transfer aborted handler ; ; NOTES: ; This routine does a dispatch to the appropriate message ; handling routine. Return will be made from there. ; ;- GetMes: SetTmo MesTmo,Minutes=1 ;Set time out for message MOV (R0)+,GetFlg ;Get pointer to expected codes string TST MesAbt ;Should transfer be aborted? BNE 20$ ;Then dispatch to the abort processor Resume FromXL ;Nope, resume host read task 10$: Suspend ;Wait awhile TST GetFlg ;Did a message arrive? BEQ 40$ ;Yes... TST MesAbt ;No, was the transfer aborted? BNE 20$ ;Yes... TST MesTmo ;No, did we time out? BNE 10$ ;Nope, indiscriminate resume... 20$: CLR GetFlg ;Yes, we're no longer waiting for ; a message 30$: CLR RcvCod ;Indicate illegal message code ; (acts the same as time-out) 40$: MOV #Codes,R1 ;R1->legal message codes 50$: CMP R1,#LCodes ;Have we run out of codes? BHIS 30$ ;Yes, then handle like time-out CMPB RcvCod,(R1)+ ;Nope, does this one match? BNE 50$ ;Nope, try another SUB #Codes+1,R1 ;Yes, determine it's offset ASL R1 ;Shift for table offset ADD R1,R0 ;Add offset to table start address ; (R0->table entry) TST MesAbt ;Has transfer been aborted? BEQ 60$ ;Nope... TST (R0)+ ;Yes, bump from time-out to ; abort entry 60$: MOV (R0),R0 ;Get handler address from entry RTS R0 ; and dispatch to it GetFlg: .WORD 0 ; : 'waiting for message' flag ;Hybrid data, when zero, we're not ; waiting for a message. When ; non-zero, contains the address of ; a .ASCIZ string containing the ; expected message codes. DclTmo MesTmo,MesHnd ;Declare message wait time-out counter ; Set on entry to GETMES, set again ; on receipt of APC. Codes: .ASCIZ /srz01xy/ ;Valid message codes LCodes: .EVEN .SBTTL MESHND - Transfer protocol finite state machine ;+ ; ; MESHND ; This task handles file transfers between the Host and VTCOM ; that use the message protocol. Message are recieved by the ; "FromXL" task and are given to this task when they are ; completely recieved. Only correct messages are seen by this ; task. Messages with bad checksums or other errors are not ; seen. Each call to "GetMes" gives a pointer to a string ; specifying the message codes which are expected at this ; point. Only expected messages are NAKed if they are recieved ; incorrectly. ; ;- Task MesHnd JMP State0 NonSwapable Code State0: CLR MesAbt ;Reset 'abort transfer' CLR SquFlg ; and compression encoding flags CLR TRANSF ;Indicate no transfer yet CLR TRNSIZ ;So 'SHOW' command can get right size JSR R0,GetMes ;Wait for a message ; s r z 0 1 x y Time Abort ; expecting "s" or "r" | | | | | | | Out | ; v v v v v v v v v .WORD MessS0,20$,20$,10$,ZxMess,ZxMess,State0,State0,State0,State0 ; EOF message, acknowledge it and then continue waiting for the start ; of a transfer. This message can only occurif the host and VTCOM are ; in disagreement about the current state. 10$: CALL OutRep .WORD "yz BR State0 ; SEND or RECEIVE file message, check for compression table and file name 20$: MOV #RcvMes,R3 ;R3->message MOV RcvSiz,R2 ;Get size of received message BNE 22$ ;Branch if we got something ;+ ;ERROR 21$: ERROR. ERRPRO ;Give protocol version error ;- BR 70$ ;and return a 'zx' packet 22$: CMP (R3)+,#$$$VER ;Is the protocol version ok? BNE 21$ ;Branch if not, give error. MOV (R3),R0 ;Get the first word of the message CMP R0,#37 ;Is it a printable character? BHI 40$ ;Yes, it's the start of the file name MOV (R3)+,SquFlg ;Nope, it's the size, in words, ; of the compression table MOV #SquTab,R1 ;R1->where to store table 30$: MOV (R3)+,(R1)+ ;Save a compression word DEC R0 ;More to do? BGT 30$ ;Yes... 40$: ASL R2 ;Determine the end of the message ADD #RcvMes,R2 50$: TSTB -(R2) ;Look for last non-null character BEQ 50$ INC R2 ;R2->Null at end of file name string ; At this point, R3->beginning of the file name string ; and R2->the null byte at the end of the string. This ; is what the OPNFIL routine wants. MOV #Block,BlkPnt ;Reset write buffer pointer CLR BlkNum ;We'll start with block zero CLR BlkErr ;Reset error indicator CLR PACSEN ;Reset count of packets sent CLR PACREC ; and received INC TRANSF ;Set the 'transfer in progress' flag CMPB #'s,RcvCod ;Is file to be sent? BEQ 60$ ;Yes... JSR R1,OpnFil ;Open a local file .WORD 1 ; for output .WORD File ; on channel 'file' .WORD 0 ; and it has no default file type BCS 70$ ;Couldn't open it... CALL OutRep ;ACK the 'receive file' message .WORD "yr JMP State1 ;Go handle incoming file 60$: JSR R1,OpnFil ;Open a local file .WORD 0 ; for input .WORD File ; on channel 'file' .WORD 0 ; and it has no default file type BCS 70$ ;Couldn't open it... CALL OutRep ;ACK the 'send file' message .WORD "ys JMP TranFl ;Go send the file 70$: JMP ZxMess ;Couldn't open the specified file, ; tell the host to abort the transfer. NonSwapable Data MesAbt: .WORD 0 ; : 'abort transfer' flag transf: .word 0 SquFlg: .WORD 0 ;Hybrid data, if zero, indicates ; compression encoding is not in ; use. When non-zero, indicates ; number of words in table. SquTab: .BLKW 37 ;Compression encoding table. Contains ; the words which can be encoded as ; single bytes for increased message ; transmission speed. The words are ; encoded by adding the table offset ; to 140(8). Swapable Data MessS0: .ASCIZ /sr/ ;Expected message codes ;+ ;ERROR ERRPRO: ERRTXT F,VER, ;- .EVEN Swapable Code .ENABL LSB ; This state is entered after the reciept of a "r" message indicating the ; transfer of a file to VTCOM from the Host. It waits for a message containing ; data to be writen to the VTCOM output file. State1: JSR R0,GetMes ; s r z 0 1 x y Time Abort ; expecting "0" or "z" | | | | | | | Out | ; v v v v v v v v v .WORD MessS1,80$,90$,50$,10$,40$,100$,100$,100$,100$ ; write message "0" data out to the file 10$: CALL WrFile ; write data to file BCS 115$ ; if error then go abort transfer else 20$: CALL OutRep ; acknoledge successful receipt of data .WORD "y0 ; message "0" ; wait for data which will come in data message "1". The two data message ; numbers must be used to prevent the same block being recieved twice in the ; case where the message acknowledge is lost. State2: JSR R0,GetMes ; s r z 0 1 x y Time Abort ; expecting "1" or "z" | | | | | | | Out | ; v v v v v v v v v .WORD MessS2,80$,90$,50$,20$,30$,100$,100$,100$,100$ ; write message "1" data out to the file 30$: CALL WrFile ; write data to file BCS 115$ ; if error then go abort transfer else 40$: CALL OutRep ; acknowledge successful receipt of data .WORD "y1 ; message "1" BR State1 ; go back to wait for message "0" ; end of file message sent, check if this was really end of file or abort 50$: CMP #"zy,RcvCod ; if was really an abort BNE 60$ ; then branch else ; file transfer complete, then try to make output file permanent CALL ClosWr ; attempt to make file permananent BR 70$ ; and go see if succeeded ; end of file said abort file transfer, delete the temporary output file 60$: CALL PurgWr ; delete temp output file 70$: BCS 120$ ; if had error in closing output file - branch ; Had no errors in end of file, acknowledge the end of file message CALL OutRep .WORD "yz ; go back and wait for another file transfer to be started 75$: JMP STATE0 ; received "s" message during file transfer, NAK it and abort file transfer ; this can only happen when the Host and VTCOM get confused. 80$: CALL OutRep .WORD "xs BR 75$ ; received "r" message during file transfer, NAK it and abort file transfer ; this can only happen when the Host and VTCOM get confused. 90$: CALL OutRep .WORD "xr BR 75$ ; file transfer aborted due to time-out waiting for message data, RESET ; command or reciept of a message that can only happen if the Host no longer ; knows what its doing. Delete the temporary output file and go back and wait ; for the start of another file transfer. 100$: CALL PurgWr JMP ZXMESS ; file transfer aborted due to I/O error in writing data to output file 115$: CALL PurgWr ; delete the output file ;+ ;ERROR 120$: CALL PrtErr ; write error message saying what the error .WORD WrErr ; was that caused abort ; go tell the Host that transfer was aborted. ;- JMP ZxMess ; pointers to I/O error messages for a write ;+ ;ERROR WrErr: .WORD ErrIsr,ErrHrd ;- ; expected message codes MessS1: .ASCIZ /0z/ MessS2: .ASCIZ /1z/ .EVEN .DSABL LSB Swapable Code .ENABL LSB ; This state is entered after the reciept of a "s" message indicating the ; transfer of a file to the Host from VTCOM. It immediatelly starts reading ; the file to be sent to the Host. TranFl: INC RdFlg ; flag that read-ahead task should run Resume RdTsk ; and start it going ; set the initially transfer size and message code number MOV #TrnMax,TrnSiz ;Start with maximum sized data packets MOV #'0,TrnCod ; first message is message "0" CLR REQSCT ;Reset required success count CLR CURSCT ; and current success count ; go make sure that we got a file block read in 10$: CLR ERROFG ;Reset block error flag CALL RdFile ; get block, if had error BCS 90$ ; in read then branch else ; set up to start outputting at the start fo the block just read MOV #Block,TrnPnt ; output TrnSiz words of the block to the Host 20$: MOV TrnCod,R1 ; get message code MOV TrnPnt,R2 ; get pointer into next part of block to send MOV TrnSiz,R3 ; get number of words to send Call OutMes ; send message with data to Host ; wait for the message to be acknolegded State3: JSR R0,GetMes ; s r z 0 1 x y Time Abort ; expecting "x" or "y" | | | | | | | Out | ; v v v v v v v v v .WORD MessS3,50$,60$,80$,100$,100$,30$,40$,20$,100$ ; message "x" received, cut the size of block transfer in half but never ; cut it down to lower than 8 words (16 bytes) 30$: INC REQSCT ;Bump the required success count CLR CURSCT ; and reset the current success count MOV SP,ERROFG ;Now indicate an error occurred ;dbg MOVB #'v,R0 ;dbg CALL TTYOUT ASR TrnSiz ;Halve data packet size CMP TrnSiz,#TrnMin ;Was it already at minimum? BHIS 20$ ;No, so use this size ASL TrnSiz ;Yes, so leave it alone BR 20$ ; and try again ; message "y", check to see if Host was acknoleging the message jsut sent 40$: CMPB TrnCod,RcvCod+1 ; if was not acking the message just sent then BNE 20$ ; go back and try transmitting it again else ; transfer was success, set so next message will go out with the alternate ; messaage code. INC TrnCod ; inc message code up one BIC #2,TrnCod ; strip it back to "0" if it went to "2" ; adjust pointer into block being sent to start of next segment to send ADD TrnSiz,TrnPnt ; add size twice since size is in words ADD TrnSiz,TrnPnt ; check to see if all of block has been transfered CMP TrnPnt,#BlkEnd ; if not all transfered BLO 20$ ; then go transfer more of block ; Here begin the heuristics for bumping packet size based on error ; rate over the serial line ;dbg MOVB #'*,R0 ;dbg CALL TTYOUT TST ERROFG ;Errors sending that block? BNE 10$ ;Yes, on to the next one... 410$: INC CURSCT ;Bump 'current success count' CMP TRNSIZ,#TRNMAX ;Already at maximum packet size? BGE 10$ ;Yes, can't get any bigger... CMP CURSCT,REQSCT ;No, have we been good long enough? BLT 10$ ;Nope... ;dbg MOVB #'^,R0 ;dbg CALL TTYOUT ASL TRNSIZ ;Yes, so double packet size BR 420$ DEC REQSCT ; and reduce required success count BNE 420$ ; (to a minimum MOV #1,REQSCT ; of one) 420$: CLR CURSCT ;We have to be good again... BR 10$ ;On to next block ; received "s" message during file transfer, NAK it and abort file transfer ; this can only happen when the Host and VTCOM get confused. 50$: CALL OutRep .WORD "xs BR 85$ ; received "r" message during file transfer, NAK it and abort file transfer ; this can only happen when the Host and VTCOM get confused. 60$: CALL OutRep .WORD "xr BR 85$ ; abort file transfer message (or end of file) acknolegde it and go abort tran 80$: CALL OutRep .WORD "yz 85$: CALL PURGRD JMP STATE0 ; error encountered in reading block of data from input file, ; check for end of file 90$: TSTB @#ERRBYT ; if error was eof error BEQ 110$ ; then branch else ;+ ;ERROR ; error reading input file other that eof error. Print error message CALL PrtErr .WORD WrErr ;- ; file transfer aborted, close input file and tell Host of abort 100$: CALL PurgRd JMP ZxMess ; end of file, close input file and go tell Host abort end of file 110$: CALL PurgRd JMP ZyMess .DSABL LSB TrnSiz: .WORD TrnMax ; : Size of data packet in words TrnPnt: .WORD Block ; points to start of words in input file block that ; were sent in last message TrnCod: .WORD '0 ; holds message number of last message ErroFG: .blkw ; : Block transfer error flag ReqSCT: .blkw ; : Required success count CurSCT: .blkw ; : Current success count ; expected message codes MessS3: .ASCIZ /xy/ .EVEN Swapable Code .ENABL LSB ; this entry is taken if a file transfer is being aborted by VTCOM due to ; RESET or some type of error ZxMess: MOV #"zx,Zmess ; set to send abort file transfer message BR 60$ ; and go send it ; this entry is only taken when a file being sent to the Host is done with ; no errors and really just tells the Host end of file ZyMess: MOV #"zy,Zmess ; set to send eof message BR 60$ ; and go send it ; waits for message that end of file transfer message was recieved ok State4: JSR R0,GetMes ; s r z 0 1 x y Time Abort ; expecting "x" or "y" | | | | | | | Out | ; v v v v v v v v v .WORD MessS4,10$,20$,30$,70$,80$,50$,40$,State0,State0 ; received "s" message during attempt to end file transfer, NAK it and forget ; about ending file transfer. This can only happen when the Host and VTCOM ; get confused. 10$: CALL OutRep .WORD "xs JMP State0 ; received "r" message during attempt to end file transfer, NAK it and forget ; about ending file transfer. This can only happen when the Host and VTCOM ; get confused. 20$: CALL OutRep .WORD "xr JMP State0 ; received "z" message during attempt to end file transfer. Host must be ; trying to abort the file transfer so just ACK it and go wait for another ; file transfer to be started. 30$: CALL OutRep .WORD "yz JMP State0 ; got ACK, check if it was for the "z" message we just sent 40$: CMP RcvCod,#"yz ; if was not BNE State4 ; then continue waiting JMP State0 ; else go wait for start of another transfer ; got NAK, check if it was for the "z" message just sent 50$: CMP RcvCod,#"xz ; if was not for "z" message BNE State4 ; then continue waiting else ; send the stop transfer message that was set up to the Host 60$: CALL OutRep Zmess: .WORD "zx ; go wait for this message to be acked BR State4 ; we got a data message "0", wait for "z" some more but if get the same data ; message again then resend the "z" message to the Host. This state can only ; happen when the Host did not get the "z" message or VTCOM and the Host are ; confused about what is happening. 70$: JSR R0,GetMes ; s r z 0 1 x y Time Abort ; expecting "x" or "y" | | | | | | | Out | ; v v v v v v v v v .WORD MessS4,10$,20$,30$,60$,80$,50$,40$,State0,State0 ; we got a data message "1", wait for "z" some more but if get the same data ; message again then resend the "z" message to the Host. This state can only ; happen when the Host did not get the "z" message or VTCOM and the Host are ; confused about what is happening. 80$: JSR R0,GetMes ; s r z 0 1 x y Time Abort ; expecting "x" or "y" | | | | | | | Out | ; v v v v v v v v v .WORD MessS4,10$,20$,30$,70$,60$,50$,40$,State0,State0 .DSABL LSB ; expected message codes MessS4: .ASCIZ /xy/ .EVEN .SBTTL Open a file for read or write ; ; This routine checks the file name given in the string for correctness and ; opens the file. If any error occurs during this process an error is printed ; on the user's console and the file is not opened. ; ; Input Registers: R3 points to beginning of file name string ; R2 points to end of file name string ; ; Calling Sequence: JSR R1,OpnFil ; .WORD 0 if file to be read, 1 if file to be written ; .WORD channel number file is to be opened on ; .RAD50 /default file name extension if there is one/ ; ; Output Registers: Destroyed ; ; Condition Codes: Carry is set if there was an I/O error encountered ; in opeing the file, an error message will ; already have been printed ; NonSwapable Code OpnFil: MOVB #'=,(R2)+ ;Append a '=' and null byte to make CLRB (R2) ; string a valid CSI output file spec ; move all the routone arguments to where they won't be swapped out if the ; USR needs to swap in over part of the program. MOV (R1)+,-(SP) ; get flag of type of open (input or output) MOV (R1)+,R4 ; get channel number MOV (R1)+,DefExt+2 ; and store default file name extension ; attempt to lock the USR in memory for the duration of this routine 10$: .TLOCK ; attempt lock of USR BCC 20$ ; if succeeds then branch else Wait Seconds=2 ; wait two seconds BR 10$ ; and try again ; we got ownership of the USR, now try to decode the file name stirng 20$: MOV SP,R2 ; save stack pointer .CSISPC #FilBlk,#DefExt,R3 ; call for file name string decoding MOV R2,SP ; clear any switch info off of stack BCS 60$ ; if had error then go tell user else CLRB @#ERRBYT ; set file name error TST FilNam ; if no file name present then BEQ 60$ ; got file name error else ; check what kind of file open this is TST (SP)+ ; if file is to be opened for reading BEQ 50$ ; then branch else ; file to be created (opened for output), check size of file wanted TST FilSiz ; if size given in file name spec then BNE 30$ ; branch else DEC FilSiz ; set to get as big a file as we can ; create a temporary file for output, it will be made real if all goes well 30$: .ENTER TskArg(R5),R4,#FilNam,FilSiz,#0 BCS 70$ ; if error in creation then branch else ; new temp file created, release the USR 40$: .UNLOCK ; return to caller with file open and indicating no error in open CLC RTS R1 ; file to be opened for input, open an existing file for input 50$: .LOOKUP TskArg(R5),R4,#FilNam,#0 BCC 40$ ; if no error in open then branch else ; error in open of file for input go print error ;+ ;ERROR CALL 80$ .WORD LUPerr ; address of .LOOKUP error text table ;- ; got error in decoding file name, go print the error 60$: TST (SP)+ ; pop the type of open flag off stack CALL 80$ .WORD CSIerr ; address of .CSI error text table ; error in creating output file, go print the error 70$: CALL 80$ .WORD ENTerr ; address of .ENTER error text table ;- ; pick up the address of the error text table and the error code from sys call 80$: MOV @(SP)+,90$ ; pick up table address MOVB @#ERRBYT,R3 ; and error code ; .ENTER and .LOOKUP errors caught by .SERR leave the channel ; open. We have to purge it otherwise we will get an 'internal error' ; on the next .ENTER or .LOOKUP on that channel. .PURGE R4 ;Do this to avoid 'Internal error' ; release control of the USR .UNLOCK ; restore the error code and print the error message MOVB R3,@#ERRBYT ; restore system error message CALL PrtErr ; print message that responds to code 90$: .WORD 0 ; for call ; return to caller with carry set if there was an error and file not open RTS R1 NonSwapable Buffer ; CSI file name decoding block FilBlk: .BLKW 39. FilNam = FilBlk FilSiz = FilNam+<4*2> NonSwapable Data ; default extensions used for file name decoding. Any default extension for ; file names is stored into this table before decoding is done. DefExt: .WORD 0,0,0,0 Swapable Data ;+ ;ERROR CSIerr: .WORD ErrFN,ErrIdv ENTerr: .WORD ErrImp,ErrDfl,ErrImp,ErrNrp LUPerr: .WORD ErrImp,ErrFnf ;- .SBTTL Write message from Host into output file ; ; This routine writes the message from the Host held in "RcvMes" out to the ; output file. ; ; Input Registers: None ; ; Calling Sequence: CALL WrFile ; ; Output Registers: Destroyed ; ; Condition Codes: Carry is set if there was an I/O error encountered ; on the file with the system error byte ; containing the code for the error ; Swapable Code ; write each word of data in the mesage buffer to the file WrFile: MOV #RcvMes,R2 ; get address of start of message buffer MOV RcvSiz,R3 ; get number of words in message BEQ ChkFer ; branch if no data in message else 10$: MOV (R2)+,R0 ; get a word of message CALL OutFil ; write it to output file BCS 20$ ; branch if error in write else DEC R3 ; decrement word count BNE 10$ ; and loop till done ; output done, carry is set if there was an error in transfer 20$: RETURN .SBTTL Close an output file making it a real file ; ; This routine writes any partial buffer to the file and then closes the file ; making it permanent. Note that the file is not made permanent if there was ; any errors in writing to it. ; ; Input Registers: None ; ; Calling Sequence: CALL ClosWr ; ; Output Registers: R0 destroyed ; ; Condition Codes: Carry is set if there was an I/O error encountered ; on the file with the system error byte ; containing the code for the error ; Swapable Code ; check to see if the output buffer is empty ClosWr: CMP BlkPnt,#Block ; if output buffer empty then BEQ 30$ ; go close output file else ; write zero word to output buffer in attempt to fill and write it out CLR R0 ; get a zero CALL OutFil ; write it out BCC ClosWr ; if no error then go check for empty buffer ; buffer has been delt with, wait for any I/O to complete 30$: CALL IoWt ; check to see if there was any error during I/O CALL ChkFer ; if was error during I/O BCS PurgWr ; then go purge output file else ; close output file making it a real file .CLOSE #File ; close output file, if had error ; return to caller with carry indicating whether had any error RETURN .SBTTL Check to see if error recorded by "RdTsk" or "WrTsk" ; ; This routine checks to see if an error has been recorded in "BlkErr" and ; returns with an indication of the error. ; ; Input Registers: None ; ; Calling Sequence: CALL ChkFer ; ; Output Registers: Unchanged ; ; Condition Codes: Carry is set if there was an I/O error encountered ; on the file with the system error byte ; containing the code for the error ; Swapable Code ; move error code into the system error code byte ChkFer: MOVB BlkErr,@#ERRBYT ; check to see if we really have an error to report TSTB BlkErr+1 ; if no error to report BEQ 10$ ; then branch with carry clear else ; we have error, set carry to indicate it SEC ; return to caller with carry set if there was an error 10$: RETURN .SBTTL Purge input/output file of transfer ; ; This routine purges the input or output file open for the transfer. For ; output files this means that they are not made real files and thus do not ; exist. ; ; Input Registers: None ; ; Calling Sequence: CALL PurgRd or CALL PurgWr ; ; Output Registers: R0 destroyed ; ; Condition Codes: Carry is set if there was an I/O error encountered ; on the file with the system error byte ; containing the code for the error ; Swapable Code ; This routine can be used to purge an input or output file PurgRd: PurgWr: ; wait for any I/O in progress to complete CALL IoWt ; close out the channel .PURGE #File ; if got no error from the purge then go check for any previous errors in I/O BCC ChkFer ; return with carry set from error in purge RETURN .SBTTL Write word of data to file ; ; This routine stores a word into the output "Block" and if it is full ; start the "WrTsk" task writing the block to the file. If an error ; occured during a transfer by the "WrTsk" it will be recorded in "BlkErr" and ; this routine will return the error code gotten by that task. ; ; Input Registers: R0 contains word to write to output file ; ; Calling Sequence: CALL OutFil ; ; Output Registers: R0 destroyed ; ; Condition Codes: Carry bit is set if there was any errors in outputing ; a block to the output file, the system error ; byte will contain the error code. ; Swapable Code ; store the word being output into the output buffer "Block" OutFil: MOV R0,@BlkPnt ; store word and ADD #2,BlkPnt ; increment the buffer pointer ; check to see if the buffer is full CMP BlkPnt,#BlkEnd ; if buffer is not full then go check to see BLO ChkFer ; if was error in previous transfer else ; buffer full, resume execution of the file write task Resume WrTsk ; suspend execution of this task until "Block" is free to use 10$: Suspend ; suspend execution CMP BlkPnt,#Block ; if "Block" not yet free to use BNE 10$ ; then go suspend again else BR ChkFer ; go check for error in transfer .SBTTL Wait for RdTsk and WrTsk to not be doing I/O ; ; This routine suspends the message task until neither "RdTsk" nor "WrTsk" ; is doing I/O. ; ; Input Registers: None ; ; Calling Sequence: CALL IOwt ; ; Output Registers: Unchanged ; ; Condition Codes: None ; Swapable Code ; suspend task until I/O not in progress IoWt: TST IOflg ; if I/O not in progress BEQ 10$ ; then branch else Suspend ; suspend a while and BR IoWt ; then go check to see if I/O done ; I/O done, return to caller 10$: RETURN .SBTTL File transfer write-ahead task ; This task writes out a block of data while the message handler accumulates ; the next block of data from the Host. The block of data is expected to be in ; "Block". The task knows there is a block ready to be output by the fact that ; "BlkPnt" points to the end of "Block". The task moves the data into the ; "IOblk" and reset "BlkPnt" to the beginning of "Block" to indicate "Block" ; can be used for more data. .ENABL LSB Task WrTsk ; suspend task till we have block to write out 10$: Suspend ; check to see if we have block to write out 20$: CMP BlkPnt,#BlkEnd ; if no block to write out BNE 10$ ; then go suspend else ; copy block of data from "Block" into "IOblk" MOV #Block,R1 MOV #IOblk,R2 MOV #256.,R3 30$: MOV (R1)+,(R2)+ DEC R3 BNE 30$ ; tell message handler task that it can continue to operate MOV #Block,BlkPnt ; flag that "Block" is now empty and Resume MesHnd ; restart message handler ; check to see if previous transfer had an I/O error TSTB BlkErr+1 ; if have had an I/O error BNE 20$ ; then don't do transfer - branch else ; no error, set flag that we are doing the I/O transfer INC IOflg ; write the block of data to the file BIS #TskWt,TskFlg(R5) .WRITC TskArg(R5),#File,#IOblk,#256.,TskRsm(R5),BlkNum CALL IOerr ; check for error BCC 40$ ; if no error then branch else ; set flag that we had error and record the error code INCB BlkErr+1 ; set error flag and MOVB @#ERRBYT,BlkErr ; record error code ; clear flag that I/O is in progress and resume execution of the ; message handler 40$: CLR IOflg ; set I/O NOT in progress Resume MesHnd ; resume message task in case it was waiting ; increment block number so next time will write to next block INC BlkNum ; go back and check if we have another block to write out BR 20$ .DSABL LSB BlkErr: .WORD 0 ; If high byte of word is non-zero then an error has ; occurred while reading (by "RdTsk") or while writing ; (by "WrTsk") the file. The low byte contains the ; system error code returned if an error occurred. BlkNum: .WORD 0 ; holds number of next block to be read (by "RdTsk") ; or written (by "WrTsk") BlkPnt: .WORD Block ; This points to the next byte in "Block" to be stored ; into by the "OutFil" routine Block: .BLKW 256. ; this holds data being exchanged between the message ; handler task and the RT file system. BlkEnd = . IOblk: .BLKW 256. ; this is the buffer that both the read-ahead task ; "RdTsk" reads data into and the "WrTsk" writes data ; out of. IOflg: .WORD 0 ; non-zero says that either the read-ahead task ; "RdTsk" or write-after task "WrTsk" is still in the ; process of doing I/O. .SBTTL File transfer block read ; ; This routine gets a block of data read from the file transfer file into ; "Block". It uses the read-ahead task "RdTsk" to do the actual read into ; a separate buffer and then copies the data into "Block". ; ; Input Registers: None ; ; Calling Sequence: CALL RdFile ; ; Output Registers: Destroyed ; ; Condition Codes: Carry bit set if error during read, system error byte ; will contain error code from read ; Swapable Code ; check to see if RdTsk still in process of reading in a block of data RdFile: TST RdFlg ; if read is done then BEQ 10$ ; branch else Suspend ; suspend task and then BR RdFile ; go check again when woken up ; check to see if an error occurred duting read of file 10$: CALL ChkFer ; if error occured BCS 30$ ; then branch else ; read a block with no error, move the block of data into the message "Block" MOV #IOblk,R2 ; get address of input buffer and MOV #Block,R3 ; of message output buffer MOV #256.,R4 ; get that there are 256 words of data 20$: MOV (R2)+,(R3)+ ; move each word into output buffer DEC R4 BNE 20$ ; we have moved data out of input buffer, restart the reader task to read ; the next block INC RdFlg ; set flag to do read ahead Resume RdTsk ; and restart the reader task ; clear carry to indicate that read was a success CLC ; return to caller with carry indicating routine success/failure 30$: RETURN .SBTTL Read ahead task for file transfers ; This task reads in a block of data while the previous block is being written ; by the message task to the Host. It is activated by setting the "RdFlg" and ; resuming the task's execution. This is done by the message task whenever ; a transmission of a file is started by a message from the Host. .ENABL LSB Task RdTsk ; suspend execution of this task till we have a file to read 10$: Suspend ; check to see if a file read ahead is to be done 20$: TST RdFlg ; if no file read ahead to be done BEQ 10$ ; then go back and suspend the task else ; set flag that we are doing I/O for the file transfer INC IOflg ; read from the file BIS #TskWt,TskFlg(R5) .READC TskArg(R5),#File,#IOblk,#256.,TskRsm(R5),BlkNum CALL IOerr ; check for I/O error BCC 30$ ; if none then branch else ; flag that we had I/O error and record the error code INCB BlkErr+1 ; flag had I/O error MOVB @#ERRBYT,BlkErr ; record the error code ; set flag that we are no longer doing I/O and that block has been read 30$: CLR RdFlg ; clear read ahead flag and CLR IOflg ; I/O in progress flag and Resume MesHnd ; get the message handler back to running ; increment the block number to read next block when task activated again INC BlkNum ; loop back and wait for another activation, this will come after the block ; just read has been removed from the input buffer BR 20$ ; The RdTsk will only run when this flag is non-zero RdFlg: .WORD 0 .DSABL LSB .SBTTL System I/O call error Pre-processor ; ; This A call to this routine should follow all asynchronous calls to the ; operating system that have completion routines. The completion routine given ; in the I/O call has to be "TskRsm(R5)". This routine will cause the calling ; task to not run until the I/O is completed and will return with the error ; code (if any) in the system error byte. ; ; Input Registers: None ; If carry bit set then error in system I/O call ; ; Calling Sequence: CALL IOerr ; ; Output Registers: Unchanged ; ; Condition Codes: Carry bit set if there was an error in the I/O ; Swapable Code ; check to see if there was error in starting the I/O IOerr: BCS 10$ ; if error then branch else ; I/O was started correctly, let other tasks run while I/O completes CALL Pass ; I/O done, check if I/O status indicated device error or end of file BIT #020001,TskIOS(R5) ; if no error indicated BEQ 20$ ; then branch else ; error in I/O, record the type of error in the system error byte CLRB @#ERRBYT ; set error to be end of file BIT #020000,TskIOS(R5) ; if was an end of file BNE 10$ ; then branch else INCB @#ERRBYT ; set we had hard device error ; clear the task is waiting for I/O flag in case it is still set 10$: BIC #TskWt,TskFlg(R5) ; set carry to indicate that we had error in I/O and return to caller SEC RETURN ; clear carry to indicate that we had no error in I/O and return to caller 20$: CLC RETURN .SBTTL Output message with no Data to Host ; ; This routine outputs a message to the Host which consists only of the ; message code. No data is involved in the message. This is used for ACK/NAK ; messages. ; ; Input Registers: None ; ; Calling Sequence: CALL OutRep ; .WORD one or two chars of message code ; ; Output Registers: R0 destroyed ; R1 destroyed ; R2 destroyed ; R3 destroyed ; R4 destroyed ; ; Condition Codes: None ; Swapable Code ; pick up the message code to be sent to Host OutRep: MOV @(SP),R1 ; pick up message code in word after call and ADD #2,(SP) ; adjust return address to word after that ; indicate message contains no data CLR R3 ; output the message to the Host CALL OutMes ; return to caller, message has been sent RETURN .SBTTL Output message with data to Host ; ; This routine is the general message outputer. It is used for all messages ; to the Host during file transfers. It formats the message with the proper ; framing characters, puts out the message type code, the encoded data, and ; the message checksum. ; ; All messages are framed by the APC (Application Code) and ST (String ; Terminator) escape sequences. These escape seuences are: ; ; APC = Escape + "_" ; start of message ; ST = Escape + "\" ; end of message ; ; The start of message is followed by a one or two character message code. ; The possible messages that can be sent by VTCOM are: ; ; Code "0" = data message zero ; "1" = data message one ; "zy" = tells Host that end of file reached on file being sent ; "zx" = tells Host that file transfer is aborted by VTCOM ; "y0" = recieved data message zero from Host correctly ; "x0" = recieved data message zero from Host incorrectly ; "y1" = recieved data message one from Host correctly ; "x1" = recieved data message one from Host incorrectly ; "ys" = recieved request to send file to Host correctly ; "xs" = recieved request to send file to Host incorrectly ; "yr" = recieved request to accept file from Host corretly ; "xr" = recieved request to accept file from Host incorretly ; "yz" = received end of file message correctly from Host ; ; The message data is encodes as three bytes for each word sent. The bytes ; contain the top 4 bits, next 6-bits and low 6-bits of a data word. All are ; converted into printable characters in the range 40 (octal) to 137 (octal) ; by having 40 (octal) added to them before transmission. ; ; The checksum is generated by adding the message code plus all the data words ; together. The checksum is the negation of that addition. ; ; Input Registers: R1 contains message type code that starts message ; R2 points to block of data to be encoded ; R3 contains number of words of data ; ; Calling Sequence: CALL OutMes ; ; Output Registers: R0 destroyed ; R1 destroyed ; R2 destroyed ; R3 destroyed ; R4 destroyed ; ; Condition Codes: None ; ; set the output bumper on and set it to wake up in 5 seconds OutMes: INC BumpFl SetTmo BmpTmo,Seconds=5 ; transmit message start framing characters to the Host MOV #Escape+<'_*400>,R0 CALL QModm ; transmit the message code to the Host MOV R1,R0 ; save code in R1 as start of checksum CALL QModm ; output code in R0 ; check to see if message includes any data TST R3 ; if message does not include any data BEQ 40$ ; then branch else ; pick up next word of message to be encoded/sent to Host 10$: MOV (R2)+,R0 ; check to see if one char encoding of word is possible TST SquFlg ; If no one char encoding has been established BEQ 30$ ; then go encode word else ; see if there is one char encoding of this word, save work registers on stack MOV R1,-(SP) MOV R2,-(SP) ; loop to look through one char encoding table for word MOV #SquTab,R1 ; get address of encoding table and MOV SquFlg,R2 ; the number of entries in the table 20$: CMP R0,(R1)+ ; check to see if word equals table entry BEQ 60$ ; if it does then branch else DEC R2 ; dec count of number of entries left to check BNE 20$ ; if not all looked at then loop else ; no one byte encoding of word, restore work registers and go do long encoding MOV (SP)+,R2 MOV (SP)+,R1 ; add word being sent to check sum, and encode/send the data word to Host 30$: ADD R0,R1 ; add word to checksum CALL 70$ ; go encode/send it to Host ; check if message send has been aborted from VTCOM command mode 35$: TST MesAbt ; if message send aborted BNE 50$ ; then branch else ; decrement count of number of words in message and check for all done DEC R3 ; if data not all sent BNE 10$ ; then loop to send another word else ; message encoded and sent, encode and send the checksum to the Host 40$: NEG R1 ; negate checksum and MOV R1,R0 CALL 70$ ; go encode and send it to the Host ; put out the end of message escape sequence to Host 50$: MOV #Escape+<'\*400>,R0 CALL QModm ; transmit the queued characters to the host CALL SndMdm ; transmit queued characters to the host INC PACSEN ;Bump count of packets sent ; shut off message bumper task CLR BumpFl ; return to caller RETURN ; word can be encoded as single char, encode it and send it 60$: SUB #SquTab+2,R1 ;Determine the table offset ASR R1 ;Convert from byte to word offset ADD #140,R1 ;And flag it as a compression byte MOV R1,R0 ; save it MOV (SP)+,R2 ; restore work registers MOV (SP)+,R1 ADD R0,R1 ; add encode char into checksum CALL QModm ; send encode char to Host BR 35$ ; and go see if more to send ; Encode/send word of data to Host, store word to encode on top of stack 70$: MOV R0,-(SP) ; load encoding mask for loop MOV #010101,R4 ; check to see if all of word has been encoded 80$: TST R4 ; if word encoded and sent BEQ 100$ ; then branch else ; zero byte to contain encoded part of word being sent CLR R0 ; rotate bit of word into byte 90$: ROL (SP) ROL R0 ; check encodeing mask to see if byte has been fully encoded ASL R4 ; if not fully encoded BCC 90$ ; then branch else ; convert encoded byte into printing char and sent it to the Host ADD #40,R0 ; convert to printing char CALL QModm ; send it to Host BR 80$ ; and go back to encode rest of word ; word encoded, pop word off of stack and return to caller 100$: TST (SP)+ RETURN .SBTTL Message Output Bumper task .ENABL LSB Task WrBump ; check to see if Bumper task has been turned on and that timeout has happened 10$: TST BumpFl ; if Bumper task not on then BEQ 20$ ; go wait till turned on else TST BmpTmo ; if bumper task on and timeout BEQ 30$ ; occured then branch else ; Bumber task not on or not yet at timeout so suspend task till happens 20$: Suspend ; wait for event BR 10$ ; then go check again ; first timeout set by "OutMes" routine occured, set a second timeout 30$: SetTmo BmpTmo,Seconds=10. ; check to see if VTCOM has been XOFFed by the Host BIT #2,Status ; if VTCOM has not been XOFFed BEQ 10$ ; then go back and wait for timeout else ; inform XL driver that XOFF is to be cleared out, it was probably a mistake .SPFUN TskArg(R5),#ModemW,#ClrDrv,#0,#0,#1 BR 10$ ; then go back and wait for timeout ; declare timeout that is to wake up the message bumper task DclTmo BmpTmo,WrBump ; when this flag is non-zero, "OutMes" routine is in use so Bumper task ; should run to keep erroneous XOFFs from stopping transmission BumpFl: .WORD 0 .DSABL LSB .SBTTL Output char to Host routine ; ; This routine write chars to the Host. ; ; Input Registers: R0 contains one or two chars being output ; ; Calling Sequence: CALL WrModm ; ; Output Registers: R0 destroyed ; ; Condition Codes: None ; Swapable Code ; store char to output into task char buffer WrModm: MOV R0,@TskBuf(R5) ; do I/O to start char out and wait for I/O to be completed BIS #TskWt,TskFlg(R5) .WRITC TskArg(R5),#Modem,TskBuf(R5),#1,TskRsm(R5),#1 CALL Pass ; I/O done, return to caller RETURN .SBTTL Queue character to send to host ; ; This routine is similar to WrModm in that it is used to send a character ; to the host. The difference is that WrModm sends the character immediately ; whereas this routine places the character into a buffer and does not do the ; actual transmission until the buffer fills up or SndMdm is called. ; ; Input Registers: R0 contains one or two characters being output ; ; Calling Sequence: CALL QModm ; ; Output registers: R0 destroyed ; ; Condition Codes: None ; Swapable Code QModm: MOV R1,-(SP) ; ; If the output buffer is full, send it to the host ; MOV HostPt,R1 ; get pointer into host output buffer CMP R1,#HostX-2. ; is there room for 2 more chars in buffer? BLOS 1$ ; branch if yes CALL SndMdm ; send the output buffer to the host MOV #HostBf,R1 ; get pointer to front of output buffer ; ; Store the character(s) into the output buffer ; 1$: MOVB R0,(R1)+ ; store 1st char into output buffer BNE 2$ ; branch if character was not null DEC R1 ; backup pointer if char was a null 2$: SWAB R0 ; right-align 2nd character MOVB R0,(R1)+ ; store 2nd character into output buffer BNE 3$ ; branch if char was not a null DEC R1 ; backup pointer if char was a null ; ; Save new output buffer pointer ; 3$: MOV R1,HostPt ; store new output buffer pointer ; ; Finished ; MOV (SP)+,R1 RETURN .SBTTL Send queued characters to host ; ; This routine sends to the host the characters stored into the output ; buffer by calls to QModm. ; ; Input Registers: None ; ; Calling sequence: CALL SndMdm ; ; Output registers: All registers are preserved ; ; Condition codes: None ; SndMdm: MOV R0,-(SP) MOV R1,-(SP) ; ; Determine how many characters are queued to be sent to host ; MOV HostPt,R1 ; get pointer into host output buffer BIT #1,R1 ; have we filled out to a word boundary? BEQ 1$ ; br if yes CLRB (R1)+ ; null fill to a word boundary 1$: SUB #HostBf,R1 ; get count of # chars in buffer BEQ 9$ ; br if buffer is empty ASR R1 ; convert char count to a word count ; ; Write the output buffer to the host ; BIS #TskWt,TskFlg(R5) .WRITC TskArg(R5),#Modem,#HostBf,R1,TskRsm(R5),#1 CALL Pass ; ; Finished ; 9$: MOV #HostBf,HostPt ; reset output buffer pointer MOV (SP)+,R1 MOV (SP)+,R0 RETURN .SBTTL Print system error message on TTY routine ; ; This routine outputs an RT system error message. It expects the error code ; to be in the RT error code byte on entry. The call to this routine includes ; providing the address of a table that points to the text of the error ; message for each error code. The table starts with a pointer to text for ; code zero and so forth. Text for negative codes is common and is defined in ; this routine. ; ; Input Registers: None ; ; Calling Sequence: CALL PrtErr ; .WORD address of error text pointer table ; ; Output Registers: Unchanged ; ; Condition Codes: None ; Swapable Code ; save registers needed as work registers PrtErr: MOV R0,-(SP) MOV R1,-(SP) ; get address of error message table specified after call to this routine MOV @4(SP),R1 ; get word after call and ;++ JLB ADD #2,4(SP) ; inc return addr past word after call ;++ JLB ; get last error code and check if it is common error code MOVB @#ERRBYT,R0 ; if the error code is specific to a system BPL 10$ ; call then branch else ; this is common system call error code, convert it into one we can decode COM R0 ; make error code positive ;+ ;ERROR MOV #HrdErr,R1 ; and get common error code table addr ; get address of error text by adding eror code to error table and picking ; up the address of the error text from there ;- 10$: ASL R0 ; code * 2 so got displacement into table ADD R0,R1 ; and add it to table start addr MOV (R1),R1 ; pick up error text addr from error table BNE 20$ ; if got it then branch else ;+ ;ERROR MOV #ErrImp,R1 ; assume this is imppossible error ; now print the error message ;- 20$: MOV R1,30$ ; store pointer to error message JSR R0,PrtMes ; and ask for it to be printed 30$: .WORD 0 ; restore work registers and return to caller MOV (SP)+,R1 ; restore work register MOV (SP)+,R0 ; restore work register SEC ; and set carry to indicate error message sent RETURN ; and return to caller ;+ ;ERROR ; common system error code table ; ; Code = -1 -2 -3 -4 -5 -6 -7 -10 ; | | | | | | | | ; v v v v v v v v HrdErr: .WORD ErrImp, ErrNhd, ErrDIO, ErrImp, ErrImp, ErrDfl, ErrImp, ErrImp ; ; -11 -12 -13 -14 -15 -16 -17 -20 ; | | | | | | | | ; v v v v v v v v .WORD ERRIMP, ERRIMP, ERRIMP, ERRIDR, ERRIMP, ERRIMP, ERRIMP, ERRIMP ; ; -21 -22 ; | | ; v v .WORD ERRIMP, ERRIMP ; common system error code texts ErrImp: ERRTXT F,INT, ErrNhd: ERRTXT F,HNL, ErrDIO: ERRTXT F,DIO, ErrDfl: ERRTXT F,DFL, ErrFN: ERRTXT F,IFN, ErrIdv: ERRTXT F,IDV, ErrNrp: ERRTXT F,FAE, ErrFnf: ERRTXT F,FNF, ErrIsr: ERRTXT F,EOF, ErrHrd: ERRTXT F,IOE, ErrIdr: ERRTXT F,IDR, ;- .EVEN .SBTTL Print error message on TTY ;+ ; ; PRTMES ; Prints an error message of the form '?facility-severity-text' ; ; Calling sequence: ; ERROR. message_descriptor_address ; or ; JSR R0,PRTMES ; .WORD message_descriptor_address ; ; where 'message_descriptor_address' is the address of a message ; descriptor which consists of the following: ; a byte which contains the mask for the error reporting ; byte, a byte which is the severity character, followed ; by the text of the message ; ; Return: ; c-bit = 1 ; ;- Swapable Code PRTMES: MOV R1,-(SP) ;Save R1 for awhile MOV (R0)+,R1 ;R1->message descriptor BISB (R1)+,@#USERRB ;Set the user error byte MOVB (R1)+,ERRSEV ;Now set the severity indicator CALL GRBTTY ;Get control of the terminal NEWLIN ;Prefix with a new line PRINT ERRFAC ; '?facility-severity-' PRINT ; 'text' CALL RLSTTY ;Release the terminal MOV (SP)+,R1 ;Restore R1 SEC ;Return carry set to caller to RTS R0 ; indicate message was printed .NLIST BEX ERRFAC: .ASCII /?VTCOM-/ ERRSEV: .ASCII /x-/<200> .LIST BEX .EVEN .SBTTL Get char from TTY keyboard ; ; This routine input a char from the users TTY ; ; Input Registers: None ; ; Calling Sequence: CALL TTYin ; ; Output Registers: R0 contains char input from TTY ; ; Condition Codes: None ; Swapable Code ; Attempt to input char from the users TTY TTYin: .TTINR ; try to get char from user BCS 10$ ; if none typed then branch else ; got char from TTY, strip it down to 7 bits BIC #^C177,R0 ; strip char down to 7-bits RETURN ; and return it to caller ; No char typed yet, wait a while and then try to input char again 10$: Wait Seconds=1/10. BR TTYin .SBTTL Print text string on TTY ; ; print text string on TTY. Text string must conform to that used in RT .Print ; system call. ; ; Input Registers: None ; ; Calling Sequence: Print Text or CALL R0,Print ; .WORD Text ; ; Text is address of start of text string ; ; Output Registers: Unchanged ; ; Condition Codes: None ; Print: MOV R1,-(SP) ; save R1 since it will be used MOV (R0)+,R1 ; pick up the address of text string Print ; print message pointed to by R1 MOV (SP)+,R1 ; restore R1 RTS R0 ; return to caller .SBTTL Print text string on TTY ; ; print text string on the TTY. ; ; Input Registers: R1 points to text to be output. The text must conform ; to the rules for text in the RT .PRINT call. ; ; Calling Sequence: Print or CALL TTYprt ; ; Output Registers: Unchanged ; ; Condition Codes: None ; ; grab control of TTY during the printing of the text TTYprt: CALL GrbTTY ; save register zero so it can be used as a tempory MOV R0,-(SP) ; loop to output the entire text string on the TTY 10$: MOVB (R1)+,R0 ; get next char of text string BMI 30$ ; if negative, string done else BEQ 20$ ; if zero, do new line and done else CALL TTYout ; output the char and BR 10$ ; go get another char ; string ended with a zero, put a carriage return/line feed at end of line 20$: NewLin ; string ended with negative char, just exit print routine 30$: MOV (SP)+,R0 ; restore R0 ; release control of the TTY and return to caller CALL RlsTTY RETURN .SBTTL Output a new line to the TTY ; ; Output a new line to the TTY ; ; Input Registers: None ; ; Calling Sequence: NewLin or CALL NL ; ; Output Registers: Unchanged ; ; Condition Codes: None ; ; grab control of the TTY NL: CALL GrbTTY ; we control the TTY, save R0 so we can use it as a word register MOV R0,-(SP) ; write carriage return/line feed to the TTY MOV #CarRet,R0 CALL TTYout MOV #LinFed,R0 CALL TTYout ; restore the contents of the work register MOV (SP)+,R0 ; release control of the TTY and return to the caller CALL RlsTTY RETURN .SBTTL Output char to TTY and optionally log char ; ; Output char to TTY and optionally log it in log file. ; ; Input Registers: R0 contains char to be output to TTY ; ; Calling Sequence: CALL TTYout ; ; Output Registers: Unchanged ; ; Condition Codes: None ; ; get exclusive control of the TTY TTYout: CALL GrbTTY ; attempt to output char to the TTY 10$: .TTOUTR ; attempt to output char to terminal BCS 20$ ; if fails then branch else ; attempt to output char to the log file CALL LogChr ; character has been output, release TTY and return to caller CALL RlsTTY RETURN ; output to terminal failed, wait a while then go try output again 20$: Wait Seconds=1/20. BR 10$ .SBTTL Grab control of the TTY printer Swapable Code ; ; This routine must be called in order for a task to gain exclusive control ; of the TTY printer. When a return is made form this routine, the task ; makeing the call is assured that no other task is outputing to the terminal ; until it calls the RlsTTY routine. If the same task calls this routine ; multiple times, it must call the RlsTTY routine the same number of times ; to actually release control of the TTY. All of the terminal output routines ; call this routine before starting there outout and call "RlsTTY" when they ; are done. It need only be called by a task when it needs the TTY for an ; exteneded period of time or doesn't want other text interspersed with what ; it is outputing. ; ; Input Registers: None ; ; Calling Sequence: CALL GrbTTY ; ; Output Registers: Unchanged ; ; Condition Codes: None ; ; check to see if the TTY is already in use GrbTTY: TST TTYstp ; if no other task has control of the BEQ 10$ ; TTY then branch else CMP R5,TTYtsk ; Check if current task is in control BEQ 10$ ; if is then branch else ; TTY is owned by another task, we must wait for TTY to become free. INC TTYwct ; count this task as waiter for TTY Suspend ; Suspend task execution until DEC TTYwct ; get woken up and then go BR GrbTTY ; check if TTY is free ; TTY free, flag that it is no longer free and return to caller 10$: MOV R5,TTYtsk ; record current task as TTY owner INC TTYstp ; and count # times task grabed it RETURN ; and return to caller .SBTTL Release control of the TTY printer ; ; this routine releases control of the TTY printer. This routine should only ; be called after the "GrbTTY" routine was called. The TTY is only released ; once the "RlsTTY" routine has been called the same number of times as the ; "GrbTTY" routine. ; ; Input Registers: None ; ; Calling Sequence: CALL RlsTTY ; ; Output Registers: Unchanged ; ; Condition Codes: None ; ; Free TTY for use by other tasks RlsTTY: DEC TTYstp ; count down TTY grabs by task, if BNE 10$ ; more RlsTTYs needed then branch else ; TTY is now free, check if anyone was waiting to use the TTY TST TTYwct ; if no one was waiting for TTY BEQ 10$ ; then branch else ; Some other task was waiting to run, make sure it has a chance to grab TTY Resume ; resume all tasks so waiter resumed CALL TimSlc ; let all tasks run so waiter can grab ; return to caller 10$: RETURN ; return to caller TTYtsk: .WORD 0 ; Address of task that owns the TTY TTYstp: .WORD 0 ; TTY in use flag. Zero indicates TTY free, non-zer ; tells number of time TTYtsk task has GrbTTY grabbed ; control of the TTY without RlsTTY releasing it. TTYwct: .WORD 0 ; Holds count of the number tasks waiting to use TTY .SBTTL Task Wait a Specified Time ; ; this routine causes a task to wait for a certain amount of time before ; resuming execution. ; ; Input Registers: R5 points to task control block of current task ; ; Calling Sequence: Wait Seconds="n" ; or ; Wait Minutes="n" ; or ; JSR R1,Wait ; .WORD number of ticks to wait ; ; Output Registers: Unchanged ; ; Condition Codes: None ; Swapable Code ; This routine is called by the Wait macro and is used to stop the execution ; of a task for a specified amount of time. ; Save R0 since it must be destoryed in making the system call Wait: MOV R0,-(SP) ; Set the flag that says don't run this task till system call done BIS #TskWt,TskFlg(R5) ; Make the system call, indicate a completion routine that will cause the ; flag just set to be cleared and the task will be run .MRKT TskArg(R5),R1,TskRsm(R5),#0 ; call scheduler, this will end this tasks execution until completion ; routine runs and clears the I/O waiting flag in the task control block CALL Pass ; we have waited the requested time, restore regs and exit CMP (R1)+,(R1)+ ; bump past wait time that followed call MOV (SP)+,R0 ; restore R0 RTS R1 ; return to caller .SBTTL Close out access to all PSECTs ; place a null entry at the end of the timeout count list so the program ; will know when it reaches the end .PSECT TmoCnt .WORD 0,0 TmoEnd: ; check to see if there is sufficient space to swap the USR in, if not then ; give an assembly error to flag that this is not enough space .PSECT UsrSwp .IF LE TmoEnd-TmoLst+.-SwArea-4096. .ERROR TmoEnd-TmoLst+.-SwArea ;Swapable Code too small .ENDC .END START