; PRTASM.MAC ; Vers equ 19 SubVers equ ' ' ; ; Prints assembly source code and other program listings on the CP/M ; LST device. 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. ; ; Configured for Epson FX-85 printer. It should work equally well on any ; Epson-compatible printer. ; ; Printer codes may be changed to work with any printer. Changes should ; be made in the first sector of the COM file at the indicated labels. ; The first byte of each string is the number of characters to send, ; followed by the actual string of up to six bytes (seven bytes total). ; ; ELISTR Escape code to put the printer in elite pitch ; (12 characters per inch). ; ; MARSTR Escape code to set the printer's left margin to ; column 8. ; ; RSTSTR Escape code to reset the printer (this program ; cleans up when it's through). ; ; BOLDON Escape code to put the printer in boldface for ; printing the header (emphasized or double-strike). ; ; BOLDOF Escape code to cancel your printer's boldface ; mode. ; ; In addition to the printer strings, there is a byte which determines ; the format of the time string printed in the header: ; ; MTIME Zero selects civilian time (3:00p); non-zero ; selects military time (15:00). ; ; PRTASM was developed using the SLRMAC assembler and SLRNK+ linker: ; slrmac prtasm/m ; slrnkp prtasm/n,/v,/a:100,/j,prtasm,zslib/s,dslibs/s,vlibs/s, ; z3libs/s,syslibs/s,/e ; ; 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 ; 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 ; ; Routines from SYSLIB, Z3LIB, VLIB, DSLIB, and ZSLIB ; ext phl4hc,pafdc,lhldc,lafdc,epstr,lpstr,cout,lout,crlf,lcrlf ext condin,getud,putud,retud,pfn3,lfn3,codend,setdma,initfcb ext f$open,f$close,f$read,bios ext z3vinit,zsyschk,prtname,zfname,z3log,gzmtop,getprt,puter2 ext stndout,stndend,tinit,dinit ext timini,rclock,gstamp ext mdat3,mtimc2,mtimm2 ; 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 . . . ; db 'MTIME>' TimFlg: db 0 ; 0=civilian time; non-zero = military db 'ELISTR>' EliStr: db 2 ; no. of characters db ESC,'M' ; code to set elite pitch db 0,0,0,0 db 'MARSTR>' MarStr: db 3 ; no. of characters db ESC,'l',8 ; code to set left margin db 0,0,0 db 'RSTSTR>' RstStr: db 3 ; no. of characters db ESC,'@',CR ; code to reset printer db 0,0,0 db 'BOLDON>' BoldOn: db 2 ; no. of characters db ESC,'G' ; code to turn boldface on db 0,0,0,0 db 'BOLDOF>' BoldOf: db 2 ; no. of characters db ESC,'H' ; code to turn boldface off db 0,0,0,0 ; 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 'PRTASM 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} {...}',CR,LF db 'At least one input filename is required.',0 MsgStp: db 'Any key skips file ... ^C aborts',0 MsgPrt: db ' 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,' ... 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: lhld Z3EAdr ; set up environment call zsyschk ; check for ZCPR rnz ; (nope!) ; call z3vinit sspd OldStk ; save old stack pointer call gzmtop ; get top of memory sphl ; ..and set new stack pointer ; call Init ; get command line and initialize data call IniClk ; initialize clock 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 '/' ; check for help request jz Usage shld ClPtr ; store pointer lxi h,MsgStp ; tell 'em how to stop this thing call epstr ; ; Now we set up printer ... ; mvi a,LstSt ; check LST status call bios jrnz GotPrt ; (printer is working) call crlf lxi h,MsgNPr call epstr jmp Exit GotPrt: call ResPtr ; reset printer lxi d,EliStr ; to set elite pitch call LstCSt lxi d,MarStr ; set left margin call LstCSt ; ; 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) ; mvi a,10 sta ErCode lxi h,MsgAmb ; no ambiguous filenames call epstr jr GetFil ; GetFl1: call clock ; get time and date call FInit ; initialize page count and DMA lxi d,InpFcb ; open input file call f$open ; open input file jnz NoFile ; (no, abort) ; ; Now we actually start printing ... ; call Head1 ; print first page header jnz UAbort call RdFile PrMore: mov a,m ; get a character cpi CpmEOF ; end of file? jz Finish ; (yes, we're done) ani 7Fh ; mask high bit cpi FF ; formfeed? jrnz PrMor1 ; (no) mvi a,CR ; yes, replace it with crlf call lout mvi a,LF PrMor1: call lout ; print it cpi LF ; linefeed? jrnz PrMor2 ; (nope) call IsLine ; linefeed means end of line jnz UAbort ; (user aborted) PrMor2: call condin ; I know, this seems redundant 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're finished with a file, so wrap up and continue ; Finish: call CloseF ; close file call PrtFF lxi h,MsgDne ; report success call epstr jmp GetFil ; ..and continue ; NoFile: lxi h,MsgNFd ; report file not found call epstr mvi a,10 ; set error flag sta ErCode 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 ; UAbort: call CloseF ; close file call PrtFF ; send formfeed lxi h,MsgUAb ; report file aborted call epstr cpi CtrlC ; ^C? jrz Abort ; (yes, end it here) jmp GetFil ; no, continue ; Abort: call ResPtr ; clear printer jmp Exit ; ; 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 CCP ; ; Subroutines . . . ; ; IsLine -- counts lines on the page, sends formfeed and prints new ; header if end of page (MaxLin) has been reached ; IsLine: push b push d push h lda LinCnt ; get line count inr a ; ..increment it sta LinCnt ; ..and store it back mov b,a ; put line count in B lda MaxLin ; get maximum lines cmp b ; at end of page? jrnz IsLin1 ; (not yet) call Header ; yes, print header jr IsLin2 ; IsLin1: xra a ; set Z flag ora a IsLin2: pop h pop d pop b ret ; ; Header prints a header and starts a new page ; Header: call PrtFF ; send formfeed Head1: xra a ; reset line counter sta LinCnt call condin ; check console for key rnz ; (user abort) call lcrlf ; send CRLF lda SpcStr ; get space count mov b,a mvi a,' ' ; send spaces to printer SpcLp: call lout djnz SpcLp call condin ; check console for key rnz ; (user abort) lxi d,BoldOn ; set for boldface call LstCSt lda ClkFlg ; check date flag ora a cz PrtDat ; (yes, we print the date) lxi h,File ; start filename header call lpstr lxi d,InpFcb+1 call lfn3 mvi a,' ' ; print a space call lout lhld PgNo ; send page number inx h shld PgNo call lhldc lxi d,BoldOf ; turn off boldface call LstCSt call lcrlf ; send two CRLF's call lcrlf xra a ; set Z flag ora a ret ; ; PrtDat -- Prints time and date in header ; PrtDat: lhld DSPtr ; print date-type string call lpstr lxi h,DatStr ; print date call lpstr mvi a,' ' call lout lxi h,TimStr ; print time call lpstr ret ; ; RdFile -- reads a sector 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 beginning 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 ; ; 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: ldax d ; get first character (count) LstC1: ora a ; end of string? rz ; (yes) inx d ; increment pointer push psw ; save count ldax d ; get next character call lout ; ..and send it to LST pop psw ; get count back, dcr a ; ..decrement it, jmp LstC1 ; ..and go again ; ; CloseF -- Closes file ; CloseF: push psw lxi d,InpFcb call f$close pop psw ret ; ; PrtFF -- sends formfeed or crlf's to printer ; PrtFF: push psw ; save AF push b lda FFFlg ; check formfeed flag ora a jrz PrtLF ; (can't formfeed) mvi a,FF ; send formfeed call lout jr PrtLF2 ; PrtLF: lda LinCnt mov b,a lda MaxLin sub b adi 5 mov b,a PrtLF1: call lcrlf djnz PrtLF1 PrtLF2: pop b pop psw ret ; ; ResPtr -- resets printer ; ResPtr: lxi d,RstStr ; and reset printer call LstCSt ret ; ; Init -- get command line, set SpcStr, get MaxLin, 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 mvi a,30 ; set for 96 elite columns - 85 for header sta SpcStr call getprt ; get printer data inx h ; get page length mov a,m sui 8 ; ..subtract 8 lines sta MaxLin ; ..and store it inx h ; get formfeed flag inx h mov a,m sta FFFlg ; lda TimFlg ; civil or military? ora a jrz Init3 ; (civil) lda SpcStr ; military, increment header spaces inr a sta SpcStr Init3: xra a sta ClkFlg ; initialize clock flag sta ErCode ; initialize error code ret ; ; FInit -- called for each new file ; FInit: lhld DmaBuf ; get DMA address call setdma ; ..and set it xra a ; initialize sta PgNo ; ..page number sta PgNo+1 ret ; ; IniClk -- initialize clock, get time and date ; IniClk: call timini ; check for clock jrz NoClk ; (none) lxi h,DatBuf ; get date call rclock jrnz NoClk ; (clock problem) ret ; NoClk: lda SpcStr ; no date, increment space string adi 25 ; ..by 25 spaces sta SpcStr sta ClkFlg ; ..and make date flag non-zero ret ; ; Clock -- get date stamp or current date and time ; Clock: lda ClkFlg ; are we using date? ora a rnz ; (no, return) lxi d,InpFcb ; point to fcb lxi h,StpBuf ; point to stamp buffer call gstamp jrz Clock0 ; (error, so get current time) lxi h,MDate ; set 'modified' string shld DSPtr lxi h,StpBuf+11 ; look at month mov a,m ora a ; do we have a mod date? jrnz Clock3 ; (yes) ; lxi h,CDate ; set 'created' string shld DSPtr lxi h,StpBuf+1 ; do we have a create date? mov a,m ora a jrnz Clock3 ; (yes) ; Clock0: lxi h,PDate ; set 'printed' string shld DSPtr lxi h,DatBuf+1 ; get current date Clock3: dcx h ; point back to beginning of date buffer lxi d,DatStr ; make date printable call mdat3 xra a ; end with a null stax d lxi d,TimStr ; make time printable lda TimFlg ; civil or military? ora a jrnz Clock4 call mtimc2 ; civil time jr Clock5 Clock4: call mtimm2 ; military time Clock5: xra a stax d ret ; ; 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 ',' ; 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 -- Prints DU and filename from InpFcb ; 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 lda call pafdc ; ..and print it mvi a,':' ; print colon call cout lxi d,InpFcb+1 ; print filename call pfn3 call stndend pop psw ret ; ; 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 Environment) DSPtr: ds 2 ; pointer to date-type string DmaBuf: ds 2 ; file buffer OldStk ds 2 ; old stack pointer ClPtr: ds 2 ; stored command line pointer ClBuf: ds 128 ; command line storage InpFcb: ds 36 ; input file control block ; end