;  
;========================================================
; FILTER.ASM					V 3.4	: 
; Copyright by Claude Ostyn   9/26/83 			:
; To allow for filtering of line feeds and all kinds	:
; of WordStar print commands and other non-printable	:
; characters;  also to allow reformatting of existing	:
; ascii files by WordStar.				:
; License for non-commercial use is granted to all	:
; CP/M microcomputer users.				:	
;========================================================

; EQUATES
 
true		equ	1
false		equ	not true
echo   		equ  	true	;if true, echo the copy, else don't
osborne 	equ  	true	; if false, no special video controls 
linelen		equ	51	;length of screen line (change to 51 for
				; Osborne 1, irrelevant if echo false)	
clearscreen	equ	1ah	;clear screen and home cursor chr 
interr		equ	true	;if true, allow interruption of copy
				;		in progress by ESC key

boot		equ	0000h     ;standard cp/m boot vector
bdos		equ	boot+5    ;disk operating system vector
tpa		equ	boot+100h ;beginning of transient program area

;bdos functions 
conin		equ	1	;console input
conout		equ	2	;console output
condir		equ	6	;direct console input
printf		equ	9	;print buffer function
readbf		equ	10	;read console buffer function	
initdsk		equ	13	;reset disk
openf 		equ	15	;open file function
closef		equ	16	;close file function
findf		equ	17	;search for file function
delete		equ	19	;delete file function
readf 		equ	20	;sequential file read
writef		equ	21	;sequential file write
makef 		equ	22	;create file function
dma   		equ	26	;set dma address

;ascii and special character codes
eof		equ	1ah		;cp/m end of file marker
cr		equ	0dh		;carriage return
lf		equ	0ah		;line feed
bel		equ	07h
backspace 	equ	08h
tab		equ	09h
escape		equ	1bh		;escape	
space		equ	20h
period		equ	2eh
colon		equ	':'
mhyphen		equ	1eh		;soft hyphen not at line break	
shyphen		equ	1fh		;soft hyphen at line break		
hhyphen		equ	2dh		;hard hyphen
nbkspace 	equ	0fh		;WordStar non-break space
rubout		equ	7fh		;rubout chr
			 
; flag values
nope		equ	0
error		equ	0ffh
quit		equ	1bh

	org	tpa       ;standard load address
start:
       	lxi	sp,stack  ;make up a local stack

signon: 
	call	dim
	call	showmsg
 	db   	clearscreen,lf,lf,9
	db	'     FILTER  Version 3.4',cr,lf,9
	db	'Copyright 1983 by Claude Ostyn',cr,lf,9
	db	'        P.O. Box 2035',cr,lf,9
	db	'     Sitka, Alaska 99835',cr,lf,lf,lf,9
	db	'     (Enter ESC to exit)',cr,lf,lf,0
	call	bright

;get input file name from user
inname:
	call	infilnam	;enter and check input file name
	cpi	quit
	jz	exittwo
;open input file
	call	opninfil	;open input file
	cpi	error
	jnz	outname		;if found, get busy with output file
	call	showmsg
	db	cr,lf,'Can',39,'t find that file.   Try again.',cr,lf,0
	jmp	inname		;get another input file name

;get output file name
outname:	
	call	outfilnm	;enter and check output file name
	cpi	quit
	jz	exit
;check if output file already exists 
	call	checkout	;check for conflicts, until file name ok
 	cpi	quit		
	jz	exit		;exit if quit code returned
	call	opnoutfil	;open the output file
menu:
	call	bright
	call	showmsg 
	db	clearscreen,cr,lf 	
	db	'Choose one letter:',cr,lf,lf,lf,0
	lxi	d,menuc
	call	printit
	lxi	d,menuw
	call	printit
	lxi	d,menup
	call	printit
	lxi	d,menus
	call	printit
	lxi	d,menur
	call	printit
	lxi	d,menul
	call	printit
	lxi	d,menuq
	call	printit
	call	coninone	;get chr from console
	sta	choice		;put choice in "choice" buffer
	call	dim
	lxi	d,showopt
	call	showmsg
	db	clearscreen,cr,lf,lf,0	;move down a couple of lines
	lda	choice
	cpi	'L'
	jz	optionlf
	cpi	'C'
	jz	optionform
	cpi	'W'
	jz	optionprint
	cpi	'P'
	jz	optionprint
	cpi	'S'
	jz	optionselect
	cpi	'R'
	jz	optionrubout
	cpi	'Q'
	jz	delexit		;delete file just created and exit
	cpi	quit
	jz	delexit
	jmp	menu		;no valid input, try again	

;option L = remove all line feeds and zero eight bits
optionlf:
	call	printit
	lxi	d,menul
	call	printit
	call	bright
	call	deldot		;inquire whether to delete dot commands
	mvi	a,nope
	sta	wsformat	;delete WS codes
	call	showmsg
	db	cr,lf,lf,0
	jmp	readinputfil

;option C = reform:  remove all carriage returns (except after period) and
;all extra spaces (except after period or colon).  Also remove
;soft hyphens created by WordStar  
optionform:
	call	printit
	lxi	d,menuc
	call	printit
	call	bright
	call	showmsg
	db	lf,lf,'Does the file contain lower case'
	db	' characters (Y/N)?  ',0
	call	coninone	
	cpi	quit
	jz	menu
	cpi	'Y'
	jnz	forming
	mvi	a,true
	sta	lowcase
