UNFORM	ORG	57000	;May be located at any address
	ENT	57000
;May be adapted for any computer
;as long as copyright notice
;remains in final version
;
;Formatted for Custom Software assembler
;ROM routines
;
;Check directory for filename
;Entry:  D = buffer with name
;A=length of filename +".DO"+1
;Exit:  H = address of file directory
;Z flag set if file not found
;all other registers destroyed
FILCHK	EQU	$5AAB
;
;Get start address of file
;Entry:  H = file directory
;Exit:  H = start of file
;D = directory entry +2
GETFIL	EQU	$5AE3

KYREAD	EQU	$7242	;Get keystroke without cursor flashing
KEYPRT	EQU	$4B44	;Print character on LCD
UPCASE	EQU	$0FE9	;Convert filename to uppercase if needed
FILES	EQU	$1F3A	;List RAM files
CURSOR	EQU	$427C	;Position cursor - H = column, L = row
CLS	EQU	$4231	;Clear screen
NEGVID	EQU	$4269	;Negative video
POSVID	EQU	$426E	;Positive video
BEEP	EQU	$4229	;Beep
;
;Delete BC number of characters in TEXT ;Entry:
;H = start of deletion
;Exit:
;H preserved, all other registers
;destroyed
DELETE	EQU	$6B9F
;
;Alternate LCD buffer
;used here for RAM-saving storage
;of lines from file
BUFFER	EQU	$FCC0
MENU	EQU	$5797	;Model 100 menu
DISPLA	EQU	$1BE0	;Display B characters from H buffer
ASCII	EQU	$39D4	;Display HL in ASCII form
;
;
;Display introduction message
	CALL	CLS	;Clear screen
	MVI	H,15	;Currsor column
	MVI	L,2	;Cursor row
	CALL	CURSOR	;Position cursor
	CALL	NEGVID	;Negative video
	DISP	INTRO1	;Print intro line on LCD
	CALL	POSVID	;Positive video
	MVI	H,20	;Cursor column
	MVI	L,3	;Cursor row
	CALL	CURSOR	;Position cursor
	DISP	INTRO2	;Print intro line on LCD
	MVI	H,8	;Cursor column
	MVI	L,4	;Cursor row
	CALL	CURSOR	;Position cursor
	DISP	INTRO3	;Print intro line on LCD
	MVI	H,9	;Cursor column
	MVI	L,6	;Cursor row
	CALL	CURSOR	;Position cursor
	DISP	INTRO4	;Print intro line on LCD
	MVI	H,3	;Cursor column
	MVI	L,7	;Cursor row
	CALL	CURSOR	;Position cursor
	DISP	INTRO5	;Print intro line on LCD
	CALL	AWAIT	;Wait for keystroke
;
;Get name of file from keyboard
GETNAM	CALL	CLS	;Clear LCD
	CALL	FILES	;Display RAM files
	MVI	H,7	;Cursor column
	MVI	L,7	;Cursor row
	CALL	CURSOR	;Position cursor
	CALL	NEGVID	;Negative video
	DISP	CHOOSE	;Print "select file" line on LCD
	CALL	POSVID	;Positive video
	MVI	H,21	;Cursor column
	MVI	L,8	;Cursor row
	CALL	CURSOR	;Position cursor
	DISP	EXTNSN	;Print ".DO" on LCD
	MVI	H,15	;Cursor column
	MVI	L,8	;Cursor row
	CALL	CURSOR	;Position cursor
;Set B letter counter at 0 -
;Maximum 6 letters in file name
	MVI	B,0
;Point H toward area to store 
;file name as it's input
	LXI	H,STORE
LOOP2	CHAR		;wait for character
	CALL	UPCASE	;Convert input character to uppercase
	CALL	KEYPRT	;Display character
	CPI	$08	;Check for "delete"
	JZ	DELET	;If "delete," get another character
	CPI	$0D	;Compare with ENTER key (end of word)
	JZ	CHECK	;If end of word, jump to next routine
	MOV	M,A	;Store character in STORE
	INX	H	;Next memory location in STORE
	INR	B	;Count of most recent letter
	MOV	A,B
	CPI	6	;check if 6th character has been reached
	JNZ	LOOP2	;Loop if not last letter
ENTER	CHAR		;Get character after 6th
	CPI	$0D	;Check for Enter key
	JZ	CHECK	;If Enter key
	CALL	BEEP	;If not Enter key
	JMP	ENTER
