.TITLE KERXK - XK driver routines .SBTTL R McQueen/N Bush/D Stevens/S Hecht ; Version number .IDENT /1.0.04/ ; Directives .LIBRARY /KERMLB/ ; PRO/Kermit macro library .SBTTL Revision History ;++ ; 1.0.00 By: Robert C. McQueen On: 1-December-1983 ; Create this module from other modules ; ; 1.0.01 By: Robert C. McQueen On: 16-Feb-1984 ; Use the correct timeout value. ; ; 1.0.02 By: Robert C. McQueen On: 17-Feb-1984 ; Add an alternate entry point to the initialization. ; One to allow setting the routine and buffer size and ; the other to default to assembly limit and no routine. ; ; 1.0.03 By: Robert C. McQueen On: 9-March-1984 ; Make $PLIT$ come out even and not odd. ; ; 1.0.04 By: Robert C. McQueen On: 26-March-1984 ; Finish edit 1.0.01 ;-- .SBTTL .MCALLs for RSX directives ; The following are the various MCALLs that KERXK uses. .MCALL ASTX$S ; AST exiting command .MCALL QIOW$S ; QIO and wait .MCALL QIOW$C ; QIO and wait .MCALL QIO$S ; QIO and no wait .MCALL MRKT$S ; Set a mark time request .MCALL CMKT$S ; Cancel mark time requests .MCALL SETF$S ; Set event flags .MCALL WTSE$S ; Wait for an event .MCALL WTLO$S ; Wait for logical-or of event flags ; The following causes the KERMIT definitions to be read and defined .MCALL KERDEF ; Get the KERMIT definitions KERDEF ; Define all of the KERMIT symbols .MCALL IOSBDF ; IOSB definitions IOSBDF ; Define the symbols .MCALL BLSRTN ; Macro to define entry point .MCALL PJMP ; Jump and return macro .MCALL ND ; If not defined macro .MCALL BIT. ; Bit mask definition macro ; The following are local parameters for KERXK ND NM.XCH, 64. ; Allow 64 characters in buffer ND XKBUFLEN, 512. ; Length of XK buffer (must be power of two) .GLOBL XKBUFLEN .SBTTL Data for the module .PSECT $OWN$, RW, D ; Make sure this goes in data space XKSTAT: .BLKB 4 ; IO status block XKBUFF::.BLKB XKBUFLEN ; Circular xk-queue. .EVEN XKFREE::.BLKW 1 ; Counter of free buffer space. XKUSED::.BLKW 1 ; Counter of used buffer space. XKNXTC::.BLKW 1 ; Pointer to first valid character. XKFREC::.BLKW 1 ; Pointer to first free character. QUEFLG: .BLKW 1 ; Flag to indicate I/O queue pending. ERRFLG: .BLKB 1 ; Flag to store an error in. .EVEN DMPRTN: .BLKW 1 ; Routine to dump XK input ; ; XK output data ; XKOBUF: .BLKB NM.XCH ; Number of XK characters allowed XKONCH::.BLKW 1 ; Number of characters in XKOBUF XKOIDX: .BLKW 1 ; Index to store characters into XOIOSB: .BLKB IB.MSZ ; IOSB for QIO .SBTTL XK.INI - Initialize the XK driver ;++ ; This routine will initialize the XK port. It will read the initial ; parameters from the XK port and store them into an internal block. It ; will then set the XK port with the parameters from the [ZZSYS]KERMITXK.SYS ; file. ; ; Usage: ; ; Macro: ; R0/ Number of characters to buffer from XK port ; (Needed for slow speeds during terminal emulation) ; R1/ Adress of routine to call from XK.AST ; JSR PC,XK.INI ; (Return - R0 contains the error status if carry set, else good return) ; ; Bliss: ; Status = XK_INI(); ; ;-- .PSECT $CODE$, RO, I XK.INT:: CLR DMPRTN ; No dump routine MOV #XKBUFLN-1,XKFREE ; Initialize the number free BR XKINIT ; Join the common code XK.INI:: MOV R1,DMPRTN ; Store the dump routine ; ; Determine the number of characters that we should be buffering ; CMP #XKBUFLN-1,R0 ; Small enough? BGT 4$ ; No, use the default TST R0 ; Have to use the default? BNE 5$ ; No, use the given 4$: MOV #XKBUFLN-1,R0 ; Yes, use the default 5$: MOV R0,XKFREE ; Store the amount we can buffer XKINIT: ; ; First we will attach to the XK so that no one else can be screwing around ;with it behind our backs. ; 7$: QIO$S #IO.ATT,#XKLUN,#XKREFN,,#XKSTAT ; Grab the device MOVB XKSTAT,R0 ; Get the status BLE 90$ ; Branch if so ; Now read the current parameters. These will be saved and restored when ;we exit. MOV #ORGXKP,R0 ; Point at parameter list MOV #CURXKL,R1 ; And get the length JSR PC,XK.RD ; Read the parameters ; Here to set the desired parameters. 30$: MOV #CURXKP,R0 ; Point at the parameter block MOV #CURXKL,R1 ; And the length JSR PC,XK.WT ; Read the info CLR XKFREC ; Init. XKFREC index to zero. CLR XKUSED ; Set the amount of used space to 0. CLR XKNXTC ; Init. XKNXTC index to zero. MOV #NM.XCH,XKONCH ; Store the max number we can output ;[02] MOV #XKBUFLEN-1,XKFREE ; Set the amount of free space to max MOV #-1,QUEFLG ; No QIO pending yet CLRB ERRFLG ; And no errors ; All done CLC ; Clear carry RTS PC ; Return to our caller ; ; Here if we failed to get the XK ; 90$: QIOW$C IO.KIL,XKLUN,XKREFN,,,,,$CODE$ ; Grab the device SEC ; Set the carry bit RTS PC ; Return to the caller .SBTTL XK.SHT - Shutdown the XK port ;++ ; This routine will shut down the XK port. It will restore the parameters ; from the orginal XK parameters. It will then return to the caller. ; ; Usage: ; ; Macro: ; JSR PC,XK.SHT ; (Return) ; ; Bliss: ; ; Status = XK_SHT (); ;-- ; .PSECT $CODE$, RO, I XK.SHT::MOV #ORGXKP,R0 ; Point at orignal parameters MOV #CURXKL,R1 ; And the length of the block JSR PC,XK.WT ; Set them QIOW$C IO.KIL,XKLUN,XKWEFN,,,,,$CODE$ ; Kill pending I/O queues. QIOW$C IO.DET,XKLUN,XKWEFN,,,,,$CODE$ ; Detach the device RTS PC ; And return .SBTTL XK.RD - Read XK parameters into parameter block ;++ ; This routine will read XK parameters into the given parameter block. ; It will use the QIO to read the XK parameters and then move them into ; the internal KERMIT parameter block. ; ; Usage: ; ; Macro: ; MOV #Parameter.block,R0 ; MOV #Parameter.block.length,R1 ; (in bytes) ; JSR PC,XK.RD ; (Return - R0 contains the status) ; ; Note that the length must be an even number of bytes ;-- .PSECT $CODE$, RO, I ; We assume that when we are called, the parameter block is in a totally ;random state. We will set to read as many of the interesting parameters ;as we can fit into the block supplied. This block should normally be ;long enough, but might not be if there is a version skew between two modules. XK.RD:: JSR R1,$SAVE4 ; Save some registers ; If the block is large than our maximum, only use what we know about CMP #PTPLEN*2,R1 ; Have enough items to fill the block? BGE 5$ ; Yes, just enter loop MOV #PTPLEN*2,R1 ; No, can only move what we have 5$: MOV R0,R2 ; Get a copy of the address MOV R1,R3 ; And the length ; Now insert the parameter types into the block MOV #PARTYP,R4 ; Point at list of interesting parameters ASR R1 ; Make the length be words 10$: MOVB (R4)+,(R0)+ ; Store the parameter type CLRB (R0)+ ; Clear its data byte SOB R1,10$ ; Loop for all that we have room for ; Now just do the QIO to read the parameters QIOW$S #SF.GMC,#XKLUN,#XKREFN,,,, ; Get the information MOV @#$DSW,R0 ; Get the result code RTS PC ; And return ; List of parameters we should read. Most important parameters first. .PSECT $PLIT$, RO, D ; Make sure this goes in data space PARTYP: .BYTE TC.RSP ; Receive speed .BYTE TC.XSP ; Transmit speed .BYTE TC.FSZ ; Frame size .BYTE TC.PAR ; Parity enable/disable .BYTE TC.EPA ; Odd or even parity (if enabled) .BYTE TC.STB ; Number of stop bits .BYTE TC.8BC ; pass 8 data bits per character .BYTE TC.BIN ; XON/XOFF enable/disable .BYTE XT.MTP ; Modem type .BYTE TC.ARC ; Auto-answer ring count .BYTE TC.XMM ; Enable/disable maintenance PTPLEN= .-PARTYP ; Number of parameters we handle .EVEN ; Make this go to the next word .SBTTL XK.WT - Write XK parameters from a parameter block ;++ ; This routine will write the XK parameters from a given parameter block. ; This routine will move the internal parameters into the QIO block for the ; SMC QIO call. ; ; Usage: ; ; Macro: ; MOV #Parameter.block,R0 ; MOV #Parameter.block.length,R1 ; (in bytes) ; JSR PC,XK.WT ; ;-- .PSECT $CODE$, RO, I XK.WT:: CMP #PTPLEN*2,R1 ; Block larger than we know about? BGE 10$ ; No, leave it alone MOV #PTPLEN*2,R1 ; Yes, only use the portion which should be ok 10$: QIOW$S #SF.SMC,#XKLUN,#XKWEFN,,,, ; Set the new parameters RTS PC ; And return .SBTTL XK.BRK - Send a break character to the XK port ;++ ; This routine will set a break character to the XK port. It is used ; by the terminal emulation processing to handle the BREAK key. ; ; Usage: ; JSR PC,XK.BRK ; (Return) ; ;-- XK.BRK:: ; ; First kill any pending I/O ; QIOW$C IO.KIL,XKLUN,XKREFN,,,,,$CODE$ ; Kill the I/O MOV #-1,QUEFLG ; No QIO pending ; ; Now send the break character ; QIOW$C IO.BRK,XKLUN,XKAEFN,,,,0,$CODE$ RTS PC ; Return to the caller .SBTTL XK.INP - Input a character from the XK port ;++ ; This routine will input a single character from the XK port. It is used ; by RECEIVE to fill a packet buffer, and by SEND to clear the XK input ; buffer. ; ; Usage: ; ; Macro: ; MOV #Buffer.address,R1 ; MOV #Time.to.wait,R2 ; Time in seconds, 0 = don't wait ; JSR PC,XK.INP ; ; On a good return (carry = 0): ; ; R0/ Number of characters read (0 or 1) ; R1 and R2 unchanged ; ; On a false return (carry = 1): ; ; R0/ IE.xxx error code ; ;-- .PSECT $CODE$, RO, I XK.INP::JSR R1,$SAVE3 ; Save R1 to R3 ; ; Before we do anything we check for an error in ERRFLG. ; TSTB ERRFLG ; Any errors? BNE 40$ ; Yes, handle them ; ; No error. Do we have any characters in the buffer ? ; TST XKUSED ; Is the buffer empty ? BEQ 20$ ; Yes, branch. ; ; Here if we have characters ; 10$: MOV XKNXTC,R3 ; Put index into R3 ADD #XKBUFF,R3 ; . . . MOVB (R3),@R1 ; Return the char. pointed at. INC XKNXTC ; Increment index to the next char. BIC #^C,XKNXTC ; Clear the high order bits INC XKFREE ; Increment the free space count. DEC XKUSED ; Decrement the used space count. JSR PC,XK.QIO ; Set up xk-que if necessary. MOV #1,R0 ; Set up return in R0. BR 30$ ; Branch. ; ; Que an I/O read if necessary then see if we need to time out. ; 20$: JSR PC,XK.QIO ; Set up xk-que if necessary. TST R2 ; Do we have no time-out ? BEQ 25$ ; Yes, branch. ; ; We need to time out, wait for GENEFN, or XKREFN flags to be set. ; BIT. GENMSK,GENEFN ; Define mask bit for GENEFN BIT. CONMSK,CONEFN ; Define mask bit for XKREFN WTLO$S 0,#GENMSK!CONMSK ; Wait for one of the 2 EFNs to be set. ; ; We either timed out or we got some characters. ; 25$: TST XKUSED ; Is the buffer empty ? BNE 10$ ; No, branch. CLR R0 ; Set up "no char" return in R0. ; ; Exit routine provided we did not branch at first tst. ; 30$: CLC ; Clear carry.( no error ) RTS PC ; Return to caller. ; ; Exit routine for an error being detected. ; 40$: MOVB ERRFLG,R0 ; Move error code in R0. SEC ; Set carry.( error ) RTS PC ; Return to caller. .SBTTL XK.CIB - Clear input buffer ;++ ; This routine will clear the XK's input buffer. This is used to dump any ;accumulated garbage before we send any data. ; ; Usage: ; JSR XK.CIB ; Clear the input buffer ; (return here always) ; ;-- .PSECT $CODE$, RO, I XK.CIB:: INC QUEFLG ; Is the queue flag clear ? BNE CIB.0 ; No, branch QIOW$C SF.SMC,XKLUN,XKREFN,,,,,$CODE$ ; Just do the function MOV #-1,QUEFLG ; Flag no QIO pending CIB.0: CLR XKUSED ; Reset used space count to zero. CLR XKFREC ; Reset index to zero. CLR XKNXTC ; Reset index to zero. MOV #XKBUFLEN-1,XKFREE ; Reset free space count to the RTS PC ; And return .PSECT $PLIT$, RO, D CIBBLK: .BYTE TC.TBF,0 ; Clear buffer function CIBLEN= .-CIBBLK ; Define the length .SBTTL XK.TIM - Set up timeout ;++ ; This routine will post the request for a timeout. It will ask that GENEFN ; be set when the timeout period has expired. If there is no timeout, it ; will not request the EFN. ; ; Usage: ; JSR PC,XK.TIM ; (return here always) ; ; On return: ; R0/ low order 0, high order number of seconds to wait on QIO's ; R1-R5 preserved ; ;-- .PSECT $CODE$, RO, I XK.TIM::MOVB SEND.TIMEOUT,R0 ; Get the receive timeout BNE 10$ ; Branch if time out MOV #377,R0 ; Otherwise use maximum wait for QIO 10$: SWAB R0 ; So that we have seconds in high order BIC #377,R0 ; Make sure 10 sec. portion is clear TST SEND.TIMEOUT ; Check if any timeout at all BEQ 99$ ; No, just go ahead and wait forever CMKT$S #GENEFN ; Make sure any old timeout is cleared MRKT$S #GENEFN,SEND.TIMEOUT,#2. ; Macro to wait (send.timeout) seconds. ; This uses the general EFN. 99$: RTS PC ; Return, R0 already set up .SBTTL XK.QIO - Check for Qio read and issue one if none ;++ ; This routine queues up a single character read from the comm-port. ; The characters read are stored in xkbuff starting at the offset ; in XKFREC. After the queue the flag is set to indicate that ; we have set up the queue. ; ; Usage: ; ; Macro: ; JSR PC,XK.QIO ( test to make sure the flag is not set) ; ; ; REGISTER USAGE: ; R0 => Offset into the buffer. ; (smashed) ; ;-- .PSECT $CODE$, RO, I XK.QIO::INC QUEFLG ; Is the queue flag clear ? BNE 99$ ; No, branch TST XKFREE ; Make sure we have at least one BLE 100$ ; Position free MOV XKFREC,R0 ; Store the offset in R0. ADD #XKBUFF,R0 ; Add offset to buffer address. CLR QUEFLG ; Flag QIO outstanding now QIO$S #IO.RAL,#XKLUN,#XKREFN,,#XKSTAT,#XK.AST, 99$: RTS PC ; All done, return to caller. 100$: MOV #-1,QUEFLG ; Nothing free, reset the flag RTS PC ; Return to the caller .SBTTL Support XK.INP -- AST handling routine -- XK.AST ;++ ; This is the AST routine to handle the reception of characters from ; the communications port. ; ; Usage: ; Called when I/O from comm port reads chars. ; ( Returns to where ever it was when called) ; ; Register usage: ; R0, R1, R2, R3 => Temporary locations for computations. ; (No registers get smashed ) ; ;-- .PSECT $CODE$, RO, I XK.AST::MOV R0,-(SP) ; Save R0. JSR PC,5$ ; Call ourself so we can use standard ; save routine. TST DMPRTN ; Have a routine to call BEQ 1$ ; No, just skip this JSR PC,@DMPRTN ; Call the routine 1$: MOV (SP)+,R0 ; Restore R0. TST (SP)+ ; Remove the IOSB from the stack. ASTX$S ; And return. ; ; We must save Registers R1 thru R3. Then clear out their old contents. ; 5$: JSR R1,$SAVE4 ; Save the registers MOV #-1,QUEFLG ; If we got here, QIO is done ; ; Make sure that connect processing runs ; SETF$S #CONEFN ; Flag connect must do something ; ; Make sure we didn't get an error. ; TSTB XKSTAT ; What was the status of the I/O ? BMI 40$ ; Branch if an error. ; ; Here if we got no error. See if we got any characters. ; TST XKSTAT+2 ; Were any characters gotten ? BEQ 30$ ; No characters, branch. ; ; Here if we got characters. ; MOV XKSTAT+2,R1 ; Get number of characters read ADD R1,XKFREC ; Increment the index, clear BIC #^C,XKFREC ; the high order byte. ADD R1,XKUSED ; Increment the used count, SUB R1,XKFREE ; Decrement the index, clear TST XKFREE ; Test the free space count. BEQ 20$ ; Branch if zero. ; ; Here if the buffer is not full. ; ; We want to queue up a request for the most characters we ; can. This is MIN(XKFREE,(XKBUFLEN-XKFREC)). MOV #XKBUFLEN,R2 ; Get max length SUB XKFREC,R2 ; Determine distance to end of buffer CMP R2,XKFREE ; Can we fill all free space? BLE 10$ ; If LE, we can only read to end of buffer MOV XKFREE,R2 ; Yes, get the max amount to read ; ; All cases, queue an I/O for R2 number of characters with time-out 0. ; 10$: MOV XKFREC,R1 ; Get the index to store characters ADD #XKBUFF,R1 ; Add it to the buffer address. INC QUEFLG ; QIO now pending QIO$S #IO.RAL!TF.TMO,#XKLUN,#XKREFN,,#XKSTAT,#XK.AST, 20$: RTS PC ; All done ; ; Here if we got no characters. ; 30$: PJMP XK.QIO ; Issue single character QIO ; ; Here if we got an I/O error ; 40$: MOVB XKSTAT,ERRFLG ; Save error code and flag the error. RTS PC ; And return .SBTTL XK.OUT - Output a buffered character to the XK ;++ ; This routine will output characters to the XK port. ; ; Usage: ; R1/ Character ; JSR PC,XKOUT ; (Return) ; ;-- .PSECT $CODE$, RO, I XK.OUT::TST XKONCH ; Have room for this character? BNE 10$ ; Yes, output it ; ; Here to wait for output to finish ; WTSE$S #XKWEFN ; No, wait for output to finish BR XK.OUT ; Loop and try again ; ; Here to output the character to the XK port ; 10$: MOV XKOIDX,R0 ; Get the current index MOVB R1,XKOBUF(R0) ; Store the character DEC XKONCH ; Decrement the number free ADD #XKOBUF,R0 ; Point to the byte QIO$S #IO.WVB,#XKLUN,#XKWEFN,,#XOIOSB,#XKOAST, INC XKOIDX ; Point to the next free location BIC #^C,XKOIDX ; Point to the next free slot RTS PC ; Return to the caller .SBTTL XKOAST - AST routine for XK output done QIOs ;++ ; This routine is called as an asynchronous trap routine for the XK ; output QIOs. This will adjust the counts and set the event flag for the ; XK outputs. ; ; Usage: ; QIO AST routine ; ;-- .PSECT $CODE$, RO, I XKOAST: INC XKONCH ; Count the character as sent TST (SP)+ ; Remove the item from the stack ASTX$S ; Return from AST .SBTTL FNDXKP - Find an XK parameter in the parameter block ;++ ; This routine will find the XK parameter in the current processing block. ; It will return the address of the item in the parameter block. ; ; Usage: ; R0/ Parameter type (TC.xxx or XT.xxx) ; R1/ Address of block (assumed to be CURXKL in length) ; JSR PC,FNDXKP ; (Return) ; ; On return: ; R0/ Offset or 0 if not found ; ;-- .PSECT $CODE$, RO, I .GLOBL FNDXKP FNDXKP: JSR R1,$SAVE2 ; Save R1 and R2 MOV #,R2 ; Get the length of the block ; Here to loop attempting to find the parameter 10$: CMPB R0,(R1)+ ; Is this the item? BEQ 20$ ; Yes, leave loop TSTB (R1)+ ; Point to the next item SOB R2,10$ ; Loop for all parameters CLR R0 ; Return a zero RTS PC ; Return the parameter ; Here if we found the parameter in the block. Return the address of ; the status of the parameter. 20$: MOV R1,R0 ; Point to the parameter RTS PC ; Return .SBTTL End of KERXK .END