forming:
	call	showmsg
	db	clearscreen
	db	'Removing all extra spaces and carriage returns...'
	db	cr,lf,lf,0
	mvi	a,nope
	sta     wsformat	;make sure ws codes will be removed
	sta	zapdot		;but not dot commands
	jmp	readinputfil

;option p = remove print control chrs and zero high bit
optionprint:
	call	printit
	lda	choice
	cpi	'W'
	jnz	notwopt
	lxi	d,menuw
	call	printit
	call	bright
	call	deldot
	jmp	wopt
  notwopt:
	lxi	d,menup
	call	printit
	call	bright
	call	wsfile		;ask if copy must be WordStar format
	cpi	quit
	jz	menu
  wopt:
	call	showmsg
	db	cr,lf,lf,'Removing all inbedded',0
	jmp	endshowp

;option s = remove selected print control chrs 
optionselect:
;enter the chrs to be removed
	call	printit
	lxi	d,menus
	call	printit
	call	bright
	call	showmsg
	db	'Enter print control characters you want to',lf,cr
	db	'remove from the file (use CTRL key), and',lf,cr
	db	'press <ESC> when done:',cr,lf,lf,0
	call	selectch
	cpi	0
	jz	menu	;nothing selected, try again
	call	wsfile		;ask if copy must be WordStar format
	cpi	quit
	jz	menu
	call	showmsg
	db	cr,lf,lf,'Removing all selected',0
  endshowp:
	call	showmsg
	db	' print control codes',0
	lda	wsformat
	cpi	true
	jz	optionsout
	call	showmsg
	db	cr,lf,'and WordStar formatting codes',0
	lda	zapdot
	cpi	true
	jnz	optionsout
	call	showmsg
	db	',',cr,lf,'including dot commands',0
   optionsout
	call	showmsg
	db	'...',cr,lf,lf,0
	jmp	readinputfil

optionrubout:
	call	bright
	call	showmsg
	db	'Removing rubout characters only...',cr,lf,lf,0 

;      file is open, start the process
readinputfil: 
	lxi	h,oldchar
	mvi	m,error		;initialize "old" chr location
	lxi	h,inend		;point past end of inbuf
	shld	inpos		;to force load, and save it
	lxi	h,outbuf	;point to beginning of outbuf
	shld	outpos		;and save it

loop:
	lxi	d,inend		;pointer to end of buffer
	lhld	inpos		;find out where we are
	mov	a,h		;test high byte for end of buffer
	cmp	d		;if zero, they're equal
	jnz	gcbuf		;not zero, get chr from buffer
	mov	a,l		;now compare low byte
	cmp	e
	cz	load		;if zero, we've hit the end
				;so load another buffer
 gcbuf:
 	mov	a,m		;get next chr from buffer
	inx	h		;bump pointer
	shld	inpos		;store it
	sta	newchar		;store chr
	cpi	eof		;end of file?
	jz	okthen		;if end of file, skip screening

gotit:
	sta	newchar		;store while processing preceding chr

; screening begins here
	cpi	error		;reject delete chr
	jz	loop
	cpi	rubout		;...and reject rubout chr
	jz	loop

;cancel the whole process if esc key is pressed
if interr
	mvi	e,0ffh
	mvi	c,condir	;read console
	call	bdos
	cpi	escape
	jz	copystop
endif

checkold:
	lda	oldchar
	cpi	error		;no older chr, so just treat as a reject
	jz	copynew
	call	sieve
	lda	errfl
	cpi	0		;check for disk error
	jnz	wrerr		;if error, go to message
  okthen:
	lda	keep
	cpi	true
	jnz	copynew		;if rejected, do not copy to buffer
	lda	oldchar		;get chr back

;echo chr to console
if echo
	call	echochar
endif

;end of screening, go on with the copy
endscreen:
	call	copyout
	lda	errfl		;check error flag
	cpi	eof
	jz	wrap		;if eof, wrap this up
	cpi	0
	jnz	wrerr		;if anything else but 0, write error

copynew: 
	lda	newchar
	sta	oldchar
	cpi	eof		;end of file?
	jz	endscreen	;then finish up
	jmp	loop		;otherwise read a new one into b
 
;copy one character into output buffer
copyout:
	xra	a
	sta	errfl		;clear error flag
	lda	oldchar
	lxi	d,outend	;end of output buffer
	lhld	outpos		;point to next char in outbuf
	mov	m,a		;store the chr
	inx	h		;increment pointer
	shld	outpos		;store the buffer pointer
	cpi	eof		;end of file chr?
	jnz	copymore
	sta	errfl		;yes, set error flag
	jmp	copydone	;and exit
   copymore:
	mov	a,h		;check top byte for end of buffer
	cmp	d
	jnz	copydone	;not equal, skip ahead
	mov	a,l		;now check the low byte
	cmp	e
	cz	store		;call subroutine to write
				;buffer to file
				;else do again
   copydone:
	ret			;(from copyout)

; read a buffer full into input buffer
load:	
	xra	a
	sta	errfl		;zero error flag
	lxi	d,inbuf		;reset the dma address for reads
	mvi	c,dma
	call	bdos
	lxi	d,infcb		;load d with input fcb
	mvi	c,readf		;and c with bdos read
	call	bdos
	sta	errfl
	lxi	h,inbuf		;reset the input pointer
	shld	inpos		;store the pointer
	ret			;(from load)

;      write the output buffer to the file
store:	lxi	d,outbuf
	mvi	c,dma		;set the dma address for write
	call	bdos
	lxi	d,outfcb
	mvi	c,writef	;write to the file
	call	bdos
	sta	errfl		;update error flag
	lxi	h,outbuf	;reset the output pointer
	shld	outpos		;store it
	ret			;(from store)

