;
;    RUN80 Version 2.0, Modified: 04/08/81 by Kelly Smith
;
;    Ver. 2.0 Mod's: changed BASE equate from 4200H to 0000H for
;                    "standard CP/M" compatiblity.
;
;                    changed all "-1" values to 0FFH to allow
;                    assembly with Digital Research's ASM.COM.
;
;                    made TITLE string a comment field to allow
;                    assemby with DR's ASM.COM and MAC.COM.
;
;                    removed "exclamation" in comment field string
;                    "SCARE YOU", that caused 'L' error using
;                    DR's ASM.COM.
;
;                    swapped order of first two characters in filetypes
;                    to OPEN (i.e., RNO and MEM) so that files WOULD open.
;                    Version 1.A was trying to OPEN NRO and EMM when using
;                    DR's ASM.COM...someone was confused here I think.
;
;                    cleaned-up some of the "free-form" editing job,
;                    to make it a little easier to read...I gave up at
;                    label CTR:, 'cause it's to much like work. Ahhh, for
;                    for the glory of Control-I...wish someone had used it.
;
; copyright (C) 1981, T. Shapin, Orange, Ca. This program may not be sold,
; but may be distributed without charge.
;
;<DP.SHAPIN>RUN80.S80.61,  7-Feb-80 09:44:46, Edit by DP.SHAPIN
;
;        TITLE   RUN80 - K AND P TEXT FORMATTER
;
; FROM DR. DOBBS JOURNAL, MAY 1979.
; FORMAT ... THE SOFTWARE TOOLS TEXT FORMATTER
;
; MIKE GABRIELSON   12/2/78
;
; THIS PROGRAM IS A SOMEWHAT MODIFIED VERSION
; OF THE FORMAT PROGRAM IN THE BOOK "SOFTWARE TOOLS" BY
; KERNIHAN AND PLAUGER. DON'T LET
; THE ABSENCE OF COMMENTS SCARE YOU. THE BOOK CONTAINS COMPLETE
; DOCUMENTATION FOR THIS CODE.
;
; MODIFIED FOR CP/M (C) DIGITAL RESEARCH BY
; TED SHAPIN
;
; START THIS PROGRAM BY GIVING THE COMMAND 'RUN80 filename'.
;  IT WILL READ A FILE filename.RNO AND WRITE A FORMATTED
;  FILE WITH THE NAME: filename.MEM
;
; FORMATTING COMMANDS
;
; COMMAND       BREAK?  DEFAULT FUNCTION
; -------       ------  ------- --------
; .BR           YES             CAUSE A LINE BREAK
; .CE   N       YES     N= 1    CENTER THE NEXT N LINES
; .FI           YES             START FILLING (MOVE WORDS TO FILL LINES)
; .FO           NO      EMPTY   FOOTER TITLE
; .HE           NO      EMPTY   HEADER TITLE
; .IN   N       NO      N=0     INDENT N SPACES
; .NF           YES             STOP FILLING
; .PG   N       YES     N=+1    BEGIN PAGE NUMBERED N
; .PL   N       NO      N=66    SET PAGE LENGTH TO N
; .RM   N       NO      N=60    SET RIGHT MARGIN TO N
; .SK   N       YES     N=1     SPACE DOWN N LINES
; .SP   N       NO      N=1     LINE SPACING IS N
; .TA N,N...    NO      CLEARS  SET TAB STOPS (10 MAX)
; .TI   N       YES     N=0     TEMPORARY INDENT OF N
; .UL   N       NO      N=1     UNDERLINE WORDS FROM NEXT N LINES
;
; -------------------------------------------------------------
;
BACKSPACE	EQU	8
EOS		EQU	0
HUGE		EQU	1000
LINSIZ		EQU	80
LIT     	EQU     '`'     ; SYMBOL TO TAKE NEXT CHAR LITERALLY
NEWLINE		EQU	0DH
NONEX   	EQU     '#'     ; SYMBOL FOR NON-EXPANDING BLANK
                        ;  IN FILLED TEXT
NO		EQU	0
PGLEN		EQU	66
PAGENUM		EQU	'#' ; SYMBOL TO PRINT PAGE NUMBER IN HEADER OR FOOTER
PGWID		EQU	60
TAB		EQU	9
TRIPLE		EQU	LINSIZ*3+3   ;UNDERLINING TRIPLES THE LENGTH
YES		EQU	0FFH
;
CR		EQU	0DH
LF		EQU	0AH
;
BASE    EQU     0000H           ; BASE OF CP/M VERSION
BDOS    EQU     BASE+5
BUFF    EQU     BASE+80H
;
FCB     EQU     BASE+05CH       ; FILE CONTROL BLOCK
FCBDN   EQU     FCB+0   ; NAME OF INPUT HEX FORMAT FILE
FCBNM   EQU     FCB+1   ; FILE NAME
FCBFT   EQU     FCB+9   ; DISK FILE TYPE (3 CHARS)
FCBRL   EQU     FCB+12  ; FILE'S CURRENT REEL NUMBER
FCBRC   EQU     FCB+15  ; FILE'S RECORD COUNT (0 TO 128)
FCBBM   EQU     FCB+16  ; START OF BIT MAP
FCBNM2  EQU     FCB+16  ; SECOND FILE NAME (OUTPUT)
FCBCR   EQU     FCB+32  ; CURRENT (NEXT) RECORD NUMBER (0 TO 127)
FCBDM   EQU     FCB+33  ; START OF DISK ALLOCATION MAP
FCBLN   EQU     FCB+33  ; FCB LENGTH
;
;
        ORG     100H+BASE
        LXI     SP,STACK
        CALL    DSKINI
        CALL    INIT
        JMP     MAINL
  
GNASC:  PUSH	H  ; GET NEXT ASCII 
        PUSH	D ; LEAVE WITH CARRY SET IF AT EOF
        PUSH	B
GNA2:   LDA	IPTR       ; INPUT BUFFER POINTER
        CPI	128        ; 128 BYTES PER SECTOR (INPUT RECORD)
        JZ	FREAD      ; READ NEXT FILE RECORD
GNA1:	MVI	D,00H
        MOV	E,A
        LXI	H,BUFF
        DAD	D
        MOV	A,M
        CPI	1AH        ; CONTROL-Z IS EOF
        JZ	GNA4
        LXI	H,IPTR
        INR	M
        CPI     LF      ; DON'T TRANSMIT
        JNZ     GNA3    ; LINE FEEDS
        INX     D
        JMP     GNA2
GNA3:   ORA     A       ; CLEAR CARRY
        JMP  GNA9

FREAD:			; READ THE NEXT FILE RECORD
        LXI     D,BUFF  ; SET DISK BUFFER ADDRESS
        MVI     C,1AH   ; SET DMA ADDR
        CALL    BDOS
        MVI	C,14H      ;READ FILE RECORD
        LXI	D,FCB
        CALL	BDOS
        ORA	A
        JNZ	GNA4
        STA	IPTR       ; ZERO PTR FOR START OF BUFFER
        JMP	GNA1

