// P R I N T // errors 5000 // get "SprucePrinters.d" get "SpruceDoc.d" get "SpruceFont.d" get "SpruceBand.d" get "spruceMisc.d" get "SpruceFiles.d" get "Orbit.d" // outgoing procedures external [ DrivePrinter ] // incoming procedures external [ SpruceError Max; Min CallSwat InitRam ChooseMailboxBin //SprucePrint AddToBin Announce AwaitPageSync IncBinSerial PrintNext ReadBands ReadClock ReadFont //CURSOR CursorChar; CursorDigit // SprucePrintRes DoFunc; ROSStatus // Timer (debug only) SetTimer; TimerHasExpired ] // incoming statics external [ BandFile bandRecordSize BinCounters breakPage breakPageCopy Capabilities Debug DebugSystem debugTrail FAvalue Func knockResult logBandRecordSize LowAdr Measure; CloseMeasure; TickMeasure; InitMeasure; measureTable nBands nPagesPrinted nRecs numMustPrint numPrinted printerForward ros12 ros13 scanTicks // # of 38 usec. ticks in 4 printer scan lines. stopsPrinting xmFonts // Font data will be read into Bank 1 via Bank 0, if set BinSerials ] // internal statics static [ imageTimeout rosCount = 0 // Pimlico/puffin/penguin command count printSeq twosided curBin; nextBin curHandling; nextHandling switchBin = false // for delayed bin switch if intermediate break page needed nextCopy debDelay = 0 // debugging: delay per page to simulate printing. ] // File-wide structure and manifest declarations. manifest [ AuxCount = 19 //keep count of sheets in Aux tray in BinCounters!19 AuxRecoveryCount = 20 //number of first sides to be replaced in BinCounters!20 sigband = 100 RTC=#430 eTicks = 4 //time to spend checking scanning fITO = 15*27 //15 seconds to get first sheet thru rITO = 2*27 //2 seconds to get subsequent sheets thru. timeOut=15*27 //time to wait for standby status to appear printHysteresis = 30 // print this many before allowing suspension, each time in debugTrailSize = 10*3 minDocumentError = 300 // Document inherently hard to print // commands to Penguin pResetCount = #60000 // 6000x- reset your count of commands received from me pNewLED = #62400 // 65nmx- last 2 nibbles contain decimal digits pSetLED = #63002 // 6602x- display last value sent with pNewLED pPrint = #63001 // 6601x- print a page pSSMain = #63040 // 6620x- page is singlesided from main tray pSSAux = #63041 // 6621x- page is singlesided from auxtray pTSFirst = #63042 // 6622x- page is first side of duplex (goes from main to aux) pTSSecond = #63043 // 6623x- page is second side of duplex (aux to some output bin) pSetBin = #63100 // 6640x- position bins (to bin indicated in last five bits) pClearMalf = #63050 // 6628x- move malf indication to status pIgTSCErr = #63004 // 6004x- ignore tsc error indication // printSeq values: warmup = 1 standby = 2 preprint = 3 printing = 4 cycledown = 5 malfClear = 6 mainSeqErr = 107 subSeqErr = 106 abnormal = 113 powerOn = 123 ] // ----------------------------------------------------------- // Print Initialization // ----------------------------------------------------------- // Returns -1 if printing is successfully completed, else nPagesPrinted, so can pick up where we left off. let DrivePrinter(pDoc, nPagesAlreadyPrinted,lvFailureCode) = valof [ let measureVec = vec 5 if (DebugSystem & #4000) ne 0 then CallSwat("DrivePrinter") unless nPagesAlreadyPrinted then BinCounters!AuxCount = 0 // if no pages printed, shouldn't be paper in aux tray let CommTab=vec 8 CommTab=(CommTab+1)&(-2) let knockKnock = false // time to let somebody in if true twosided = pDoc>>DocG.duplex ne 0 curBin = 0 let overflowBin = 0 // usually is . . . test (Capabilities & mMailbox) ne 0 ifso [ unless BinCounters!0 > 50 do // can't move trays [ //find the bin we would like it to go in let mtray = ChooseMailboxBin((lv pDoc>>DocG.CreatStr)>>STR.char^1) if BinCounters!mtray <50 do curBin = mtray // if there is room ] breakPageCopy=curBin?1001,1002 ] ifnot [ if (Capabilities & mMultiBin) ne 0 do //filling bins from bottom up - find 1st nonfull [ until (BinCounters!(curBin) < 50) % (curBin eq 18) do [ curBin = curBin + 1 ] overflowBin = 18 ] ] let binMax = (curBin eq overflowBin)? 200, 50 InitializeHardware() InitMeasure(measureTable, 2000, measureVec) let backup = 0 let xportModel = 0 @lvFailureCode = (ROSCommand(pResetCount) % ROSCommand(pIgTSCErr))? 121, 0 @lvFailureCode = ROSCheck() if @lvFailureCode resultis SetupRecovery(pDoc, lvFailureCode, lv xportModel, nPagesAlreadyPrinted) let needFonts, needInit =true, true let FontTable, nfRecs, LeftOver, LeftOverGuard = nil, nil, nil, nil let buf = nil let tim = nil imageTimeout = fITO //allow penguin to get ready if BinCounters!AuxRecoveryCount do nPagesPrinted = BinCounters!AuxCount let nextPage=NextSheet(pDoc, nPagesPrinted) // Print (cont.) Loop [PrintLoop // Find out which page to print next: let page=nextPage if page eq 0 then [ knockKnock = false; IncBinSerial(BinSerials+curBin); break ] // Finished! if knockKnock then break // go let somebody in if needFonts do // Read in font and relocate the pointers to characters: [ let font=page>>PageG.fontLoad*(size FontG/16)+pDoc>>DocG.Fonts FontTable = ReadFont(font, LowAdr, nRecs) // call returns location of ICC table unless xmFonts do for i=0 to pDoc>>DocG.ICCtotal-1 do // if rasters are in bank 1, they start at 0 FontTable!i=FontTable!i + LowAdr FontTable=FontTable-100000b // Figure out where buffers are. let nRecords= xmFonts? (pDoc>>DocG.ICCtotal+bandRecordSize-1) rshift logBandRecordSize, font>>FontG.nRecords nfRecs=nRecs-nRecords // max allowable band list size LeftOver=LowAdr+(font>>FontG.nRecords lshift logBandRecordSize) LeftOverGuard = LeftOver+(loSize&(-4))-4 buf = (LeftOver+loSize+8)&(-2) // ~~ all the 4's, 8's and 10's in here are cowardly slack needFonts = false ] // Now read in the bands for this page ReadBands(page, buf, nfRecs, nBands) if needInit do // if this is the first time thru or paper handling changed [ if stopsPrinting eq binFull then [ @lvFailureCode = 130; break ] // Initialize the Orbit, etc. InitializeHardware() // wait for ready or some serious condition or for long enough tim = @RTC unless Debug do [ let x = ROSCheck(standby) unless x then break // ready if x > 0 then resultis SetupRecovery(pDoc, lvFailureCode, lv xportModel, nPagesPrinted, x) //result was -1: not ready yet, but may be soon. Have we waited long enough? if (@RTC - tim) > timeOut resultis SetupRecovery(pDoc, lvFailureCode, lv xportModel, nPagesPrinted, 120) ] repeat xportModel = 0 // paper cleared from transport mechanism // tell penguin to print a page and how to handle the paper curHandling = nextHandling let bin = nextBin test (nextBin eq overflowBin) & (curBin ne overflowBin) then // switching to overflow bin: do break page [ nPagesPrinted = nPagesPrinted - 1; binMax = 200; bin = curBin;switchBin = true ] or [ switchBin = false ] if ROSCommand(pSetBin + 2+bin) % ROSCommand(curHandling) % ROSCommand(pPrint) do [ @lvFailureCode = 122; break ] curBin = nextBin // Try to empty Orbit buffers to clear them. This is to minimize toner dumping on first (blank) page. compileif loSize ls 256 then [ foo=nil ] for i=0 to 31 do DoFunc(fReadBlock, 256, LeftOver) needInit = false ] // ----------------------------------------------------------- // Print real printing function // ----------------------------------------------------------- // first, set up to display page number we're printing on penguin's led nPagesPrinted=nPagesPrinted+1 let m,n = nil, nil m, n, m = nPagesPrinted rem 100, m rem 10, (m/10) lshift 4 //and build table so orbit will send them thru during page imaging CommTab!0 = 180 CommTab!1 = #62400 + m +n //set up leds and CommTab!2 = 150 CommTab!3 = #63002 //display'em CommTab!4 = -1 // this will be set to an appropriate band number if there is another page CommTab!5 = #63001 //command to print another page CommTab!6 = -1 // Start Orbit working on the present page. DoFunc(fControl, #21) //Reset, clear behind DoFunc(fROSCommand, adBufferReset) LeftOver!0=0 //Initialize LeftOver table LeftOverGuard!1=-1 tim=@RTC // Start the microcode!!!! DoFunc(fSlot, CommTab, 20000, nBands-1, FAvalue, LeftOver, FontTable, buf-1, nil, nil, (nextCopy lshift 5)+4) if Debug then [ while (@RTC-tim) < debDelay loop; tim = @RTC ] // ~~delay // Assume the page we are printing will be OK and prepare for whatever comes next: rosCount = rosCount + 2 // for the led setting commands CursorDigit(nPagesPrinted) // See who's (maybe) knocking at my door: if we are not planning to defer to spooler, // don't trouble to shut down here, just to find that out there. Would be too expensive if (DebugSystem) eq 0 & numPrinted ge numMustPrint & knockResult & (nPagesPrinted-nPagesAlreadyPrinted) > printHysteresis then knockKnock = true // Find next page to work on: nextPage=NextSheet(pDoc, nPagesPrinted) needInit = true if (not knockKnock) & nextPage & //if nobody waiting and there is more to do (nextBin eq curBin ) & (nextHandling eq curHandling ) & // and penguin doesn't need a pause (not switchBin) & (stopsPrinting eq false) then // and no forced bin switch then continue printing. // enable interband communication with penguin to set up the next revolution // keep track of what I've sent so we don't get out of sync [ needInit = false CommTab!4 = sigband rosCount = rosCount + 1 ] needFonts = (nextPage>>PageG.fontLoad ne page>>PageG.fontLoad) & (nextPage ne 1) // Now see if we actually printed this properly. Wait for Orbit to announce it is finished. while @#720 ne 0 do if (@RTC-tim) gr imageTimeout then [ InitializeHardware() // clear hardware, #720 // Machine dead or Orbit got behind and it wasn't detected // Will generally be overriden by more specific analysis @lvFailureCode = 103 // Manual intervention required break ] // Check Orbit status let nonF = 0 let stat=Func!9 //Orbit printing status. // Fatal error detected by microcode -- invalid Band entry -- possibly broken processor if stat< 0 do [@lvFailureCode = x ; break ] //result was -1: ok if cycledown expected and still printing if (running eq printing) % ((@RTC - tim) > 27) do [ @lvFailureCode = 114; break ] ] repeat // Now wait for page sync to go off unless AwaitPageSync(0) then @lvFailureCode = 101 //Now decide what to do: if (@lvFailureCode > 0) & (@lvFailureCode < minDocumentError) do [ backup = 1; break ] imageTimeout = rITO +(needInit?11*27,0) // next image should be out in less than 2 seconds xportModel = ((xportModel & 3) lshift 1) + 1 test curHandling eq pTSFirst then [ BinCounters!AuxCount = (BinCounters!AuxCount) +1 if BinCounters!AuxRecoveryCount do [ BinCounters!AuxRecoveryCount = BinCounters!AuxRecoveryCount -1 unless BinCounters!AuxRecoveryCount do nPagesPrinted = nPagesAlreadyPrinted ] ] or // note that bin counters not incremented if filling aux tray [ AddToBin(binMax, curBin); if (curHandling eq pTSSecond) do [ BinCounters!AuxCount = (BinCounters!AuxCount) - 1 ] ] ]PrintLoop repeat InitializeHardware() CloseMeasure() resultis (knockKnock % @lvFailureCode)? SetupRecovery(pDoc,lvFailureCode,lv xportModel, nPagesPrinted-backup), -1 ] // -------------------------------------------------------------------------- // code = ROSCheck(expectedPrintSeq) // -------------------------------------------------------------------------- // analyze ROS status // code = 0 if there are no malfunctions, ROS is ready to print // -1 if the only thing wrong is that penguin needs to cycle round to the expected state // otherwise, a code for the error detected // // expectedPrintSeq where caller thinks penguin should be(printing, cycling down, standing by, don't care) // ROSCheck first screens the current status for any malfunction indications, and // returns right away if there are none. Otherwise, it applies the current status // against a printer-dependent table of possible conditions, yielding a failure // code. // A check verifies that the laser is on and the polygon is // scanning (SOS/EOS are seeing things), by making sure that the low four bits of the line // count (indicated in status word) are changing. The code reads the line count, waits // approximately the time needed for four scan lines (generous margin), then reads the // count again, reporting a problem if the values are equal. This test happens only when // the running parameter is false. The wait time (scanTicks) is computed in PrintInit, as: // ResolutionS is scaled by 10 as stored, PaperSpeedInches by 100. The (fast 10 bits of) // the RTC ticks at 38 usec. intervals. Thus the # ticks wanted for four lines is: // ( 4 lines x 10^6 usec/sec x 1/38 tick/usec.) / ((PaperSpeedInches/100 x ResolutionS/10) lines/sec) // or about (106 x 10^3) / (PaperSpeedInches/100 x ResolutionS/10) ticks -- about 30 for Dover. // This is only approximate because ROSStatus time, about 10 ticks for Dover, is not included. // Part of the reason for waiting four line times is to account for possible delays in obtaining // the correct line count status. Later, this method can be used to check for correct polygon speed. // The scanTicks static is shifted left by 6 to match the clock reading function's results and ROSCheck(expected, printingSecondSide; numargs na) = valof [ if na < 2 then printingSecondSide = false ros12 = ROSStatus(12) ros13 = ROSStatus(13) let scanning, auxTrayProblem = true, false let malFnStatus = ros13M let auxTrayEmpty = (ros13 & #100) ne 0 let mainTrayEmpty = (ros13 & #200) ne 0 let subSeq = (ros13 rshift 8)&7 let mainSeq = (ros13 rshift 11)&7 let mode = ros13 rshift 14 let printSeq = selecton mainSeq into [ case 1: powerOn case 2: warmup case 3: standby case 4: 0 case 5: malfClear default: mainSeqErr //Mainseq error ] unless na do expected = printSeq // printing not valid state if nothing expected unless printSeq do [ printSeq = selecton subSeq into [ case 0: printing case 1: preprint case 2: cycledown case 4: abnormal default: subSeqErr // subSeq error ] ] let ready = (printSeq eq expected) & // penguin is where we think it should be (malFnStatus eq 0) & (not mainTrayEmpty) & ((ros12 & #12377) eq (rosCount & #377)) & //no malf and no queue problems mode eq 1 // and printer is in remote print mode if ready & (printSeq ne printing) then [ let lineCount = ROSStatus(7) & #170000 let rtc = ReadClock() until (ReadClock()-rtc) ge scanTicks loop scanning = (ROSStatus(7)𩠐) ne lineCount if expected ne cycledown do // can't check while paper may be moving [ if (BinCounters!AuxCount eq 0) & (not auxTrayEmpty) then auxTrayProblem = 72 ] ] if printingSecondSide & auxTrayEmpty then auxTrayProblem = 70 if ready & scanning & (auxTrayProblem eq false) % Debug resultis 0 // identify the problem or condition preventing ready let malf = selecton malFnStatus into [ case 1: 81 // processor interlock open case 2: 82 // zone IIA jam case 3: 83 // zone IIB jam case 4: 84 // zone III jam case 5: 85 // sorter interlock open // case 6: 86 // stop print push button pressed: not used in remote print. case 7: 71 // aux tray empty case 8: 115 // main tray empty - shouldn't happen with mainTrayProblem being watched. case 9: 108 // ink low case 10: 109 // trays don't match case 11: 110 // fuser not ready case 12: 111 // main tray paper misfeed case 13: 80 // aux tray paper misfeed case 14: 112 // cycle control state error case 15: 124 // paperexit jam case 16: 125 // (output) bin enter jam case 0: 0 default : -1 ] let noResponse = EngineCommErr(ros12) //check status of spruce communication with penguin let code = 0 test mode ne 1 then code = 100 // local mode - Penguin not listening or test ros12 < 0 then code = 104 // wait state - Penguin not listening or test malf > 0 then [ let x = ROSCommand(pClearMalf) unless (auxTrayProblem eq 70) do code = malf // good malfunction code ] or test noResponse then code = 116 // penguin not talking or test printSeq > malfClear then code =printSeq // State error or test malf then code = 117 // unknown malfunction code or test not scanning then code = 118 // laser appears to be off or test auxTrayProblem then code = auxTrayProblem or test mainTrayEmpty then code = 132 or test printSeq ne expected then code = -1 or code = 119 // can't figure out what's wrong resultis code ] // NextSheet(pDoc, afterPage) // ----------------------------------------------------------------------------------- // Setup paper handling info, copy number and pointer to PageG structure that describe the next image. // It is assumed that the PageG entities represent consecutively numbered pages. whichSide identifies a // page as 'odd' or 'even'; 'odd' pages face the same way the break page does --up, in the normal penguin mode [down, if "printerForward"]. // All pages are paired except: the break page, the first page if it is "even", the last page if it is "odd" // NextSheet makes two passes thru the document: // ** on the first pass all images that will be facing down are printed and stored in the aux tray in reverse order. // **on the second pass the pages that will be face up in the output bin are printed. This pass is complicated // by the fact that there may be face-down sheets for which there is no corresponding face-up image // -- these sheets must be passed thru at the appropriate time -- and by the fact that there may be face-up sheets for which // there is no corresponding face-down sheet -- these sheets must be fed from the main tray instead of the aux. and let NextSheet(pDoc, afterPage) = valof [ nextBin = SelectBin(curBin) if (nextBin eq 0) & (curBin ne 0) & breakPage & (afterPage < pDoc>>DocG.nPages) do // switching to overflow bin - leave break page in mailbox [ nextHandling = pSSMain; nextCopy = 1011; breakPageCopy = 1012 IncBinSerial(BinSerials+curBin) resultis ((pDoc>>DocG.nPages)-1)*(size PageG/16)+pDoc>>DocG.Pages ] let nPages, nBreaks = pDoc>>DocG.nPages, breakPage unless twosided & ((nPages - nBreaks) gr 0) do [ nextHandling = pSSMain; resultis PrintNext(pDoc, lv nextCopy, afterPage) ] nPages = nPages - nBreaks let bkPage = nPages*(size PageG/16)+pDoc>>DocG.Pages // if there is one let nCopies = pDoc>>DocG.nCopies let page = pDoc>>DocG.Pages // pointer to first page let firstPageEven = (page>>PageG.whichSide eq 0)? 1,0 page = (nPages-1) * (size PageG/16) + page // pointer to last page let lastPageOdd = (page>>PageG.whichSide ne 0)? 1,0 let nDoubles = (nPages - firstPageEven - lastPageOdd)/2 let nSheets = nDoubles + firstPageEven + lastPageOdd let nAuxSides, auxIncr, nAuxBreaks, incr, relImageIndex, copy = nil, nil, nil, nil, nil, 0 test printerForward ifso [ nAuxSides, auxIncr, nAuxBreaks, incr, nBreaks = nDoubles + lastPageOdd, lastPageOdd, nBreaks, firstPageEven, 0 ] ifnot [ nAuxSides, auxIncr, nAuxBreaks, incr = nDoubles + firstPageEven, firstPageEven, 0, lastPageOdd ] let totAuxSides = nAuxSides * nCopies + nAuxBreaks page = 0 test afterPage ls totAuxSides ifso // then need to fill Aux tray [ nextHandling = pTSFirst copy = afterPage / nAuxSides test copy eq nCopies then page = bkPage or [ nextCopy = printerForward? nCopies-copy, copy+1 relImageIndex = (afterPage rem nAuxSides)*2 + 1 - auxIncr if printerForward do relImageIndex = nPages -1- relImageIndex page = relImageIndex * (size PageG/16) + pDoc>>DocG.Pages ] ] ifnot // then put paper out - from aux tray or as single sheets from main tray [ afterPage = afterPage - totAuxSides nextHandling = pTSSecond test afterPage < nAuxBreaks then page = 1 or // no image -- just feed breakpage thru [ afterPage = afterPage - nAuxBreaks test afterPage < nSheets*nCopies ifso // if there are more data pages . . . [ copy = afterPage/nSheets nextCopy = printerForward? copy+1, nCopies - copy relImageIndex = afterPage rem nSheets test (relImageIndex +auxIncr) eq nSheets then page = 1 // no image -- just feed page thru or // there is an image to do [ if relImageIndex < incr do nextHandling = pSSMain // single sheet let pageIndex = (afterPage rem (nSheets - auxIncr))*2 + 1 - incr unless printerForward do pageIndex = nPages - 1 - pageIndex page = pageIndex*(size PageG/16) + pDoc>>DocG.Pages ] ] ifnot // no more data pages [ if afterPage