*  PROGRAM:  BANNER
*  AUTHOR:  RICHARD CONN
*  VERSION:  1.0
*  DATE:  22 FEB 82
*  DERIVATION:  From Public Domain Program, Author Unknown
*  PREVIOUS VERSIONS:  None

VERS    EQU     10      ;VERSION NUMBER
*********************************
*                               *
*   B A N N E R  P R I N T      *
*                               *
*********************************
*
*  BANNER is a program which prints large (5x7) block letters
* on the LST: Device in response to user input from the
* keyboard.  BANNER is invoked in several ways:
*       BANNER <text>
*               -- <TEXT> is printed on the LST: device; ex:
*                       BANNER HELLO
*       BANNER /T
*               -- Multi-Text Line Input Mode; the user may
*                  type several successive lines to be printed;
*                  the user exits by typing ^C
*       BANNER /?
*               -- Built-In HELP Information is printed
*

*********************************
*  User-Customized Parameters   *
*********************************

TEXT    EQU     'T'     ;OPTION CHAR TO INVOKE TEXT ENTRY MODE
MCHARS  EQU     16      ;MAX NUMBER OF CHARS/LINE

*********************************
*  Constants                    *
*********************************

BDOS            EQU             0005H
FCB             EQU             005CH
BUFF            EQU             0080H
CTRLC           EQU             03H
CR              EQU             0DH
LF              EQU             0AH
TAB             EQU             09H
READLN          EQU             10      ;Input Line Editor
OUTL            EQU             5       ;OUTPUT TO LST:
OUTC            EQU             2       ;OUTPUT TO CON:

       ORG     100H
START:
       LXI     H,0     ;SAVE STACK PTR
       DAD     SP      ;HL=OLD SP
       SHLD    STACK
       LXI     SP,STACK        ;NEW STACK
       CALL    PMSG
       DB      'BANNER  Version '
       DB      VERS/10+'0','.',(VERS MOD 10)+'0'
       DB      CR,LF
       DB      0
       LDA     FCB+1   ;CHECK FOR OPTION
       CPI     '/'     ;OPTION CAUGHT?
       JNZ     ONE$LINE        ;IF NO OPTION, PROCESS ONLY 1 LINE
       LDA     FCB+2   ;CHECK FOR TEXT INPUT
       CPI     TEXT    ;TEXT CHAR?
       JZ      BTEXT   ;INPUT TEXT LINES
;
;  Print BANNER Help Message
;
HELP:
       CALL    PMSG    ;PRINT MESSAGE TO USER
       DB      CR,LF,'BANNER is invoked by a command line like:'
       DB      CR,LF,'         BANNER <text>'
       DB      CR,LF,'In which case <TEXT> will be printed and control '
       DB      CR,LF,'returned to CP/M, like "BANNER HELLO" to print'
       DB      CR,LF,'"HELLO", or'
       DB      CR,LF,'         BANNER /o'
       DB      CR,LF,'where ''/o'' is ''/T'' to invoke Text Entry'
       DB      CR,LF,'Mode in which the user may enter several sequential '
       DB      CR,LF,'Banner Lines to be printed.  Any other option '
       DB      CR,LF,'prints this Help Message and Exits.'
       DB      0
*
*  Exit BANNER
*
EXIT:
       LHLD    STACK   ;GET OLD STACK PTR
       SPHL            ;LOAD SP
       RET
*
*  PRINT BANNER WHEN CONTAINED IN COMMAND LINE
*
ONE$LINE:
       LXI     H,BUFF  ;PT TO INPUT LINE
       MOV     A,M     ;GET CHAR COUNT
       ORA     A       ;PRINT HELP MESSAGE IF NO CHARS
       JZ      HELP
       MOV     B,A     ;CHAR COUNT IN B
       DCR     B       ;COUNT DOWN BY 1 TO ELIMINATE LEADING SPACE
       JZ      HELP
       LXI     D,BLINE ;PT TO BANNER LINE BUFFER
       MOV     A,B     ;GET CHAR COUNT
       STAX    D       ;STORE CHAR COUNT
       INX     H       ;PT TO FIRST CHAR (SPACE)
       INX     H       ;PT TO CHAR AFTER SPACE
       INX     D       ;PT TO NEXT POSITION IN BUFFER
OL1:
       MOV     A,M     ;GET NEXT CHAR
       STAX    D       ;PUT IT
       INX     H       ;PT TO NEXT
       INX     D
       DCR     B       ;COUNT DOWN
       JNZ     OL1
       CALL    BANNER  ;PRINT BANNER
       JMP     EXIT    ;EXIT TO CP/M
*
*  Process Multiple Line Input
*
BTEXT:
       CALL    PMSG
       DB      CR,LF,'BANNER Multiple Line Input'
       DB      CR,LF,' Input Line or ^C to Return to CP/M'
       DB      CR,LF
       DB      0