GNA4:	MVI	A,1AH        ; INDICATE EOF
        STC		; BY SETTING CARRY
GNA9:   POP	B
	POP	D
	POP	H
	RET
;
;       
PUTC:   PUSH    H       ; SEND A CHAR TO DISK BUFFER AND
        PUSH    B       ; WRITE IT IF IT IS FULL
        PUSH    D
        PUSH    PSW     ; SAVE CHAR IN CASE ITS A CR
PUTC2:  LHLD    OPTR
        MOV     M,A     ; CHAR TO WRITE
        INX     H
        SHLD    OPTR
        MOV     A,L     ; NOW SEE IF 128 BUFFER IS FULL
        CPI     128
        CNC     SAVDSK  ; WRITE THE BUFFER
        POP     PSW
        CPI     NEWLINE ; IF IT WAS A CR,
        JNZ     PUTCX   ; WE NEED TO ADD
        MVI     A,LF
        PUSH    PSW     ; A LF
        JMP     PUTC2
PUTCX:  POP     D
        POP     B
        POP     H       ; DONE
        RET
;
SAVDSK:
        LXI     H,DBUFF ;POINT TO START OF AREA TO SAVE
SAVD10:
        SHLD    OPTR            ;RESET POINTER
        XCHG                    ;SET BUFFER ADR TO DBUFF
        MVI     C,26
        CALL    BDOS
        LXI     D,OUTFCB                ;WRITE NEXT RECORD TO DISK
        MVI     C,21
        CALL    BDOS
        CPI     0               ;WAS THERE AN ERROR?
        RZ              ; NO. RETURN
        LXI     D,DERMES        ;YES: COMPLAIN, CLOSE FILE & RET
        MVI     C,9
        CALL    BDOS
        LXI     D,OUTFCB
        MVI     C,16
        CALL    BDOS
        JMP     RETCPM
;
AWAIT:  MVI     C,11    ; TEST CONSOLE STATUS
        CALL    BDOS    ; FOR A CHARACTER
        ANI     1       ; READY
        JZ      AWAIT
        RET             ; GOT ONE
  
DSKINI:                 ; INITIALIZE INPUT AND OUTPUT DISK FILES
        LXI     D,IDMSG ; PRINT SIGN ON MSG
        CALL    PRINT
        LDA     FCBNM
        CPI     20H
        JZ      NONAME  ; PRINT HELP MESSAGE
        LXI     H,'RN'  ; FILL IN 'RNO' FOR
        SHLD    FCBFT   ; FILE NAME EXTENSION
        MVI     A,'O'
        STA     FCBFT+2
;
        LXI     H,OUTFCB
        LXI     D,FCB
;
        MVI     B,13    ; NOW
DSKL:   LDAX    D       ; MOVE OUTPUT FILE NAME TO OUTFCB
        MOV     M,A     ; INCLUDING REEL NO.
        INX     H
        INX     D
        DCR     B
        JNZ     DSKL
;
        LXI     H,'ME'  ; FILL IN 'MEM' FOR
        SHLD    OUTFCB+9 ; FILE NAME EXTENSION
        MVI     A,'M'
        STA     OUTFCB+11
;
        LXI     D,OUTFCB
        PUSH    D
        MVI     C,13H   ; DELETE OUTPUT FILE NAME
        CALL    BDOS

        POP     D
        MVI     C,22    ; MAKE A NEW OUTPUT FILE
        CALL    BDOS

        CPI     0FFH
        JZ      XOUTF   ; CANNOT CREATE NEW OUTPUT FILE NAME
        XRA     A
        STA     ORECN   ; SET OUTPUT RECORD NUMBER TO 0
        LXI     H,DBUFF ; INITIALIZE DISK BUFFER
        SHLD    OPTR    ; POINTER
        MVI     C,0FH   ; OPEN INPUT FILE
        LXI     D,FCB
        CALL    BDOS

        CPI     0FFH
        JZ      NOINF   ; INPUT FILE NOT FOUND
        LXI     H,IPTR  ; SET POINTER TO END OF RECORD BUFF
        MVI     M,128   ; SO INITIAL READ WILL HAPPEN
        RET     

NOINF:  LXI     D,NOINFM
        CALL    PRINT   
        JMP     RETCPM
        
NOINFM: DB      CR,LF,'INPUT (.RNO) FILE NOT FOUND$'
        
DERMES: DB      CR,LF,'ERROR WRITING OUTPUT RECORD$'

IXM:    LXI     D,IXMS
        CALL    PRINT   ;       PRINT   MSG
        JMP     RETCPM
        
IDMSG:  DB      CR,LF,'RUN80 TEXT FORMATTER, Ver. 2.0 as of 04/08/81',CR,LF,'$'
 
IXMS:   DB      CR,LF,'INPUT READ ERROR$'
        
NONAME: DB      CR,LF,'START THIS PROGRAM AS: RUN80 filename$'
        
XOUTF:  LXI     D,XOUTM
        CALL    PRINT   ;       PRINT   MSG
        JMP     RETCPM

XOUTM:  DB      'CANNOT CREATE OUTPUT FILE$'
        
PRINT:  MVI     C,9     ;PRINT A MSG TO '$'
        CALL    BDOS
        RET     

RETCPM  JMP     BASE
;
;       DATA    AREAS
;
IPTR:   DW      0       ;POINTER TO ASCII CHAR. IN INPUT BUFFER
OUTFCB: DS      33      ; FCB FOR OUTPUT FILE
ORECN   EQU     OUTFCB+32       ; RECORD NUMBER
;
        DS      2       ; SAFETY SPACE
MAINL:
        LXI	H,INBUF
        CALL	GETLIN
        JC	FLUSH
        MVI	A,'.'       ; COMMAND STARTS WITH PERIOD
        CMP	M
        JNZ	ITSTEXT
        CALL	 COMAND
        JMP	MAINL
ITSTEXT:
        CALL	TEXT
        JMP	MAINL
FLUSH:  CALL	BRK        ; FINISH ANY POSSIBLE LINE 0
        LHLD	LINENO
        MOV	A,H
        ORA	L
        CZ      FINISH  ; TRULY AT TOP OF A PAGE
        LXI	H,HUGE
        CALL	SPACE
FINISH: MVI     A,1AH   ; WRITE CP/M EOF CHARACTER
        CALL    PUTC
        LHLD    OPTR    ; SMALL CHANCE THAT WE JUST
        LXI     D,DBUFF ; FINISHED WRITING A DISK BUFFER
        CALL    COMPARE
        CNZ     SAVDSK  ; WRITE BUFFER ON DISK
        MVI     C,16    ; NOW CLOSE
        LXI     D,FCB   ; INPUT FILE
        CALL    BDOS
        LXI     D,OUTFCB        ; AND CLOSE
        MVI     C,16    ; OUTPUT FILE
        CALL    BDOS
        CALL	RETCPM