;close this mess up, we're done
wrap:   call	store		;flush the output buffer
	cpi	0
	jnz	wrerr
	lxi	d,outfcb	;close the output file
	mvi	c,closef
	call	bdos
	call	showmsg
	db	clearscreen,cr,lf,'File copied okay.',cr,lf,0
	lda	ambighyph	;check if flag was used for 
	cpi	true		;		ambiguous hyphen
	jnz	exittwo
	call	showmsg
	db	cr,lf,lf
	db	'One or more ambiguous hyphen was found and',cr,lf
	db	'replaced by a "#-" pair of characters in the',cr,lf
	db	'output file.  You will need to use your editor',cr,lf
	db	'to do the necessary correction(s).',cr,lf,lf,0

	jmp	exittwo		;exit after message
 
copystop:
	call	showmsg
	db	clearscreen,bel,'Copy stopped...',cr,lf,lf
	db	'Do you want to save what has been copied so far? ',0
    stoptwo:
	call	coninone
	cpi	'Y'
	jz	wrap		;if yes, close file
	cpi	'N'
	jz	delexit
	cpi	quit
	jnz	stoptwo

delexit:
;if exit from menu; delete created file first
	lxi	d,outfcb	
	mvi	c,delete
	call	bdos
exit:
	call	bright
	call	showmsg
	db	clearscreen
	db	cr,lf,lf,'Terminated at operator',39
	db	's request - file not copied!',cr,lf,0 
	
;exit, unless...
exittwo:
	call	showmsg
	db	cr,lf,lf,9,'Copy another file?  ',0
  exitthree:
	call	coninone
	cpi	'Y'
	jz	start
	cpi	'N'
	jz	exitzap
	cpi	quit
	jz	exitzap
	sui	32
	jm	exitthree
	call	showmsg
	db	7,8,space,8,0
	jmp	exitthree

exitzap:
	call	showmsg
	db	cr,lf,lf,9,'...Program terminated.',cr,lf,lf,0
	jmp	boot
 
wrerr:
	lxi	d,outfcb
	mvi	c,closef
	call	bdos
	call	showmsg
	db	cr,lf,lf,7,'Write error on output file... ',7,0
	jmp	exitzap	;and quit
 
;      *** sieve subroutines ***
;  chr by chr screening is done here

sieve:
	lda	wsformat	;check wordstar format flag
	cpi	true
	jz	nolopoff
	lda	oldchar
	ani	7fh		;zero high bit 
	sta	oldchar		;if not WordStar format
nolopoff:
	lxi	h,keep
	mvi	m,true		;assume we keep chr
	lda	choice
	cpi	'R'
	rz
	lda	zapdot		;do we remove dot commands?
	cpi	true
	cz	dotcheck	;process dot commands if necessary
	lda	choice
	cpi	'L'
	jnz	notl
	call	filterlf	;filter out line feeds, pad empty lines
	lda	keep
	cpi	true
	jnz	discard
	call	pfilter		;if "L", filter out control characters
	jmp	done
  notl:
	lda	keep
	cpi	true
	jnz	discard
	lda	choice
	cpi	'C'
	jnz	notc
	call	reformat
	jmp	done
   notc:
	cpi	'P'
	jnz	notp
	call	pfilter
	jmp	done
   notp:
	cpi	'W'
	jnz	notw
	call	pfilter
	jmp	done
   notw:
	call	sfilter
  done:
	lda	errfl
	cpi	0		;if disk error detected, return
	rnz			;(from sieve)
	lda	keep
	cpi	true
	jnz	discard
	lda	wsformat	;if keeping WS format, skip next step 
	cpi	true
	jz	donagain
	call	wscheck		;remove WS formatting codes
     ;check if we should delete dot command
	lxi	h,keep
	mov	a,m	
	cpi	true
	jnz	discard		;no point in checking if not a keeper
	lda	dotcomfl	;check the dot command flag
	cpi	true
	jnz	donagain		;if it's on, discard chr
  discard:
	mvi	m,nope
  donagain:
	lda	oldchar
	sta	lastread	;update last read (not necessarily copied)
	lda	keep
	cpi	true
	rnz			;(from sieve)
 endsieve:
;at this point, update last and penultimate copied chrs
	call	update
	ret			;(from sieve)

 update:
	lda	lastchar	;get last previous chr
	sta	penultimate	;store in buffer for the one before

	lda	oldchar		;get the chr back
	sta	lastchar	;put chr in last previous
				; chr buffer
	ret
		

dotcheck:
;dot command lines are flagged here, except with 'L' option
	lda	oldchar
	cpi	period		
	jnz	endofdot	;not a period, maybe a cr?
	lda	lastread	;it's a period; does it
	cpi	lf		;follow a lf?
	jz	dotcom		;yes, must be a dot command
	cpi	0		;if not, is it the first chr in file?
	rnz			;no: keep the period and return
    dotcom:			;yes, definitely a dot command	
	mvi	a,true		
	sta	dotcomfl	;set the dot command flag!
    junk:   
	mvi	a,nope		;and start junking chrs...
	sta	keep
	ret			;(from dotcheck)
    endofdot:
	cpi	cr		;is it a cr?
	jnz	lfmaybe		;no, maybe lf?
	lda	dotcomfl	;is the dot command flag on? 
	cpi	true
	rnz			;(from dotcheck)
    dotend:
	mvi	a,nope		;dot flag was on, reset it
	sta	dotcomfl	; to off...
	sta	keep		;and discard the cr
	mvi	a,0		;force a 0 in lastread,
	sta	oldchar		; in case another dot command follows
	ret			;(from dotcheck)
    lfmaybe:
	cpi	lf
	rnz			;no, ignore
	lda	choice
	cpi	'C'
	rz			;if reformatting, skip this step
	lda	lastchar
	cpi	cr
	jnz	junk		;discard lf if does not follow a cr
	ret

