; OE.MAC ; Vers equ 15 SubVers equ ' ' ; revision level ; ; Prints odd- or even-numbered pages of NewWord and WordStar files ; to the CP/M LST device. For ZCPR3 and Epson compatible printers only. ; ; USAGE: ; ; OE {dir:}fn.ft {{dir:}fn.ft {...}} /option ; ; At least one input filename is required. If a DIR or DU specification ; is not given, the current drive and/or user is assumed. Multiple ; filenames may be separated by spaces, commas, or both. An option is ; also required and must be either "O" for printing odd-numbered pages ; or "E" for printing even-numbered pages. ; ; For NewWord and WordStar 3.3 under CP/M Plus: To save all the printer ; codes for printing, the file must first be printed to a disk file using ; the PUT command: ; PUT PRINTER {OUTPUT TO} FILE {d:} [NO FILTER] ; After invoking PUT as above, call NewWord or WordStar and send the file ; to the printer in a normal way, but you MUST answer "Y" to the formfeeds ; question if you have not already installed that choice as your default. ; The printer output will be redirected to the disk file given in the PUT ; command line. The file thus created will be printed normally by OE, ; but only odd- or even-numbered pages will be printed, so you can use ; both sides of the pages. Under Z-System, you can use the RECORDER ; IOP to do the same thing as CP/M Plus's PUT. ; ; For WordStar, Release 4: You may follow the same procedure as above, ; but it's easier to ask WordStar to redirect output to a disk file. ; When WordStar requests the name of the printer driver, enter the name ; of the driver followed by ">" and the name of the disk file. For ; example, ; FX85>TEST.PRN ; If you're using your default printer driver, you can answer the prompt ; with only the redirection command: ; >TEST.PRN ; ; This program has been tested on a Commodore 128 with Z3Plus and a ; Micromint SB180FX with ZCPR3, using an Epson FX-85 printer and ; NewWord 2.26 with the Epson FX80 driver, and WordStar 4.0 with the ; Epson FX85 driver. I believe it should work equally well on any CP/M ; 2.x or higher machine running Z-System and using an Epson-compatible ; printer and WordStar. ; ; HISTORY: ; ; Version 1.5 -- July 22, 1990 -- Gene Pizzetta ; OE now accepts a list of files. Whether the printer is put into ; near-letter-quality mode is controlled by a configuration byte ; after the label "NLQ>" in the first sector of the COM file (non- ; zero to send NLQ string to printer). Now sets program error ; flag to 10 if a file is not found or if an ambiguous file spec ; is given, to 19 if no option or an invalid option is given, and ; to 4 if OE is aborted by the user. Added type-3 header. Replaced ; much of the code with library routines. Now initializes and ; de-initializes the terminal. ; ; Version 1.4 -- August 18, 1989 -- Gene Pizzetta ; Added DU support, TCAP support, and "//" to call up help screen. ; CCP is now preserved. Code was modified to use SYSLIB routines, ; OE is now ZCPR3 specific. ; ; Version 1.3 -- October 14, 1988 -- Gene Pizzetta ; Modified to remove CP/M Plus dependencies. (I got an SB180FX.) ; ; Version 1.2 -- January 11, 1988 -- Gene Pizzetta ; Added batch (SUBMIT) compatibility by ensuring that OE feeds the ; same number of pages, whether in odd or even mode. ; ; Version 1.1 -- October 12, 1987 -- Gene Pizzetta ; Added additional trapping code for the WordStar Epson FX-85 ; printer driver. Added optional sending of Near-Letter-Quality ; code sequence at beginning of each run. ; ; Version 1.0 -- June 21, 1987 -- Gene Pizzetta ; First release for NewWord and Epson FX-80 printer driver. ; ; Gene Pizzetta ; 481 Revere Street ; Revere, MA 02151 ; ; Voice: (617) 284-0891 ; Newton Centre Z-Node: (617) 965-7259 ; GEnie: E.PIZZETTA ; ; System addresses ; CpmFcb equ 05Ch ; default file control block CpmDma equ 080h ; default DMA buffer ; ; Character codes ; BEL equ 07h ; bell TAB equ 09h ; tab LF equ 0Ah ; linefeed FF equ 0Ch ; formfeed CR equ 0Dh ; carriage return CpmEof equ 1Ah ; CP/M end of file (^Z) ESC equ 1Bh ; escape ; ; Routines from SYSLIB, Z3LIB, and VLIB ; ext cout,lout,condin,pafdc,phl4hc,epstr,pfn3,crlf,setdma ext codend,getud,putud,retud,initfcb,f$open,f$close,f$read ext z3vinit,stndout,stndend,prtname,gzmtop,z3log,tinit ext dinit,zfname,puter2 ; MACLIB Z80 ; ; TYP3HDR.MAC, Version 1.1 -- Extended Intel Mnemonics ; This code has been modified as suggested by Charles Irvine so that ; it will function correctly with interrupts enabled. ; Extended Intel mnemonics by Gene Pizzetta, April 30, 1989. ; Entry: jr Start0 ; must use relative jump db 0 ; filler db 'Z3ENV',3 ; type-3 environment Z3EAdr: dw 0FE00h ; filled in by Z33 dw Entry ; intended load address ; ; Configuration area . . . ; ; When OE begins sending even-numbered pages to the printer, the printer ; does not know it's supposed to be in near-letter-quality mode because ; the driver only sends that code once for page 1, unless there is an ; additional toggle within the document. Setting the NLQ equate to true ; will cause OE to put the printer into near-letter-quality mode before ; it begins sending the document. ; db 'NLQ>' NLQ: db 0 ; 0 = draft, non-zero = near-letter-quality ; Start0: lxi h,0 ; point to warmboot entry mov a,m ; save the byte there di ; protect against interrupts mvi m,0C9h ; replace warmboot with a return opcode rst 0 ; call address 0, pushing RetAddr onto stack RetAddr: mov m,a ; restore byte at 0 dcx sp ; get stack pointer to point dcx sp ; ..to the value of RetAddr pop h ; get it into HL and restore stack ei ; we can allow interrupts again lxi d,RetAddr ; this is where we should be xra a ; clear carry flag push h ; save address again dsbc de ; subtract -- we should have 0 now pop h ; restore value of RetAddr jz Start ; if addresses matched, begin real code ; lxi d,NotZ33Msg-RetAddr ; offset to message dad d xchg ; switch pointer to message into DE mvi c,9 jmp 0005h ; return via BDOS print string function ; NotZ33Msg: db 'Not Z33+$' ; abort message if not Z33-compatible ; ; Messages . . . ; MsgUse: db 'O/E Version ' db Vers/10+'0','.',Vers mod 10+'0',SubVers db ' (loaded at ',0 MsgUs1: db 'h)',CR,LF db 'Usage:',CR,LF,' ',0 MsgUs2: db ' {dir:}fn.ft {{dir:}fn.ft {...}} /option',CR,LF db 'For Epson FX printers.',CR,LF db 'Option and at least one input filename are required.',CR,LF db 'Options:',CR,LF db ' O print odd-numbered pages',CR,LF db ' E print even-numbered pages',CR,LF db 'Paging in input file must be by formfeeds.',0 MsgInv: db BEL,'Option O or E required.',0 MsgNFd: db BEL,' File not found',0 MsgAmb: db BEL,' Ambiguous Filename',0 MsgOdd: db 'ODD',0 MsgEvn: db 'EVEN',0 MsgStp: db '-numbered pages ... Any key stops printing',0 MsgPrt: db ' Printing: ',0 MsgUAb: db BEL,' Aborted',0 MsgDne: db ' Done',0 ; ; Start of program . . . ; Start: lhld Z3EAdr ; set up environment call z3vinit sspd OldStk ; save old stack pointer call gzmtop ; get top of memory sphl ; ..and set up new stack ; call Init ; get command line and initialize data call tinit ; initialize terminal call condin ; clear console buffer call putud ; save current DU lxi h,ClBuf+1 ; check for input file call EatSpc ; ignore spaces ora a jz Usage ; (no file given) cpi '/' ; help request? jz Usage shld ClPtr ; store pointer call GetOpt call SidRpt ; report which side we're printing lxi h,MsgStp ; tell 'em how to abort call epstr ; ; Re-entry point for printing additional files ; GetFil: call GetFn ; get filename jz Done ; no more files call PrtFn ; print filename ora a jrz GetFl1 ; (not ambiguous) ; lxi h,MsgAmb ; no ambiguous filenames mvi a,10 sta ErCode jmp ErExit GetFl1: call FInit ; initialize page mode lxi d,InpFcb ; open input file call f$open jnz NoFile ; ; Now we actually start printing . . . ; call RdFile PrMore: mov a,m ; get a character cpi ESC ; is it an escape? jrz IsEsc ; (yes, go handle it) cpi CpmEof ; end of file? jz Finish ; (yes, we're done) mov e,a ; move character to E lda Mode ; what's current mode? ora a ; ..non-printing? mov a,e ; get back character jrz PrMor1 ; (yes, skip print) call lout ; no, print it PrMor1: cpi FF ; is it a formfeed? cz IsFF ; (yes, toggle mode) call condin ; check console for abort jnz UAbort ; (user aborted) dcx b ; decrement counter inx h ; increment pointer mov a,c ; check counter ora b jrnz PrMore ; (more to go, continue printing) call RdFile ; buffer finished, read some more file jr PrMore ; ..and return to printing ; ; We've got an escape sequence which might contain a ^Z (EOF) or a FF ; so we've got to send it to the printer if appropriate and ignore ; such inappropriate signals ... ; IsEsc: call EscPrt ; print the escape character mov a,m ; get next byte, check for 3- or ; ..4-byte printer escape code cpi 'Y' ; IF high-speed double-density graphics jrz Is4Byt cpi 'A' ; ELSE IF 72nds line spacing jrz Is3Byt cpi 'l' ; ELSE IF set left margin jrz Is3Byt cpi 'j' ; ELSE IF 216ths reverse LF jrz Is3Byt cpi 'C' ; ELSE IF set page length jrz Is4Byt cpi '3' ; ELSE IF 216ths line spacing jrz Is3Byt cpi 'J' ; ELSE IF 216ths LF jrz Is3Byt cpi 'Q' ; ELSE IF set right margin jrz Is3Byt cpi 'N' ; ELSE IF skip perforations jrz Is3Byt cpi '!' ; ELSE IF master select jrz Is3Byt jmp EscEnd ; ELSE its a 2-byte code ; ENDIF ; Is4Byt: call EscPrt ; print it, and mov a,m ; ..get next byte cpi 'O' ; is it really a 4-byte? jrnz EscEnd ; (no) jr Is3Byt ; print it, and the next one ; Is3Byt: call EscPrt ; print it, and mov a,m ; ..get last byte, EscEnd: call EscPrt ; ..print it, jmp PrMore ; ..and return to MAIN ; ; We're finished with a file, so wrap up and continue ; Finish: call CloseF ; close file lda Mode ; ending before last page? ora a jrz Finis2 ; (no) lda PgMode ; were we printing even pages? cpi 'E' cz PrtFF ; if so, send extra formfeed Finis2: call PrtFF ; send a final formfeed lxi h,MsgDne ; report success call epstr jmp GetFil ; ..and continue ; ; We're aborting here, under various circumstances ... ; Usage: lxi h,MsgUse ; print usage message call epstr lxi h,Entry ; point to load address call phl4hc ; ..and print it lxi h,MsgUs1 call epstr call prtname ; print our name lxi h,MsgUs2 call epstr jr Exit ; ..and abort ; InvOpt: lxi h,MsgInv ; report no option given call epstr mvi a,19 ; set error flag sta ErCode jr Exit ; ..and abort ; NoFile: lxi h,MsgNFd ; report file not found mvi a,10 ; set error flag sta ErCode jr ErExit ; ..and abort ; UAbort: call PrtFF ; send final formfeed lxi h,MsgUAb ; report user aborted mvi a,4 ; set error flag sta ErCode jr ErExit ; ; This is a normal exit ... ; ErExit: call epstr Done: call ResPtr ; reset printer Exit: call dinit ; clear terminal lda ErCode ; get error code call puter2 lspd OldStk ; restore old stack ret ; ..and return to CP/M ; ; Subroutines ... ; ; SidRpt -- prints on console the current side of the page we're ; printing. ; SidRpt: lda PgMode ; find out what mode we're running cpi 'O' ; odd pages? jz SidRp1 ; (yes) lxi h,MsgEvn ; no, must be even pages call epstr ret ; SidRp1: lxi h,MsgOdd ; printing odd pages call epstr ret ; ; CloseF -- Closes file. ; CloseF: push psw lxi d,InpFcb call F$close pop psw ret ; ; PrtFF -- sends formfeed to printer ; PrtFF: push psw ; save AF mvi a,FF call lout pop psw ret ; ; EscPrt is called by the escape sequence trapping routine to ; send the escape sequences to the LST: device. ; EscPrt: mov e,a ; save escape character lda Mode ora a mov a,e ; get back character cnz lout ; if mode=print, print it dcx b ; decrement counter inx h mov a,c ; check counter ora b cz RdFile ; end of buffer, read more file ret ; ; ResPtr -- Resets printer on exit ; ResPtr: mvi a,ESC call lout mvi a,'@' call lout mvi a,CR call lout ret ; ; IsFF toggles the printing mode between "do print" and "don't print" ; everytime a formfeed is found by MAIN ; IsFF lda Mode ; get current mode xri 0FFh ; reverse it sta Mode ; and store it ret ; ; RdFile reads a segment of the disk file into memory and resets ; the counter and buffer pointer ; RdFile: lxi d,InpFcb ; read in a segment of file call f$read lxi b,128 ; buffer size lhld DMABuf ; set pointer to start of DMA buffer rz ; (not end of file) mvi a,CpmEof ; make EOF (^Z) the first byte mov m,a ; ..of the input buffer ret ; ; Init -- get command line and initialize data ; Init: lxi h,CpmDma ; move command line to buffer lxi b,128 lxi d,ClBuf ldir call codend ; get end of program shld DmaBuf ; ..and store it for later xra a sta ErCode ; initialize error code ret ; ; FInit -- called for each new file ; FInit: lhld DmaBuf ; get DMA address call setdma ; ..and set it lda PgMode ; initialize mode cpi 'O' ; odd pages? jrnz FInit1 ; (no) mvi a,0FFh ; set mode to print first page sta Mode jr FInit2 ; FInit1: xra a ; even, so set mode to skip first page sta Mode FInit2: lda NLQ ; check for near letter quality ora a rz ; (nope) mvi a,esc ; send escape sequence call lout ; ..to put printer in mvi a,'x' ; ..in near-letter-quality call lout ; ..mode ... mvi a,1 call lout ret ; ; GetOpt -- gets option from command line and sets mode ; GetOpt: lda ClPtr call Slash ; look for option ora a ; was an option given? jz InvOpt ; (no option) cpi 'E' ; for even pages? jrz Even cpi 'O' ; for odd pages? jnz InvOpt ; report unknown option ; mvi a,'O' ; set PgMode flag to Odd sta PgMode ret ; Even: mvi a,'E' ; set PgMode flag to Even sta PgMode ret ; ; Slash returns with the command tail option in A ; Slash: mvi b,' ' ; look for space in command tail mvi c,0 ; ..or a null call Scan cmp b ; was it a space rnz ; (no, return) inx h ; increment pointer mov a,m ; get next byte cpi '/' ; is it a slash? jrnz Slash ; (no, try again) inx h ; yes, increment pointer again mov a,m ; ..and get option ret ; ; Scan is called by Slash to look for either of two delimiter characters ; in B and C, in this case a space or a null. ; Scan: mov a,m ; get byte cmp b ; is it a space? rz ; (yes, return) cmp c ; a null then? rz ; (yes, return) inx h ; increment pointer jr Scan ; ..and keep looking ; ; EatSpc -- Gobbles up spaces and tabs ; EatSpc: mov a,m inx h cpi ' ' ; is it a space? jrz EatSpc ; (yes) cpi TAB ; is it a tab? jrz EatSpc ; (yes) dcx h ret ; ; GetFn -- gets next filename in file list ; GetFn: call getud ; set default DU lhld ClPtr ; get pointer GetFn1: call EatSpc ; ignore spaces ora a ; end of tail? rz ; (yes, we're through) cpi '/' ; options? rz ; (yes, we're through) cpi ',' ; comma? inx h jrz GetFn1 ; (yes, ignore it) dcx h lxi d,InpFcb ; point to FCB xra a call zfname shld ClPtr ; store pointer call z3log ; log into file DU call initfcb mov b,a ; save ZFNAME code xra a inr a ; return non-zero okay mov a,b ; get back code ret ; ; PrtFn -- say what we're printing ; PrtFn: push psw call crlf lxi h,MsgPrt ; report we're printing call epstr call stndout call retud ; get DU mov a,b ; put drive in A adi 'A' ; make it printable call cout mov a,c ; put user in A call pafdc ; ..and print it mvi a,':' call cout lxi d,InpFcb+1 ; print filename call pfn3 call stndend pop psw ret ; ; Uninitialized data ; DSEG ; Mode: db 0 ; the "do print/don't print" flag PgMode: db 0 ; print odd or even flag ErCode: ds 1 ; program error code DmaBuf: ds 2 ; file buffer address OldStk: ds 2 ; stack pointer storage ClPtr: ds 2 ; command line buffer pointer ClBuf: ds 128 ; command line storage InpFcb: ds 36 ; input file control block ; end ;