;
;  PROGRAM:  PRINT III (PRINT3)
;  VERSION:  1.3
;  DATE:  17 May 83
;  AUTHOR:  RICHARD CONN
;  PREVIOUS VERSIONS:  1.2 (26 Apr 83), 1.1 (26 Apr 83), 1.0 (22 Apr 83)
;
VERS    equ     13

FALSE   equ     0
TRUE    equ     NOT FALSE

timeok  equ     FALSE   ;TRUE to enable TIME feature, FALSE to disable

;
;       PRINT3 is THE file print utility for ZCPR2.  Installable by GENINS,
; PRINT3 provides a wide range of options for the user.  First, PRINT3 allows
; the user to employ wild cards and file name lists (lists of files separated
; by commas, like: file1,file2,file3,...).  Second, PRINT3 provides the
; following options:
;               E       Exact Mode
;                               No heading appears, no line numbering,
;                               no page numbering, tab expansion, form
;                               feeds
;               F       File Name Toggle
;                               The Heading Line can optionally contain
;                               the name of the file
;               H@head@ Heading Text
;                               The user may specify the text of the heading
;                               to appear at the top of every page
;               I       Inspect Files
;                               The user approves each file to be printed
;                               before the printing process begins
;               L       Toggle Line Numbering
;                               Each line may or may not begin with a line
;                               number
;               M       Multiple Runs Toggle
;                               The user may or may not be prompted to set
;                               the Top of Form on his printer; Multiple Runs
;                               ON means that he will not be prompted
;               N       Toggle Page Numbering
;                               The numbering of each page is turned on or
;                               off
;               Onn     Offset Each Line
;                               Move each line in the indicated number of
;                               characters from the left
;               Snnnn   Skip to Specified Page
;                               Printing begins on the indicated page
;               T       Toggle Time Display
;                               Time/Date information is optionally included
;                               in the page header
;


;
;       This program is Copyright (c) 1982, 1983 by Richard Conn
;       All Rights Reserved
;
;       ZCPR2 and its utilities, including this one, are released
; to the public domain.  Anyone who wishes to USE them may do so with
; no strings attached.  The author assumes no responsibility or
; liability for the use of ZCPR2 and its utilities.
;
;       The author, Richard Conn, has sole rights to this program.
; ZCPR2 and its utilities may not be sold without the express,
; written permission of the author.
;

;
;  BASIC SYSLIB ROUTINES NEEDED BY TEMPLATE
;
ESIZE   EQU     16      ; SIZE OF DIR ENTRY (FROM SYSLIB DIRF ROUTINE)

       EXT     DIRFS   ; DIRECTORY PROCESSOR
       EXT     DIRPACK ; PACK DIRECTORY

       EXT     ZGPINS  ; INIT BUFFERS
       EXT     ZFNAME  ; FILE NAME PROCESSOR

       EXT     INITFCB ; INIT FCB
       EXT     RETUD   ; RETURN CURRENT USER/DISK
       EXT     PUTUD   ; SAVE CURRENT USER/DISK
       EXT     GETUD   ; RESTORE CURRENT USER/DISK
       EXT     LOGUD   ; LOG INTO USER/DISK
       EXT     PRINT   ; PRINT STRING PTED TO BY RET ADR
       EXT     PADC    ; PRINT A IN DEC
       EXT     COUT    ; CONSOLE OUTPUT ROUTINE
       EXT     CONDIN  ; CONDITIONAL INPUT ROUTINE
       EXT     CIN     ; CONSOLE INPUT ROUTINE
       EXT     CAPS    ; CAPITALIZE ROUTINE
       EXT     CRLF    ; NEW LINE ROUTINE
       EXT     CLINE   ; COMMAND LINE STRING SAVE ROUTINE
       EXT     CODEND  ; CODE END COMPUTATION ROUTINE

       EXT     F$OPEN  ; FILE OPEN
       EXT     F$READ  ; BLOCK READ
       EXT     F$CLOSE ; FILE CLOSE

       EXT     EVAL10  ; STRING TO BINARY CONVERSION
       EXT     LHLDC   ; LST: HL AS DEC OUTPUT
       EXT     LPSTR   ; LST: (HL) STRING OUTPUT
       EXT     LPRINT  ; LST: STRING OUTPUT
       EXT     LCRLF   ; LST: NEW LINE
       EXT     LOUT    ; LST: OUTPUT
       EXT     MOVEB   ; MOVEB ROUTINE

       if      timeok
       EXT     TIME    ; TIME Library Module Routine
       endif

;
;  CP/M EQUATES
;
CPM     EQU     0       ; WARM BOOT
BDOSE   EQU     CPM+5   ; BDOS ENTRY
FCB     EQU     CPM+5CH ; FCB
TBUFF   EQU     CPM+80H ; INPUT LINE BUFFER
DEL     EQU     7FH     ; <DEL>
CR      EQU     13      ; <CR>
FF      EQU     12      ; <FF>
LF      EQU     10      ; <LF>
CTRLC   EQU     'C'-'@' ; ^C
CTRLG   EQU     'G'-'@'
CTRLH   EQU     'H'-'@'
CTRLI   EQU     'I'-'@'
CTRLX   EQU     'X'-'@'
CTRLZ   EQU     'Z'-'@'

;
;  OTHER EQUATES
;
EOLD    EQU     0FFH    ; END OF LOAD DELIMITER

;
;  Branch to Start of Program
;
       JMP     START

;
;******************************************************************
;
;  SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format
;
;       This data block precisely defines the data format for
; initial features of a ZCPR2 system which are required for proper
; initialization of the ZCPR2-Specific Routines in SYSLIB.
;

;
;  EXTERNAL PATH DATA
;
EPAVAIL:
       DB      0FFH    ; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES)
EPADR:
       DW      40H     ; ADDRESS OF EXTERNAL PATH IF AVAILABLE