;take out line feeds, and pad blank lines with a space 
filterlf:
	lda	oldchar
	cpi	lf
	rnz
	lxi	h,keep
	lda	newchar		;check next chr
	cpi	cr		;if a cr, means a blank line
	jz	pad
	mvi	m,nope		;trash lf
	ret
pad:
	mvi	a,space		;yes, make this lf into a space
	sta	oldchar
	mvi	m,true
	ret

;reformat by removing extra spaces and cr
reformat:
	call	pfilter		;kill if print control code
	lxi	h,keep
	mov	a,m
	cpi	true
	rnz			;return if it was control code

	lda	crflag
	cpi	true		;if a cr was just discarded,
	jz	crflset		;go check if we need to restore it
	lda	oldchar
	cpi	cr
	jz	checkcr
	cpi	lf
	jz	checklf
	cpi	space
	jz	checkspace
	cpi	tab
	jz	checkspace
	mvi	a,nope 		;not cr, lf or space
	sta	joinfl		;turn off rejoin flag (if ON)
	lda	oldchar
	cpi	hhyphen
	jz	hhyphnck
	cpi	mhyphen
	jz	rejoinw
	cpi	shyphen
	jz	rejoinw
	ret			;no further processing

  ;a cr was just discarded...
  crflset:
	lda	oldchar
	cpi	cr
	jz	trashit		;throw out cr, space, tab
	cpi	lf
	jz	checklf		;	...and process lf
	cpi	space
	jz	trashit
	cpi	tab
	jz	trashit
	cpi	'('
	jz	clearcrfl
	call	islowcs		;is it lower case
	cpi	true
	cz	setlowc
	jz	clearcrfl
	lda	lastchar
	cpi	lf		;if last chr copied was not a lf,
	cnz	addacr		;		 restore cr and lf
   clearcrfl:
	mvi	a,nope
	sta	crflag		;reset crflag
	ret			;(from reformat, and keep the chr)

	;set lowercase flag, just in case it was not set already
      setlowc:
	sta	lowcase
 	ret

    ;add a cr and lf before current chr 
  addacr:
	lda	oldchar
	sta	spare	
	mvi	a,cr
	sta	oldchar	
	call	update
	call	copyout
	lda	errfl		;check error flag
	cpi	0
	rnz			;if anything but 0, write error
	mvi	a,lf
	sta	oldchar
	call	update
	call	copyout
	lda	errfl		;check error flag
	cpi	0
	rnz			;if anything but 0, write error
	lda	spare
	sta	oldchar
	ret

 hhyphnck:
;checking if we have a hard hyphen in middle of line
; (in that case, we want to keep it) 
	lda	lastchar
	cpi	hhyphen		;if last chr was a hyphen, it's
	rz			;a dash --keep it
	lda	newchar		;check the next chr
	cpi	hhyphen		;if hyphen, it's a dash - keep it
	rz
	cpi	cr
	rnz
;hard hyphen at end of line (ambiguous) 
  ambigu:
	mvi	a,'#'		;replace hyphen with
	sta	oldchar		;flag character
	call	update
	call	copyout		;and copy it
	lda	errfl		;check error flag
	cpi	0
	rnz			;if anything but 0, write error
	mvi	a,hhyphen
	sta	oldchar		;now copy hyphen
	mvi	a,true
	sta	ambighyph	;set warning flag
	sta	joinfl		;set join flag to discard cr,lf and spaces
	ret

;soft hyphen at end of line; set flag to discard all cr, lf and spaces
rejoinw:
	mvi	a,true
	sta	joinfl
	jmp	trashit

;check for cr and keep it if paragraph break
checkcr:
	lda	joinfl		;if rejoining, discard
	cpi	true
	jz	trashit
	lda	lastchar	;check last character
	cpi	lf		;was it a lf?
	jz	killcr
	mov	b,a
	lxi	h,crtable	
	call	isitin
	cpi	true		;found?
	rz
	mov	a,b		;get chr back
	cpi	space		;if last one was a space,
	jnz	morchk
	lda	penultimate	;	check penultimate chr
	mov	b,a
	call	isitin
	cpi	true
	rz
	mov	a,b		;check penultimate again
	cpi	lf
	jz	killcr
    morchk:
	lda	lowcase		;if lower case in text,
	cpi	true		;do next check,
	jnz	killcr		;otherwise kill
	lda 	lastchar
	call	isupcse		;check if upper case
	cpi	true
	rz	

  killcr:
	lxi	h,keep
	mvi	m,nope
	mvi	a,true
	sta	crflag		;set 'discarded cr flag'
	ret			;from crcheck

;check for cases where this is a space
checkspace:
	lda	joinfl
	cpi	true
	jz	trashit
	lda	lastchar	;check previous char
	cpi	0		;first chr in file?
	jz	trashit
	cpi	space		;was it a space?
	jz	twaspace	;yes, further checking
	cpi	lf		;was it a lf?
	jz	trashit 
	lda	lastread	;was last read chr a soft hyphen?
	cpi	shyphen		;was it a soft hyphen?
	jz	trashit		;yes, discard this space
	cpi	mhyphen		;or the other kind of soft hyphen?
	jz	trashit
	ret			;no, copy the space
   ;the last retained and copied character was a space
   twaspace:
	lda	penultimate	;check the character before last
	cpi	period		;was it a period?
	rz			;yes, copy this space
	cpi	colon		;was it a colon then?
	rz			;yes, copy this space
	cpi	'?'
	rz
	cpi	'!'
	rz
	jmp	trashit
 
