; PRTRLE.Z80 ; ; Prints high resolution run length encoded (RLE) graphics files on ; the Epson-compatible graphics printers. For ZCPR3 only. ; Vers equ 20 SubVers equ ' ' ; ; USAGE: ; ; PRTRLE {dir:}fn.ft {{dir:}fn.ft} {...} {/options} ; ; 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. ; ; Medium resolution graphics files are not supported. ; ; OPTIONS: ; ; F Send form feed to printer after each picture (normally ; sent only every three pictures). ; ; H Do not print header (giving filename) above each ; picture. ; ; For details see accompanying documentation file. ; ; HISTORY: ; ; Version 2.0 -- December 26, 1990 -- Gene Pizzetta ; Can't believe I never got around to converting this little ; exercise from my Commodore 128 days. Now it's a Z utility. ; Eliminated several CP/M 3-specific BDOS calls. Added DU ; support. Eliminated warm boot on exit. Incorporated library ; routines. Translated to Zilog opcodes. Added LST-status ; check. Now accepts a file list. Aborts on ^C, but any other ; key skips to next file. Sets error flag. Added configuration ; bytes and command line options. ZCNFG configuration. Epson- ; specific because I don't know how any other printers handle ; graphics. ; ; Version 1.1 -- September 20, 1987 -- Gene Pizzetta ; Added page numbering and boldfacing in header. ; ; Version 1.0 -- September 17, 1987 -- Gene Pizzetta ; First release as CP/M-Plus utility. ; ; Gene Pizzetta ; 481 Revere St. ; Revere, MA 02151 ; ; Voice: (617) 284-0891 ; Newton Centre Z-Node: (617) 965-7259 ; Ladera Z-Node Central: (213) 670-9465 ; GEnie: E.PIZZETTA ; ; System addresses ; CpmFcb equ 05Ch ; default file control block CpmDma equ 080h ; default DMA buffer LstSt equ 15 ; BIOS lst status GSpCnt equ 24 ; number of leading spaces for graphics GLnCnt equ 2 ; number of blank lines after header HSpCnt equ 57 ; number of leading spaces for header ; ; Character codes ; CtrlC equ 03h ; ^C 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 ; ; The following buffer size is in sectors (records) of 128 bytes. ; It may be set to any number of sectors up to a maximum of 255. ; InSiz equ 128 ; input buffer in sectors (128=16K) ; ; Routines from VLIB, Z3LIB, and SYSLIB ; .request vlib,z3lib,syslib ; ext pafdc,epstr,lpstr,pfn3,lfn3,cout,lout,lcrlf,condin,bios ext getud,putud,retud,setdma,initfcb,f$open,f$close,f$read,$memry ext z3vinit,zsyschk,prtname,zfname,z3log,puter2 ext stndout,stndend,tinit,dinit ; public pstr ; jp Start ; db 'Z3ENV',1 ; Z-system header Z3EAdr: dw 0FE00h ; ; Configuration area . . . ; dw 0 ; filler db 'PRTRLE' ; for ZCNFG db Vers/10+'0',Vers mod 10+'0' HdrFlg: db 0 ; FFh=no header FFFlag: db 0 ; FFh=form feed after each picture ; the following printer strings are for Epson FX printers LinStr: db 3 ; no. of characters db ESC,'J',3 ; code for 216-inch line feed db 0,0,0,0,0 DotStr: db 5 ; no. of characters db ESC,'*',5,0,1 ; code for 576-dot graphic line db 0,0,0 BoldOn: db 4 ; no. of characters db ESC,'E',ESC,'G' ; code to turn boldface on db 0,0,0,0 BoldOf: db 2 ; no. of characters db ESC,'F',ESC,'H' ; code to turn boldface off db 0,0,0,0 DftTyp: db 'RLE' ; default filetype ; ; Messages . . . ; MsgUse: db 'PRTRLE Version ' db Vers/10+'0','.',Vers mod 10+'0',SubVers,CR,LF db 'Prints RLE graphics files on Epson printers.',CR,LF db 'Usage:',CR,LF,' ',0 MsgUs1: db ' {dir:}fn{.ft} {...} {/options}',CR,LF db 'Default filetype is RLE.',CR,LF db 'At least one filename required.',CR,LF db 'Options:',CR,LF db ' F ',0 MsgUs2: db 'Form feed after each picture.',CR,LF db ' H ',0 MsgUs3: db 'Print header.',0 MsgDnt: db 'Don''t ',0 MsgStp: db 'Any key skips file ... ^C aborts',0 MsgPrt: db CR,LF,' Printing: ',0 MsgNFd: db BEL,' File not found',0 MsgUAb: db BEL,' Aborted',0 MsgAmb: db BEL,' Ambiguous filename',0 MsgDne: db ' Done',0 MsgMed: db BEL,' Medium resolution not supported',0 MsgNSt: db BEL,' Non-standard RLE file',0 MsgUEx: db BEL,' Unexpected end-of-file',0 MsgNPr: db BEL,CR,LF,' ... Printer off line.',0 MsgInv: db BEL,' Invalid option.',0 ; ; Printer strings . . . ; File: db ' File: ',0 ; ; Start of program . . . ; Start: ld hl,(Z3EAdr) ; set up environment call zsyschk ; check for ZCPR ret nz ; (nope) ; call z3vinit ld (OldStk),sp ; save old stack pointer ld sp,OldStk ; ..and set new stack pointer ; call Init ; get command line and initialize data call tinit ; initialize terminal call putud ; save current DU ; ld hl,ClBuf+1 ; check for input file call EatSpc ; ignore spaces or a jp z,Usage ; (no file given) cp '/' ; check for help request jp z,Usage ld (ClPtr),hl ; store pointer call GetOpt ; check for options ld hl,MsgStp ; tell 'em how to stop this thing call epstr ; ; Now we set up printer ... ; ld a,LstSt ; check LST status call bios jr nz,GetFil ; (printer is working) ld hl,MsgNPr call epstr jp Exit ; ; Re-entry point for printing additional files ; GetFil: call GetFn ; get filename jp z,Exit ; no more files call PrtFn ; print filename or a jr z,GetFl1 ; (not ambiguous) ; ld a,10 ld (ErCode),a ld hl,MsgAmb ; no ambiguous filenames call epstr jr GetFil ; GetFl1: call FInit ; initialize DMA, data, and GetC ld de,InFcb ; open input file call f$open ; open input file jr nz,NoFile ; (no, abort) ; ; Now we actually start printing ... ; call PrtHdr ; print page header GHdrLp: call GetC ; get a character jr z,UExEnd ; (end of file) cp ESC ; escape code? jr z,GetEsc ; (yes, handle it) jr GHdrLp ; and continue ; GetEsc: call GetC ; get character jr z,UExEnd ; (end of file) cp 'G' ; are we setting up for graphics? jr nz,GHdrLp ; (no, try again) call GetC jr z,UExEnd cp 'H' ; set for hires? jr z,PrLoop ; (yes, we can print) cp 'N' ; end of graphics? jr z,Finish ; (yes, we end here) or a ; is it a null? jr z,Finish ; (yes, same as "N") cp 'M' ; medium resolution? jr z,NoMed ; (yes, we can't handle it) jp NonStd ; otherwise, this is non-standard ; PrLoop: call GetC jr z,UExEnd ; (yes, we're done) cp ESC ; an escape? jr z,GetEsc ; (yes, handle it) cp 20h ; a control character? jr c,PrLoop ; (yes, ignore it) sub 20h ; subtract the bias call GrfPrt ; print it jr nz,UAbort ; (aborted by console) jr PrLoop ; ..and return to printing ; ; We're finished with a file, so wrap up and continue ; Finish: call WrapUp ; wrap up graphics ld hl,MsgDne ; report success call epstr jp GetFil ; ..and continue ; NoMed: call WrapUp ld hl,MsgMed ; can't handle medium resolution ld a,4 ; set error code jr ErrEnd ; NonStd: call WrapUp ld hl,MsgNSt ; non-standard file ld a,4 ; set error code jr ErrEnd ; UExEnd: call WrapUp ld hl,MsgUEx ; unexpected end-of-file ld a,4 ; set error code jr ErrEnd ; NoFile: ld hl,MsgNFd ; report file not found ld a,10 ; set error flag ErrEnd: ld (ErCode),a ; store error code call epstr ; print message jp GetFil ; ..and continue ; ; We're aborting here, under various circumstances ... ; Usage: ld hl,MsgUse ; ..print usage message call epstr call prtname ld hl,MsgUs1 call epstr ld a,(FFFlag) or a ld hl,MsgDnt call nz,epstr ld hl,MsgUs2 call epstr ld a,(Header) or a ld hl,MsgDnt call z,epstr ld hl,MsgUs3 call epstr jr Exit ; ..and abort ; UAbort: push af ; save character call WrapUp ; close file and finish up ld hl,MsgUAb ; report file aborted call epstr pop af ; get character cp CtrlC ; ^C? jp nz,GetFil ; no, continue ; ; This is a normal exit ... ; Exit: call dinit ; clear terminal ld a,(ErCode) ; get error code call puter2 ld sp,(OldStk) ; restore old stack ret ; ..and return to CCP ; ; Subroutines . . . ; ; GrfPrt -- sends a graphics dot string to the LST device. The number ; of bytes to send is in A. The dot/no-dot toggle is at DotTog ; GrfPrt: or a jr z,GrfTog push af ; save byte count ld a,(DotCnt) inc a ld (DotCnt),a ld a,(DotTog) call lout ld a,(DotCnt) or a jr z,NewLin GrfPr1: pop af ; recover byte count dec a jr GrfPrt ; GrfTog: ld a,(DotTog) or a jr nz,GrfTg1 inc a ; make A=1 jr GrfTg2 ; GrfTg1: xor a GrfTg2: ld (DotTog),a xor a ret ; ; NewLin -- sets up printer for new line ; NewLin: call condin ; aborted by console? jr nz,NewLn1 ; (yes) ld de,LinStr ; set for 216-inch line feed call LstCSt ld a,CR call lout ld b,GSpCnt ; insert leading spaces ld a,' ' GSpcLp: call lout djnz GSpcLp ld de,DotStr ; set for 576-dot graphics line call LstCSt jr GrfPr1 ; NewLn1: pop bc ; adjust stack ret ; ; WrapUp -- finishes current graphics line and form feeds printer. ; WrapUp: call CloseF ; close file ld a,(DotCnt) ; any characters needed to fill line? inc a ld (DotCnt),a xor a ; send a null call lout ld a,(DotCnt) or a jr nz,WrapUp call lcrlf ; send CR and LF call PrtFF ; send form feed ret ; ; GetC -- returns character from file. Assumes file has been ; successfully opened and GetIni has been called. Returns character ; or ^Z (end-of-file) in A. Zero set (Z) on end of file. ; Affected: AF ; GetC: push bc push de push hl ld a,(GetCnt) ; get counter cp 128 ; done with buffer? jr nc,GetC1 ; (yes, get next sector) GetChr: ld hl,(GetPtr) ; point to current DMA buffer ld e,a ; move counter to DE ld d,0 add hl,de ; ..and add it to HL inc a ; increment counter ld (GetCnt),a ld a,(hl) ; get next character cp CpmEof GetEof: pop hl pop de pop bc ret ; GetC1: ld a,(GetMax) ld d,a ld a,(GetSec) ; get sector count cp d ; end of buffer? jr z,GetC2 ; (yes, we need to read more) inc a ; increment sector count ld (GetSec),a ; ..and store it ld hl,(GetPtr) ; get current sector pointer ld de,128 ; ..add 128 to it add hl,de ld (GetPtr),hl ; ..and store it xor a jr GetChr ; GetC2: ld a,(GetFlg) ; check end-of-file flag cp CpmEof jr z,GetEof ; (end of file) call ReadF ld hl,(InBuf) ld (GetPtr),hl xor a ld (GetSec),a jr GetChr ; ; GetIni -- must be called before first call to GetC. ; GetIni: ld hl,0 ; initialize flags ld (GetMax),hl ld (GetFlg),hl ; and GetSec ld a,128 ld (GetCnt),a ld hl,(InBuf) ; initialize pointer ld (GetPtr),hl ret ; ; ReadF -- read disk into input buffer ; ReadF: ld a,InSiz ld b,a dec a ld (GetMax),a ld hl,(InBuf) ReadF2: call RdSec jr nz,ReadF3 ld de,128 add hl,de djnz ReadF2 ret ; ReadF3: inc b ; we didn't read that last sector ld a,CpmEof ld (GetFlg),a ld a,InSiz ; store number of sectors read sub b ld (GetMax),a ret ; RdSec: call setdma ; set DMA address ld de,InFcb ; ..and read sector call f$read ret ; ; PrtHdr -- prints a header and starts a new page ; PrtHdr: call lcrlf ; send CRLF ld a,(Header) ; do we print a header? or a jr nz,PrtHd1 ; (no) ld b,HSpCnt ; get space count ld a,' ' ; send spaces to printer HSpcLp: call lout djnz HSpcLp ld de,BoldOn ; set for boldface call LstCSt ld hl,File ; start filename header call lpstr ld de,InFcb+1 call lfn3 ld a,' ' ; print a space call lout ld de,BoldOf ; turn off boldface call LstCSt PrtHd1: ld b,GLnCnt GLinLp: call lcrlf ; send CRLF's djnz GLinLp ret ; ; LstCSt -- Print counted string on list device -- Expects address of ; string in DE -- Number of characters in string is first byte, ; followed immediately by string itself. ; LstCSt: ld a,(de) ; get first character (count) ld b,a LstC1: dec b ; end of string? ret m ; (yes) inc de ; increment pointer ld a,(de) ; get next character call lout ; ..and send it to LST jr LstC1 ; ..and go again ; ; CloseF -- Closes file ; CloseF: ld de,InFcb jp f$close ; close file and return ; ; PrtFF -- sends formfeed or crlf's to printer ; PrtFF: ld a,(FFeed) ; do we form feed every time? or a jr nz,PrtFF1 ; (yes) ld a,(PicCnt) ; check picture count inc a ld (PicCnt),a cp 3 ; third picture? ret nz ; (no) xor a ; yes, zero picture count ld (PicCnt),a PrtFF1: ld a,FF jp lout ; send formfeed and return ; ; Init -- get command line, set HSpCnt, and initialize data ; Init: ld hl,CpmDma ; move command line to buffer ld bc,128 ld de,ClBuf ldir ld hl,($memry) ; get end of program ld (InBuf),hl ; ..and set buffer address ; ld hl,(HdrFlg) ; move defaults ld (Header),hl xor a ld (ErCode),a ; initialize error code ld (DotCnt),a ; initialize line character count ld (PicCnt),a ; initialize picture count inc a ld (DotTog),a ; initialize dot toggle ret ; ; FInit -- called for each new file ; FInit: ld hl,(InBuf) ; get buffer address call setdma ; ..and set it ld a,1 ; initialize DotTog ld (DotTog),a jp GetIni ; initialize GetC and return to caller ; ; EatSpc -- Gobbles up spaces and tabs ; EatSpc: ld a,(hl) inc hl cp ' ' ; is it a space? jr z,EatSpc ; (yes) cp TAB ; is it a tab? jr z,EatSpc ; (yes) dec hl ret ; ; GetFn -- gets next filename in file list ; GetFn: call getud ; set default DU ld hl,(ClPtr) ; get pointer GetFn1: call EatSpc ; ignore spaces or a ; end of tail? ret z ; (yes, we're through) cp '/' ret z cp ',' ; comma? inc hl jr z,GetFn1 ; (yes, ignore it) dec hl ld de,InFcb ; point to FCB xor a call zfname ld (ClPtr),hl ; store pointer push af ; save zfname code ld hl,InFcb+9 ; is there a filetype? ld a,(hl) cp ' ' jr nz,GetFn2 ; (yes) push de ld de,DftTyp ; point to default filetype ex de,hl ; ..and move it ld bc,3 ldir pop de ; point to fcb GetFn2: call z3log ; log into file DU call initfcb pop bc ; recover zfname code in B xor a inc a ; return non-zero okay ld a,b ; get back code ret ; ; PrtFn -- Prints DU and filename from InFcb ; PrtFn: push af ld hl,MsgPrt ; report we're printing call epstr call stndout call retud ; get DU ld a,b ; put drive in A add a,'A' ; make it printable call cout ld a,c ; put user in A lda call pafdc ; ..and print it ld a,':' ; print colon call cout ld de,InFcb+1 ; print filename call pfn3 call stndend pop af ret ; ; GetOpt -- look for options. ; GetOpt: ld hl,CLPtr FnLp: ld a,(hl) ; move past filename or a ret z cp ' ' jr z,GetOp1 cp TAB jr z,GetOp1 inc hl jr FnLp ; GetOp1 call EatSpc or a ret z cp '/' jr z,GotOpt jr FnLp ; GotOpt: inc hl ld a,(hl) or a ret z cp 'F' jr z,OptF cp 'H' jr z,OptH cp ' ' jr GotOpt ld hl,MsgInv ; invalid option call epstr ld a,19 ld (ErCode),a jp Exit ; ; option setting routines ; OptF: ld a,(FFeed) cpl ld (FFeed),a jr GotOpt ; OptH: ld a,(Header) cpl ld (Header),a jr GotOpt ; ; Following causes ZSYSCHK to use EPSTR. ; pstr: jp epstr ; ; Uninitialized data . . . ; DSEG ; DotTog: ds 1 ; 1=print dot, 0=no dot DotCnt: ds 1 ; dots-per-line count PicCnt: ds 1 ; current page picture count Header: ds 1 ; FFh=no header FFeed: ds 1 ; FFh=form feed after each picture ErCode: ds 1 ; code for program error flag InBuf: ds 2 ; file buffer address GetMax: ds 1 ; number of sectors read into input buffer GetFlg: ds 1 ; GetC end-of-file flag GetSec: ds 1 ; GetC sector counter GetCnt: ds 1 ; GetC counter GetPtr: ds 2 ; GetC pointer ClPtr: ds 2 ; stored command line pointer ClBuf: ds 128 ; command line storage InFcb: ds 36 ; input file control block ds 50 ; stack OldStk ds 2 ; old stack pointer ; end