;
;Check if file is in RAM
;
;Increase count of letters
;in A by 4 to
;length of file name + ".DO" + 1
CHECK	ADI	4
;Add ".DO" to stored file name
	MVI	M,'.'	;Store period
	INX	H	;Next memory location in STORE
	MVI	M,'D'	;Store "D"
	INX	H	;Next memory location in STORE
	MVI	M,'O'	;Store "O"
	INX	H	;Next memory location in STORE
	MVI	M,0	;Store 0 (end of file name)
	LXI	D,STORE	;File name location for FILCHK
	CALL	FILCHK	;Check directory for file name
	JNZ	GOFILE	;File in RAM
NOPE	CALL	BEEP	;File not in RAM
	CALL	CLS	;Clear LCD
	MVI	H,10	;Cursor column
	MVI	L,5	;Cursor row
	CALL	CURSOR	;Position cursor
	DISP	STORE	;Print file name on LCD
	DISP	NO	;Display "File not in RAM" message
	CALL	AWAIT	;Wait for keystroke
	JMP	GETNAM	;Return to get another name
;Await keypress subroutine
AWAIT	MVI	H,13	;Cursor column
	MVI	L,8	;Cursor row
	CALL	CURSOR	;Position cursor
	CALL	NEGVID	;Negative video
	DISP	PRESS	;Print "Press any key" on LCD
	CALL	POSVID	;Positive video
LOOP1	CALL	KYREAD	;Wait for keystroke
	RNZ		;Return if any key is pressed
	JMP	LOOP1	;No key pressed
GOFILE	CALL	GETFIL	;Go to the file in RAM
	SHLD	FLSTRT	;Store start of file for later
;
;Start measuring lines
;
;B will contain counter
;looking for most characters
;in one line
	MVI	B,0
;C will contain length of longest line
;updated as longer lines are found
	MVI	C,0
;Line being measured is stored in
;alternate LCD buffer
LINCHK	LXI	D,BUFFER
LOOP3	MOV	A,M	;Get character from file
	CPI	$1A	;Check for EOF
	JZ	ENDFIL	;If EOF, jump
	CPI	$0D	;Check for end of line
	JZ	COMPAR	;Jump if end of line
	STAX	D	;Store letter in BUFFER
	INX	D	;Next space in BUFFER
	INR	B	;Increment character counter
	MOV	A,B	;Get character count
	CPI	150	;End program if line >=150
	JZ	TOOLNG	;Jump if line is too long
	INX	H	;Increment to next space in BUFFER
	JMP	LOOP3
COMPAR	INX	H	;Get past $0A (^J) at end of line
	INX	H
	MOV	A,C	;Get count on longest line to date
;Is latest line (B)
;longer than longest (C)?
	CMP	B
	JNC	NOCHG	;New line not longer
;New line IS longer -  New length in C
	MOV	C,B
	PUSA		;Store all info on previous line
;
;Move all characters in
;most recent line to BUFFER + 151
	LXI	H,BUFFER	;Point to BUFFER
	LXI	D,BUFFER+151	;Point to BUFFER + 151
	LXI	B,150	;Maximum number of characters in line
	LDIR		;Move characters
	POPA		;Get back info on previous line
NOCHG	MVI	B,0	;reset line character counter
	JMP	LINCHK	;Check next line
;If any line in file >= 150 bytes,
;end program
TOOLNG	CALL	CLS	;Clear LCD
	CALL	BEEP	;Beep
	MVI	H,1	;Cursor column
	MVI	L,3	;Cursor row
	CALL	CURSOR	;Position cursor
	DISP	LONG1	;Print 1st Too Long message
	MVI	H,6	;Cursor column
	MVI	L,5	;Cursor row
	CALL	CURSOR	;Position cursor
	DISP	LONG2	;Print 2nd too long message
	CALL	AWAIT	;Wait for keystroke
EXIT	CALL	MENU	;Terminate program
ENDFIL	PUSH	B	;Store longest line length (from C)
	CALL	CLS	;Clear LCD
	MVI	H,1	;Cursor column
	MVI	L,1	;Cursor row
	CALL	CURSOR	;Position cursor
	DISP	LINLTH	;Print line length message
	POP	B	;Retrieve longest line length from C
	MOV	L,C	;Line length to L for ASCII call
	MVI	H,0	;So H = line length
	PUSH	B	;Store longest line length in C
	CALL	ASCII	;Display line length
	MVI	H,1	;Cursor column
	MVI	L,2	;Cursor row
	CALL	CURSOR	;Position cursor
	DISP	FOLLOW	;Display "following" message
	DISP	STORE	;Display file name
	MVI	H,1	;Cursor column
	MVI	L,4	;Cursor row
	CALL	CURSOR	;Position cursor
	CALL	NEGVID	;Negative video
	POP	B	;retrieve longest line length
	LXI	H,LINLEN;Point to line length storage
	MOV	M,C	;Store longest line length