BTEXTL:
       CALL    PMSG
       DB      'BANNER? ',0
       LXI     D,INLINE        ;INPUT LINE
       MVI     C,READLN        ;BDOS READLN FUNCTION
       CALL    BDOS
       CALL    PMSG            ;NEW LINE
       DB      CR,LF,0
       CALL    BANNER          ;PRINT BANNER LINE
       JMP     BTEXTL          ;CONTINUE
*
*  PRINT BANNER CONTAINED IN BLINE BUFFER (BLINE=CHAR CNT)
*
BANNER:
       LXI     H,BLINE ;STORE ENDING <NULL>
       MOV     A,M     ;GET CHAR COUNT
       CPI     MCHARS+1        ;BEYOND CHAR LIMIT?
       JC      BANNER1 ;CONTINUE IF NOT
       CALL    PMSG
       DB      CR,LF,'Truncation Error -- Line too long',CR,LF
       DB      0
       JMP     EXIT    ;RETURN TO CALLER
BANNER1:
       INX     H       ;PT TO FIRST CHAR
       ADD     L       ;ADD TO LOW-ORDER BYTE
       MOV     L,A     ;PT TO BYTE
       MVI     M,0     ;STORE <NULL>
       MVI     D,80H   ;PT TO MSB+1
CBAN:
       LXI     H,BLINE+1       ;SET PTR TO FIRST CHAR
       MOV     A,M             ;CHECK FOR NO CHARS
       ORA     A               ;ENDING ZERO?
       JZ      CB4             ;4 CRLF'S IF SO
       SHLD    NEXTCH
       CALL    CRLF    ;NEW LINE
       MOV     A,D     ;GET BIT PTR
       RRC             ;ROTATE
       ANI     7FH     ;MASK OUT MSB
       MOV     D,A     ;SET BIT PTR
       JZ      CB2     ;NEW LINE AND THEN EXIT
CB1:
       CALL    CONIN   ;GET NEXT CHAR
       CALL    CAPS    ;CAPITALIZE
       MOV     C,A     ;CHAR IN C
       ORA     A       ;DONE?
       JZ      CBAN    ;DO NEXT LINE IF SO
       CALL    CONV    ;GET ADDRESS OF DATA IN HL
       JC      CB1     ;SKIP IF ERROR
       CALL    PRINT   ;PRINT 5 CHARS FOLLOWED BY TWO SPACES
       JMP     CB1     ;CONTINUE
CB4:
       CALL    CRLF    ;4 <CR> <LF>
       CALL    CRLF
       CALL    CRLF
CB2:
       CALL    CRLF    ;1 CRLF
       RET

*
*  COMPUTE POINTER TO TABLE ENTRY OF CHAR IN REG A
*    ON INPUT, A=CHAR; ON OUTPUT, HL=PTR TO TABLE ENTRY (1ST BYTE)
*
CONV:
       PUSH    B               ;SAVE BC
       PUSH    D               ;SAVE DE
       SUI     ' '             ;CONVERT <SP> TO 0
       RC                      ;INVALID CHAR
       CPI     7BH-' '         ;IN RANGE?
       JNC     CNVER           ;INVALID CHAR
       MOV     E,A             ;VALUE IN E
       MVI     D,0             ;VALUE IN DE
       MOV     H,D             ;VALUE IN HL
       MOV     L,E
       DAD     H               ;VALUE * 2
       DAD     H               ;VALUE * 4
       DAD     D               ;HL = VALUE * 5
       LXI     D,CHARS         ;POINT TO BEGINNING OF TABLE
       DAD     D               ;HL PTS TO ELEMENT IN TABLE
       POP     D               ;RESTORE DE
       POP     B               ;RESTORE BC
       ORA     A               ;CLEAR CARRY
       RET
CNVER:
       STC                     ;SET CARRY FOR INVALID CHAR
       POP     D               ;RESTORE DE
       POP     B               ;RESTORE BC
       RET
*
*  CAPITALIZE CHAR IN A
*
CAPS:
       CPI     61H             ;LOWER CASE?
       RC
       ANI     5FH             ;MASK OUT BIT 5
       RET
*
*  PRINT CHAR IN C ACCORDING TO THE ENTRY PTED TO BY HL
*    BIT MASK IS IN D
*
PRINT:
       PUSH    B               ;SAVE BC (C=CHAR)
       MVI     B,5             ;5 BYTES/CHAR
PRINT1:
       MOV     A,M             ;GET BIT SET
       ANA     D               ;MASK FOR BIT IN QUESTION
       JZ      PRINT3          ;IF ZERO, PRINT <SP>
       CALL    LSTOUT          ;PRINT CHAR IN C