;
FOOTER: DS LINSIZ+3     ; INCLUDES CR, LF AND EOS
HEADER: DS LINSIZ+3
INBUF:  DS TRIPLE       ; HOLDS INPUT LINE
OUTBUF: DS TRIPLE       ; LINES TO BE FILLED COLLECT HERE
WRDBUF: DS TRIPLE       ; USED BY PUTWRD
BACKUP: DS 1    ; ROOM FOR ONE CHAR PUSHED BACK ONTO INPUT STREAM
BOTTOM: DS 2    ; LAST LIVE LINE = PLVAL-M3VAL-M4VAL
CEVAL:  DS 2    ; NUMBER OF INPUT LINES TO CENTER
CURPAG: DS 2    ; CURRENT OUTPUT PAGE NO. (INIT = 0)
DIR:    DS      1       ; DIRECTION OF SPREADING LINE TO ADD SPACES
FILL:   DS 1    ; FILL IF YES (INIT = YES)
INVAL:  DS 2    ; CURRENT INDENT VALUE. (INIT = 0)
LAST:   DS 2
LINENO: DS 2    ; NEXT LINE TO BE PRINTED. (INIT = 0)
LLVAL:  DS 2
LSVAL:  DS 2    ; CURRENT LINE SPACING (INIT = 1)
M1VAL:  DS 2    ; MARGIN BEFORE AND INCLUDING HEADER (INIT = 3)
M2VAL:  DS 2    ; MARGIN AFTER HEADER
M3VAL:  DS 2    ; MARGIN AFTER LAST TEXT LINE
M4VAL:  DS 2    ; BOTTOM MARGIN, INCLUDING FOOTER
NB:     DS 2
NNE:    DS 2
NEWPAG: DS 2    ; NEXT OUTPUT PAGE NUMBER. (INIT = 1)
NEXTRA: DS 2
NHOLES: DS 2
OUTP:   DS 2    ; LAST CHAR POSITION IN OUTBUF. (INIT = 0)
OUTW:   DS 2    ; WIDTH OF TEXT NOW IN OUTPUT BUFF. (INIT = 0)
OUTWDS: DS 2    ; NUMBER OF WORDS IN OUTBUF. (INIT = 0)
PLVAL:  DS 2    ; PAGE LENGTH IN LINES. (INIT = PAGELEN)
RMVAL:  DS 2    ; CURRENT RIGHT MARGIN. (INIT = PAGEWID)
SPVAL:  DS 2
TABSON: DS 1    ; SPACE FOR TABS FLAG. (INIT = NO TABS)
TABPOS: DS 11   ; SPACE FOR UP TO 10 TAB STOPS AND END MARKER
TIVAL:  DS 2    ; TEMPORARY INDENT VALUE. (INIT = 0)
ULVAL:  DS 2    ; NUMBER OF INPUT LINES TO UNDERLINE
WAITUP: DS 1            ;YES IF FORMAT SHOULD WAIT AT START OF EACH
;                       ; PAGE
CMDTAB:
        DB      'BR'
        DW      BREAKC
        DB      'CE'
        DW      CTRC
        DB      'FI'
        DW      FILLC
        DB      'FO'
        DW      FOOTC
        DB      'HE'
        DW      HDR
        DB      'IN'
        DW      IND
        DB      'NF'
        DW      NOFILL
        DB      'PG'
        DW      PAGEC
        DB      'PL'
        DW      LGT
        DB      'RM'
        DW      MARGIN
        DB      'SK'
        DW      SKP
        DB      'SP'
        DW      SPACEC
        DB      'TA'
        DW      TABSET
        DB      'TI'
        DW      TEMPI
        DB      'UL'
        DW      UNDERC
;
        DB      0       ; FLAG END OF TABLE
;
;-----
; ALPHA - TEST CHARACTER FOR LETTER
; ACCEPTS: A = CHARACTER
; RETURNS: CARRY SET IF A = A-Z OR a-z
;-----
;
ALPHA:  CPI	'a'
        JC	NOTLC
        CPI	'z'+1
        RC
NOTLC:  CPI	'Z'+1
        RNC
        CPI	'A'
        CMC
        RET
;
; BRK - END CURRENT FILLED LINE
;-----
;
BRK:    PUSH	PSW
        PUSH	B
        PUSH    D
        PUSH	H
        LXI	D,0
        LHLD	OUTP
        CALL	COMPARE
        JNC	EMPTY
        LXI	D,OUTBUF
        DAD	D
        MVI	M,EOS
        DCX     H
        MVI     M,NEWLINE
        XCHG
        CALL	PUT
EMPTY:  LXI	H,0
        SHLD	OUTP
        SHLD	OUTW
        SHLD	OUTWDS
        POP	H
        POP     D
        POP	B
        POP	PSW
        RET
;
;-----
; CENTER - CENTER A LINE BY SETTING TIVAL
; ACCEPTS: HL = A LINE
;-----
;
CTR:    PUSH H
        CALL WIDTH
        LHLD TIVAL
        XCHG
        CALL DELESSHL
        XCHG
        LHLD RMVAL
        DAD D
        CALL DIVBY2
        LXI D,0
        CALL MAX
        SHLD TIVAL
        POP H
        RET
;
;-----
; CHRBAK - PUSH CHARACTER BACK ONTO INPUT
; ACCEPTS: B = CHARACTER
;-----
;
CHRBAK: MOV A,B
        STA BACKUP
        RET
;
;-----
; COMMAND - PERFORM FORMATTING COMMAND
;-----
;
COMAND:
        LXI D,CMDTAB
        INX H           ;GET ADDR KEYWORD (SKIP .)
        CALL SEARCH     ;COMMAND IN TABLE?
        RC              ;NO, IGNORE
        PUSH D          ;STACK DISPATCH ADDRESS
        CALL GETVAL
        RET             ;JUMP TO COMMAND HANDLER
;                       ;DE = ADDR END OF COMMAND.
;                       ;HL = ARGUMENT VALUE
BREAKC: CALL BRK
        RET
;
CTRC:
        CALL BRK
        LXI B,0
        PUSH B
        INX B
        LXI D,HUGE
        PUSH D
        XCHG
        LHLD CEVAL
        CALL PSET
        SHLD CEVAL
        RET
;
FOOTC:
        LXI H,FOOTER
        CALL GETTL
        RET
;
TEMPI:
        CALL    BRK
        LXI B,0
        PUSH B
        XCHG
        LHLD RMVAL
        PUSH H
        LHLD TIVAL
        CALL PSET
        SHLD TIVAL
        RET