;Move longest line length to B for DISPLA
	MOV	B,C
	LXI	H,BUFFER+151	;Point to longest line
	CALL	DISPLA	;Display longest line
	CALL	POSVID	;Positive video
	MVI	H,5	;Cursor column
	MVI	L,8	;Cursor row
	CALL	CURSOR
	DISP	CONT	;Display "Continue?" message
CTINUE	CHAR		;Get character from keyboard
	CALL	UPCASE	;Convert to upper case
	CPI	'Y'
	JZ	START	;If Yes
	CPI	'N'
	JZ	EXIT	;If No
	JMP	CTINUE	;If anything else
;
;Start unformatting
START	CALL	CLS	;Clear LCD
	MVI	H,10	;Cursor column
	MVI	L,3	;Cursor row
	CALL	CURSOR
	DISP	WORK	;Display "Unformatting" message
	LDA	LINLEN	;Load longest line length
	ADD	A	;Double line length
	MVI	C,0
;Subtract 3 arriving at 2/3 line length
LOOP4	SUI	3
;C contains count 2/3 of longest line -
;Increment C
	INR	C
	JNC	LOOP4
	MOV	A,C	;Get 2/3 length
	STA	THIRDS	;Store length
	LHLD	FLSTRT	;Point to start of file
;Set flags at 0
;Only necessary if program is CALLed
	MVI	A,0
	STA	ENDER
	STA	NEW
	STA	OLD
LNSTRT	MVI	B,0	;B contains count of current line
	MVI	A,0
	STA	TAB	;Initialize tab counter
;Initialize first-character-is space
;flag
	STA	SPACE
	MOV	A,M
	CPI	$20	;Check for space as 1st character
	CZ	SPACST	;If space, increment space counter
	CPI	$09	;Check for tab
	CZ	TABCNT	;If tab, increment tab counter
	CZ	SPACST	;If tab, increment space counter
	CPI	$0D	;Check for end of line
	JZ	PROCES	;Jump if end of line
LOOP5	CPI	$1A	;Check for end of file
;If end of file, set end flag
	CZ	ENDBYT
	JZ	PROCES	;If end of file, process last line
	CPI	$09	;Check for tab
	CZ	TABCNT	;If tab, increment tab counter
	INX	H	;Next character
	INR	B	;Increment count of current line
	MOV	A,M	;Get character from file
	CPI	$0D	;Check for end of line
	JZ	PROCES	;Jump if end of line
	JMP	LOOP5	;Get another character
PROCES	CPI	$1A	;Check for end of file
	CZ	ENDBYT	;If end of file
	SHLD	NEXTPT	;Store address of new carriage return
	LHLD	LASTPT	;Point to old carriage return
	MOV	A,H	;Get old carriage return address
	CPI	0	;Check if H has 0 flag
;If H has zero flag, do not change
;previous carriage return
	JZ	KEEPO
	LDA	NEW	;Check for saving last return
	CPI	0
	JNZ	KEEPO	;Jump to keep old routine
	LDA	SPACE
;Check if space or tab was 1st character of current line
	CPI	0
;If space or tab at top of current line
;do not change previous carriage return
	JNZ	KEEPO
LINE	MOV	A,B	;Get line length
	CPI	0	;Check for blank line
	JZ	KEEPON	;Jump if blank line
	PUSH	A
	LDA	THIRDS	;Get 2/3 length
	MOV	C,A
	POP	A
	CMP	C	;Compare with 2/3 line length
;If less than 2/3
;do not change current
;carriage return
	CC	KEEPN
	LDA	TAB	;Get tab count
	CPI	1	;Check for tabs
;If more than 1 tab, don't
;change this or previous line
	JNC	KEEPON
	LDA	OLD	;Get old carriage return flag
	CPI	1
	JZ	advanc	;End process if not 0
	DCX	H	;Point to last character of previous line
	MOV	A,M	;Get last character of previous line
	INX	H	;Point to previous carriage return
	CPI	$2E	;Check for period
	JZ	TWOSPC	;If period, add two spaces
	CPI	$21	;Check for exclamation point
	JZ	TWOSPC	;If exclamation point, add two spaces
	CPI	$3F	;Check for question mark
	JZ	TWOSPC	;If question mark, add two spaces
	CPI	$3A	;Check for colon
	JZ	TWOSPC	;If colon, add two spaces