;check for lf, and replace with a space, except if the preceding
;carriage return has been retained, or if there was a soft hyphen
;at the end of the line: in that case, ignore the hyphen and the lf
checklf:
	lda	joinfl
	cpi	true
	jz	trashit
	lda	lastchar
	cpi	cr		;was the last copied chr a cr?
	rz			;if yes, just copy this lf as is
	cpi	lf		;was it a lf?
	jz	trashit
	lda	crflag
	cpi	true
	jnz	trashit		;if crflag off
	lda	lastchar
	cpi	space		;or if last chr was a space
	jz	trashit		;	discard the lf
	mvi	a,space		;otherwise replace it with a space
	sta	oldchar
	ret

;reset keep flag to discard
trashit:
	mvi	m,nope
	ret
	
;filter out print control characters
pfilter:
	lxi	h,ptable
	jmp	zapcode
sfilter:
	lxi	h,stable
 zapcode:
	lda	oldchar
	mov	b,a
	call	isitin
	cpi	true		;if character was found, 
	rnz
	lxi	h,keep
	jmp	trashit
	
;additional checking if wordstar format file output 
wscheck:
	lxi	h,keep
	lda	oldchar
	cpi	shyphen		;soft hyphen at end of line
	jz	mkhyphen
	cpi	mhyphen		;soft hyphen imbedded in line
	jz	trashit
	cpi	lf		
	jz	afterdot	
	cpi	nbkspace	;non-break space
	jz	nbksp
	ret

  nbksp:
	mvi	a,space		;make it into a regular space
	sta	oldchar		;and store it back
	ret			;(from wscheck)
  
;make soft hyphens into hard hyphens
  mkhyphen:
	mvi	a,hhyphen
	sta	oldchar
	ret

;suppress lf after erased dot command
  afterdot:
	lda	lastchar
	cpi	cr
	jnz	trashit
	ret

;      ** utility subroutine area **
 
;enter input file name
infilnam:
	call	initstor	;initialize file name storage
	call	showmsg
	db	cr,lf,'Name of file to copy from:  ',0
	lxi	d,infcb		;point to fcb for input file
	call	cinbuff		;get the name and put in fcb
	cpi	quit
	rz
	lda	infcb		;check first byte in fcb
	cpi	0		;if it is zero, no drive was specified
	rnz			;
	call	getdrive	;no drive specified, get one
	cpi	quit
	rz
	sta	infcb		;and put designator in fcb
	ret

;user enters output file name here
outfilnm:
	call	inittwo		;initialize file name storage
	call	showmsg
	db	cr,lf,lf,'  Name of file to copy to:  ',0
	lxi	d,outfcb	;point to fcb for output file
	call	cinbuff		;get the line and put in fcb
	cpi	quit
	rz
	lda	outfcb		;check first byte in fcb
	cpi	0		;if it is zero, no drive was specified
	cz	getdrive	;no drive specified, get one
	cpi	quit
	rz
	sta	outfcb		;and put designator in fcb
	call	samename	;check if not trying to overwrite itself
	cpi	error
	rnz
	call	showmsg
	db	cr,lf,lf,'Input and output files'
	db	' cannot be the same!',cr,lf,0
	jmp	outfilnm	;get another output file name

;check if output file already exists 
checkout:
	call	findoutfil
	cpi	error		;if error, file does not exist yet
	jz	creatfil	;good, go create it
				;if no error, file already exists
  ;overwrite existing file?
  exists:
	call	showmsg
	db	cr,lf,lf,'Output file already exists...',cr,lf
	db	'OK to overwrite (Y/N)?   ',0
    overw:
	call	coninone	;query for overwrite, and kill if okay
	cpi	'Y'		;yes?
	jz	delandcreat	;yes, overwrite
	cpi	'N'
	jnz	overw
	call	showmsg		;no...
	db	cr,lf,'Use another name for output file (Y/N)?  ',0
	call	coninone
	cpi	'Y'
	jz	othernam
	jmp	otherdisk	;try another disk?
  othernam:
	call	outfilnm	;get new name
	cpi	quit
	rz			;(from checkout)
	jmp	checkout	;and try again
  ;create new file
  delandcreat:
	call	showmsg
	db	cr,lf,lf,'Deleting old output file...',cr,lf,lf,0
	lxi	d,outfcb	;destination fcb
	mvi	c,delete	;delete before create
	call	bdos
  creatfil:
	lxi	d,outfcb	;destination fcb
	mvi	c,makef		;try to create the file
	call	bdos
	cpi	error
	rnz			;no error, return (from checkout)
	call	showmsg
	db	cr,lf,lf,'No room on this disk...',0
  otherdisk:
	call	showmsg
	db	cr,lf,9,9,'Try another disk  (Y/N)?   ',0
  	call	coninone
	cpi	'Y'
	jz	whichdrive
	mvi	a,quit
	ret			;(from checkout)

  ;determine drive for copy
  whichdrive:
	lda	infcb		;check drive designator for input file
    	cpi	1		;is it 1 (drive a)
	jz	newinb		;yes, new disk will go in drive b 
	mvi	a,1		;no, new disk in drive a
	sta	outfcb		;put designator in outfcb
	jmp	newinnow	;and prompt for disk
    newinb:
	mvi	a,2		;designator for drive b
	sta	outfcb		;in outfcb 
    newinnow:
	call	showmsg
	db	cr,lf,lf,'This may not work if the new disk does not'
	db	cr,lf,'contain the CP/M system tracks...',cr,lf,lf 
	db	'Insert disk for copy in drive ',0
	lda	outfcb		;get designator back
	adi	40h		;make it into a letter
	call	cout		;and show it
	call	showmsg
	db	':',cr,lf,'and press any key, or ESC to exit  ',0
	mvi	c,conin		;wait for a key to be pressed
	call	bdos		
	cpi	quit
	jnz	checkout	;back to beginning of subroutine
	mvi	a,quit
	ret			;(from checkout)

