; PRTASM.MAC ; Vers equ 20 SubVers equ ' ' ; ; Prints assembly source code and other program listings. For ZCPR3 only. ; ; USAGE: ; ; PRTASM {dir:}fn.ft {{dir:}fn.ft} {...} ; ; 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. ; ; Default configuration is for Epson FX-85 printers, but configuration ; can be easily changed with ZCNFG. ; ; In addition to the printer strings, there is a byte which determines ; whether the time string in the header is printed in civilian or military ; format. ; ; HISTORY: ; ; Version 2.0 -- November 24, 1990 -- Gene Pizzetta ; Just got a larger printer buffer and I wanted it to fill faster, ; so I've tried to speed up PRTASM by adding a larger file buffer ; and making other changes in the code. Also incorporated several ; code improvements suggested by Howard Goldstein. Program size ; was cut by 3 sectors to less that 4K, and speed increased by ; about 20 percent. No longer supports type-3 assembly. Added ; ZCNFG configuration (the hardest part). Converted to Zilog ; mnemonics. ; ; Version 1.9 -- July 18, 1990 -- Gene Pizzetta ; Now prints source of date and time: "Modified", "Created", or ; just "Printed" if no file stamp is available. A few bugs have ; also been squashed, including a rather serious failure to set ; the correct DMA address if the military time flag was false, ; discovered by Howard Goldstein. Howard also found that if a ; a key was pressed while the header was being sent to the ; printer, PRTASM would not detect it. ; ; Version 1.8 -- July 11, 1990 -- Gene Pizzetta ; I found that I was frequently calling PRTASM several times on ; a command line to print several files, so I've modified it to ; accept a list of files. In addition, the file date is usually ; more important than the current date, so now the file date is ; used in the header, if it has a modification (first choice) or ; create stamp, otherwise, the current date is used. J. I. ; Mortensen made some useful modifications, so I added them to ; this version, which I have been working on for a while. But ; we're back to Intel mnemonics. If any files in the file list ; are not found, PRTASM skips to the next file after printing ; an error message. Printing can be aborted at any time with ; ^C. Any other key will abort the file currently being ; printed, and move to the next in the list. A two-byte page ; counter is now used so it will number past 255. Formfeeds ; in the file are trapped and replaced with a single carriage ; return/linefeed. A printer capable of 96 columns in elite ; pitch is assumed. Page length and formfeed ability are ; obtained from the environment. A configuration byte allows ; selection of civil or military time. Patching for non-Epson ; escape codes no longer requires re-assembly. Added a type-3 ; header. Program error flag is set if any file in the file list ; cannot be found or if a file specification is ambiguous. ; ; Version 1.7 -- June 16, 1990 -- J. I. Mortensen ; Translated to Zilog mnemonics. Time of day added to header. ; ZSLIB routines added for printing time and date. Added LST ; status check and ZCPR3 check. Masks high bits for WordStar ; files. ; ; Version 1.6 -- August 7, 1989 -- Gene Pizzetta ; Minor change in header format. ; ; Version 1.5 -- July 26, 1989 -- Gene Pizzetta ; Now prints date in header line if a real time clock is working ; under DateStamper or ZDOS. Must be linked using the version 4 ; libraries. ; ; Version 1.4 -- June 10, 1989 -- Gene Pizzetta ; Slightly changed screen display. ; ; Version 1.3 -- March 11, 1989 -- Gene Pizzetta ; Rewritten again to add DU filespec and ZCPR3 TCAP support, ; and to incorporate SYSLIB routines. It now preserves the CP ; and no longer warm boots. PRTASM is now ZCPR3 specific. ; ; Version 1.2 -- October 13, 1988 -- Gene Pizzetta ; Quickly rewritten to eliminate CP/M Plus dependencies. (I just ; got an SB180FX.) Also added printer clean-up on user abort. ; This version still sends a failure code to the BDOS on error, ; to support CP/M Plus conditional submits. CP/M 2.2 doesn't ; recognize it, but it does no harm. ; ; Version 1.1 -- September 20, 1987 -- Gene Pizzetta ; Added page numbering and boldface filename in header. ; ; Version 1.0 -- September 17, 1987 -- Gene Pizzetta ; First release configured for Epson and compatible printers. ; For CP/M Plus only. ; ; 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 ; ; 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 ZSLIB, DSLIB, VLIB, Z3LIB, and SYSLIB ; .request zslib,dslib,vlib,z3lib,syslib ; ext pafdc,lhldc,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,getprt,puter2 ext stndout,stndend,tinit,dinit ext timini,rclock,gstamp ext mdat3,mtimc2,mtimm2 ; public pstr ; jp Start ; must use relative jump ; db 'Z3ENV',1 ; type-3 environment Z3EAdr: dw 0FE00h ; filled in by Z33 ; ; Configuration area . . . ; dw 0 ; filler db 'PRTASM' ; for ZCNFG db Vers/10+'0',Vers mod 10+'0' TimFlg: db 0 ; 0=civilian time; non-zero = military ; Default configuration for the following printer strings is Epson FX-85. EliStr: db 2 ; no. of characters db ESC,'M' ; code to set elite pitch db 0,0,0,0,0,0 MarStr: db 3 ; no. of characters db ESC,'l',8 ; code to set left margin db 0,0,0,0,0 RstStr: db 3 ; no. of characters db ESC,'@',CR ; code to reset printer db 0,0,0,0,0 BoldOn: db 2 ; no. of characters db ESC,'G' ; code to turn boldface on db 0,0,0,0,0,0 BoldOf: db 2 ; no. of characters db ESC,'H' ; code to turn boldface off db 0,0,0,0,0,0 ; ; Messages . . . ; MsgUse: db 'PRTASM Version ' db Vers/10+'0','.',Vers mod 10+'0',SubVers,CR,LF db 'Usage:',CR,LF,' ',0 MsgUs1: db ' {dir:}fn.ft {...}',CR,LF db 'At least one filename required.',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 MsgNPr: db BEL,CR,LF,' ... Printer off line.',0 ; ; Printer strings . . . ; File: db ' File: ',0 CDate: db ' Created: ',0 MDate: db 'Modified: ',0 PDate: db ' Printed: ',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 IniClk ; initialize clock 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 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,GotPrt ; (printer is working) ld hl,MsgNPr call epstr jp Exit GotPrt: call ResPtr ; reset printer ld de,EliStr ; to set elite pitch call LstCSt ld de,MarStr ; set left margin call LstCSt ; ; Re-entry point for printing additional files ; GetFil: call GetFn ; get filename jr z,Done ; 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 Clock ; get time and date call FInit ; initialize page count, DMA, 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 Head1 ; print first page header PrLoop: call GetC jr z,Finish ; (yes, we're done) and 7Fh ; mask high bit cp FF ; formfeed? call nz,lout ; (no) call z,lcrlf ; yes, replace it with crlf cp LF ; linefeed? jr nz,PrLoop ; (nope) call IsLine ; linefeed means end of line jr nz,UAbort ; (user aborted) jr PrLoop ; ..and return to printing ; ; We're finished with a file, so wrap up and continue ; Finish: call CloseF ; close file call PrtFF ld hl,MsgDne ; report success call epstr jr GetFil ; ..and continue ; NoFile: ld hl,MsgNFd ; report file not found call epstr ld a,10 ; set error flag ld (ErCode),a jr 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 jr Exit ; ..and abort ; UAbort: push af ; save character call CloseF ; close file call PrtFF ; send formfeed 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 ... ; Done: call ResPtr ; reset printer 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 . . . ; ; 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 ; ; IsLine -- counts lines on the page, sends formfeed and prints new ; header if end of page (MaxLin) has been reached ; IsLine: ld hl,LinCnt ; point to line count inc (hl) ; ..AND increment it ld a,(MaxLin) ; get maximum lines cp (hl) ; at end of page? call z,Header ; (yes, print header) jp condin ; check for abort and return ; ; Header prints a header and starts a new page ; Header: call PrtFF ; send formfeed Head1: xor a ; reset line counter ld (LinCnt),a call lcrlf ; send CRLF ld a,(SpcStr) ; get space count ld b,a ld a,' ' ; send spaces to printer SpcLp: call lout djnz SpcLp ld de,BoldOn ; set for boldface call LstCSt ld a,(ClkFlg) ; check date flag or a call z,PrtDat ; (yes, we print the date) ld hl,File ; start filename header call lpstr ld de,InFcb+1 call lfn3 ld a,' ' ; print a space call lout ld hl,(PgNo) ; send page number inc hl ld (PgNo),hl call lhldc ld de,BoldOf ; turn off boldface call LstCSt call lcrlf ; send two CRLF's jp lcrlf ; ..and return ; ; PrtDat -- Prints time and date in header ; PrtDat: ld hl,(DSPtr) ; print date-type string call lpstr ld hl,DatStr ; print date and time jp lpstr ; ; 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,(FFFlg) ; check formfeed flag or a jr z,PrtLF ; (can't formfeed) ld a,FF jp lout ; send formfeed and return ; PrtLF: ld a,(LinCnt) ld b,a ld a,(MaxLin) sub b add a,5 ld b,a PrtLF1: call lcrlf djnz PrtLF1 ret ; ; ResPtr -- resets printer ; ResPtr: ld de,RstStr ; and reset printer jr LstCSt ; ; Init -- get command line, set SpcStr, get MaxLin, 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 call getprt ; get printer data inc hl ; get page length ld a,(hl) sub 8 ; ..subtract 8 lines ld (MaxLin),a ; ..and store it inc hl ; get formfeed flag inc hl ld a,(hl) ld (FFFlg),a ; ld a,(TimFlg) ; civil or military? or a ld a,30 jr z,Init3 ; (civil) inc a Init3: ld (SpcStr),a xor a ld (ClkFlg),a ; initialize clock flag ld (ErCode),a ; initialize error code ret ; ; FInit -- called for each new file ; FInit: ld hl,(InBuf) ; get buffer address call setdma ; ..and set it ld hl,0 ; initialize ld (PgNo),hl ; ..page number jp GetIni ; initialize GetC and return to caller ; ; IniClk -- initialize clock, get time and date ; IniClk: call timini ; check for clock jr z,NoClk ; (none) ld hl,DatBuf ; get date call rclock ret z ; (we have a good clock) ; NoClk: ld a,(SpcStr) ; no date, increment space string add a,25 ; ..by 25 spaces ld (SpcStr),a ld (ClkFlg),a ; ..and make date flag non-zero ret ; ; Clock -- get date stamp or current date and time ; Clock: ld a,(ClkFlg) ; are we using date? or a ret nz ; (no, return) ld de,InFcb ; point to fcb ld hl,StpBuf ; point to stamp buffer call gstamp jr z,Clock0 ; (error, so get current time) ld hl,MDate ; set 'modified' string ld (DSPtr),hl ld hl,StpBuf+11 ; look at month ld a,(hl) or a ; do we have a mod date? jr nz,Clock3 ; (yes) ; ld hl,CDate ; set 'created' string ld (DSPtr),hl ld hl,StpBuf+1 ; do we have a create date? ld a,(hl) or a jr nz,Clock3 ; (yes) ; Clock0: ld hl,PDate ; set 'printed' string ld (DSPtr),hl ld hl,DatBuf+1 ; get current date Clock3: dec hl ; point back to beginning of date buffer ld de,DatStr ; make date printable call mdat3 ld a,' ' ; put a space after date ld (de),a inc de ; make time printable ld a,(TimFlg) ; civil or military? or a jr nz,Clock4 call mtimc2 ; civil time jr Clock5 Clock4: call mtimm2 ; military time Clock5: xor a ld (de),a ret ; ; 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 ',' ; 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 call z3log ; log into file DU call initfcb ld b,a ; save ZFNAME code 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 ; ; Following causes ZSYSCHK to use EPSTR. ; pstr: jp epstr ; ; Uninitialized data . . . ; DSEG ; LinCnt: ds 1 ; current line count PgNo: ds 2 ; current page number StpBuf: ds 15 ; file stamp in BCD DatBuf: ds 6 ; date string in BCD DatStr: ds 9 ; printable date string TimStr: ds 7 ; printable time string SpcStr: ds 1 ; no. of spaces in header ClkFlg: ds 1 ; non-zero = no clock FFFlg: ds 1 ; non-zero = can formfeed ErCode: ds 1 ; code for program error flag MaxLin: ds 1 ; maximum line count (from env) DSPtr: ds 2 ; pointer to date-type string 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