;************************************************
;*                                              *
;*              FILE PRINT UTILITY              *
;*                                              *
;************************************************
;
;       By              Bill Bolton
;                       Software Tools
;                       P.O. Box 80
;                       Newport Beach
;                       NSW, 2016
;                       AUSTRALIA
;
;       CP/M-86 History (most recent version at top)
;
;       Version 1.0     Adapted from PAGE utility
;                       4/Mar/82
;
       TITLE   'PRINT utility for CP/M-86'
       PAGEWIDTH       132
       EJECT

;************************************************
;*                                              *
;*             MISCELLANEOUS EQUATES            *
;*                                              *
;************************************************

TFCB    EQU     005CH                   ;TRANSIENT PROGRAM FCB
TBUF    EQU     0080H                   ;TRANSIENT PROGRAM BUFFER
OPEN    EQU     15                      ;OPEN FUNCTION CODE
READ    EQU     20                      ;READ FUNCTION CODE
BS      EQU     08H                     ;ASCII Back Space
TAB     EQU     09H                     ;ASCII TAB
LF      EQU     0AH                     ;ASCII Line Feed
FORMF   EQU     0CH                     ;ASCII Form Feed
CR      EQU     0DH                     ;ASCII Carriage Return
SPACE   EQU     20H                     ;ASCII Blank
FILLER  EQU     00H                     ;USED AS FF NULL
PAGSZ   EQU     66                      ;LINES/PAGE
MAXLN   EQU     58                      ;USABLE LINES/PAGE
NULLS   EQU     1                       ;NULLS AFTER A FORM FEED
ODEV    EQU     5                       ;CON:=2  LST:=5
M       EQU     Byte Ptr 0[BX]          ;Handy for ASM86

       EJECT
       CSEG

;************************************************
;*                                              *
;*      GET PARAMETERS FROM COMMAND LINE        *
;*                                              *
;************************************************

PRINT:
       MOV     BX,TBUF                 ;POINT TO BUFFER
       MOV     CH,M                    ;GET COUNT OF CHARACTERS IN CMD
       MOV     AL,M                    ;GET COUNT AGAIN
       OR      AL,AL                   ;TEST FOR ZERO
       JNZ     L_1
       JMP     PERR                    ;YES, NO ARGUMENTS
L_1:
       INC     BX                      ;SKIP COUNT
SKP1:
       MOV     AL,M                    ;GET BYTE
       CMP     AL,' '                  ;SPACE?
       JNZ     SKP2                    ;NO
       INC     BX                      ;BUMP POINTER
       DEC     CH                      ;DECRMENT COUNT
       JNZ     SKP1                    ;SKIP TO FIRST NON-SPACE
       JMPS    PERR                    ;NO, NON SPACE AFTER FILE ID
SKP2:
       MOV     AL,M                    ;GET CHAR
       CMP     AL,' '                  ;SPACE?
       JZ      SKP3                    ;YES
       INC     BX                      ;BUMP POINTER
       DEC     CH                      ;DECREMENT COUNT
       JNZ     SKP2                    ;SKIP TO SPACE
       JMP     PEND                    ;NO SPACE AFTER FILE ID
SKP3:
       MOV     AL,M                    ;GET CHAR
       CMP     AL,' '                  ;TEST FOR NON,SPACE
       JNZ     SKP4                    ;FOUND ARGUMENTS
       INC     BX                      ;BUMP POINTER
       DEC     CH                      ;DECREMENT COUNT
       JNZ     SKP3
       JMP     PEND
SKP4:
       MOV     DX,(Offset PARMS)
SKP4A:
       MOV     AL,M
       MOV     SI,DX
       MOV     [SI],AL
       INC     BX
       INC     DX
       DEC     CH
       JNZ     SKP4A
       MOV     AL,' '
       MOV     SI,DX                   ;INDICATE END OF PARMS
       MOV     [SI],AL

;************************************************
;*                                              *
;*           PROCESS PARAMETERS                 *
;*                                              *
;************************************************