;
;  INTERNAL PATH DATA
;
INTPATH:
       DB      0,0     ; DISK, USER FOR FIRST PATH ELEMENT
                       ; DISK = 1 FOR A, '$' FOR CURRENT
                       ; USER = NUMBER, '$' FOR CURRENT
       DB      0,0
       DB      0,0
       DB      0,0
       DB      0,0
       DB      0,0
       DB      0,0
       DB      0,0     ; DISK, USER FOR 8TH PATH ELEMENT
       DB      0       ; END OF PATH

;
;  MULTIPLE COMMAND LINE BUFFER DATA
;
MCAVAIL:
       DB      0FFH    ; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE?
MCADR:
       DW      0FF00H  ; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE

;
;  DISK/USER LIMITS
;
MDISK:
       DB      4       ; MAXIMUM NUMBER OF DISKS
MUSER:
       DB      31      ; MAXIMUM USER NUMBER

;
;  FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK
;
DOK:
       DB      0FFH    ; ALLOW DISK CHANGE? (0=NO, 0FFH=YES)
UOK:
       DB      0FFH    ; ALLOW USER CHANGE? (0=NO, 0FFH=YES)

;
;  PRIVILEGED USER DATA
;
PUSER:
       DB      10      ; BEGINNING OF PRIVILEGED USER AREAS
PPASS:
       DB      'chdir',0       ; PASSWORD FOR MOVING INTO PRIV USER AREAS
       DS      41-($-PPASS)    ; 40 CHARS MAX IN BUFFER + 1 for ending NULL

;
;  CURRENT USER/DISK INDICATOR
;
CINDIC:
       DB      '$'     ; USUAL VALUE (FOR PATH EXPRESSIONS)

;
;  DMA ADDRESS FOR DISK TRANSFERS
;
DMADR:
       DW      80H     ; TBUFF AREA

;
;  NAMED DIRECTORY INFORMATION
;
NDRADR:
       DW      00000H  ; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY
NDNAMES:
       DB      64      ; MAX NUMBER OF DIRECTORY NAMES
DNFILE:
       DB      'NAMES   '      ; NAME OF DISK NAME FILE
       DB      'DIR'           ; TYPE OF DISK NAME FILE

;
;  REQUIREMENTS FLAGS
;
EPREQD:
       DB      0FFH    ; EXTERNAL PATH?
MCREQD:
       DB      000H    ; MULTIPLE COMMAND LINE?
MXREQD:
       DB      0FFH    ; MAX USER/DISK?
UDREQD:
       DB      000H    ; ALLOW USER/DISK CHANGE?
PUREQD:
       DB      000H    ; PRIVILEGED USER?
CDREQD:
       DB      0FFH    ; CURRENT INDIC AND DMA?
NDREQD:
       DB      0FFH    ; NAMED DIRECTORIES?
Z2CLASS:
       DB      11      ; CLASS 11
       DB      'ZCPR2'
       DS      10      ; RESERVED

;
;  END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA
;
;******************************************************************
;

;
;  **** Special Initial Value Area for GENINS
;
LWIDTH:
       DB      132     ; WIDTH OF LINE
LTPP:
       DB      44      ; LINES OF TEXT PER PAGE
LSPP:
       DB      5       ; LINES TO SKIP PER PAGE
CWIDTH:
       DB      80      ; WIDTH OF SCREEN
CTPP:
       DB      22      ; LINES OF TEXT PER SCREEN
CSPP:
       DB      1       ; LINES TO SKIP PER SCREEN
;
;  NOTE:  LTPP + LSPP + 2 (HEADER SIZE) = TOTAL LINES PER PAGE ON PRINTER
;  NOTE:  CTPP + CSPP + 1 (FOOTER SIZE) = TOTAL LINES PER SCREEN ON CONSOLE
;
DLNUMFL:
       DB      0       ; LINE NUMBER FLAG (DEFAULT TO NO)
DPNUMFL:
       DB      0FFH    ; PAGE NUMBER FLAG (DEFAULT TO YES)
DEXACT:
       DB      0       ; EXACT PRINT FLAG (DEFAULT TO NO)
DFNPFL:
       DB      0FFH    ; FILE NAME PRINT FLAG (DEFAULT TO YES)
DTIMEPFL:
       DB      0FFH    ; TIME PRINT FLAG (DEFAULT TO YES)
DMULTFL:
       DB      0FFH    ; MULTIPLE RUN FLAG (DEFAULT TO YES)
DINSPECT:
       DB      0       ; INSPECT FILES (DEFAULT TO NO)
DOFFSET:
       DB      0       ; INITIAL COLUMN OFFSET (DEFAULT TO NO COLUMNS)
;
;  OTHER BUFFERS
;
SKIPFL:
       DB      0       ; SKIP FLAG (DEFAULT TO NO)
SKIPNUM:
       DS      2       ; PAGE NUMBER TO SKIP TO
LNUM:
       DS      2       ; CURRENT LINE NUMBER
PNUM:
       DS      2       ; CURRENT PAGE NUMBER
HBUF:
       DS      100     ; BUFFER FOR HEADING
;
       if      timeok
TIMEBF:
       DS      100     ; BUFFER FOR TIME STAMP
       endif
;
LNUMFL:
       DB      0       ; LINE NUMBER FLAG (DEFAULT TO NO)
PNUMFL:
       DB      0FFH    ; PAGE NUMBER FLAG (DEFAULT TO YES)
EXACT:
       DB      0       ; EXACT PRINT FLAG (DEFAULT TO NO)
FNPFL:
       DB      0FFH    ; FILE NAME PRINT FLAG (DEFAULT TO YES)
TIMEPFL:
       DB      0FFH    ; TIME PRINT FLAG (DEFAULT TO YES)
MULTFL:
       DB      0FFH    ; MULTIPLE RUN FLAG (DEFAULT TO YES)
INSPECT:
       DB      0       ; INSPECT FILES (DEFAULT TO NO)
