; ; 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. ; ;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 @