;
HDR:
        LXI H,HEADER
        CALL GETTL
        RET
;
IND:
        LXI B,0
        PUSH B
        XCHG
        LHLD RMVAL
        DCX H
        PUSH H
        LHLD INVAL
        CALL PSET
        SHLD INVAL
        SHLD TIVAL
        RET
;
NOFILL:
        CALL BRK
        MVI A,NO
        STA FILL
        RET
;
FILLC:
        CALL BRK
        MVI A,YES
        STA FILL
        RET
;
LGT:
        PUSH H
        LHLD M1VAL
        XCHG
        LHLD M2VAL
        DAD D
        XCHG
        LHLD M3VAL
        DAD D
        XCHG
        LHLD M4VAL
        DAD D
        INX H
        XTHL
        LXI B,HUGE
        PUSH B
        LXI B,PGLEN
        XCHG
        LHLD PLVAL
        CALL PSET
        SHLD PLVAL
        XCHG
        LHLD M3VAL
        CALL DELESSHL
        XCHG
        LHLD M4VAL
        CALL DELESSHL
        SHLD BOTTOM
        RET
;
MARGIN:
        XCHG
        LHLD TIVAL
        INX H
        PUSH H
        LXI H,HUGE
        PUSH H
        LXI B,PGWID
        LHLD RMVAL
        CALL PSET
        SHLD RMVAL
        RET
;
PAGEC:  PUSH H
        PUSH PSW
        LXI D,0
        LHLD LINENO
        CALL COMPARE
        LXI H,HUGE
        CC SPACE
        POP PSW
        POP D
        LXI B,-HUGE
        PUSH B
        LXI B,HUGE
        PUSH B
        LHLD CURPAG
        MOV B,H
        MOV C,L
        INX B
        CALL PSET
        SHLD CURPAG
        SHLD NEWPAG
        RET
;
SKP:    LXI B,0         ;STACK MINIMUM
        PUSH B
        INX B           ;FORM DEFAULT
        LXI D,HUGE      ;STACK MAXIMUM
        PUSH D
        XCHG
        LHLD SPVAL
        CALL PSET
        SHLD SPVAL
        CALL SPACE
        RET
SPACEC: LXI B,1         ;GET MINIMUM AND DEFAULT
        PUSH B          ;STACK MINIMUM
        LXI D,HUGE      ;STACK MAXIMUM
;
        PUSH D
        XCHG            ;DE := ARGUMENT VALUE
        LHLD LSVAL
        CALL PSET
        SHLD LSVAL
        RET
;
UNDERC: LXI B,1
        PUSH B
        DCX B
        LXI D,HUGE
        PUSH D
        XCHG
        LHLD ULVAL
        CALL PSET
        SHLD ULVAL
        RET
;
;-----
; COMPARE - COMPARE TWO SIGNED NUMBERS
; ACCEPTS:  NUMBERS IN DE, HL
; RETURNS:  Z SET IF DE = HL
;           C SET IF DE < HL
;           C CLEAR IF DE >= HL
; DO THE USUAL SUBTACT, BUT COMPLEMENT THE CARRY
; IF THE ORIGINAL SIGNS ARE NOT EQUAL
;-----
;
COMPARE:
        MOV A,E
        SUB L
        MOV A,D
        SBB H
        PUSH PSW
        MOV A,D
        XRA H
        ANI 80H
        JNZ SIGNEQ
        POP PSW
        RET
;
SIGNEQ: POP PSW
        CMC
        RET
;
;-----
; DELESSHL - SUBTRACT HL FROM DE
; RETURNS: HL = DE - HL
;-----
;
DELESSHL:
        MOV A,E
        SUB L
        MOV L,A
        MOV A,D
        SBB H
        MOV H,A
        RET
;
;-----
; DIVBY2 - DIVIDE SIGNED NUMBER IN HL BY TWO
; (SHIFT RIGHT BUT PROPAGATE THE SIGN)
;-----
;
DIVBY2: MOV A,H
        RAL
        MOV A,H
        RAR
        MOV H,A
        MOV A,L
        RAR
        MOV L,A
        RET
;
;-----
; DIVIDE - DIVIDE DE BY HL
; ACCEPTS: HL = DIVISOR
;          DE = DIVIDEND
; RETURNS: HL = QUOTIENT
;-----
;
DIVIDE: MOV B,H         ;MOVE DIVISOR TO BC
        MOV C,L
        LXI H,0         ;FORM INITIAL QUOTIENT
DVLOOP: MOV A,E         ;DIVIDE BY SUBTRACTION
        SUB C           ;(DE := DE - BC)
        MOV E,A
        MOV A,D
        SBB B
        MOV D,A
        RC
        INX H
        JMP DVLOOP
;
;-----
; EXTEND - EXTEND A STRING BY INSERTING ONE CHARACTER
; ACCEPTS: C = CHAR TO INSERT
;          HL = ADDR OF WHERE TO INSERT IT IN STRING
;-----
;
EXTEND: PUSH    H
        PUSH    B
EXTD2:  MOV     A,M     ; GET CHAR TO BE MOVED
        MOV     M,C     ; REPLACE IT WITH CHAR IN C
        MOV     C,A     ; AND REPEAT
        INX     H       ; STEP TO NEXT CHAR
        CPI     EOS     ; UNTIL THE END OF STRING
        JNZ     EXTD2
        MOV     M,C     ; STORE THE LAST CHAR = EOS
        POP     B
        POP     H
        RET
;-----
; GETCHR - GET NEXT CHARACTER
; RETURNS:  CARRY SET IF EOF
;           ELSE A = CHARACTER
;-----
;
GETCHR: LDA BACKUP      ;ANY CHARACTER BACKED UP?
        CPI NEWLINE     ;(NEWLINE CAN'T BE BACKED UP,
;                       ; IT MEANS BACKUP IS EMPTY)
        JZ GETC         ;NO, READ NEXT CHARACTER
        MOV B,A         ;YES, SAVE CHARACTER IN B
        MVI A,NEWLINE   ;GET NEWLINE
        STA BACKUP      ;EMPTY BACKUP
        MOV A,B         ;RESTORE CHARACTER
        ORA A           ;CLEAR CARRY
        RET
GETC:   CALL GNASC      ;GET NEXT CHARACTER
        RET
;
;-----
; GETLIN - FILL BUFFER WITH NEXT INPUT LINE
; ACCEPTS:  HL (SAVED) = ADDR BUFFER
; RETURNS:  CARRY SET IF EOF
;           ELSE LINE TERMINATED BY EOL, EOS
;-----
;
GETLIN: MOV D,H         ;SAVE ADDR OF  BUFFER IN DE
        MOV E,L
        MVI C,0         ;INITIALIZE LINE LENGTH