;open input file
opninfil:
 	xra	a
	lxi	d,infcb		;load input fcb pointer
	mvi	c,openf
	call	bdos		;open the input file
	ret

;find destination file
findoutfil: 
	mvi	c,initdsk	;reset disk, just in case
	call	bdos
	lxi	d,outfcb  	;destination fcb
	mvi	c,findf   	;try to find the file
	call	bdos
	ret

;open destination file
opnoutfil:
	lxi	d,outfcb	;destination fcb
	mvi	c,openf		;open file
	call	bdos
	ret

;read a file name from console and put it into fcb
cinbuff:
	xchg			;(de was infcb or outfcb, now hl)
	shld	curfcb		;store current fcb address at curfcb
  buffin:
	lxi	h,inbuf+1	;zero chr counter
	mvi	m,0
	dcx	h		;set maximum length
	mvi	m,20	
	xchg			;put inbuf pointer into de register
	mvi	c,readbf	;read input buffer function
	call	bdos
	lxi	h,inbuf+1	;get chr counter
	mov	a,m		;check length
	adi	quit
	cpi	quit		;if zero, put "quit" in a and exit
	rz
	mov	e,m		;length into lsb of de register pair
	mvi	d,0		;zero msb
	dad	d		;add length to start
	inx	h		;plus one points to end
	mvi	m,0		;put zero as end marker
   ;now move the file name
	lhld	curfcb		;get current fcb address again	
	xchg			;and put it in de
	mvi	b,8		;counter for file name chrs
	mvi	c,3		;counter for type chrs
	lxi	h,inbuf+2	;point hl to first chr in buffer
	lda	inbuf+3		;read second char. in buffer
	cpi	0		;is it end marker?
	jz	nodrive		;if yes, no drive was selected
	cpi	':'		;is it a colon?
	jnz	nodrive		;no, no drive was selected
	lxi	h,inbuf+4	;yes, point hl to file name
	lda	inbuf+2		;read drive chr
	call	ucase		;make upper case
	cpi	'A'		;drive A?
	jz	setdriven	;yes, set it into fcb
	cpi	'B'		;drive B?
	jnz	badname		;not an acceptable name
   setdriven:
	sbi	64		;subtract 64
	stax	d 		;and put drive select byte in fcb
   nodrive:
	inx	d		;point to fcb+1
	mov	a,m		;read chr of name
	cpi	quit
	rz			;return if "quit"
	cpi	0		;end marker?
	jz	donebuff	;if yes, done
	cpi	'.'		;is it a period?
	jz	settype		;yes, go to type area in fcb
	call	ucase		;make it upper case
	stax	d		;no, store in fcb
	inx	h		;point to next chr
	dcr	b		;decrement counter
	mov	a,b		;and check it
	cpi	0		;8 chrs read?
	jz	settype		;yes, go to type area
 	jmp	nodrive		;no, read another one
  settype:
	push	h
	lhld	curfcb		;get current fcb address again	
	xchg			;and put it in de
	pop	h
	mvi	b,9		;put a count of 9 in b
  increm:
	inx	d		;point to next fcb byte
	dcr	b		;and do it nine times
	jnz	increm
  movtyp:
	inx	h		;read next chr
	mov	a,m		
	cpi	0		;end marker?
	jz	donebuff	;if yes, done
	call	ucase		;convert to upper case
	stax	d		;store it in fcb
	inx	d		;and point to next fcb location
	dcr	c		;decrement counter
	jnz	movtyp		;not done, next chr
  donebuff:
				;yes, return
	ret			;(from cinbuff)	

badname:
	call	showmsg
	db	cr,lf,7,'Bad file name, try again',cr,lf
	db	'	(or just RETURN to exit):  ',0
	jmp	buffin

;get drive designator, in case it was not specified with file name
getdrive:
	call	showmsg
	db	cr,lf,'	   On which drive?  ',0
	call	coninone	;get one chr from console
	cpi	quit
	rz
	sui	40h		;set A=1, B=2, etc.
	jm	drerror		;can't be negative
	cpi	1
	rz			;if A, return
	cpi	2
	rz			;if B, return
drerror:
	call	showmsg
	db	cr,lf,'That drive does not exist!',cr,lf,0
	jmp	getdrive

;check that names are not the same
samename:
	push	h
	push	d
	lxi	h,infcb
	lxi	d,outfcb
	mvi	b,12
   nextfcb:
	ldax	d		;read byte from outfcb
	cmp	m		;compare with infcb
	jnz	notsame
	inx	d
	inx	h
	dcr	b
	jnz	nextfcb
	mvi	a,error		;error found
	pop 	d
	pop	h
	ret			;(from samename)
   notsame:
	mvi	a,nope		;no error
	pop	d
	pop	h
	ret			;(from samename)