OFFSET:
       DB      0       ; OFFSET ON PAGE

;
;  Start of Program
;
START:
       LXI     H,0     ; GET STACK PTR
       DAD     SP
       SHLD    STACK   ; SAVE IT
       LXI     SP,STACK        ; SET SP
       CALL    PUTUD   ; SAVE CURRENT USER/DISK AWAY
       CALL    RETUD   ; GET CURRENT USER/DISK
       MOV     A,B     ; SAVE DISK
       STA     DISK
       MOV     A,C     ; SAVE USER
       STA     USER

       CALL    ZGPINS  ; INIT BUFFERS

       LXI     H,TBUFF ; SAVE COMMAND LINE
       CALL    CLINE
       SHLD    CMDLNE  ; SAVE PTR

;
;  **** Banner of Program
;
       CALL    PRINT
       DB      'PRINT III,  Version '
       DB      VERS/10+'0','.',(VERS MOD 10)+'0',0

;
;  Check for Help Request
;
       LDA     FCB+1   ; GET FIRST CHAR OF FILE NAME
       CPI     ' '     ; NO FILE SPEC?
       JZ      HELP
       CPI     '/'     ; OPTION CAUGHT?
       JNZ     ECONT

;
;  **** Print Help Information
;
HELP:
       CALL    PRINT
       DB      CR,LF,'PRINT III Command --'
       db      cr,lf,' PRINT III is invoked by the following command line:'
       db      cr,lf,'         PRINT file1,file2,...,filen o...'
       db      cr,lf,'where each "filen" is an ambiguous file name and type'
       db      cr,lf,'and "o" is zero or more of the following options:'
       db      cr,lf,' E       Exact Print (expand tabs, form feed, no line'
       db      cr,lf,'                 or page numbers, no heading)'
       db      cr,lf,' F       Toggle File Name Display'
       db      cr,lf,' H@head@ Specify Page Heading (@ is any printing char)'
       db      cr,lf,' I       Inspect and Select Files First'
       db      cr,lf,' L       Toggle Numbering of Each Line'
       db      cr,lf,' M       Toggle Multiple Runs (MR=No TOF Msg)'
       db      cr,lf,' N       Toggle Numbering of Each Page'
       db      cr,lf,' Onn     Offset Printout by nn Characters from Left'
       db      cr,lf,' Snnnn   Skip to Specified Page before Printing'
;
       if      timeok
       db      cr,lf,' T       Toggle Time Display (if available) in header'
       endif
;
       db      cr,lf,'Examples:'
       db      cr,lf,'         PRINT MYFILE.TXT,*.MAC LH''SAMPLE'''
       db      cr,lf,'                 -- Number Lines, SAMPLE is Heading'
       db      cr,lf,'         PRINT MYFILE.* S25E'
       db      cr,lf,'                 -- Skip to Page 25, Exact Print'
       db      cr,lf,' At any time, ^C aborts PRINT III and ^X skips to next '
       db      'file'
       DB      CR,LF,0

;
;  RETURN TO OS
;
RETURN:
       LHLD    STACK   ; GET OLD STACK
       SPHL            ; SET IT
       RET

;
;  PROGRAM'S INIT ROUTINE
;
ECONT:
       CALL    INIT    ; PROG INIT ROUTINE
;
;  EXTRACT FLAGS IF PRESENT
;
       LXI     H,0     ; SET FILE COUNT
       SHLD    FILECNT
       LHLD    CMDLNE  ; PT TO BUFFER
;
;  SKIP TO FILE NAME STRING
;
       CALL    SBLANK  ; SKIP OVER BLANKS
;
;  SKIP TO END OF FILE NAME STRING
;
       CALL    SNBLANK ; SKIP OVER NON-BLANKS
;
;  CHECK FOR LEADING SLASH ON OPTION AND SKIP IT IF SO
;
OPT:
       CPI     '/'     ; OPTION CHAR?
       JNZ     OPTION
       INX     H       ; SKIP SLASH
;
;  PROCESS LIST OF OPTIONS
;
OPTION:
       MOV     A,M     ; GET BYTE
       ORA     A       ; DONE?
       JZ      DSPEC
       INX     H       ; PT TO NEXT CHAR
       CPI     ' '     ; SKIP OVER SPACES
       JZ      OPTION
       MOV     C,A     ; COMMAND IN C
       LXI     D,OPTAB ; PT TO OPTION TABLE
OPTL:
       LDAX    D       ; GET OPTION LETTER
       ORA     A       ; END OF TABLE?
       JZ      HELP    ; HELP IF SO
       CMP     C       ; MATCH?
       JZ      OPTM    ; PROCESS IF SO
       INX     D       ; PT TO NEXT ENTRY
       INX     D
       INX     D
       JMP     OPTL
;
;  PROCESS OPTION
;
OPTM:
       PUSH    H       ; SAVE HL ON STACK
       LXI     H,OPTION        ; GET RETURN ADDRESS
       XTHL            ; ... ON STACK AND RESTORE HL
       INX     D       ; PT TO ADDRESS
       LDAX    D       ; GET ADDRESS LOW
       MOV     B,A     ; ... IN B
       INX     D
       LDAX    D       ; GET ADDRESS HIGH
       MOV     D,A     ; ... IN D
       MOV     E,B     ; LOW IN E
       PUSH    D       ; PUT ADDRESS ON STACK
       MOV     A,C     ; COMMAND IN A
       RET             ; "CALL" OPTION ROUTINE

;
;  **** PROGRAM INIT ROUTINE
;       THIS ROUTINE IS USED BY THE PROGRAM TO PERFORM ANY INITS
;
INIT:
       lxi     h,dlnumfl       ;copy defaults into buffers
       lxi     d,lnumfl
       mvi     b,8             ;8 bytes
       call    moveb           ;do copy
       xra     a       ;A=0
       sta     skipfl  ;set no skip
       sta     hbuf    ;set no heading