PPARM:
       MOV     DX,(Offset PARMS)       ;POINT TO PARMS AREA
PLP0:
       MOV     BX,(Offset PTAB)        ;POINT TO PARM TABLE
       MOV     CH,(Offset PTABS)       ;SIZE OF PARM TABLE
PLP1:
       MOV     SI,DX                   ;GET PARM BYTE
       MOV     AL,[SI]
       CMP     AL,' '                  ;END OF PARMS?
       JZ      PEND                    ;YES, DONE
       CMP     AL,00                   ;END OF PARMS?
       JZ      PEND                    ;YES, DONE
       CMP     AL,M                    ;COMPARE TO TABLE ENTRY
       JZ      PFND                    ;FOUND IT
       INC     BX                      ;BUMP PARM TABLE PTR
       INC     BX
       INC     BX
       DEC     CH                      ;DECREMENT COUNT
       JNZ     PLP1                    ;KEEP LOOKING
PERR:
       MOV     DX,(Offset PERMSG)      ;POINT TO PARM ERROR MSG
       MOV     CL,09                   ;WRITE MSG
       INT     224
       MOV     CL,0                    ;EXIT
       MOV     DL,0
       INT     224
PFND:
       INC     BX                      ;POINT TO ROUTINE ADDR
       PUSH    DX                      ;SAVE PARM POINTER
       MOV     DL,M
       INC     BX
       MOV     DH,M                    ;GET ROUTINE ADDR
       MOV     BX,(Offset PRET)        ;RETURN ADDRESS
       PUSH    BX                      ;SIMULATE CALL
       XCHG    BX,DX                   ;GET ROUTINE ADDR
       JMP     BX                      ;EXIT TO PARM PROC. ROUTINE
PRET:
       POP     DX                      ;GET PARM POINTER AGAIN
       INC     DX                      ;POINT TO NEXT ONE
       JMPS    PLP0                    ;CONTINUE
;
;************************************************
;*                                              *
;*      PARAMETER PROCESSING ROUTINES           *
;*                                              *
;************************************************

NPARM:
       MOV     AL,0FFH                 ;SET 'NO HEADING' SWITCH
       MOV     Byte Ptr NSWT,AL
       RET
;
CPARM:
       MOV     AL,0FFH                 ;SET 'NO PAGINATION' SWITCH
       MOV     Byte Ptr CSWT,AL
       RET

;************************************************
;*                                              *
;*              OPEN FILE FOR INPUT             *
;*                                              *
;************************************************

PEND:
       MOV     DX,TFCB                 ;POINT TO FCB
       CALL    FOPEN                   ;OPEN FILE
       JNB     L_2
       JMP     ERR                     ;IF ERROR, EXIT
L_2:
       CALL    HEAD                    ;PRINT HEADING

;****************************************
;*                                      *
;*          MAIN PROCESSING LOOP        *
;*                                      *
;****************************************

LOOP:
       CALL    GETBT                   ;GET A BYTE
       JNB     L_3
       JMP     ERR                     ;ERROR
L_3:
       CMP     AL,1AH                  ;EOF?
       JZ      DONE                    ;YES
       CMP     AL,CR                   ;CR?
       JZ      CRET                    ;YES
       CMP     AL,LF                   ;LF?
       JZ      LFEED                   ;YES
       CMP     AL,TAB                  ;TAB?
       JZ      TABMOV                  ;YES
       CMP     AL,FORMF                ;FORM FEED?
       JZ      FFEED                   ;YES
       CMP     AL,BS                   ;BACKSPACE
       JZ      BSPACE                  ;YES
       CMP     AL,SPACE                ;ODD CONTROL CHR?
       JB      LOOP                    ;YES, IGNORE IT
       CALL    PBYT                    ;OTHERWISE PRINT IT
       JMPS    LOOP                    ;CONTINUE

;************************************************
;*                                              *
;*              FINAL PROCESSING                *
;*                                              *
;************************************************

