;--------------------------------------------------------------------- ; SPOOLER RSX v1.2 (development version) ;--------------------------------------------------------------------- ; sectrs equ 16 ; No of sectors to buffer in disk read operations. ; Sectrs=16 is a 2k buffer ; qentry equ 16 ; Max no of spool queue entries ; ;--------------------------------------------------------------------- ; RSX function nos reserved for communicating with loader. ; intspl equ 40 ; Install spooler RSX and setup spool directory getnxt equ 41 ; Return fcb for next file to add to queue qcopy equ 42 ; Copy filename entry to queue ; Data format is (nos in brackets are no of bytes): ; Drive(1),filename(8),extension(3),pos(1),size(3). getdir equ 43 ; Return spool directory drive and user no qlist equ 44 ; List spool queue abortp equ 45 ; Cancel one spooled file bugoff equ 46 ; Abort spool and uninitialise ; ;--------------------------------------------------------------------- ; RSX Header follows: ; db 0,0,0,0,0,0 jp monitr ; BDOS calls routed to RSX "listener" nxtrsx: jp 0 ; Jump vector to next module/BDOS (set by CCP Loader) prev: dw 0 ; Address of previous module remove: db 0ffh ; Remove flag (0=permanent) db 0 ; Nonbank=FALSE rsxnam: db 'SPOOLER ' db 0,0,0 ; ;--------------------------------------------------------------------- ; Monitor BDOS calls for any communication with RSX. ; This routine is called normally via the RSX header. ; monitr: ld a,c ; A=BDOS function no cp calrsx ; RSX call? jp nz,nxtrsx ; No, so go to next RSX or BDOS jp talk ; Else check if ours & talk to caller ; ;--------------------------------------------------------------------- ; BDOS call intercept routine. ; Calls to the BDOS entry point are redirected here. ; rbase: ld a,(nospol) or a jp nz,gosys ; Not active - no intercepts ld a,c cp scan1 ; Else, is it a directory search? jp nz,nolock ; ld a,0ffh ld (lockfl),a ; If so, lock out disk access until complete ; nolock: ld a,c cp open jp z,unlock ; Remove lockout if its another disk function... cp wrsect jp z,unlock cp rdsect jp z,unlock cp close jp z,unlock cp make jp z,unlock cp delete jp z,unlock cp rnread jp z,unlock cp rnwrit jp z,unlock cp wrzfil jp nz,staylk ; unlock: xor a ld (lockfl),a ; Disk access lock = OFF ; ; Save user regs & stack before testing for console input calls. ; staylk: ld h,d ld l,e ld (bdosde),hl ; Save user parms ld a,c ld (bdosfn),a ; Save user function no ld hl,0 add hl,sp ld (bdostk),hl ; Save user stack ld sp,stack2 ; ; Test for console status requests & handle if so ; cp const ; BDOS function 11? jp z,notcin ; Yes: jump to fill buffer if empty ; Note: BIOS intercept calls splout after BDOS call made ; ; Test for Direct Console I/O ; cp dircon ; Direct console I/O? jp nz,ndirio ; No - jump past ; ; Handle Direct Console I/O requests ; ld a,e cp 0fdh ; Wait for input call? jp c,docall ; No, but <0fdh, so must output a character jp z,chstat ; Yes - poll until BDOS const TRUE ; notcin: call chkbuf ; Read buffer if empty (applies to 0feh & 0ffh calls) jp docall ; And do the BDOS call ; ; Handle Conin & Fill Console Buffer Calls - others jump past ; ndirio: cp conin ; Conin call? jp z,chstat ; If so, poll status & spool until char ready cp conbuf ; Fill console buffer call? jp nz,docall ; Jump out if not - else poll as above ; ; Poll chkbuf and BDOS const (via direct I/O) until key waiting ; Note that if the intercepted call was from the CCP and there ; is a multiple line RSX resident, the poll is skipped as ; a CCP command has already been entered. ; chstat: ld c,scblk ld de,scb18 call gosys ; SCB 18h bits 5 & 7 show whether multi-line and 0a0h ; RSX is ready to provide I/P to CCP cp 20h ; (Bit 5 set, bit 7 unset) jp z,docall ; If so don't poll const - go to BDOS chst2: call chkbuf ; Fill spool buffer if necessary ld c,dircon ld e,0feh call gosys ; Get console status from BDOS (NB sets locks) or a ; Key waiting? jp z,chst2 ; No - continue to poll ; ; Restore registers & stack before doing user's BDOS call ; docall: ld hl,(bdostk) ld sp,hl ; Restore stack, ld a,(bdosfn) ld c,a ; BDOS function no ld hl,(bdosde) ex de,hl ; & parms ; ;--------------------------------------------------------------------- ; Jump vector to BDOS (doesn't pass thru any more RSX's). ; Sets internal BDOS re-entrancy lock flag before calling. ; gosys: ld a,0ffh ld (bdosfl),a ; BDOS call flag=TRUE call bios ; Do the user's BDOS call - addr filled in by init nbdos equ $-2 push af ; Save returned value xor a ld (bdosfl),a ; BDOS call flag=FALSE pop af ret ; To caller (maybe internal BDOS call) ; ;--------------------------------------------------------------------- ; Talk to calling process ; Uses standard DR RSX parm block ; talk: ld a,(de) ; A=RSX function no inc de inc de ; DE addresses first parameter in RSX call block cp intspl jp z,instal ; Install request ; cp getdir jp z,givdir ; Return spool directory (also cancels uninstallation) ; ld b,a ld a,(remove) or a ; Is RSX installed? jp nz,passit ; No: none of calls below valid, so pass on ; ld a,b cp bugoff ; Disconnect? jp z,uninit ; Act if so - routine checks if already done ; cp getnxt ; Return next fcb? jp z,allocq ; Allocate place in queue & return next fcb ; cp qlist ; List spool queue? jp z,passq ; If so, pass back the spool queue address ; cp abortp ; Cancel spooling of one file? jp z,abort1 ; Yes - act ; cp qcopy ; Copy queue entry & open file? jp z,cpynam ; Yes - copy filename ; ; Pass call on to next RSX or BDOS. ; passit: dec de dec de jp nxtrsx ; Not one of our calls: pass it on ; ;--------------------------------------------------------------------- ; Copy filename [DE] to queue (range checks made during getnxt call). ; If the file is the first (queue empty) it is opened, and system ; intercepts are enabled. ; cpynam: ex de,hl ld e,(hl) inc hl ld d,(hl) ; DE=start address ld a,(qnext) ; A=next queue no push af ; Save for later call pointq ; HL=16*(qnext-1), or offset for new entry push hl ld b,16 ; 16 bytes to move call blkmov ; Copy to it pop hl ld bc,12 ; Offset for "queue" no entry add hl,bc pop af ; A=qnext push hl push af ld hl,pnext ; CALL PSTRNG pop af push af ; CALL PHEX ld c,cr ; CALL CHROUT ld c,lf ; CALL CHROUT pop af pop hl ld (hl),a ; Store in queue cp qentry ; Have we used the last entry in the queue? jp nz,qspace xor a ; Yes: start at beginning again qspace: inc a ; Else use next one ld (qnext),a ; Update end of queue ld a,(qnow) or a ; Test current queue no to see if it's the first entry ret nz ; No - already spooling ; inc a ld (qnow),a ; Else need to update current file pointer ld hl,ifcb call makfcb ; Make an fcb for the file call openit ; And open it for spooling ; NB ERROR CHECKING REQUIRED HERE xor a ld (nospol),a ; Allow BDOS & BIOS intercepts ret ; pnext: db 'Copy to queue: qnext = $' ; ;--------------------------------------------------------------------- ; Return H = user & L = drive of spool directory. ; If this RSX has not yet been installed, or has already been ; uninstalled, the call is passed on. If the RSX is waiting to ; uninstall (finish=0ffh), the uninstallation is cancelled, ; and the queue reset. ; givdir: ld a,(remove) ; RSX temporary/permanent flag or a ; If temporary, either not installed or uninstalled... jp nz,passon ; So must pass it on (only attached RSX is not installed) ld a,(finish) ; "Waiting to uninstall" flag or a jp z,workin ; If false, working so return drive ; revive: xor a ; Else waiting to uninstall, so... ld (finish),a ; Cancel uninitialisation ld (qnow),a ; Queue empty, so reset pointers: qnow=0... inc a ld (qnext),a ; ...and qnext=1 ; workin: ld a,(splusr) ld h,a ; H=user ld a,(spldrv) ld l,a ; L=drive ret ; ; RSX not yet installed, or already uninstalled. ; Pass the call to the next RSX up the chain, to check ; whether it is active. If not the BDOS will return HL=00ffh. ; passon: dec de dec de ; DE addresses parameter block again ld c,calrsx jp nxtrsx ; Interrogate next RSX for the drive ; Returns HL=00ffh if none present ; ;--------------------------------------------------------------------- ; Allocate and make an fcb for the next place in the spool queue. ; allocq: ld a,(qnow) ; Queue no of file now being printed ld b,a ld a,(qnext) cp b ; Check whether space available: qnext<>qnow if so ld a,0ffh ; Default=NO ret z ; Can't accept file: flag error ex de,hl ; [HL]=address of fcb ld e,(hl) inc hl ld d,(hl) ex de,hl ; HL=addr of fcb ld a,(qnext) call makfcb ; Make spool fcb at [HL], with index no A ld hl,alloc2 ; call pstrng ;DEBUG xor a ; A=0 means OK ret ; alloc2: db 'Allocating fcb',cr,lf,'$' ; ;--------------------------------------------------------------------- ; Cancel spooling of file with queue no at [DE+2]. ; Sends a formfeed to the printer if the file is currently ; being printed. No other range checks are made by this routine. ; Note that this routine will NOT delete the copy of the file in ; the spool directory unless it is currently being printed. ; The calling program must handle this, using makfcb() and erafil(). ; abort1: ld a,(de) ; A=queue no to cancel ld b,a ld a,(qnow) cp b ; Test whether qnow = queue no to cancel jp z,abort2 ; Print to cancel is current one ; ; Abort spooling of file by setting queue entry to 0ffh ; ld a,b ; A=queue no to cancel call pointq ; HL=address of queue entry ld de,12 add hl,de ; HL addresses queue no entry ld (hl),0ffh ; Indicates deleted ret ; ; Cancelling current print: formfeed & open next file ; abort2: ld c,fmfeed call lstchr ; Formfeed call nxtfil ; Open next file (erases current) xor a ; A=0 signifies abort made ret ; ;--------------------------------------------------------------------- ; Pass back the spool queue (for listing). ; HL is returned containing the address of the spool queue, the ; first 3 bytes of which are qnow (the file currently being spooled), ; qnext (the next free queue position) and qmax (the maximum queue no). ; The queue itself consists of 16 byte entries for each file, as ; follows: ; 1 byte for the source drive and user no ; 8 bytes for the filename ; 3 bytes for the filetype ; 1 byte for the queue index no (note this may not be the queue no) ; 3 bytes for the file size, in sectors. ; passq: ld hl,qnow ret ; ;--------------------------------------------------------------------- ; Check whether RSX already installed, and act if not. ; Note that if RSX is already installed, init routines will ; have been overwritten by the disk buffer. ; The RSX parameter block should contain the following ; information on entry: ; ; [DE ] = Low byte of BDOS entry point address ; [DE+1] = High " " " " " " ; [DE+2] = Current temporary file drive ; [DE+3] = User no of spool directory ; instal: ld a,(remove) ; RSX status flag - temporary/permanent or a ret z ; No action if already working jp init ; Else initialise system intercepts ; ;--------------------------------------------------------------------- ; Character spooling routine. ; If the printer is free, a character is sent to it via BIOS. ; Characters to spool are provided by getchr - note however ; that if the buffer is empty it will be filled by chkbuf ; (or the next file opened) unless a disk/reentrancy lockout ; is in progress. ; ; Note that this routine does NOT check for the eof marker (^Z), ; since files generated by the spool loader are not terminated ; with such a marker, but are padded out with null characters, ; which are ignored by the printer. ; This is to allow the spooler to be used with object files ; (eg redirected output from a graphics package). ; splout: call lstat ; Printer busy? or a ret z ; Yes, so ret ; RET call chkbuf ; Check buffer status - A=0 if empty or a ; Buffer empty? ret z ; Yes - waiting to read, or hard eof occured, so ret call getchr ; Else get a byte from the input buffer ld c,a ; Byte to C for possible print ; cpi cpmeof ;Have we reached the eof marker? ; jz nxtfil ;Yes - eof reached ; RET ; JMP CHROUT jp lstchr ; Else print the byte ; ;--------------------------------------------------------------------- ; Routine to return a byte from the input buffer. ; This routine assumes the buffer contains valid data - ; NO CHECKS ARE MADE FOR INVALID BUFFER VARIABLES. ; getchr: ld hl,(icount) ; HL=buffer count dec hl ld (icount),hl ; Decrement count ld hl,(iptr) ; HL=buffer pointer ld a,(hl) ; A=char inc hl ld (iptr),hl ; Increment buffer pointer ret ; ;--------------------------------------------------------------------- ; Routine to check whether the spooler disk buffer is empty, ; and if so fill it from the spooled file. Note that the read ; will not take place if disk accesses are currently locked ; out, to prevent a re-entrant BDOS call, or confusing BDOS ; while a directory search was in progress. ; A=0 is returned if the read could not take place due to ; such a lockout. ; If eof or a disk read error occurs, the next file is opened. ; If it cannot be opened, the RSX is uninstalled, & A=0 is returned. ; chkbuf: ld a,(finish) cpl ; A=0 if spooler finished... or a ; ...also tells splout that buffer is empty ret z ; If so, no action ld a,(qnow) or a ; Any files to spool (may have been re-activated by NOSPOOL) ret z ; No: do nothing (wboot intercept sets finish flag) ld hl,(icount) ; HL=buffer count ld a,l or h ; Null? ret nz ; No - ret ld b,a ; B is count indicator (=0) in case can't read ld a,(lockfl) ld hl,bdosfl or (hl) ; BDOS & disk lockout flags ld a,b ; A=0 in case can't read ret nz ; If either of the above are true, don't try to read call rdbuf ; Try to read buffer (also sets buffer vars) ld hl,(icount) ld a,h or l ; Count=0? ret nz ; No - read OK ld (openfl),a ; Else eof or disk read error - finish with file ; CALL CHKBF2 call nxtfil ; Open next one if possible ld a,(finish) ; "Waiting to uninstall" flag cpl ; Complement: A=0 if finished ret ; As "buffer empty" indicator chkbf2: ld hl,chkbf3 jp pstrng chkbf3: db 'EOF or disk read error in chkbuf',cr,lf,'$' ; ;--------------------------------------------------------------------- ; Routine to read "sectrs" sectors of data from the input file ; to the spooler buffer. DMA address and multi-sector count ; are unaffected by this routine. ; Note that if physical eof occurs during the read, the buffer ; count is set according to the number of sectors read before ; eof occured. If, on exit, icount=0000h, no sectors were read, ; or a disk read error occured, and spooling on the file should ; be curtailed. ; rdbuf: ld a,0ffh ld (nospol),a ; No BIOS intercepts while reading (for err msgs etc) ; ld c,9 ld de,rdmes ; CALL GOSYS ;Debug - comment out in working version ld de,scb3c ; SCB parm block - get DMA address ld c,scblk call gosys ; HL=current DMA address ld (cpmdma),hl ; ld de,ibuff ld c,setdma call gosys ; Set DMA address to RSX input buffer ; ld de,scb4a ; SCB parm block - get multi sector count ld c,scblk call gosys ; A=current multi sector count ld (scount),a ; Saved ; ld e,sectrs ld c,nosecs call gosys ; Set RSX multi sector count ; call gousr ; To spool dir user area (in case new extent) ld c,rdsect ld de,ifcb call gosys ; Try to read sector(s) push af ; Save error indicator for later check push hl ; And no of sectors read (if D, CY=1 if H