; ------------------------------------------------------------- ; TRANSPARENT OUTPUT SPOOLER FOR RT-11 V04 .ENABL LC ; This software is the property of the Government of Canada. ; It may be freely used by any person or agency provided that ; such use is not for gain or reward except as authorized ; by her Majesty or her designated representative. ; Support for this program may be obtained from: ; National Defence Headquarters ; Ottawa Ontario Canada ; K1A 0K4 ; Attn: Mr.Greg.L.Adams ; DACS 2-2-4 ; Phone: (613) 993-9624 ; The department reserves the right to withdraw support without ; notice and does not assume responsibility for the correct ; operation of this software. ; ------------------------------------------------------------- .TITLE SPINT - SPOOLER INTERUPT GENERATOR / .IDENT /V01.00/ .NLIST BEX ; This task is entered every 10 ticks to check IF the SP handler ; has its interrupt request bit (INTEN) set. If so, a fake in- ; terrupt is generated and a buffers worth of data is retreived. ; The buffer is written to the spooler scratch file to be copied ; to the output device by the SPOUT task at a later time. ; ------------------------------------------------------------- ; Author: GA ; Edit History: ; GA 5-MAY-80 SET SP SHOW didn't report correct free blocks. .page ; ----- Macros and other definitions --------------------------- .GLOBL SDCTAB ,$BUSY ,$CRPND ,B.TAIL ,B.USED ,B.BEG .GLOBL B.END ,B.HEAD ,SDCESZ ,D.BLK ,D.STAT ,SP$VEC .GLOBL DEVBUF ,NUMDEV ,TIMERON,SPINIT ,CR.DVDN,CR.TIMER .GLOBL SP$DEV ,SP$CSR ,SP$BPR ,SP$BNR ,SP$WCR ,FAKEIO .GLOBL ERROR ,INTEN ,NEXT ,LAST ,KILL ,OFF ,ON .GLOBL ENDPGS ,SHOW ,$NEXT ,$LAST ,$HOLD ,$KILL .MCALL .WRITW ,.PRINT ,.EXIT .MCALL .WRITC ,.READW T$VEC = 020 ;IOT vector address. ; ----- Pure data ------------------------------------------------- .PSECT PURE DEVNUM: .WORD 0 ;Working storage for device number. ; ----- Check for an interrupt request ---------------------------- .PSECT SPOOL FB.BUF == . ;Start of flag buffer. FB.BLK:.WORD 0 ;Flagged block number. FB.WCT:.WORD 0 ;Flagged word count. FB.SIZ = .-FB.BUF CK.INTEN == . MOV #SP$CSR,R3 ;Get address of spooler CSR. BIT #177770,(R3) ;Are there any bits set in the CSR. BEQ RETIME ;No, then go start another mark time. BIT #INTEN,(R3) ;Is it an interrupt request. BNE 1$ ;Yes, then go process it. JMP SETSP ;No, then it must be a SET command. 1$: ;Fall through to the next block. ; ----- Get spooled device number and verify that it is legal ----- GETNUM: MOVB SP$DEV(R3),R1 ;R1 = spooled device number. BIC #177770,R1 MOV R1,DEVNUM ;Save the device number. CMP R1,#NUMDEV-1 ;Is this a legal device number? BLE 1$ ;Yes, then carry on by all means. .PRINT #BADDEV ;No, then what a silly user! BIS #ERROR,(R3) ;Set error bit in the CSR. CALL INTGEN ;Generate a pseudo interrupt. BR RETIME ;Go start another mark time. 1$: CALL CINDEX ;Creat an index into the SDCTAB. .PAGE ; ----- Verify the device still has room in the scratch file ------ CKROOM: MOV B.END(R4),R1 ;Determine no. of unused blocks in the SUB B.BEG(R4),R1 ;scratch file available for this dev. SUB B.USED(R4),R1 CMP R1,#2 ;There must be at least 2. BLE RETIME ;If not, come back when not so full. ; ----- All ok so get a buffer's worth of data from SP ------------- MOV #SPINIT,SP$BPR(R3) ;Tell SP where to stuff it. CALL INTGEN ;Go fake an interrupt. ; ----- Process the I/O request ---------------------------------- PROIO: BIT #$BUSY,D.STAT(R4) ;Is the device busy? BEQ 1$ ;No, then go write a flag block. TST SP$BNR(R3) ;Is this block 0 (i.e. start of file)? BEQ 1$ ;Yes, then go write a flag block. CMP SP$WCR(R3),#256. ;Is this block only partially filled? BEQ 2$ ;Yes, then write a flag block. 1$: MOV SP$BNR(R3),FB.BLK ;Set the flag block, block number. MOV SP$WCR(R3),FB.WCT ;Set the flag block, word count. MOV #FB.BUF,R5 ;Indicate this write is a flag block. CALL SFWRITE ;Write it to the scratch file. 2$: MOV #SPINIT,R5 ;Indicate this write is a data block. CALL SFWRITE ;Write it to the scratch file. BIT #$BUSY,D.STAT(R4) ;Is the device already busy? BNE 3$ ;Yes, then lucky us. MOV DEVNUM,R1 ;Set up a fake device done completion CALL CR.DVDN ;routine entry to start the device. BIS #$BUSY,D.STAT(R4) ;Flag it as busy now. 3$: CALL CR.TIMER ;Reschedule ourself and return. RETURN ; ----- Restart another mark-time for INTEN bit checking ----------- RETIME: CALL TIMERON ;Call the timer start routine. RETURN ;Return to the caller. .PAGE ; ----- Interrupt generator subprogram ----------------------------- INTGEN: MOV @#SP$VEC,@#T$VEC ;Initialize IOT vector. MOV @#SP$VEC+2,@#T$VEC+2 BIT #INTEN,(R3) ;Is INTEN bit still set? BNE 1$ ;Yes, go IOT. CMP (SP)+,R0 ;No, then tidy the stack and BR RETIME ;go reschedule ourself. 1$: IOT ;I/O trap. RETURN ;Return to caller. ; ----- Subroutine to write to scratch file ------------------------- SFWRITE:.WRITW R2,#10,R5,#256.,B.TAIL(R4) ;Give it some tail. BCC 1$ ;Write ok? .PRINT #IOERR ;No, then were dead. .EXIT 1$: INC B.USED(R4) ;Count the block as being added. INC B.TAIL(R4) ;Update the tail pointer. CMP B.TAIL(R4),B.END(R4) ;Are we at the end? BLOS 2$ MOV B.BEG(R4),B.TAIL(R4) ;Yes, then wrap around. 2$: RETURN ;Return to the caller. ; ----- Subroutine to create an index into the SDCTAB ---------------- CINDEX: CLR R4 ;R4 => address of SDCTAB entry. 1$: DEC R1 ;Does R4 contain proper offset? BLT 2$ ;Yes, then go add SDCTAB base address. ADD #SDCESZ,R4 ;No, increment offset by entry size. BR 1$ 2$: ADD #SDCTAB,R4 ;Add the base address. RETURN ;Return to the caller. ; ----- Messages --------------------------------------------------- BADDEV: .ASCIZ "SPOOL-I-Illegal SP unit number specified." IOERR: .ASCIZ "SPOOL-F-I/O error on scratch file." SORRY: .ASCIZ "SPOOL-I-Sorry, that set option is not yet implemented." .EVEN .PAGE ; ----- Processing for SP handler SET commands --------------------- SETSP: MOV (R3),R1 ;Get unit number for the set. BIC #7,(R3) ;Clear the unit number in the CSR. BIC #177770,R1 ;Strip all but unit number bits. MOV R1,DEVNUM ;Save the device number. CMP R1,#NUMDEV-1 ;Is the unit number legal? BLE 1$ ;Yes, carry on. .PRINT #BADDEV ;No, report a bad device. BIC #177777-INTEN-ERROR,(R3);Clear everybody but INTEN & ERROR. JMP RETIME ;Reschedule ourself. 1$: CALL CINDEX ;Create an index into the SDCTAB. SETWHO: BIT #NEXT,(R3) ;Find out which set is requested. BEQ 1$ ;Not this one! BIC #NEXT,(R3) ;This one so clear the request. BIT #$BUSY,D.STAT(R4) ;Is the device busy. BEQ 77$ ;Yes, then don't request next. BIS #$NEXT,D.STAT(R4) ;No, set device specific next flag. BR 77$ ;Go set for a timed reschedule of us. 1$: BIT #LAST,(R3) ;Repeat for all set bits. BEQ 2$ BIC #LAST,(R3) .PRINT #SORRY BR 77$ 2$: BIT #OFF,(R3) BEQ 3$ BIC #OFF,(R3) BIS #$HOLD,D.STAT(R4) BR 77$ 3$: BIT #ON,(R3) BEQ 4$ BIC #ON,(R3) BIC #$HOLD,D.STAT(R4) BR 77$ 4$: BIT #ENDPGS,(R3) BEQ 5$ BIC #ENDPGS,(R3) .PRINT #SORRY BR 77$ 5$: BIT #KILL,(R3) BEQ 6$ BIC #KILL,(R3) BIT #$BUSY,D.STAT(R4) BEQ 77$ BIS #$KILL,D.STAT(R4) BIS #$NEXT,D.STAT(R4) 77$: JMP RETIME 6$: ;The old fall through. .PAGE ; ----- When all bits fail then it must ba a show ------------------ DOSHOW: BIT #SHOW,(R3) ;Well is it? BEQ 77$ BIC #SHOW,(R3) ;Clear it so we don't repeat. MOV #"0 ,SPUNIT ;Set up to show who the status ADD DEVNUM,SPUNIT ;report is for. .PRINT #WHO ;And tell the user. BIT #$BUSY,D.STAT(R4) ;Is the device busy? BNE 1$ .PRINT #IDLE ;No, then report its idle. BR 77$ ;We're done. 1$: .PRINT #ACTIVE ;Report the device is active. BIT #$HOLD,D.STAT(R4) ;Is output on hold? BEQ 2$ .PRINT #ONHOLD ;Yes, then better tell the human. 2$: MOV B.USED(R4),R5 ;R5 = blocks left to output. MOV #NUMBER,R1 ;R1 => target string. CALL COMPUTE ;Compute the value in decimal. .PRINT #NUMBER ;Print how many blocks are used. 3$: MOV B.END(R4),R5 ;Compute how many free blocks SUB B.BEG(R4),R5 ;are available in the scratch file. SUB B.USED(R4),R5 ; SUB #2,R5 ;Always 2 less than full. GA- MOV #LEFT,R1 CALL COMPUTE .PRINT #LEFT 77$: JMP RETIME .PAGE ; ----- Subroutine to convert binary do decimal ascii ------------- COMPUTE:CLR (R1) ;Initialize the target string. CLR 2(R1) CLR 4(R1) 1$: DEC R5 ;The last block counted? BLT 2$ ;Yes, then go output the count. MOV R1,R0 ;No, then count it. ADD #5,R0 CALL BUMP BR 1$ 2$: ADD #"00,(R1) ;Make the block count ascii. ADD #"00,2(R1) ADD #"00,4(R1) MOVB #11,(R1) RETURN ;Return to the caller. ; ----- Recursive subroutine to count bytes by 10 ----------------- BUMP: INCB (R0) ;Count the item. CMPB (R0),#10. ;Did it reach 10 decimal? BLT 1$ ;No, go exit. CLRB (R0) ;Yes, clear this level. DEC R0 ;Select next level. CALL BUMP ;RECURSE for the next level. 1$: RETURN ;Return up when done. ; ------ Device status messages ----------------------------------- WHO: .ASCII "SPOOL-I-Unit 0" SPUNIT: .ASCIZ "0 status." IDLE: .ASCIZ <11>"Device is idle." ACTIVE: .ASCIZ <11>"Device is active." ONHOLD: .ASCIZ <11>"** Output is on hold. **" .EVEN NUMBER: .ASCIZ "000000. blocks are spooled up for output." .EVEN LEFT: .ASCIZ "000000. blocks are free to be spooled." .EVEN .PAGE ; ----- Processing for spooled device writes done ----------------- SDWTDN::CLR R1 ;Determine which device has post MOV #SDCTAB,R4 ;completion routine processing needed. 1$: BIT #$CRPND,D.STAT(R4) ;This guy? BNE 2$ ;Yes, how about that, we found him. ADD #SDCESZ,R4 ;No, then move to the next entry. INC R1 ;Increment the device count. BR 1$ ;Continue looking, why not! 2$: MOV R1,DEVNUM ;Store the device number safely away. BIC #$CRPND,D.STAT(R4) ;Clear the postcompretproflagbit. 3$: BIT #$HOLD,D.STAT(R4) ;Is the output on hold? BEQ 4$ ;No, carry on. CALL FAKEIO ;Yes, then do a timed wait to fake RETURN ;output done. 4$: ;Next block. ; --------------------------------------------------------------------- NEXTWT: TST B.USED(R4) ;Is there anything more in storage? BNE 1$ ;Yes, continue. BIC #$BUSY!$KILL!$NEXT,D.STAT(R4) ;No, then set free of control RETURN ;and postcompretpro is done. 1$: SWAB R1 ;Calculate where the buffer is. ASL R1 ;BUFADR=(DEVNUM*512.)+DEVBUF ADD #DEVBUF,R1 CALL SFREAD ;Go read the scratch file. MOV #256.,FB.WCT ;Presume the write will be = 1 block. MOV R1,R3 ;R3 => DEVBUF for this device. ADD #FB.SIZ,R3 ;Move past data values. MOV #FB.BUF+4,R0 ;R0 => pattern matching flag buffer. MOV #64.,R5 ;R5 = number of words to be matched. 2$: CMP (R3)+,(R0)+ ;Do the two locations match? BNE DVWRIT ;No, then this isn't a flag block. DEC R5 ;All locations compared? BGT 2$ ;No, then check some more. MOV 2(R1),FB.WCT ;Yes, so store word count and block MOV (R1),D.BLK(R4) ;number (they are possibly different). BIT #$NEXT,D.STAT(R4) ;Are we searching for the start of the BEQ 3$ ;next output data? No, carry on. TST D.BLK(R4) ;Yes, then is this block 0? BNE 3$ ;No, then carry on. BIT #$KILL,D.STAT(R4) ;Is this a full kill? BNE 3$ ;Yes, then don't unnext it. BIC #$NEXT,D.STAT(R4) ;No, then we're there so unflag. 3$: CALL SFREAD ;Now read in the real data block. .PAGE ; ----- Write to the device ---------------------------------------------- DVWRIT: BIT #$NEXT,D.STAT(R4) ;Are we seeking next start of data? BEQ 1$ ;No, go write the output. MOV DEVNUM,R1 ;Yes, then fake it as if the output CALL CR.DVDN ;has been done. BR 2$ ;Don't do the write. 1$: .WRITC R2,DEVNUM,R1,FB.WCT,#CR.DVDN,D.BLK(R4) ;Write to the device. 2$: INC D.BLK(R4) ;Increment the device output block. RETURN ; ------------------------------------------------------------------- SFREAD:.READW R2,#10,R1,#256.,B.HEAD(R4) ;Get the head block. BCC 1$ ;Any read errors? .PRINT #IOERR ;Yes, report and exit. .EXIT 1$: DEC B.USED(R4) ;Count the block as being read. INC B.HEAD(R4) ;Update the head block pointer. CMP B.HEAD(R4),B.END(R4) ;Is our head at the top? BLOS 2$ MOV B.BEG(R4),B.HEAD(R4) ;Yes, then reset it to the start. 2$: RETURN ;No, then just return. .END