DONE:
       CALL    TPAGE                   ;MOVE TO TOP OF PAGE
       MOV     CL,0                    ;EXIT TO BDOS
       MOV     DL,0
       INT     224

;************************************************
;*                                              *
;*              PROCESS TABS                    *
;*                                              *
;************************************************

TABMOV:
       MOV     BX,(Offset COL)         ;POINT TO COLUMN
TBLP:
       MOV     AL,SPACE                ;PRINT ONE SPACE
       CALL    PBYT
       MOV     AL,M                    ;GET COLUMN
       AND     AL,07H                  ;MODULO 8
       JNZ     TBLP                    ;IF NOT AT TAB STOP, KEEP TYPING
       JMPS    LOOP
       CALL    PBYT                    ;PRINT BYTE
       JMPS    LOOP

;************************************************
;*                                              *
;*              PROCESS <CR>                    *
;*                                              *
;************************************************

CRET:
       XOR     AL,AL
       MOV     Byte Ptr COL,AL
       MOV     AL,CR
       CALL    PBYT                    ;PRINT <CR>
       JMPS    LOOP                    ;CONTINUE IN MAIN LOOP

;************************************************
;*                                              *
;*              PROCESS <LF>                    *
;*                                              *
;************************************************

LFEED:
       MOV     AL,Byte Ptr CSWT        ;GET PAGINATION SWITCH
       OR      AL,AL                   ;SEE IF 'NO PAGINATION'
       JNZ     LFEED2                  ;TRUE, SKIP PAGINATION TEST
       MOV     AL,Byte Ptr LINE        ;GET LINE COUNT
       CMP     AL,MAXLN                ;PAGE OVERFLOW?
       JZ      FFEED                   ;YES
LFEED2:
       MOV     AL,Byte Ptr LINE        ;GET LINE  COUNT
       INC     AL                      ;BUMP LINE COUNT
       MOV     Byte Ptr LINE,AL
       MOV     AL,LF
       CALL    PBYT
       JMP     LOOP

;****************************************
;*                                      *
;*              PROCESS <BS>            *
;*                                      *
;****************************************

BSPACE:
       MOV     AL,BS
       CALL    PBYT
       MOV     AL,Byte Ptr COL         ;GET COLUMN COUNTER
       OR      AL,AL                   ;IF ZERO, DONT DECREMENT IT
       JNZ     L_4
       JMP     LOOP
L_4:
       DEC     AL
       MOV     Byte Ptr COL,AL         ;DECREMENT COLUMN COUNT
       JMP     LOOP

;************************************************
;*                                              *
;*              PROCESS FORM FEED               *
;*                                              *
;************************************************

FFEED:
       MOV     AL,CR
       CALL    PBYT                    ;PRINT CR
       CALL    TPAGE                   ;POSITION AT TOP OF PAGE
       CALL    HEAD                    ;PRINT HEADING
       JMP     LOOP                    ;CONTINUE IN MAIN LOOP

;************************************************
;*                                              *
;*              HEADING PRINT ROUTINE           *
;*                                              *
;************************************************

HEAD:
       MOV     AL,Byte Ptr NSWT        ;NO HEADING SWITCH
       OR      AL,AL                   ;IF SET, NO HEADING
       JNZ     HEADA
       MOV     BX,(Offset FMSG)        ;POINT TO MESSAGE
       CALL    PSTRNG                  ;PRINT STRING
       MOV     BX,TFCB+1               ;POINT TO NAME
       MOV     CH,8                    ;SIZE OF NAME
       CALL    PCNT                    ;PRINT COUNT
       MOV     AL,SPACE                ;PRINT A SPACE
       CALL    PBYT
       MOV     BX,TFCB+9               ;POINT TO TYPE
       MOV     CH,03                   ;SIZE OF TYPE
       CALL    PCNT                    ;PRINT COUNT
       MOV     BX,(Offset PMSG)        ;POINT TO MESSAGE
       CALL    PSTRNG                  ;PRINT STRING
       MOV     AL,Byte Ptr PAGEN       ;GET PAGE NUMBER
       INC     AL                      ;BUMP IT
       MOV     Byte Ptr PAGEN,AL       ;SAVE IT
       CALL    DEC                     ;CONVERT TO DECIMAL
       MOV     BX,(Offset DECWRK)      ;POINT TO DEC STRING
       MOV     CH,3
       CALL    PCNT                    ;PRINT PAGE NUMBER
       MOV     AL,CR                   ;PRINT CR
       CALL    PBYT
       MOV     AL,LF
       CALL    PBYT                    ;PRINT LF
       MOV     AL,LF
       CALL    PBYT                    ;AND SECOND
       MOV     AL,LF
       CALL    PBYT                    ;AND A THIRD
       MOV     AL,3                    ;SET NUMBER OF LINES PRINTED
       JMPS    HEADB