;
       if      timeok
       call    time    ;get time string
       lxi     d,timebf        ;store in buffer
initt:
       mov     a,m     ;get byte
       stax    d       ;put byte
       inx     h       ;pt to next
       inx     d
       ora     a       ;done?
       jnz     initt
       endif
;
       RET

;
;  **** OPTION TABLE
;       EACH OPTION IS A CAPITAL LETTER OR SPECIAL CHAR FOLLOWED BY
;               AN ADDRESS; THE TABLE IS TERMINATED BY A BINARY ZERO
;
OPTAB:
       db      'E'
       dw      optexact
       db      'F'
       dw      optfn
       db      'H'
       dw      opthd
       db      'I'
       dw      optinsp
       db      'L'
       dw      optln
       db      'M'
       dw      optmult
       db      'N'
       dw      optpn
       db      'O'
       dw      optoffs
       db      'S'
       dw      optskip
;
       if      timeok
       db      'T'
       dw      opttime
       endif
;
       DB      0       ; END OF TABLE
;
;  Option:  E (Toggle exact mode)
;
optexact:
       lda     exact   ;get flag
       cma             ;flip it
       sta     exact   ;put flag
       ret
;
;  Option:  F (Toggle file name display)
;
optfn:
       lda     fnpfl   ;get flag
       cma             ;flip it
       sta     fnpfl   ;put flag
       ret
;
;  Option:  H (Set Heading)
;
opthd:
       lxi     d,hbuf  ;pt to heading buffer
       mov     a,m     ;get delim
       ora     a       ;none?
       rz
       mov     b,a     ;delim in B
       inx     h       ;pt to next char
opthd1:
       mov     a,m     ;get next char
       ora     a       ;done?
       jz      opthd3
       cmp     b       ;done by trailing delim?
       jz      opthd2
       stax    d       ;save char
       inx     h       ;pt to next
       inx     d
       jmp     opthd1
opthd2:
       inx     h       ;skip over delim
opthd3:
       xra     a       ;store ending 0
       stax    d
       ret
;
;  Toggle Inspect Option
;
optinsp:
       lda     inspect ;flip flag
       cma
       sta     inspect
       ret
;
;  Set Line Number Flag
;
optln:
       lda     lnumfl  ;flip flag
       cma
       sta     lnumfl
       ret
;
;  Set Multiple Run Flag
;
optmult:
       lda     multfl  ;flip flag
       cma
       sta     multfl
       ret
;
;  Set Page Numbering Flag
;
optpn:
       lda     pnumfl  ;flip flag
       cma
       sta     pnumfl
       ret
;
;  Set Page Offset
;
optoffs:
       call    eval10  ;get number
       mov     a,e     ;get low-order byte
       sta     offset  ;set offset
       ret
;
;  Set Skip Flag and get number
;
optskip:
       mvi     a,0ffh  ;set flag
       sta     skipfl
       call    eval10  ;get number
       xchg
       shld    skipnum ;set page number to skip to
       xchg            ;HL pts to next char
       mov     a,d     ;see if page number was zero
       ora     e
       rnz
       xra     a       ;if zero, just turn off skip flag
       sta     skipfl
       ret
;
       if      timeok
;
;  Set Time Flag
;
opttime:
       lda     timepfl ;flip flag
       cma
       sta     timepfl
       ret
;
       endif
;
;  BEGIN MOVING THROUGH FILE NAMES, SEPARATED BY COMMAS
;
DSPEC:
       LHLD    CMDLNE  ; PT TO FIRST BYTE
       CALL    SBLANK  ; SKIP TO NON-BLANK
;
;  MAJOR REENTRY POINT WHEN FILE SPECS ARE SEPARATED BY COMMAS
;    HL PTS TO FIRST BYTE OF NEXT FILE SPEC
;
DSPEC1:
       LXI     SP,STACK        ; RESET STACK
       CALL    GETUD   ; RESET USER IF NECESSARY
       LXI     D,NTFCB ; PT TO FCB IN DE, PT TO FIRST CHAR OF FILE NAME IN HL

       CALL    ZFNAME  ; EXTRACT FILE NAME INTO FCB, AND GET DISK AND USER
       JZ      DERR    ; ERROR HANDLER

       SHLD    NEXTCH  ; SAVE PTR TO DELIMITER WHICH ENDED SCAN
       MOV     A,B     ; SAVE POSSIBLE DRIVE SPEC
       CPI     0FFH    ; CURRENT DISK?
       JZ      DSPEC2
       LDA     MDISK   ; GET MAX DISK NUMBER
       DCR     B       ; ADJUST TO WITHIN BOUNDS 0-15
       CMP     B       ; WITHIN BOUNDS?
       MOV     A,B     ; GET DISK NUMBER IN A
       JNC     USPEC
DERR:
       CALL    PRINT
       DB      CR,LF,'Invalid Disk Specification',0
       JMP     DRETURN

;
;  SET CURRENT DISK
;
DSPEC2:
       LDA     DISK    ;GET CURRENT DISK
       MOV     B,A     ;... IN B

;  CHECK FOR USER NUMBER
USPEC:
       MOV     A,C     ; GET NEW USER NUMBER
       CPI     0FFH    ; DEFAULT USER?
       JZ      USPEC1
       CPI     '?'     ; ALL USERS NOT ALLOWED?
       JZ      UERR
       LDA     MUSER   ; GET MAX USER NUMBER
       CMP     C
       MOV     A,C     ; USER NUMBER IN A
       JNC     FCT
UERR:
       CALL    PRINT
       DB      CR,LF,'Invalid User Number',0
       JMP     DRETURN
USPEC1:
       LDA     USER    ;GET CURRENT USER
       MOV     C,A     ;... IN C