;ask if copy must keep soft space markers and soft carriage returns
wsfile:
	call	showmsg
	db	cr,lf,lf
	db	'Should the copy retain the WordStar formatting',cr,lf
	db	'codes, such as soft spaces, soft returns',cr,lf
	db	'and soft hyphens?  ',cr,lf,lf,0
	call	coninone	;get answer
	cpi	'Y'		
	jnz	deldot
	lxi	h,wsformat	;point to ws format flag
	mvi	m,true		;turn it on
	ret
deldot:
	call	showmsg
	db	cr,lf,lf
	db	'Do you want to retain any dot commands found',cr,lf
	db	'in the copy?',cr,lf,lf,0 
	call	coninone
	cpi	'Y'
	rz			;return if keep, 
	mvi	a,true		;otherwise signal to zap
	sta	zapdot
	ret

;enter chrs to be filtered out
selectch:
	lxi	h,stable+2	;point to selection buffer
	mvi	b,0		;chr count	
selectagain:
	push	b
	push	h
	mvi	e,0ffh
	mvi	c,condir
	call	bdos		;direct input from console for no echo
	cpi	escape		;is it an escape chr?
	jz	endselect	;if escape, selection complete	
	cpi	backspace
	jz	backone
	mov	d,a		;store the chr
	lxi	h,ptable	;point to table of print control chrs	
valtest:
	mov	a,m		;get first valid control chr from table 
	cpi	escape		;end of part of table we want to check?
	jz	badentry	;no match in table, must be bad
	inx	h		;point to next in table
	cmp	d		;valid entry?
	jnz	valtest		;no, check next in table
	push	d
	mvi	e,'^'		;
	mvi	c,conout	;print "^"
	call	bdos
	pop	d
	mov	a,d		;get the chr again
	pop	h
	mov	m,a		;copy it into selection buffer
	adi	64		;make a into corresponding printing char.
	push	h
	mov	e,a
	mvi	c,conout	;and print it
	call	bdos
	pop	h
	pop	b
	mov	a,b
	cpi	30		;check if selection buffer full
	jz 	selectout	;if it is, end selection
	inx	h
	inr	b
	jmp	selectagain	;otherwise, get another chr

badentry:
	pop	h
	pop	b
	jmp	selectagain	;and try again

endselect:
	pop	h		;pop stack
	pop	b		;twice...
selectout:
	mov	a,b
	ret			;(from selectch)

;back up one character
backone:
	pop	h
	pop	b
	mov	a,b
	cpi	0
	jz	selectagain
	dcx	h
	dcr	0
	mvi	m,'$'
	mvi	a,backspace
	call	cout
	call	cout
	mvi	a,space
	call	cout
	call	cout
	mvi	a,backspace
	call	cout
	call	cout
	jmp	selectagain

if echo
;echo chr to console
echochar:
	cpi	lf		;is the chr a lf?
	rz			;yes, don't show it
	push	b		;save b & c
	push 	psw		;save a before echo to console
   echonow:
	ani	7fh		;zero high bit for display
	cpi	cr		;if cr, back to start of line
	jz	newscreen
	cpi	tab
	jnz	echoit
	mvi	a,period	;if tab, show period instead
   echoit:
	call	cout		;chr was not a lf, show it
	lda	echopos		;increment position record
	inr	a
	sta	echopos
	cpi	linelen		;full line?
	jnz	endecho		;no
   newscreen:
	mvi	a,0
	sta	echopos		;yes, reset it
	mvi	a,cr		;and send a cr to screen
	call	cout
	push	b
	mvi	b,linelen
   spacscrn:
	mvi	a,space		;overwrite old line with spaces
	call	cout
	dcr	b
	jnz	spacscrn
	pop	b
	mvi	a,cr
	call	cout
  endecho:
	pop	psw
	pop	b
	ret			;(from echochar)
endif

;look for chr in b in table pointed to by hl
isitin:	
	xra	a		;check for end of table (0) 
	cmp	m
	jz	notin		;end of table, no match
	mov	a,b		;get the processed chr back in a
	ani	7fh		;zero high bit
	cmp	m		;and compare with chr in table
	jz	itsin		;if it is in table, do further checking
	inx	h		;point to next chr in table
	jmp	isitin 		;and compare again
  itsin:
	mvi	a,true
	ret			;(from isitin)
  notin:
	mvi	a,nope		;(from isitin)
	ret 

;get one chr from console and show it in uppercase
coninone:
	push	b
	push	d
	push	h
  tryakey:	
	xra	a		;clear flag register
	mvi	e,0ffh
	mvi	c,condir	;direct input from console for no echo
	call	bdos
	cpi	0		;if no key pressed, try again
	jz	tryakey
	mov	b,a		;copy chr into b
	sui	32
	jm	nocout		;do not echo if control chr.
	mov	a,b
	call	ucase		;make it upper case
	mov	b,a
	cpi	quit
	cnz	cout		;and show it
  nocout:	
	mov	a,b
	pop	h
	pop	d
	pop	b
	ret

;output chr in a to console
cout:
	push	b
	push	d
	push	h
	push	psw
	mvi	c,conout
	mov	e,a
	call	bdos
	pop	psw
	pop	h
	pop	d
	pop	b
	ret

;message pointed to by stack to console (modifies register a)
showmsg:
	xthl			;get stack return address into hl
	xra	a		;clear flags and accumulator
	add	m		;get one chr of the message
	inx	h		;point to next
	xthl			;restore stack
	rz			;return if done
	call	cout		;else display chr
	jmp	showmsg		;and repeat