PRINT2:
       INX     H               ;PT TO NEXT BYTE
       DCR     B               ;COUNT DOWN
       JNZ     PRINT1
       MVI     C,' '           ;PRINT THREE SPACES
       CALL    LSTOUT
       CALL    LSTOUT
       CALL    LSTOUT
       POP     B               ;RESTORE BC
       RET
PRINT3:
       MOV     E,C             ;SAVE CHAR
       MVI     C,' '           ;PRINT <SP>
       CALL    LSTOUT
       MOV     C,E             ;GET CHAR
       JMP     PRINT2
*
*  SUPPORT ROUTINES
*
PMSG:
       XTHL            ;GET PTR TO STRING
       MVI     B,0     ;SET TAB COUNTER
PMSG1:
       MOV     A,M     ;GET NEXT BYTE
       INX     H       ;PT TO NEXT
       ORA     A       ;DONE?
       JZ      PMSG2
       CPI     TAB     ;TABULATE?
       JZ      PMSG$TAB
       MOV     C,A     ;CHAR IN C
       CALL    CONOUT  ;PRINT CHAR
       INR     B       ;INCR CHAR COUNT
       JMP     PMSG1
PMSG$TAB:
       MVI     C,' '   ;PRINT <SP>
       CALL    CONOUT  ;PRINT
       INR     B       ;INCR POSITION COUNT
       MOV     A,B     ;GET IT
       ANI     7       ;DONE?
       JNZ     PMSG$TAB
       JMP     PMSG1   ;PROCESS NEXT CHAR
PMSG2:
       XTHL            ;RESTORE HL, PTR
       RET
CRLF:
       MVI     C,CR
       CALL    LSTOUT
       MVI     C,LF
       JMP     LSTOUT
LSTOUT:
       PUSH H ! PUSH B ! PUSH D
       MOV     E,C
       MVI     C,OUTL          ;OUTPUT TO LST:
       CALL    BDOS
       POP D ! POP B ! POP H
       RET
CONOUT:
       PUSH H ! PUSH B ! PUSH D
       MOV     E,C
       MVI     C,OUTC          ;OUTPUT TO CON:
       CALL    BDOS
       POP D ! POP B ! POP H
       RET
CONIN:
       PUSH H ! PUSH D ! PUSH B
       LHLD    NEXTCH  ;GET NEXT CHAR PTR
       MOV     A,M     ;GET CHAR
       INX     H       ;PT TO NEXT
       SHLD    NEXTCH
       ANI     7FH     ;MASK MSB IF ANY
       POP B ! POP D ! POP H
       RET