;
;  LOAD DIRECTORY AND PERFORM FUNCTION
;
FCT:
       MOV     A,B     ; SAVE NEW DISK/USER AWAY
       STA     CDISK   ; CURRENT DISK
       MOV     A,C
       STA     CUSER   ; CURRENT USER
       CALL    LOGUD   ; LOG INTO ACCOUNT
       CALL    CODEND  ; PT TO END OF CODE
       CALL    RETUD   ; GET USER NUMBER FOR DIRFS
       MVI     A,11000000B     ; SELECT SYS AND NON-SYS FILES
       ORA     C       ; OR IN USER NUMBER
       LXI     D,NTFCB ; PT TO FCB
       CALL    INITFCB ; INIT THE FCB
       CALL    DIRFS   ; LOAD DIR, SELECT FILES, PACK, AND ALPHABETIZE
;
;  DETERMINE BEGINNING OF SCRATCH AREA (SCRATCH) AND SZIE IN PAGES (BCNT)
;
       PUSH    H       ; SAVE PTR AND COUNT
       PUSH    B
       LXI     D,ESIZE ; SET PTR TO NEXT FREE BLOCK
FCTFRE:
       MOV     A,B     ; DONE?
       ORA     C
       JZ      FCTFR1
       DAD     D       ; PT TO NEXT
       DCX     B       ; COUNT DOWN
       JMP     FCTFRE
FCTFR1:
       INR     H       ; NEXT PAGE
       MVI     L,0
       SHLD    SCRATCH ; SET PTR TO SCRATCH AREA
       XCHG            ; PTR IN DE
       LHLD    BDOSE+1 ; COMPUTE BLOCK BUFFER SIZE
       MOV     A,H     ; ADJUST FOR ZCPR2
       SUI     10
       SUB     D       ; A=SIZE IN BLOCKS
       STA     BCNT    ; SET BLOCK COUNT
       POP     B       ; RESTORE AND SAVE REGS
       POP     H
;
;  ALLOW USER TO INSPECT FILES
;
       PUSH    H
       PUSH    B
       CALL    ICHECK  ; CHECK FOR INSPECT OPTION AND INSPECT IF SET
       POP     B       ; RESTORE COUNT AND PTR
       POP     H

;
;  PERFORM FUNCTION; HL PTS TO FILE AND BC CONTAINS NUMBER OF FILES
;
FCTL:
       MOV     A,B             ; CHECK FOR COMPLETION (COUNT = 0)
       ORA     C
       JZ      FCTL1
       DCX     B               ; COUNT DOWN
       LXI     SP,STACK        ; RESET STACK
       PUSH    B               ; SAVE COUNT AND POINTER
       PUSH    H
       CALL    FUNCTION        ; PERFORM FUNCTION
FCTLNXT:
       LXI     SP,STACK-4      ; RESTORE STACK
       POP     H               ; GET PTR AND COUNT
       POP     B
       LXI     D,ESIZE         ; PT TO NEXT ENTRY
       DAD     D
       JMP     FCTL

;
;  CHECK FOR NEXT FILE SPEC
;
FCTL1:
       CALL    GETUD   ; RETURN TO HOME USER/DISK
       LHLD    NEXTCH  ; GET PTR
       MOV
A,M     ; GET DELIM
       CPI     ','     ; ANOTHER FILE?
       JNZ     DRETURN
       INX     H       ; PT TO CHAR AFTER COMMA
       JMP     DSPEC1  ; CONTINUE PROCESSING

;
;  **** EMERGENCY ABORT
;
ABORT:
       CALL    PRINT
       DB      CR,LF,'** PRINT III Abort **',CR,LF,0
       CALL    GETUD   ; RETURN HOME AND FALL THRU TO DRETURN
;
;  **** FUNCTION COMPLETE -- CLEANUP AND EXIT
;       FILL THIS IN WITH CLEANUP CODE FOR EXIT
;
DRETURN:
       JMP     RETURN

;
;  **** INSPECT FILES -- THIS ROUTINE IS TO PERFORM A FILE INSPECTION
;       ON INPUT, HL PTS TO FIRST 16-BYTE ENTRY AND BC=NUMBER OF ENTRIES
;
ICHECK:
       mov     a,b     ;any files?
       ora     c
       rz
       push    h       ;save ptrs
       push    b
       lxi     d,esize ;size of entry
ichk1:
       mvi     m,0     ;clear MSBytes
       dad     d       ;pt to next
       dcx     b       ;count down
       mov     a,b     ;done?
       ora     c
       jnz     ichk1
       pop     b       ;restore ptrs
       pop     h
       lda     inspect ;inspect?
       ora     a       ;0=no
       rz
       call    print
       db      cr,lf,'PRINT III File Inspect Mode',0
ichk2:
       call    print
       db      cr,lf,'Select ',0
       call    prfn    ;print file name
       call    print
       db      ' -- (Y/N/Q=Select Rest/S=Skip Rest/other=Y)? '
       db      0
       call    cin     ;get response
       call    caps    ;capitalize
       call    cout    ;echo
       cpi     'Q'     ;select rest?
       jz      ichkyr
       cpi     'S'     ;skip rest
       jz      ichknr
       cpi     'N'     ;no to this one?
       jnz     ichk3
       mvi     m,0ffh  ;set NO flag in file FCB
ichk3:
       dad     d       ;pt to next one
       dcx     b       ;count down
       mov     a,b     ;done?
       ora     c
       jnz     ichk2
       RET
;  Check Rest of Files as Selected
ichkyr:
       call    print
       db      cr,lf,' Rest of Files Selected',0
       ret
;  Check Rest of Files as NOT Selected
ichknr:
       mvi     m,0ffh  ;set NO flag
       dad     d       ;pt to next
       dcx     b       ;count down
       mov     a,b     ;done?
       ora     c
       jnz     ichknr
       call    print
       db      cr,lf,' Rest of Files NOT Selected',0
       ret