HEADA:
       XOR     AL,AL
HEADB:
       MOV     Byte Ptr LINE,AL        ;RESET LINE COUNT
       XOR     AL,AL                   ;RESET COLUMN
       MOV     Byte Ptr COL,AL         ;RESET COLUMN
       RET

;************************************************
;*                                              *
;*              CHARACTER PRINT ROUTINE         *
;*                                              *
;************************************************

PBYT:
       PUSH    BX
       PUSH    CX
       PUSH    AX
       MOV     DL,AL
       MOV     CL,ODEV
       INT     224                     ;PRINT
       POP     AX
       CMP     AL,SPACE                ;NON-PRINTING?
       JB      PBY2                    ;YES, DONT BUMP COL
       MOV     BX,(Offset COL)         ;INCREMENT COLUMN
       INC     M
PBY2:
       MOV     CL,11                   ;GET CONSOLE STATUS
       INT     224
       CMP     AL,00                   ;BREAK?
       JZ      L_5
       MOV     CL,0                    ;YES, DONE
       MOV     DL,0
       INT     224
L_5:
       POP     CX
       POP     BX
       RET

;************************************************
;*                                              *
;*              STRING PRINT ROUTINE            *
;*                                              *
;************************************************

PSTRNG:
       MOV     AL,M                    ;GET BYTE
       CMP     AL,'$'                  ;STRING END?
       JNZ     L_6
       RET                             ;YES, DONE
L_6:
       CALL    PBYT                    ;PRINT BYTE
       INC     BX                      ;BUMP POINTER
       JMPS    PSTRNG                  ;LOOP

;************************************************
;*                                              *
;*      STRING PRINT ROUTINE (COUNT IN B)       *
;*                                              *
;************************************************

PCNT:
       MOV     AL,M                    ;GET BYTE
       CALL    PBYT                    ;PRINT IT
       INC     BX                      ;BUMP POINTER
       DEC     CH                      ;DECREMENT COUNT
       JNZ     PCNT
       RET

;************************************************
;*                                              *
;*              DECIMAL OUTPUT ROUTINE          *
;*                                              *
;************************************************

DEC:
       MOV     BX,(Offset DECWRK)
       MOV     CL,100
       CALL    DIGIT
       MOV     CL,10
       CALL    DIGIT
       MOV     CL,1
       CALL    DIGIT
       RET

DIGIT:
       MOV     M,'0'
DI0:
       SUB     AL,CL
       JS      DI1
       INC     M
       JMPS    DI0
DI1:
       ADD     AL,CL
       LAHF
       INC     BX
       SAHF
       RET

;************************************************
;*                                              *
;*              TOP OF PAGE ROUTINE             *
;*                                              *
;************************************************

TPAGE:
       MOV     AL,FORMF                ;ISSUE FORM FEED
       CALL    PBYT
       MOV     CH,NULLS                ;NUMBER OF NULLS
TPAGE2:
       MOV     AL,FILLER
       CALL    PBYT
       DEC     CH                      ;PRINT N FILLERS
       JNZ     TPAGE2
       RET