GETNXT: MVI A,LINSIZ    ; GET MAXIMUM CHARACTERS PER LINE
        CMP C           ;LINE BUFFER FULL?
        JZ LINFIN       ;YES, FORCE NEW LINE
        CALL    GETCHR  ;GET NEXT CHARACTER
        JC LINEND       ;HANDLE EOF
        MOV B,A         ;SAVE CHARACTER IN B
        CPI NEWLINE     ;END OF LINE?
        JZ      LINFIN  ; FOUND END OF LINE
        MOV M,B         ;SAVE CHARACTER IN BUFFER
        INX H           ;POINT TO NEXT CHARACTER POSITION
        INR C           ;ADJUST # OF CHARACTERS SAVED
        JMP GETNXT      ;GET NEXT CHARACTER
;
LINFIN: MVI M,NEWLINE   ;END LINE WITH CARRIAGE RETURN
        INX H           ;AND
        MVI M,EOS       ;END OF STRING
        XCHG            ;RESTORE HL
        ORA A           ;CLEAR CARRY
        RET
LINEND: MOV A,C         ;BUFFER EMPTY?
        ORA C           ;( COUNT ZERO?)
        JNZ LINFIN      ;NO, PROVIDE EOL 
        STC             ;YES, TRUE EOF
        RET