;
;  **** FUNCTION -- MAIN FUNCTION OF TEMPLATE
;       ON ENTRY, HL PTS TO NAME OF FILE (16 BYTES) AND USER IS LOGGED INTO
;               DIRECTORY CONTAINING INDICATED FILE
;
FUNCTION:
;
;  FILE PRINT Routine -- Print the File Whose Name is Pointed to by
;       HL; we are already logged into the correct directory
;
       mov     a,m     ;file selected?
       ora     a       ;0=yes
       rnz
       lda     multfl  ;multiple runs?
       ora     a       ;0=no
       jnz     fprint  ;go right into function
       call    print
       db      cr,lf,'File: ',0
       call    prfn    ;print file name
       call    print
       db      ' -- Please Set Top of Form'
       db      cr,lf,' Strike Any Key When Ready ',0
       call    cin     ;get response
       cpi     ctrlc   ;abort?
       jz      abort
       cpi     ctrlx
       rz
fprint:
       call    prinit  ;init print buffers
       call    fload   ;load buffer initially
       call    prhead  ;print heading line
       lhld    scratch ;pt to first char in file
       shld    nxtln   ;set pointer to next line
fprloop:
       call    prline  ;print line of file
       jnz     fprloop ;done if EOF
       lda     ltpp    ;check for new page
       mov     b,a
       lda     lcount  ;LTPP and Line Count are Equal if New Page
       cmp     b       ;equal?
       jnz     fprlp1
       call    lcrlf   ;advance 2 lines
       call    lcrlf
fprlp1:
       call    page    ;advance to top of next page
       ret
;
;  Init Print Buffers and Print File Name
;
prinit:
       lxi     d,tfcb  ;set up FCB
       mvi     b,12    ;12 bytes
       call    moveb
       lxi     h,0     ;HL=0
       shld    lnum    ;set line number
       inx     h       ;HL=1
       shld    pnum    ;set page number
       lda     ltpp    ;set line count
       sta     lcount
       call    print
       db      cr,lf,'Printing File ',0
       lxi     h,tfcb  ;pt to FCB
       call    prfn    ;print file name
       ret
;
;  FILE LOAD (FLOAD) Routine -- Initial Load of memory buffer
;
fload:
       lxi     d,tfcb  ;pt to file fcb
       call    initfcb ;init file's fcb
       call    f$open  ;open file for input
       jz      fload1  ;open was OK
       call    print
       db      cr,lf,'File ',0
       xchg            ;HL pts to FCB
       call    prfn    ;print file name
       call    print
       db      ' NOT Found',0
       pop     d       ;clear return address
       ret             ;abort printout of this file
;
;  This is an entry point for further memory loads of the file
;
fload1:
       lda     bcnt    ;get number of blocks to load
       mov     c,a     ;... in C
       lhld    scratch ;get address of first block to load into
       shld    nxtblk  ;set pointer to next block to load
fload2:
       call    rdblk   ;read a block (128 bytes)
       jnz     eof     ;eof encountered?
       call    rdblk   ;read another block (128 bytes)
       jnz     eof     ;eof encountered?
       dcr     c       ;count down
       jnz     fload2
       lhld    nxtblk  ;pt to next byte to load
       mvi     m,eold  ;mark end of load
       ret
eof:
       lxi     d,tfcb  ;close file
       call    f$close
       lhld    nxtblk  ;ensure ^Z
       mvi     m,ctrlz
       ret
rdblk:
       lxi     d,tfcb  ;pt to FCB
       call    f$read  ;read next block
       ora     a       ;error?
       rnz
       lhld    nxtblk  ;get ptr to next block
       xchg            ; as dest
       lxi     h,tbuff ;ptr to DMA address
       mvi     b,128   ;copy 128 bytes
rdblk1:
       mov     a,m     ;get byte
       ani     7fh     ;mask out msb
       stax    d       ;put byte
       inx     h       ;pt to next
       inx     d
       dcr     b       ;count down
       jnz     rdblk1
       xchg            ;new nxtblock
       shld    nxtblk
       ret

;
;  Line Print Routine
;       Print Next Line with Optional Disk Load
;       Input Parameter is NXTLN, which is the address of the first char
; on the next line
;       Output Parameter is Zero Flag, with Z meaning done with print, NZ
; meaning more yet to print
;
prline:
       call    proffs  ;print offset
       lhld    lnum    ;increment line number
       inx     h
       shld    lnum
       lhld    nxtln   ;pt to first char of next line
       mvi     c,0     ;init char count
       mov     a,m     ;get first char of line
       cpi     ctrlz   ;EOF?
       cnz     prlnum  ;print line number (optional)
prl1:
       mov     a,m     ;get char
       cpi     eold    ;end of load?
       jz      prload
       cpi     ctrlz   ;eof?
       jz      prexit
       inx     h       ;pt to next char
       cpi     ctrli   ;tab?
       jz      prtab
       cpi     cr      ;<CR>?
       jz      prcr
       cpi     ff      ;form feed?
       jz      prff
       cpi     lf      ;end of line?
       jz      prldn
       cpi     ctrlh   ;back space?
       jz      prbs
       cpi     ctrlg   ;ring bell?
       jz      prbell
       cpi     del     ;delete char?
       jz      prl1    ;skip it
       cpi     ' '     ;other control char?
       jc      prl1    ;skip if other control char
       call    prout   ;print char
       inr     c       ;increment char count
       call    eoltest ;check to see if at end of line and newline if so
       jmp     prl1
;
;  End of Load Reached -- Load More of File from Disk
;
prload:
       push    b       ;save char count
       call    fload1  ;use load routine
       pop     b       ;get char count
       lhld    scratch ;next byte is here
       jmp     prl1    ;continue processing
;
;  Tabulate
;
prtab:
       mvi     a,' '   ;space
       call    prout
       inr     c       ;new char
       call    eoltest ;process EOL
       mov     a,c     ;done?
       ani     7
       jnz     prtab   ;continue tabulation
       jmp     prl1    ;continue processing