;convert chr in register a to upper case 
ucase:
	cpi	quit
	rz
	push	b
	mov	b,a
	call	islowcs		;lower case?
	cpi	true
	jnz	notup		;if no, skip
	mov	a,b
	ani	5fh		;make uppercase
	pop	b 
	ret
 notup:
	mov	a,b
	pop	b	
	ret

;is chr in register a lower case? if yes, set a=true
islowcs:
	push	b
	mov	b,a
	xra	a		;clear flag register
	mov	a,b
	sui	97
	jm	isntlow		;if <97, not lower case	
	mov	a,b
	adi	133
	jc	isntlow		;if >122, not lower case
	mvi	a,true
	pop	b
	ret
    isntlow:
	mvi	a,nope
	pop	b
	ret			;(from islowcs)

;check for upper case chr
isupcse:			;returns a=true if chr in a is uppercase
	push	b
	mov	b,a
	xra	a
	mov	a,b
	sui	65
	jm	isntup		;cannot be less than 'A'
	mov	a,b
	adi	165		;cannot be larger than 'Z'
	jc	isntup
	mvi	a,true
	pop	b
	ret
   isntup
	mvi	a,nope
	pop	b
	ret

;print function
printit:
	mvi	c,printf
	jmp	bdos		;will return directly from bdos

;dim screen;  codes shown are for Osborne 
dim:
  if osborne
	call	showmsg
	db	escape,')',0
  endif
	ret

;normal brightness
bright:
  if osborne
	call	showmsg
	db	escape,'(',0
  endif
	ret

;initialize storage areas
initstor:
 ; zero fcb, flags and buffers	
	lxi	d,outend	;point de to end of storage
	lxi	h,infcb		;point de to beginning
    movzero:
	mvi	m,0
	inx	h
	mov	a,h		;check top byte of address
	cmp	d		;for outend
	jnz	movzero
	mov	a,l		;now check low byte
	cmp	e
	jnz	movzero
	lxi	h,infcb+1	;point to name areas
	mvi	b,11		;in 11 chrs incl. type
	call	spastor		;go put spaces
inittwo:
	lxi	h,outfcb	;zero drive indicator in outfcb
	mvi	m,0
	lxi	h,outfcb+1	;point to name areas
	mvi	b,11		;11 char incl. type
	call	spastor
 	ret			;(from initstor and inittwo)
 
   spastor:
	mvi	a,space
	mov	m,a		;put a space there
	inx	h
	dcr	b
	mov	a,b
	cpi	0
	jnz	spastor		;if not done, repeat
	ret			;(from spastor)
;      ** storage area **
 
;   tables of chrs to be processed
;option p (remove all print control chrs, except dot commands)
ptable:		db	01,02,03,04,05,06,07,0bh,0ch,0eh,10h
		db	11h,12h,13h,14h,15h,16h,17h,18h,19h
		db	escape,0
;option s (selected by user)
stable:		db   	'$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$',0
;characters after which a cr will be kept when reformatting 
crtable:	db	period,colon,'?!%|>]*_1234567890',0 
;menu
menul:	db	'L - Remove line feeds,',cr,lf
	db	'       and pad blank lines with a space.',cr,lf,lf,'$'
menuc:	db	'C - Remove extra carriage returns and spaces',cr,lf
	db	'    (leave only paragraph breaks, "unjustify")',cr,lf,lf,'$'
menuw:	db	'W - Change a WordStar file into an ASCII file',cr,lf,lf,'$'
menup:	db	'P - Remove all imbedded print commands',cr,lf,lf,'$'
menus:	db	'S - Remove selected imbedded print commands',cr,lf,lf,'$'
menur:	db	'R - Remove any "rubout" characters (7fh) only',cr,lf
	db	9,'(done automatically by other options above)',cr,lf,lf,'$'
menuq:	db	'Q - Quit',cr,lf,lf,'$'
showopt: db	'Option selected:',cr,lf,'$' 

;stack storage next 64 bytes
		ds	64
stack		ds	1	;label end of stack (it works backwards)
curfcb:		ds	2   	;current fcb address

;buffer and flag area

infcb:		db	0,20h,20h,20h,20h,20h,20h,20h,20h,20h,20h,20h
		db	0,0,0,0,0,0,0,0,0,0,0,0
		db	0,0,0,0,0,0,0,0
		db	0
outfcb:		db	0,20h,20h,20h,20h,20h,20h,20h,20h,20h,20h,20h
		db	0,0,0,0,0,0,0,0,0,0,0,0
		db	0,0,0,0,0,0,0,0
		db	0

;flags and counters
choice:		db	0	;menu choice
crflag:		db	0	;just erased a cr
spare:		db	0	;
lowcase:	db	0	;true if lower case in text
ambighyph:	db	0	;true if ambiguous hyphen found
newchar:	db	0	;newer of chr pair in process
oldchar:	db	0	;older of chr pair in process
lastread:	db	0	;last read chr
lastchar:	db	0	;last copied chr 		
penultimate:	db	0	;we store the one before last here	
joinfl:		db	0	;true if rejoining word	
wsformat:	db	0	;if true, keep 8th bit and non-break spaces
zapdot:		db	0	;if true, delete dot commands
dotcomfl:	db	0	;if true, processing a dot command
keep:		db	0	;if true, copy this chr
echopos:	db	0	;column counter for console
errfl:		db	0	

inpos:		db	0,0
outpos: 	db	0,0
inbuf:		ds	80h
inend:
outbuf: 	ds	80h
outend:
		db	' Copyright 1983 by Claude Ostyn'  
	end