;************************************************
;*                                              *
;*         I/O ERROR MESSAGE ROUTINE            *
;*                                              *
;************************************************

ERR:
       MOV     DX,(Offset ERMSG)
       MOV     CL,09H                  ;WRITE MSG
       INT     224
       MOV     CL,0
       MOV     DL,0
       INT     224

;************************************************
;*                                              *
;*                 F O P E N                    *
;*          ROUTINE TO OPEN A DISK FILE         *
;*                                              *
;*        INPUT:     DX= pointer to FCB         *
;*       OUTPUT:     CARRY=ERROR                *
;*                                              *
;************************************************

FOPEN:
       MOV     CL,OPEN                 ;OPEN CODE
       INT     224                     ;ISSUE OPEN
       CMP     AL,0FFH                 ;ERROR?
       JZ      FOERR                   ;YES
       XOR     AL,AL                   ;CLEAR CARRY
       RET
FOERR:
       STC
       RET

;********************************************************
;*                                                      *
;*                       G E T B T                      *
;*                 ROUTINE TO READ A BYTE               *
;*                                                      *
;*                OUTPUTS:     AL = BYTE                *
;*                             CARRY = ERROR            *
;*                                                      *
;********************************************************

GETBT:
       MOV     BX,TBUF+128
       XCHG    BX,DX                   ;BUFFER END ADDR. IN DE
       MOV     BX,Word Ptr INPTR       ;CURRENT POINTER IN HL
       CMP     BX,DX                   ;TEST FOR END OF BUFFER
       JZ      GETB2                   ;YES, READ
GETB1:
       MOV     AL,M                    ;GET BYTE
       INC     BX                      ;BUMP POINTER
       INC     Word Ptr INPTR          ;BUMP POINTER
       OR      AL,AL                   ;RESET CARRY
       RET
GETB2:
       MOV     CL,READ                 ;READ CODE
       MOV     DX,TFCB                 ;FCB ADDRESS
       INT     224                     ;ISSUE READ
       CMP     AL,00                   ;ERROR?
       JNZ     IERR                    ;YES
       MOV     BX,TBUF                 ;RESET BUFFER POINTER
       MOV     Word Ptr INPTR,BX
       JMPS    GETB1                   ;CONTINUE
IERR:
       STC
       RET

       EJECT

;************************************************
;*                                              *
;*                 D  A  T  A                   *
;*                                              *
;************************************************

L_8     EQU     $
       DSEG
       ORG     Offset L_8

       DB      'PRINT.CMD - File PRINT Utility, CP/M-86 Version 1.0, '
       DB      '(C) Copyright March 1982, '
       DB      'By Bill Bolton, Software Tools, Sydney, Australia'

NSWT    DB      0                       ;NO HEADING SWITCH
CSWT    DB      0                       ;NO PAGINATION SWITCH

INPTR   DW      TBUF+128                ;INPUT POINTER
DECWRK  DB      '000'                   ;DECIMAL WORK AREA
COL     DB      0                       ;COLUMN COUNTER
LINE    DB      0                       ;LINE COUNTER
PAGEN   DB      0                       ;PAGE COUNTER

FMSG    DB      CR,'FILE: $'
PMSG    DB      '                                            PAGE $'
PERMSG  DB      'PARAMETER ERROR',CR,LF,'$'
ERMSG   DB      'ERROR',CR,LF,'$'

PARMS   RS      16                      ;PARAMETER STORAGE AREA

;************************************************
;*                                              *
;*           PARAMETER PROCESSING TABLE         *
;*                                              *
;*            ENTRIES ARE 3 BYTES WIDE          *
;*                                              *
;*            BYTE 1 = PARAMETER BYTE           *
;*                                              *
;*            BYTES 2,3 = ROUTINE ADDRESS       *
;*                                              *
;************************************************

PTAB    DB      'N'
       DW      (Offset NPARM)
       DB      'C'
       DW      (Offset CPARM)
PTABS   EQU     ((Offset $)-(Offset PTAB))/3

       END