;
;  Exit with Zero Flag Set if Done
;
prexit:
       xra     a       ;set zero flag
       ret
;
;  Carriage Return -- Reset Character Count and Continue
;
prcr:
       call    prout   ;send CR to printer
       mvi     c,0     ;reset char count
       jmp     prl1    ;continue processing
;
;  Form Feed -- Advance to Top of Next Page
;
prff:
       call    page    ;page eject with heading
       mvi     c,0     ;reset char count
       jmp     prl1    ;continue processing
;
;  Line Feed -- End of Routine
;
prldn:
       call    prout   ;echo LF to printer
       shld    nxtln   ;set ptr to first char of next line
       mvi     a,0ffh  ;set not done
       ora     a       ;set flags
       ret
;
;  Backspace on Printer
;
prbs:
       mov     a,c     ;check for beginning of line
       ora     a
       jz      prl1    ;continue if at BOL
       mvi     a,ctrlh ;backspace
       call    prout
       dcr     c       ;back up char position
       jmp     prl1    ;continue
;
;  Ring Bell on Printer
;
prbell:
       call    prout   ;ring the bell
       jmp     prl1    ;continue without advancing char position
;
;  Test for End of Line and Process if so
;
eoltest:
       lda     offset  ;get offset
       mov     b,a     ;... in B
       lda     lwidth  ;get line width
       sub     b       ;compute remaining width
       sui     4       ;4 chars less for continuation mark
       mov     b,a     ;result in B
       lda     lnumfl  ;line numbering (lines are 7 chars shorter if so)
       ora     a       ;0=no
       jz      eolt1
       mov     a,b     ;reduce by 7 for line numbers
       sui     7
       mov     b,a
eolt1:
       mov     a,b     ;get line width
       cmp     c       ;there?
       rnz             ;continue if not
       mov     a,m     ;get next char
       cpi     cr      ;new line next?
       rz              ;continue if so
       cpi     ctrlh   ;backspace next?
       rz              ;continue if so
       mvi     a,' '   ;print continuation chars
       call    prout
       mvi     a,'<'
       call    prout
       mvi     a,'<'
       call    prout
       mvi     a,cr    ;new line
       call    prout
       mvi     a,lf
       call    prout
       mvi     c,0     ;reset char position
       lda     skipfl  ;skipping?
       ora     a       ;0=no
       rnz
       lda     lnumfl  ;printing line numbers?
       ora     a       ;0=no
       rz
       call    lprint
       db      '     : ',0
       ret
;
;  Output a character to the printer
;       A = Character
;
prout:
       mov     b,a     ;char in B
       call    condin  ;check for abort
       jz      prout1
       cpi     ctrlc   ;abort?
       jz      abort
       cpi     ctrlx   ;abort this one file?
       jz      cxabort
prout1:
       lda     skipfl  ;skipping?
       ora     a       ;set flags (Z=no skip=print char)
       mov     a,b     ;restore char
       cz      lout    ;send character to printer
       cpi     lf      ;special tests if it is a line feed
       rnz             ;done if non-LF char
       lda     lcount  ;decrement line counter
       dcr     a
       sta     lcount
       rnz
;
;  Paging Required
;       Skip to top of next page; reset LCOUNT (Lines Left on Page Count);
;       print header
;
prout0:
       lda     ltpp    ;get number of text lines per page
       sta     lcount  ;set as new line count
       push    h       ;save ptr
       lhld    pnum    ;increment page number
       inx     h
       shld    pnum
       lda     lspp    ;get number of lines to skip per page
       call    lineskp ;skip lines
       pop     h       ;restore ptr
       mov     a,m     ;check next character
       cpi     ctrlz   ;EOF?
       jnz     prhead  ;print 2-line heading if NOT EOF
       ret
;
;  Abort current file with final page eject
;
cxabort:
       lda     lcount  ;get count of remaining lines
       call    lineskp ;skip lines
       lda     lspp    ;number of lines to skip per page
       call    lineskp ;skip lines
       jmp     fctlnxt ;continue with next file
;
;  Skip out rest of page
;       Form Feed Function
;
page:
       lda     lcount  ;get count of remaining lines
       call    lineskp ;skip lines
       jmp     prout0  ;process top of new page
;
;  Skip out lines on page
;       A = number of lines to skip
;
lineskp:
       mov     b,a     ;line count in B
       ora     a       ;any?
       rz
       lda     skipfl  ;skipping?
       ora     a
       rnz
lines1:
       mvi     a,cr    ;output new line to printer
       call    lout
       mvi     a,lf
       call    lout
       dcr     b       ;count down
       jnz     lines1
       ret
;
;  Print Line Number (optional)
;
prlnum:
       lda     skipfl  ;skipping?
       ora     a       ;0=no
       rnz
       lda     lnumfl  ;get flag
       ora     a       ;0=don't number lines
       rz
       push    h       ;save ptr
       lhld    lnum    ;get line number
       call    lhldc   ;print line number
       call    lprint  ;print separator
       db      ': ',0
       pop     h       ;restore ptr
       ret
;
;  Print 2-line heading and control skipping
;
prhead:
       push    h       ;save ptr
       lda     skipfl  ;currently skipping?
       ora     a       ;0=no
       cnz     skiptst ;test for shut off
       lda     exact   ;exact says no heading
       ora     a       ;0FFH=yes
       jnz     prhead1
       call    proffs  ;print offset
       lda     pnumfl  ;number pages?
       ora     a       ;0=no
       cnz     prpnum  ;print page heading and number
       lda     fnpfl   ;print file name?
       ora     a       ;0=no
       cnz     prfname ;print file name
;
       if      timeok  ;time available?
       lda     timepfl ;print time?
       ora     a       ;0=no
       cnz     prtime  ;print time
       endif
;
       lda     hbuf    ;print heading?
       ora     a       ;0=no
       cnz     prhdg   ;print heading