;Replace carriage return with one space
	MVI	M,$20
	INX	H	;Point to ^J
;BC contains number of characters
;to delete
	LXI	B,1
	CALL	DELETE	;Delete ^J
	LHLD	NEXTPT	;Point to recent carriage return
;To compensate for deleted ^J, move back pointer one
	DCX	H
	SHLD	NEXTPT	;Store modified carriage return
	LDA	ENDER	;Check for end of file
	CPI	0
	JNZ	EXIT	;End program if end of file
	JMP	ADVANC	;Start another line
;Keep old carriage return
KEEPO	MVI	A,0
	STA	NEW	;Reset last carriage return flag
	MVI	A,1
	STA	OLD	;Set save old flag
	JMP	LINE
;Keep old & new carriage returns
;Since previous carriage return
;will not be deleted, 0
;put in LASTPT as flag
KEEPON	MVI	A,1
	STA	NEW	;Set last carriage return flag
ADVANC	MVI	A,0
	STA	OLD	;Zero old carriage return counter
	LHLD	NEXTPT	;Point to most recent carriage return
	SHLD	LASTPT	;Move it to previous storage
	INX	H	;Go past ^M
	INX	H	;Go past ^J
	LDA	ENDER	;Check for end of file
	CPI	0
	JNZ	EXIT	;End program if end of file
	JMP	LNSTRT	;Start another line
;Keep new carriage return
KEEPN	STA	NEW	;Set NEW flag
	RET
;Flag space or tab as 1st character in line
SPACST	STA	SPACE	;Put number in space counter
	RET
TABCNT	PUSA		;Push all registers
	LXI	H,TAB	;Point toward tab count storage
	MOV	A,M	;Get current figure
	ADI	1	;Add one to tab count
	MOV	M,A	;Store new figure
	POPA		;Get back previous registers
	RET		;Return to get next character
;Replace carriage return with two spaces
TWOSPC	INX	H
	INX	H	;Advance 2 bytes to check for end of file
	MOV	A,M
	CPI	$1A	;Check for end of file
	JZ	EXIT	;End program
;Go back to carriage return if not
;end of file
	DCX	H
	DCX	H
	MVI	M,$20	;Change to space
	INX	H	;Point to ^J
	MVI	M,$20	;Change to space
	LDA	ENDER	;Check for end of file
	CPI	0
	JNZ	EXIT	;End program if end of file
	JMP	ADVANC	;Start another line
ENDBYT	STA	ENDER	;Set end of file flag
	RET		;Return
;Deleting character entered in
;file name
DELET	MOV	A,B
	DCR	A	;Decrement counter
;If too many letters are deleted beep
;and go back to top of loop
	JM	BEEPER
	DCR	B
	DCX	H
	JMP	LOOP2	;Get another character
BEEPER	CALL	BEEP	;Beep
	MVI	B,0
	JMP	LOOP2
HOLD	JMP	HOLD
INTRO1	DM	 UNFORM.CO 
	DB	0
INTRO2	DM	by
	DB	0
INTRO3	DM	Don Zeikel [CIS 75775,1430]
	DB	0
INTRO4	DB	$AB	;Copyright symbol
	DM	1987, all rights reserved.
	DB	0
INTRO5	DM	Removes unnecessary carriage returns.
	DB	0
NO	DM	  IS NOT IN RAM!
	DB	0
PRESS	DM	 PRESS ANY KEY 
	DB	0
STORE	DS	9
CHOOSE	DM	 SELECT .DO FILE TO UNFORMAT 
	DB	0
EXTNSN	DM	.DO
	DB	0
LONG1	DM	FILE HAS LINE OF 150 OR MORE CHARACTERS.
	DB	0
LONG2	DM	IT IS ALREADY UNFORMATTED!
	DB	0
LINLTH	DM	Formatted line length: 
	DB	0
FOLLOW	DM	The following is a line from 
	DB	0
LINLEN	DS	1	;Store length of longest line
FLSTRT	DW		;Store address of file
LASTPT	DW		;Store address of last carriage return
NEXTPT	DW		;Store address of present carriage return
SPACE	DS	1	;Flag if space or tab starts line
TAB	DS	1	;Tab counter
ENDER	DS	1	;End of file flag
NEW	DS	1	;Keep new carriage return flag
OLD	DS	1	;Keep old carriage return flag
THIRDS	DS	1	;2/3 longest line length
CONT	DM	CONTINUE (Y/N)?
	DB	0
WORK	DM	UNFORMATTING...
	DB	0
	END
