
;	TITLE	'ENTAB - REPLACE SPACES WITH TABS V1.5 '
;
; A SOFTWARE TOOL AS DESCRIBED BY KERNIGAN AND PLAUGHER
; WHICH COMPRESSES SPACES INTO APPROPRIATE TABS AS PER
; CP/M CONVENTIONS. IT IS USEFUL FOR COMPACTING A PROGRAM
; AFTER A MODEM TRANSFER WHICH EXPANDS THE TABS.
;
;
; REVISION HISTORY
;
;10/10/81.
;
; ADDED EQU FOR MODIFIED CP/M AND QUOTE AS DEFAULT DELIM. T. SHAPIN
;
; 9/5/81
;	1.	REWROTE	COMPRESSION ROUTINE TO WORK PROPERLY
;
;	2.	ADDED FEATURE TO DISABLE COMPRESSION INSIDE
;		QUOTED STRINGS,	SINCE SOME LANGUAGES AND
;		ASSEMBLERS DON'T LIKE TABS IN THEIR STRINGS.
;		FOR EXAMPLE,  AN ASSEMBLY FILE MAY HAVE	A LINE:
;
;			PASSWORD:	DB	'        '
;
;		IS THAT	THE SAME THING AS THIS LINE?
;
;			PASSWORD:	DB	09H
;
;		MOST DEFINITELY	NOT, BUT THAT IS WHAT WE WOULD
;		GET IF ENTAB COMPRESSED	THOSE BLANKS INTO 1 TAB
;		CHARACTER.   ENTAB WILL	PROMPT FOR THE STRING
;		FIELD DELIMITING CHARACTER EACH	TIME IT	IS RUN
;		(NORMALLY ' FOR ASSEMBLERS, AND " FOR BASICS).
;
;	3.	MODIFIED OUTPUT	FILE CODE TO SAVE THE ORIGINAL
;		FILE AS	A "BAK"	FILE IF	THE OUTPUT FILE	IS GOING
;		TO THE SAME DRIVE AS THE INPUT FILE.  IF THE
;		OUTPUT FILE IS GOING TO	A DIFFERENT DRIVE, THEN
;		THE ORIGINAL FILE WILL BE LEFT UNCHANGED.
;		PREVIOUSLY, THE	ORIGINAL FILE WAS ALWAYS ERASED.
;
BIAS	EQU	0H   	; 0 FOR STANDARD CP/M, 4200H FOR MODIFIED
BDOS	EQU	5+BIAS	;CPM ENTRY
FCB	EQU	5CH+BIAS ;DEFAULT FCB
;
PRINT	EQU	9
BUFFIN	EQU	10
OPEN	EQU	15
CLOSE	EQU	16
DELETE	EQU	19
READ	EQU	20
WRITE	EQU	21
CREATE	EQU	22
RENAME	EQU	23
SETDMA	EQU	26

BLANK	EQU	' '
TAB	EQU	9
CR	EQU	0DH
LF	EQU	0AH
EOF	EQU	1AH		;CONTROL Z
DOLLAR	EQU	'$'
;
REC	EQU	16		;NUMBER	OF SECTORS PER TRANSFER
;
	ORG	100H+BIAS
	LXI	H,0		;SET UP	OUR OWN	STACK
	DAD	SP		;SAVE THEIRS
	SHLD	STACK
	LXI	SP,STACK
;
	LXI	D,SGNMSG	;TELL THE FOLKS	OUR NAME
	MVI	C,PRINT
	CALL	BDOS
	JMP	GETFCB
;
SGNMSG:	DB	CR,LF,'ENTAB - VERS 1.5 - 10/9/81',CR,LF,LF,'$'
;
GETFCB:	LXI	H,FCB		;GET THE TYPED IN FILE NAME
	LXI	D,FCB1
	MVI	C,16
	CALL	MOVE		;AND MOVE IT TO	OUR OWN
	LXI	H,FCB
	LXI	D,FCB2
	MVI	C,16
	CALL	MOVE		;AND AGAIN
;
	LXI	D,FCB1
	MVI	C,OPEN
	CALL	BDOS		;OPEN IT
	INR	A
	JNZ	FILSEC		;NO ERROR
	LXI	D,FNFMSG	;'FILE NOT FOUND'
	MVI	C,PRINT
	CALL	BDOS
	JMP	EXIT
;
FNFMSG:	DB	'File Not Found',CR,LF,DOLLAR
;
FILSEC:	LXI	H,FCB2+9	;SEC NAME
	MVI	M,DOLLAR	;MAKE IT '.$$$'
	INX	H
	MVI	M,DOLLAR
	INX	H
	MVI	M,DOLLAR
	LDA	FCB+16		;IF USER ENTERS	A DESTINATION
	ORA	A		;.. DRIVE CODE ...
	JNZ	DEFALT
	LDA	FCB1
DEFALT:	STA	FCB2		;.. STASH IT IN	OUTPUT FILE FCB
;
DEL:	MVI	A,0		;MAKE NEXT RECORD 0
	STA	FCB1+32
	STA	FCB2+32
	LXI	D,FCB2		;DELETE	ANY EXISTING TEMPORARY FILES
	MVI	C,DELETE
	CALL	BDOS
;
	LXI	D,FCB2
	MVI	C,CREATE
	CALL	BDOS		;NOW CREATE TEMPORARY OUTPUT FILE
	INR	A
	JNZ	GTDEL		;FILE CREATED
	LXI	D,DSKMSG	;'NO DIRECTORY SPACE'
	MVI	C,PRINT
	CALL	BDOS
	JMP	EXIT		;CAN'T KEEP GOING
;
PROMPT:	DB	'Enter the String Delimiter (CR='') ? $'
BEGMSG:	DB	CR,LF,'Beginning Blanks Compression',CR,LF,'$'
;
GTDEL:	LXI	D,PROMPT	;ASK FOR DELIMITER CHARACTER
	MVI	C,PRINT
	CALL	BDOS
;
;WE INPUT THE DELIMITER	CHARACTER BY USING THE BUFFER INPUT
;ROUTINE TO ALLOW THE USER TO CORRECT AN INPUT ERROR, OR TO
;ABORT TO CP/M.	 A SINGLE CONIN	CALL, ALTHOUGH REQUIRING LESS
;CODE, WOULD NOT ALLOW ANY MARGIN FOR ERROR IN SPECIFYING THE
;DELIMITER CHARACTER.
;
	LXI	D,CIBUF		;SETUP CONSOLE INPUT BUFFER
	MVI	A,2
	STAX	D		;ALLOW 2 CHARACTERS MAXIMUM
	MVI	C,BUFFIN	;GET THE DELIMITER CHARACTER
	CALL	BDOS
	LDA	CIBUF+1		;HOW MANY CHARACTERS ENTERED?
	ORA	A
	MVI	A,''''		;DEFAULT TO QUOTE
	JZ	SAVDEL		;USE DELIMITER OF 0 IF NO INPUT
	LDA	CIBUF+2		;ELSE, EXTRACT 1ST CHARACTER
SAVDEL:	STA	DELIM+1		;SAVE IT IN-LINE FOR COMPARISON

	LXI	D,BEGMSG	;TELL USER WE'VE BEGUN
	MVI	C,PRINT
	CALL	BDOS
;
;BEGIN SCANNING	INPUT BUFFER AND PACK THE SPACES INTO TABS
;
ENTAB:	XRA	A
	STA	COLUMN		;INITIALIZE TAB	POSITION
	STA	BLANKS		;CLEAR BLANK ACCUMULATOR
	STA	SFLG		;CLEAR STRING FLAG
NEXCHR:	CALL	GETC		;GET NEXT CHARACTER FROM FILE
	STA	CHAR		;SAVE INPUT CHARACTER
	CPI	TAB
	JNZ	NOTAB
	CALL	DUMBLK		;ON TAB, DUMP PENDING BLANKS
TABIT:	MVI	A,TAB		;PASS THE TAB ALONG
	CALL	PUTC
	XRA	A
	STA	COLUMN		;CLEAR TAB POSITION
	JMP	NEXCHR		;GET THE NEXT CHARACTER
;
NOTAB:	CPI	LF		;END OF	LINE MARKERS?
	JZ	EOL
	CPI	CR
	JNZ	NOEOL
EOL:	CALL	DUMBLK		;DUMP TRAILING BLANKS IF SO
	LDA	CHAR		;THEN DUMP THE CR OR LF
	CALL	PUTC
	JMP	ENTAB		;AND START NEXT	LINE
;
NOEOL:	CPI	BLANK		;SPACE CHARACTER?
	JNZ	NOBLK
	LXI	H,BLANKS	;INCREMENT BLANK COUNTER
	INR	M
	LDA	SFLG		;ARE WE	IN A QUOTED STRING?
	ORA	A
	JNZ	STRING		;BLANK NOT SIGNIFICANT IF SO
	CALL	BUMCOL		;BUMP TAB POSITION
	JNZ	NEXCHR		;CONTINUE SCANNING IF NOT AT TAB STOP
	STA	BLANKS		;OTHERWISE, CLEAR BLANKS COUNTER
	JMP	TABIT		;PUT TAB INTO OUTPUT FILE
;
STRING:	CALL	BUMCOL		;INSIDE	QUOTED STRING, BUMP TAB	POSITION
	JMP	NEXCHR		;CONTINUE SCANNING TIL END OF STRING
;
NOBLK:	CPI	EOF		;CP/M FILE EOF?
	JZ	EOFILE
	CPI	0FFH		;GETBUF	EOF?
	JZ	EOFILE
	CALL	DUMBLK		;CHARACTER NONE	OF ABOVE - FLUSH BLANKS
	CALL	BUMCOL		;BUMP TAB POSITION
	LDA	CHAR
	PUSH	PSW
	CALL	PUTC		;SEND LITERAL CHARACTER
	POP	PSW
DELIM:	CPI	0		;IS CHARACTER STRING DELIMITER?
	JNZ	NEXCHR
	LDA	SFLG		;TOGGLE	STRING FLAG IF SO
	CMA
	STA	SFLG
	JMP	NEXCHR		;SCAN FOR NEXT CHARACTER
;
DUMBLK:	LDA	BLANKS		;FLUSH THE ACCUMULATED BLANKS
	ORA	A
	RZ
	DCR	A
	STA	BLANKS
	MVI	A,' '
	CALL	PUTC
	JMP	DUMBLK
;
BUMCOL:	LDA	COLUMN		;INCREMENT TAB POSITION
	INR	A
	ANI	7
	STA	COLUMN
	RET
;
EOFILE:	CALL	PUTC		;OUTPUT	EOF CHARACTER
	CALL	PUTBUF		;WRITE UNFINISHED BUFFER
	LXI	D,FCB2
	MVI	C,CLOSE
	CALL	BDOS		;CLOSE THE NEW FILE
;
	LDA	FCB2		;GET DESTINATION DR CODE TO INPUT FCB
	STA	FCB1
	LXI	H,FCB1		;SETUP RENAME FCB FOR ORIGINAL FILENAME
	LXI	D,FCB1+16
	PUSH	D		;SAVE POINTER TO 2ND RENAME FIELD
	PUSH	D
	MVI	C,9
	CALL	MOVE		;MOVE FILENAME TO 2ND RENAME FIELD
	XCHG
	MVI	M,'B'		;MAKE 2ND RENAME FILETYPE="BAK"
	INX	H
	MVI	M,'A'
	INX	H
	MVI	M,'K'
	POP	D		;RECALL	POINTER	TO 2ND RENAME FIELD
	MVI	C,DELETE	;ERASE ANY EXISTING BAK	FILE ON	OUTPUT DRIVE
	CALL	BDOS
	POP	H		;RECALL	RENAME FIELD POINTER AGAIN
	MVI	M,0		;CLEAR DR IN 2ND RENAME	FIELD
;
	LXI	D,FCB1		;RENAME	ANY FILE ON OUTPUT DRIVE WITH INPUT
	MVI	C,RENAME	;..FILENAME TO TYPE "BAK"
	CALL	BDOS
;
KEEPIT:	LXI	H,FCB1+1	;MOVE THE NEW FILE NAME
	LXI	D,FCB2+16	;TO FCB2
	XRA	A
	STAX	D
	INX	D
	MVI	C,11
	CALL	MOVE
;
	LXI	D,FCB2
	MVI	C,RENAME
	CALL	BDOS		;RENAME	.$$$ TO	ORIGINAL
;
	LXI	D,DNMSG		;TELL THEM WE'RE DONE
	MVI	C,PRINT
	CALL	BDOS
	JMP	EXIT
;
DNMSG:	DB	'Blanks Compression Completed',CR,LF,DOLLAR
;
EXIT:	LXI	D,80H+BIAS	;RESET DEFAULT DMA
	MVI	C,SETDMA
	CALL	BDOS
;
	LHLD	STACK
	SPHL
	RET			;BACK TO CP/M
;
;
; MOVE -MOVE (HL -> (DE) FOR C BYTES
;
MOVE:	MOV	A,M
	STAX	D
	INX	H
	INX	D
	DCR	C
	JNZ	MOVE
	RET
;
;
GETC:	LHLD	CICNT		;ANY CHAR IN BUFFER?
	MOV	A,H
	ORA	L
	CZ	GETBUF		;GET SOME IF NOT
	LHLD	CIPNT		;POINTER TO CHAR
	MOV	A,M		;GET THE CHARACTER
	INX	H		;INCREMENT THE POINTER
	SHLD	CIPNT
	LHLD	CICNT		;REDUCE	THE COUNT
	DCX	H
	SHLD	CICNT
	RET

;
GETBUF:	MVI	A,REC		;RECORDS PER BUFFER
	LXI	H,0
	SHLD	CICNT		;INIT FO READ
	LXI	H,CIBUF
	SHLD	CIPNT		;INIT THE POINTER
GBUF:	SHLD	DMAADD		;IPDATE	DMA ADDRESS
	XCHG			;FOR BDOS CALL
	STA	SECCNT		;UPDATE	SECTORS	TO GO
	ORA	A
	RZ			;RETURN	IF NONE
	MVI	C,SETDMA
	CALL	BDOS		;SET LOAD ADDRESS
	LXI	D,FCB1
	MVI	C,READ
	CALL	BDOS		;GET A SECTOR
	PUSH	PSW		;SAVE DISK STATUS
	LHLD	CICNT
	LXI	D,128		;# OF BYTES READ
	DAD	D
	SHLD	CICNT		;UPDATE	IT
	LHLD	DMAADD		;UPDATE	POINTER
	POP	PSW		;GET DISK STATUS
	ORA	A
	JZ	ROK		;READ WAS OK
	MVI	M,0FFH		;OUR OWN END OF	FILE
	RET

ROK:	DAD	D		;THIS TO
	LDA	SECCNT
	DCR	A		;ONE LESS LEFT
	JMP	GBUF
;
PUTC:	LHLD	COPNT		;POINTER
	MOV	M,A		;PUT CHAR IN BUFFER
	INX	H
	SHLD	COPNT		;INCREMENT POINTER
	LHLD	COCNT
	INX	H		;BUMP IT
	SHLD	COCNT
	MOV	A,H		;SEE IF	END OF BUFFER
	CPI	REC*128/256
	CZ	PUTBUF
	RET
;
PUTBUF:	LHLD	COCNT		;# OF CHAR IN BUFFER
	SHLD	TEMP
	LXI	H,0
	SHLD	COCNT		;REINIT	FOR NEXT TIME
	LXI	H,COBUF
	SHLD	COPNT		;REINIT	FOR NEXT TIME
PBUF:	SHLD	DMAADD		;UPDATE	WRITE ADDRESS
	XCHG			;TO DE FOR CPM CALL
	LHLD	TEMP		;ANY LEFT TO WRITE
	MOV	A,L
	ORA	H
	RZ			;IF NOT
	MVI	C,SETDMA
	CALL	BDOS
	LXI	D,FCB2		;WRITE FILE BLOCK
	MVI	C,WRITE
	CALL	BDOS
	ORA	A		;CHECK FOR ERROR
	JNZ	WRERR		;.AND TAKE CARE	OF IT
	LHLD	TEMP		;BYTE COUNT
	LXI	D,-128		;PER SECTOR
	DAD	D		;THIS MANY LEFT
	SHLD	TEMP
	MOV	A,H		;GET SIGN BIT
	ORA	A		;TEST IT
	RM			;ALL DATA WRITTEN (NOT A FULL BUFFER)
	LHLD	DMAADD		;UPDATE	BUFFER
	LXI	D,128
	DAD	D
	JMP	PBUF
;
WRERR:	LXI	D,DSKMSG	;'WRITE ERROR'
	MVI	C,PRINT
	CALL	BDOS
	JMP	EXIT		;THIS WILL FIX STACK
;
DSKMSG:	DB	'Disk or Directory Full',CR,LF,DOLLAR
;
	DS	48		;SOME STACK SPACE
STACK:	DW	0
COLUMN:	DB	0
BLANKS:	DB	0
SFLG:	DB	0
CHAR:	DB	0		;INPUT CHAR
SECCNT:	DB	0		;SECTORS TO READ
CICNT:	DW	0
COCNT:	DW	0
TEMP:	DW	0
DMAADD:	DW	0
FCB1:	DS	33
FCB2:	DS	33
CIPNT:	DW	CIBUF
COPNT:	DW	COBUF
CIBUF:	DS	128*REC
COBUF:	DS	128*REC
	END
@