prhead1:
       pop     h       ;restore ptr
prhead2:
       lda     skipfl  ;skipping?
       ora     a
       rnz
       call    lcrlf   ;new line
       jmp     lcrlf
;
;  Test for completion of skipping
;
skiptst:
       lhld    pnum    ;get page number
       xchg            ;... in DE
       lhld    skipnum ;get page to skip to
       mov     a,h     ;compare them
       cmp     d
       rnz
       mov     a,l
       cmp     e
       rnz
       xra     a       ;A=0 to stop skipping
       sta     skipfl  ;set flag
       ret
;
;  Print Line Offset
;
proffs:
       lda     skipfl  ;skipping?
       ora     a
       rnz
       push    b       ;save BC
       lda     offset  ;get offset
       ora     a       ;any?
       jz      proff2
       mov     c,a     ;offset in B
proff1:
       mvi     a,' '   ;space over
       call    prout
       dcr     c       ;count down
       jnz     proff1
proff2:
       pop     b
       ret
;
;  Print Page Number
;
prpnum:
       lda     skipfl  ;skipping?
       ora     a
       rnz
       call    lprint  ;print header
       db      'Page ',0
       lhld    pnum    ;print current page number
       call    lhldc   ;print as decimal
       ret
;
;  Print File Name
;
prfname:
       lda     skipfl  ;skipping?
       ora     a
       rnz
       call    prdash  ;print separator
       lxi     h,tfcb+1        ;pt to first char
       mvi     b,8     ;8 chars
       call    lfn1
       mvi     a,'.'
       call    lout
       mvi     b,3     ;3 chars
       call    lfn1
       ret
lfn1:
       mov     a,m     ;get char
       ani     7fh     ;mask
       call    lout    ;send to printer
       inx     h       ;pt to next
       dcr     b       ;count down
       jnz     lfn1
       ret
;
;  Print Separator
;
prdash:
       call    lprint
       db      ' -- ',0
       ret
;
       if      timeok
;
;  Print Time
;
prtime:
       lda     skipfl  ;skipping?
       ora     a
       rnz
       call    prdash  ;print separator
       lxi     h,timebf        ;pt to time stamp
       call    lpstr   ;print
       ret
;
       endif
;
;  Print Header
;
prhdg:
       lda     skipfl  ;skipping?
       ora     a
       rnz
       call    prdash  ;print separator
       lxi     h,hbuf  ;pt to heading
       call    lpstr   ;print
       ret
;
;  UTILITIES
;       SBLANK  -- SKIP BLANKS PTED TO BY HL UNTIL NON-BLANK ENCOUNTERED; HL
;       SNBLANK -- SKIP NON-BLANKS PTED TO BY HL UNTIL BLANK OR EOL; HL
;       PRFN    -- PRINT FILE NAME PTED TO BY HL; AFFECT NOTHING
;

;
;  SKIP UNTIL NON-BLANK
;
SBLANK:
       MOV     A,M     ; LOOK FOR BLANK
       INX     H       ; PT TO NEXT
       CPI     ' '     ; BLANK?
       JZ      SBLANK
       DCX     H       ; BACK UP
       RET

;
;  SKIP UNTIL BLANK OR EOL
;
SNBLANK:
       MOV     A,M     ; GET CHAR
       INX     H       ; PT TO NEXT
       CPI     ' '     ; BLANK?
       JZ      SNB1
       ORA     A       ; EOL?
       JNZ     SNBLANK
SNB1:
       DCX     H       ; BACK UP
       RET

;
;  PRINT FILE NAME PTED TO BY HL
;       OUTPUT TO CON:
;
PRFN:
       PUSH    H       ; SAVE REGS
       PUSH    B
       CALL    RETUD   ; GET CURRENT USER/DISK
       MOV     A,B     ; PRINT DISK
       ADI     'A'     ; LETTER
       CALL    COUT
       MOV     A,C     ; PRINT USER
       CALL    PADC
       CALL    PRINT
       DB      ': ',0
       INX     H       ; PT TO FILE NAME
       MVI     B,8     ; PRINT NAME
       CALL    PRNT
       MVI     A,'.'   ; DECIMAL
       CALL    COUT
       MVI     B,3     ; PRINT TYPE
       CALL    PRNT
       POP     B       ; GET REGS
       POP     H
       RET

;
;  PRINT CHARS PTED TO BY HL FOR B BYTES
;       OUTPUT TO CON:
;
PRNT:
       MOV     A,M     ; GET CHAR
       CALL    COUT
       INX     H       ; PT TO NEXT
       DCR     B       ; COUNT DOWN
       JNZ     PRNT
       RET

;
;  BUFFERS
;
DISK:
       DS      1       ; HOME DISK NUMBER
USER:
       DS      1       ; HOME USER NUMBER
CDISK:
       DS      1       ; CURRENT DISK NUMBER
CUSER:
       DS      1       ; CURRENT USER NUMBER
CMDLNE:
       DS      2       ; PTR TO COMMAND LINE STRING
NEXTCH:
       DS      2       ; PTR TO NEXT CHAR IN MULTIFILE COMMAND LINE
FILECNT:
       DS      2       ; COUNT OF NUMBER OF FILES RENAMED
SCRATCH:
       DS      2       ; ADDRESS OF FIRST FREE BYTE
BCNT:
       DS      1       ; NUMBER OF PAGES IN SCRATCH AREA
NTFCB:
       DS      36      ; FCB FOR NEW FILE
;
;  PRINT3 Buffers
;
tfcb:
       ds      36      ; FCB for current file
nxtblk:
       ds      2       ; pointer to next block to load
nxtln:
       ds      2       ; pointer to next line to read
lcount:
       ds      1       ; count of text lines left on page
;
;  Stack
;
       DS      100     ; STACK AREA
STACK:
       DS      2       ; OLD STACK PTR

       END