*
*  CHARACTER TABLE
*    THE CHARACTERS REPRESENTED IN THIS TABLE ARE IN A 5X7 FORMAT
*    THE FIRST BYTE IN EACH ENTRY REPRESENTS THE FIRST CHAR TO PRINT, ETC
*    THE BITS 6 TO 0 REPRESENT LINES (SUCCESSIVE) TO PRINT FOR THE CHAR
*
*  FOR EXAMPLE, THE ENTRY FOR '*' IS 22H,14H,7FH,14H,22H; THIS GENERATES:
*
*         *     00100   R       6
*       * * *   10101   E       5
*        ***    01110   A       4
*         *     00100   D       3
*        ***    01110           2
*       * * *   10101   D       1
*         *     00100   O       0
*               ^^^^^   W
*               21712   N       ^
*               24F42           B
*               HHHHH           I
*                               T
*
CHARS:
       DB      00H,00H,00H,00H,00H     ;<SP>
       DB      00H,00H,7DH,00H,00H     ;EXCLAMATION MARK
       DB      00H,70H,00H,70H,00H     ;"
       DB      14H,7FH,14H,7FH,14H     ;#
       DB      12H,2AH,7FH,2AH,24H     ;$
       DB      62H,64H,08H,13H,23H     ;%
       DB      36H,49H,35H,02H,05H     ;&
       DB      00H,00H,70H,00H,00H     ;'
       DB      1CH,22H,41H,00H,00H     ;(
       DB      00H,00H,41H,22H,1CH     ;)
       DB      22H,14H,7FH,14H,22H     ;*
       DB      08H,08H,3EH,08H,08H     ;+
       DB      00H,01H,06H,00H,00H     ;,
       DB      08H,08H,08H,08H,08H     ;-
       DB      00H,03H,03H,00H,00H     ;.
       DB      02H,04H,08H,10H,20H     ;/
       DB      3EH,45H,49H,51H,3EH     ;0
       DB      11H,31H,7FH,01H,01H     ;1
       DB      21H,43H,45H,49H,31H     ;2
       DB      22H,41H,49H,49H,36H     ;3
       DB      0CH,14H,24H,7FH,04H     ;4
       DB      7AH,49H,49H,49H,46H     ;5
       DB      3EH,49H,49H,49H,26H     ;6
       DB      43H,44H,48H,50H,60H     ;7
       DB      36H,49H,49H,49H,36H     ;8
       DB      30H,49H,49H,49H,3EH     ;9
       DB      00H,00H,36H,00H,00H     ;:
       DB      00H,01H,16H,00H,00H     ;;
       DB      08H,14H,22H,41H,00H     ;<
       DB      14H,14H,14H,14H,14H     ;=
       DB      00H,41H,22H,14H,08H     ;>
       DB      20H,40H,4DH,50H,20H     ;?
       DB      7EH,41H,5DH,4DH,39H     ;@
       DB      3FH,48H,48H,48H,3FH     ;A
       DB      7FH,49H,49H,49H,36H     ;B
       DB      7FH,41H,41H,41H,41H     ;C
       DB      7FH,41H,41H,41H,3EH     ;D
       DB      7FH,49H,49H,49H,41H     ;E
       DB      7FH,48H,48H,48H,40H     ;F
       DB      7FH,41H,41H,49H,4FH     ;G
       DB      7FH,08H,08H,08H,7FH     ;H
       DB      41H,7FH,41H,80H,80H     ;I
       DB      03H,01H,01H,01H,7FH     ;J
       DB      7FH,08H,14H,22H,41H     ;K
       DB      7FH,01H,01H,01H,01H     ;L
       DB      7FH,20H,10H,20H,7FH     ;M
       DB      7FH,30H,08H,06H,7FH     ;N
       DB      7FH,41H,41H,41H,7FH     ;O
       DB      7FH,48H,48H,48H,78H     ;P
       DB      7FH,41H,45H,43H,7FH     ;Q
       DB      7FH,48H,4CH,4AH,79H     ;R
       DB      32H,49H,49H,49H,26H     ;S
       DB      40H,40H,7FH,40H,40H     ;T
       DB      7FH,01H,01H,01H,7FH     ;U
       DB      70H,0CH,03H,0CH,70H     ;V
       DB      7FH,02H,04H,02H,7FH     ;W
       DB      63H,14H,08H,14H,63H     ;X
       DB      60H,10H,0FH,10H,60H     ;Y
       DB      43H,45H,49H,51H,61H     ;Z
       DB      7FH,41H,41H,41H,00H     ;[
       DB      20H,10H,08H,04H,02H     ;\
       DB      00H,41H,41H,41H,7FH     ;]
       DB      04H,08H,10H,08H,04H     ;^
       DB      01H,01H,01H,01H,01H     ;_
       DB      00H,40H,20H,10H,00H     ;'
       DB      3FH,48H,48H,48H,3FH     ;A
       DB      7FH,49H,49H,49H,36H     ;B
       DB      7FH,41H,41H,41H,41H     ;C
       DB      7FH,41H,41H,41H,3EH     ;D
       DB      7FH,49H,49H,49H,41H     ;E
       DB      7FH,48H,48H,48H,40H     ;F
       DB      7FH,41H,41H,49H,4FH     ;G
       DB      7FH,08H,08H,08H,7FH     ;H
       DB      41H,7FH,41H,80H,80H     ;I
       DB      03H,01H,01H,01H,7FH     ;J
       DB      7FH,08H,14H,22H,41H     ;K
       DB      7FH,01H,01H,01H,01H     ;L
       DB      7FH,20H,10H,20H,7FH     ;M
       DB      7FH,30H,08H,06H,7FH     ;N
       DB      7FH,41H,41H,41H,7FH     ;O
       DB      7FH,48H,48H,48H,78H     ;P
       DB      7FH,41H,45H,43H,7FH     ;Q
       DB      7FH,48H,4CH,4AH,79H     ;R
       DB      32H,49H,49H,49H,26H     ;S
       DB      40H,40H,7FH,40H,40H     ;T
       DB      7FH,01H,01H,01H,7FH     ;U
       DB      70H,0CH,03H,0CH,70H     ;V
       DB      7FH,02H,04H,02H,7FH     ;W
       DB      63H,14H,08H,14H,63H     ;X
       DB      60H,10H,0FH,10H,60H     ;Y
       DB      43H,45H,49H,51H,61H     ;Z

*
*  BUFFERS
*
       DS      40      ;20-ELT STACK
STACK:
       DS      2       ;TOP OF BANNER STACK; CP/M STACK
NEXTCH:
       DS      2
LLEN    EQU     80      ;NUMBER OF CHARS IN LINE, MAX
INLINE:
       DB      LLEN    ;CHAR COUNT FOR INLINE
BLINE:
       DS      LLEN+1  ;NUMBER OF BYTES IN LINE