;
;-----
; GETTL - COPY TITLE
; ACCEPTS:  DE = ADDR END OF COMMAND
;           HL = ADDR DESTINATION
;
;-----
;
GETTL:          ; called after leading blanks and tabs have been skipped
        LDAX D
        INX D
        CPI ''''
        JZ COPYTL
        CPI '"'
        JZ COPYTL
        DCX D
COPYTL: CALL SCOPY
        RET
;
;-----
; GETVAL - EVALUATE OPTIONAL NUMERIC ARGUMENT
; ACCEPTS:  DE = ADDR OF END OF COMMAND
; RETURNS:   A = ARGTYP
;           HL = ARGUMENT VALUE
;           DE = ADDR OF COMMAND PAST ARGUMENT
;-----
;
FNDARG: INX H           ;GET ADDR NEXT CHARACTER
GETVAL:
        MOV A,M         ;GET NEXT CHARACTER
        CPI ' '         ;BLANK?
        JZ FNDARG       ;YES, KEEP SEARCHING FOR ARGUMENT
        CPI TAB         ;NO, TAB?
        JZ FNDARG       ;YES, KEEP SEARCHING
        CPI ','         ; COMMA ALSO CAN BE
        JZ FNDARG       ; ALLOWED AS DELIMITER
        INX H           ;ASSUME CHARACTER IS '+' OR '-'
        CPI '+'         ;IS IT '+'?
        JZ CTOI         ;YES
        CPI '-'         ;NO, IS IT '-'?
        JZ CTOI         ;YES
        DCX H           ;NO, RESTORE ADDR ARGUMENT
CTOI:   PUSH PSW        ;SAVE ARGTYP
        XCHG            ;KEEP ADDR ARGUMENT IN DE
        LXI H,0         ;INITIALIZE ARGUMENT CHARACTER
VALOOP: LDAX D          ;GET NEXT ARGUMENT CHARACTER
        CPI '0'         ;< '0'?
        JC GOTVAL       ;YES, END OF ARGUMENT
        CPI '9'+1       ;NO, >'9'?
        JNC GOTVAL      ;YES, END OF ARGUMENT
        INX D           ;GET ADDR NEW ARGUMENT CHARACTER
        MOV B,H         ;NO, SAVE CURRENT VALUE
        MOV C,L         ;IN BC
        DAD H           ;DOUBLE CURRENT VALUE
        DAD H           ;QUADRUPLE
        DAD B           ;QUINTUPLE
        DAD H           ;DECTUPLE
        SUI '0'         ;CONVERT NEW CHARACTER IN BINARY
        ADD L           ;ADD TO NEW VALUE
        MOV L,A
        JNC VALOOP
        INR H
        JMP VALOOP
GOTVAL: POP PSW         ;RESTORE ARGTYP
        RET
;
;-----
; GETWRD - GET NEXT WORD
; ACCEPTS:  HL = ADDR TEXT
; RETURNS:  CARRY SET IF NONE FOUND
;           ELSE HL = ADDR EORD, EOS
;                DE = ADDR TEXT FOLLOWING WORD
;-----
;
GETWRD: MOV A,M
        INX H
        CPI ' '
        JZ GETWRD
        CPI TAB
        JZ GETWRD
        DCX H
        MOV D,H
        MOV E,L
GWLOOP: MOV A,M
        INX H
        CPI EOS
        JZ WRDEND
        CPI NEWLINE
        JZ WRDEND
        CPI TAB
        JZ WRDEND
        CPI ' '
        JNZ GWLOOP
WRDEND: DCX H
        MVI M,EOS
        CALL COMPARE
        XCHG
        CMC
        INX D
        RET
;
;-----
; INIT - INITIALIZE VARIABLES
;-----
;
INIT:   MVI A,NO
        STA WAITUP      ;SAVE WAIT OPTION FLAG
        STA TABSON      ; TURN TABS OFF
        STA TABPOS      ; FLAG END OF TABSTOP ARRAY
        MVI A,NEWLINE   ;INITIALIZE
        STA BACKUP      ;BACKUP CHARACTER
        STA HEADER
        STA FOOTER
        MVI A,EOS
        STA HEADER+1
        STA FOOTER+1
        MVI A,YES
        STA FILL
        LXI H,0
        SHLD INVAL
        SHLD TIVAL
        SHLD CEVAL
        SHLD ULVAL
        SHLD LINENO
        SHLD CURPAG
        SHLD OUTP
        SHLD OUTW
        SHLD OUTWDS
        INX H
        SHLD LSVAL
        SHLD NEWPAG
        INX H
        SHLD M2VAL
        SHLD M3VAL
        INX H
        SHLD M1VAL
        SHLD M4VAL
        LXI H,PGWID
        SHLD RMVAL
        LXI H,PGLEN
        SHLD PLVAL
        XCHG
        LHLD M3VAL
        CALL DELESSHL
        XCHG
        LHLD M4VAL
        CALL DELESSHL
        SHLD BOTTOM
        RET
;
;-----
; LEADBL - "DELETE"  LEADING BLANKS, SET TIVAL
; ACCEPTS: HL = ADDR LINE
; RETURNS: HL = ADDR FIRST NON-BLANK
;-----
;
LEADBL: CALL BRK
        LXI D,0
LBLOOP: MOV A,M
        INX D
        INX H
        CPI ' '
        JZ LBLOOP
        DCX H
        CPI NEWLINE
        RZ
        XCHG
        SHLD TIVAL
        XCHG
        RET
;
;-----
; LENGTH - COMPUTE LENGTH OF STRING
; ACCEPTS:  HL = ADDR STRING
; RETURNS:  DE = LENGTH
;-----
; 
LENGTH: LXI D,0
LENEXT: MOV A,M
        CPI EOS
        RZ
        INX D
        INX H
        JMP LENEXT
;
;-----
; MAX DETERMINE LARGER OF TWO NUMBERS
; ACCEPTS: NUMBERS IN DE, HL
; RETURNS:  HL = LARGER
;           DE = SMALLER
;-----
;
MAX:    CALL MIN
        XCHG
        RET
;
;-----
; MIN DETERMINE SMALLER OF TWO NUMBERS
; ACCEPTS: NUMBERS IN DE, HL
; RETURNS:  DE = LARGER
;           HL = SMALLER
;-----
;
MIN:    CALL COMPARE
        RNC
        XCHG
        RET
;
;-----
; PFOOT - PUT OUT PAGE FOOTER
;-----
;
PFOOT:  LHLD M3VAL
        CALL SKIP
        LXI D,0
        LHLD M4VAL
        CALL COMPARE
        RNC
        LXI D,FOOTER
        LHLD CURPAG
        CALL PUTTL
        LHLD M4VAL
        DCX H
        CALL SKIP
        RET
;
;-----
; PHEAD - PUT OUT PAGE HEADER
;-----
;
PHEAD:  PUSH D
        PUSH H          
        LDA WAITUP      ;GET W: OPTION FLAG
        CPI YES         ;WAIT FOR OPERATOR?
        CZ AWAIT        ;YES, WAIT FOR CONSOLE INPUT
        LHLD NEWPAG
        SHLD CURPAG
        INX H
        SHLD NEWPAG
        LHLD M1VAL
        PUSH H
        LXI D,0
        CALL COMPARE
        JNC SKPHDR
        DCX H
        CALL SKIP
        LXI D,HEADER
        LHLD CURPAG
        CALL PUTTL
SKPHDR: LHLD M2VAL
        PUSH H
        CALL SKIP
        POP H
        POP D
        DAD D
        INX H
        SHLD LINENO
        POP H
        POP D
        RET
;
;-----
; PUT - PUT OUT LINE WITH PROPER SPACING AND INDENTING
; ACCEPTS:  HL = ADDR LINE
;-----
;
PUT:    PUSH H
        LHLD LINENO
        MOV A,H
        ORA L
        JNZ NOT0
        CALL PHEAD
        JMP HEADED
NOT0:   XCHG
        LHLD BOTTOM
        XCHG
        CALL COMPARE
        JNC HEADED
        CALL PHEAD
HEADED: LHLD TIVAL
TITEST: MOV A,H
        ORA L
        JZ TIZERO
        MVI A,' '
        CALL PUTC
        DCX H
        JMP TITEST
TIZERO: LHLD INVAL
        SHLD TIVAL
        POP H
        CALL PUTLIN     ; PUT LINE OUT IN DISK BUFFER
        LHLD BOTTOM
        XCHG
        LHLD LINENO
        CALL DELESSHL
        XCHG
        LHLD LSVAL
        DCX H
        CALL MIN
        CALL SKIP
        LHLD LSVAL
        XCHG
        LHLD LINENO
        DAD D
        SHLD LINENO
        XCHG
        LHLD BOTTOM
        XCHG
        CALL COMPARE
        CC PFOOT
        RET
;
;-----
; PUTDEC - OUTPUT DECIMAL NUMBER
; ACCEPTS:  HL = NUMBER
;-----
;
PUTDEC: PUSH D
        PUSH H
        LXI D,TENTAB
        PUSH D
        MVI C,1
DOTPOS: XTHL
        MOV E,M
        INX H
        MOV D,M
        INX H
        XTHL
        MVI B,0FFH
DOTDIV: INR B
        MOV A,L
        SUB E
        MOV L,A
        MOV A,H
        SBB D
        MOV H,A
        JNC DOTDIV
        DAD D
        XRA A
        ORA B
        JNZ DOTPRI
        ORA C
        JNZ DOTSKP
DOTPRI: ORI '0'
        MVI C,0
        CALL PUTC
DOTSKP: MOV A,E
        CPI 1
        JNZ DOTPOS
        POP D
        POP H
        POP D
        RET
TENTAB: DW  10000
        DW 1000
        DW 100
        DW 10
        DW 1
;
;-----
; PUTLIN - OUTPUT LINE
; ACCEPTS: HL = ADDR LINE
;-----
;
PUTLIN: MOV A,M
        CPI EOS
        RZ
        LDA     FILL    ; IF FILLING
        CPI     NO      ; THEN POSSIBLE
        MOV     A,M     ; (GET CHAR BACK)
        JZ      PUTL6   ; TRANSLATION NEEDED
        CPI     LIT     ; IS NEXT CHAR TO BE
        JNZ     PUTL2   ; SENT LITERALLY?
        INX     H       ; YES. SO
        MOV     A,M     ; GET IT
        JMP     PUTL6
PUTL2:  CPI     NONEX   ; IS IT A NON-EXPANDABLE BLANK?
        JNZ     PUTL6   ; NO
        MVI     A,' '   ; YES. SO CHANGE IT TO BLANK.
PUTL6:  CALL PUTC
        INX H
        JMP PUTLIN
;-----
; PUTTL - PUT OUT TITLE LINE WITH OPTIONAL PAGE NUMBER
; ACCEPTS:  DE = ADDR LINE
;           HL = PAGE #
;-----
;
PUTTL:  LDAX D
        INX D
        CPI EOS
        RZ
        CPI PAGENUM
        JNZ TLCOPY
        CALL PUTDEC
        JMP PUTTL
TLCOPY: CALL PUTC
        JMP PUTTL
;
;-----
; PUTWRD - PUT A WORD IN OUTBUF
; ACCEPTS: HL = ADDR WORD
;-----
;
PUTWRD: PUSH D
        PUSH H
        CALL WIDTH
        MOV B,D         ;BC := W
        MOV C,E
        POP H
        PUSH H
        CALL LENGTH
        LHLD OUTP
        DAD D
        INX H
        SHLD LAST
        LHLD RMVAL
        XCHG
        LHLD TIVAL
        CALL DELESSHL
        SHLD LLVAL
        XCHG
        LHLD OUTW
        DAD B
        CALL COMPARE
        JC MAYFIT
        LHLD LAST
        XCHG
        LXI H,TRIPLE
        CALL COMPARE
        JC FITS
MAYFIT: LXI D,0
        LHLD OUTP
        CALL COMPARE
        JNC FITS
        XCHG
        LHLD LAST
        XCHG
        CALL DELESSHL
        SHLD LAST
        LHLD LLVAL
        XCHG
        LHLD OUTW
        CALL DELESSHL
        INX H
        SHLD NEXTRA
        PUSH B
        PUSH H
        CALL SPREAD
        POP H
        POP B
        LXI D,0
        CALL COMPARE
        JNC FLUSHIT
        INX D
        LHLD OUTWDS
        CALL COMPARE
        JNC FLUSHIT
        LHLD NEXTRA
        XCHG
        LHLD OUTP
        DAD D
        SHLD OUTP
FLUSHIT:        
        CALL BRK
FITS:   LXI D,OUTBUF
        LHLD OUTP
        DAD D
        POP D
        CALL SCOPY
        LHLD LAST
        SHLD OUTP
        LXI D,OUTBUF
        DAD D
        DCX H
        MVI M, ' '
        LHLD OUTW
        DAD B
        INX H
        SHLD OUTW
        LHLD OUTWDS
        INX H
        SHLD OUTWDS
        POP D
        RET
;
;-----
; SCOPY - COPY STRINGS
; ACCEPTS:  DE = ADDR SOURCE
;           HL = ADDR DESTINATION
;-----
;
SCOPY:  LDAX D
        MOV M,A
        CPI EOS
        RZ
        INX D
        INX H
        JMP SCOPY
;
;-----
; SEARCH - SEARCH TABLE FOR A COMMAND
; ACCEPTS:
;           DE = ADDR CMMND TABLE
;           HL = ADDR COMMAND
; RETURNS:  CARRY SET IF COMMAND NOT IN TABLE
;           ELSE DE = ADDRESS FROM TABLE
;                HL = ADDR END OF COMMAND
;-----
;
SEARCH: PUSH    H       ; SAVE START OF COMMAND
        LDAX    D       ; GET TABLE ENTRY
        ANA     A       ; AT END?
        JNZ     SRCH1   ; NO
        POP     H       ; NOT FOUND
        STC             ; YES
        RET
SRCH1:  XRA     M       ; COMBINE WITH FIRST COMMAND LETTER
        ANI     5FH     ; MAKE UPPER CASE
        JNZ     SRCH3   ; NO MATCH
        INX     D
        INX     H
        LDAX    D
        XRA     M
        ANI     5FH     ; CHECK SECOND CHARACTER
        JNZ     SRCH2   ; NO MATCH
; WE FOUND A COMMAND
        INX     D
        XCHG            ; POINT HL TO COMMAND ADDRESS
        MOV     E,M     ; PUT COMMAND ADDRESS
        INX     H
        MOV     D,M     ; IN DE
        POP     H       ; GET OLD START OF COMMAND
        INX     H       ; POINT PAST COMMAND
SRCH8:  INX     H       ; NOW MOVE TO END OF CMND WORD
        MOV     A,M
        CPI     EOS
        JZ      SRCH9   ; NO MORE ON LINE
        CPI     ' '     ; BLANK
        JZ      SRCH9
        CPI     TAB     ; OR TAB
        JNZ     SRCH8   ; MUST BE A LETTER
SRCH9:  ORA     A       ; CLEAR CARRY
        RET
;
SRCH3:  INX     D
SRCH2:  INX     D
        INX     D       ; BYPASS COMMAND ADDRESS
        INX     D
        POP     H
        JMP     SEARCH  ; AND LOOK SOME MORE
;
;
;-----
; SET - SET PARAMETER AND CHECK RANGE
; ACCEPTS:  A = ARGTYP
;          BC = DEFAULT VALUE
;          DE = ARGUMENT VALUE
;          HL = PARAMETER VALUE
;          TOP OF STACK = MAXIMUM VALUE
;          NEXT STACKED = MINIMUM VALUE
; RETURNS: HL = NEW PARAMETER VALUE
;-----
;
PSET:   CPI NEWLINE     ;IS THERE AN ARGUMENT?
        JZ DEFAULTED    ;NO
        CPI '+'         ;YES, INCREASE?
        JZ RELPLUS      ;YES
        CPI '-'         ;NO, DECREASE?
        XCHG            ;(ASSUME NOT: USE ABSOLUTE ARGUMENT)
        JZ RELMINUS     ;YES
MINMAX: POP B           ;GET RETURN ADDRESS
        POP D           ;HL = PARAM, GET MAXVAL
        CALL MIN        ;USE SMALLER OF MAXVAL AND PARAM
        POP D           ;GET MINVAL
        CALL MAX        ;USE LARGER OF MINVAL AND PARAM
        PUSH B
        RET
DEFAULTED:
        MOV H,B         ;USE DEFVAL
        MOV L,C         ;FOR PARAM
        JMP MINMAX
RELPLUS:
        DAD D           ;INCREASE PARAM
        JMP MINMAX
RELMINUS:
        CALL DELESSHL   ;DECREASE PARAM
        JMP MINMAX
;
;-----
; SKIP - OUTPUT BLANK LINES
; ACCEPTS:  HL = # OF LINES TO SKIP
;-----
;
SKIP:   MOV     A,H
        ORA L
        RZ
        MVI A,NEWLINE
        CALL PUTC
        DCX H
        JMP SKIP
;
;-----
; SPACE - SPACE SOME LINES OR TO BOTTOM OF PAGE
; ACCEPTS: HL = # OF LINES
;-----
;
SPACE:  CALL BRK
        MOV B,H
        MOV C,L
        LHLD BOTTOM
        XCHG
        LHLD LINENO
        CALL COMPARE
        RC
        PUSH B
        MOV A,H
        ORA L
        CZ PHEAD
        INX D
        CALL DELESSHL
        POP B
        MOV D,B
        MOV E,C
        CALL MIN
        CALL SKIP
        LHLD LINENO
        DAD B
        SHLD LINENO
        XCHG
        LHLD BOTTOM
        XCHG
        CALL COMPARE
        CC PFOOT
        RET
;
;-----
; SPREAD - SPREAD WORDS TO JUSTIFY RIGHT MARGIN
;-----
;
SPREAD: LXI D,0
        LHLD NEXTRA
        CALL COMPARE
        RNC
        INX D
        LHLD OUTWDS
        CALL COMPARE
        RNC
        LDA DIR
        CMA
        STA DIR
        DCX H
        SHLD NHOLES
        LHLD NEXTRA
        SHLD NNE
        XCHG
        LHLD OUTP
        DCX H
        PUSH H
        DAD D
        LXI D,TRIPLE-2
        CALL MIN
        LXI D,OUTBUF
        DAD D
        XTHL
        DAD D
        POP D
        XCHG
SPLOOP: DCX D
        DCX H
        CALL COMPARE
        RNC
        LDAX D
        MOV M,A
        CPI ' '
        JNZ SPLOOP
        PUSH D
        PUSH H
        LDA DIR
        PUSH PSW
        LHLD NNE
        CPI 0
        JNZ SP1
        DCX H
SP1:    XCHG
        LHLD NHOLES
        CALL DIVIDE
        POP PSW
        CPI 0
        JNZ SP2
        INX H
SP2:    SHLD NB
        XCHG
        LHLD NNE
        XCHG
        CALL DELESSHL
        SHLD NNE
        LHLD NHOLES
        DCX H
        SHLD NHOLES
        LXI D,0
        LHLD NB
SP3:    CALL COMPARE
        JNC SP4
        XTHL
        DCX H
        MVI M,' '
        XTHL
        DCX H
        JMP SP3
SP4:    POP H
        POP D
        JMP SPLOOP
;-----
; TABS - EXPAND TABS IN A LINE TO SPACES USING A TABPOS ARRAY
; ACCEPTS: HL = ADDR OF LINE
; RETURNS: HL UNCHANGED
;-----
;
TABS:   PUSH    H       ; SAVE FOR EXIT
        MVI     B,1     ; B COUNTS POSITION OF CHAR IN LINE
        LXI     D,TABPOS+10 ; ADDRESS OF FIRST TAB STOP
; LOOK FOR A TAB
TABS2:  MOV     A,M     ; GET CHAR
        CPI     TAB     ; IS IT A TAB?
        JZ      TABS4   ; YES
        CPI     EOS     ; IS IT THE END OF THE LINE
        JZ      TABSX   ; YES
TABS3:  INX     H       ; ADVANCE TO NEXT CHAR IN LINE
        INR     B       ; ADVANCE POSITION COUNT
        JMP     TABS2
TABS4:  LDA     FILL    ; IF WE NOT FILLING
        CPI     YES
        JZ      TABS5
        MVI     C,' '   ; USE A SPACE
        JMP     TABS6   ; OTHERWISE
TABS5:  MVI     C,NONEX ; USE A NON-EXPANDABLE BLANK
TABS6:  MOV     M,C     ; REPLACE TAB
TABS7:  INX     H
        INR     B
TABS8:  LDAX    D       ; GET TAB STOP POSITION
        CPI     0       ; AT END?
        JZ      TABSX   ; YES
        CMP     B       ; CHECK IT AGAINST LINE POSITION
        JZ      TABS3   ; ONE POSITION TOOK US TO NEXT STOP
        JC      TABS9   ; TRY THE NEXT TAB STOP
        CALL    EXTEND  ; MOVE THE LINE BY INSERTING REG C
        JMP     TABS7   ; SEE IF AT STOP NOW
;
TABS9:  DCX     D       ; TRY NEXT TAB STOP POSITION
        JMP     TABS8
;
TABSX:  POP     H
        RET
;
;-----
; TABSET - SCAN COMMAND LINE FOR TAB STOP POSITIONS
; ACCEPTS: HL = FIRST VALUE
;-----
;
;
TABSET:         ; SET TABSTOP ARRAY
        LXI     B,10
        MVI     A,NO    ; TURN OFF
        STA     TABSON  ; TABS. (DEFAULT)
TABSE2: MOV     A,L     ; GET FIRST VALUE
        LXI     H,TABPOS        ; ARRAY OF STOPS
        DAD     B
        MOV     M,A     ; AND STORE IT
        CPI     0       ; IF ZERO,
        RZ              ; WE ARE DONE
        MVI     A,YES
        STA     TABSON  ; TURN ON TABS
        XCHG            ; HL POINTS AT DELIM. OF VALUE
        PUSH    B       ; SAVE COUNT
        CALL    GETVAL  ; LOOK FOR MORE TAB POSITIONS
        POP     B
        DCX     B
        JNZ     TABSE2
        RET
;
;-----
; TEXT - PROCESS TEXT LINES
; ACCEPT:  HL = ADDR LINE
;-----
;
TEXT:   LDA     TABSON  ; SHOULD WE LOOK FOR TABS?
        CPI     NO
        JZ      TEXT2
        CALL    TABS    ; GO CONVERT TABS
TEXT2:  MOV A,M
        CPI ' '
        JZ MOVLFT
        CPI NEWLINE
        JNZ UNDERG
MOVLFT: CALL LEADBL
UNDERG:
        PUSH H
        LXI D,0
        LHLD ULVAL
        CALL COMPARE
        JNC CENTG
        LXI H,TRIPLE
        LXI D,WRDBUF
        POP B
        PUSH B
        CALL UNDERL
        LHLD ULVAL
        DCX H
        SHLD ULVAL
CENTG:
        LXI D,0
        LHLD CEVAL
        CALL COMPARE
        JNC ALLBLANK
        POP H
        CALL CTR
        CALL PUT
        LHLD CEVAL
        DCX H
        SHLD CEVAL
        RET
ALLBLANK:
        POP H
        MOV A,M
        CPI NEWLINE
        JNZ UNFILLED
        CALL PUT
        RET
UNFILLED:
        LDA FILL
        CPI NO
        JNZ FILLED
        CALL PUT
        RET
FILLED: CALL GETWRD
        RC
        CALL PUTWRD
        XCHG
        JMP FILLED
;
;-----
; UNDERL - UNDERLINE A LINE
; ACCEPTS:  BC = ADDR LINE
;           DE = ADDR TEMPORARY BUFFER
;           HL = SIZE OF LINE BUFFERS
;
;-----
;
UNDERL: PUSH B
        PUSH D
        DAD D           ;GET ADDR END OF TEMP BUF +1
        DCX H           ;GET ADDR LAST POSSIBLE CHAR POSITION
        DCX H           ;(NEWLINE & EOS TAKE LAST TWO)
        DCX H
        DCX H
        DCX H
        XCHG            ;DE "= ADDR LAST,  HL := TEMP BUF
UNLOOP: LDAX B
        CPI NEWLINE
        JZ ULINED
        CALL COMPARE
        JC ULINED
        LDAX B
        INX B
        MOV M,A
        INX H
        CPI ' '
        JZ UNLOOP
        CPI TAB
        JZ UNLOOP
        CPI BACKSPACE
        JZ UNLOOP
        MVI M,BACKSPACE
        INX H
        MVI M,'_'
        INX H
        JMP UNLOOP
ULINED: MVI M,NEWLINE
        INX H
        MVI M,EOS
        POP D
        POP H
        CALL SCOPY
        RET
;
;-----
; WIDTH - COMPUTE WIDTH OF CHARACTER STRING
; ACCEPTS:  HL = ADDR STRING
; RETURNS:  DE = WIDTH
;-----
;
WIDTH:  LXI D,0
WIDNXT: MOV A,M
        CPI EOS
        RZ
        INX H
        CPI BACKSPACE
        JZ NEGWID
        CPI NEWLINE
        JZ WIDNXT
        INX D
        JMP WIDNXT
;
NEGWID: DCX D
        JMP WIDNXT
;
OPTR:   DS      2       ; POINTER INTO DISK OUTPUT BUFFER
        DS      48
STACK   EQU     $
;
DBUFF   EQU     (($ SHR 8)+1) SHL 8     ;START OF FREE STORAGE FOR DISK BUFFER
;
        END
@