home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
cpm
/
programs
/
spell
/
spell20.mac
< prev
next >
Wrap
Text File
|
1994-07-13
|
56KB
|
2,255 lines
;
; SPELL version 2.0 December 22, 1982
;
; written by Michael C. Adler
;
; History of SPELL's dictionary:
; The first SPELL using this dictionary was probably written
; by Ralph Gorin at Stanford. It was transported to MIT by
; Wayne Mattson. Both the program at MIT and the dictionary
; were most recently revised by William Ackerman.
;
; Thanks to all for the effort spent desigining
; the dictionary!
; -Michael
;
;
.Z80
TITLE SPELL
; JMP START <--Include this line if assembled under anything
; but M80.
DB '(C) 1982 Michael C. Adler. '
DB 'This program has been released into the public domain '
DB 'by the author. It may neither be sold for profit nor '
DB 'included in a sold software package without permission '
DB 'of the author.'
;
; SPELL will compare words in a document to a dictionary
; and mark any words that are not found. It is intended for use
; with WordStar's ^QL (Find Misspelling) command. Words are marked
; with a preceding null if they are not found.
;
; WARNING: SPELL requires a Z80 processor.
;
; MODIFICATION HISTORY:
;
; 2.0 - December 22, 1982 Michael Adler
; -Bug fix: modified to rename files correctly at end of
; run if output drive is different from input drive.
; -Bug fix: modified to recognize hyphenated words in indented
; text.
; -Enhanced to allow user specified dictionaries on command
; line.
; 1.3 - October 10, 1982 Michael Adler
; -Bug fix: crashed if number of unique words was multiple
; of 256.
; 1.2 - October 8, 1982 Michael Adler
; -Modified MEMWRD routine to store words backwards in
; memory starting just below the BDOS. Thus, maximum
; memory is utilized since pointers to words and the words
; grow toward each other.
; -FILE.UDC is now deleted if it is empty.
; -Added messages displaying number of words, number of
; unique words, and number of words not found.
; 1.1 - August 28, 1982 Michael Adler
; -Crashed if file.ADD had a zero length word. Fixed.
; -Fixed bug in AUXDIC: stopped reading from .ADD file
; when 0 encountered.
; -Fixed compatability with WS R(un program). WS initializes
; byte at 6CH with default drive instead of 0. Caused output
; to be directed to wrong drive sometimes.
; -Set SPELL to ignore lines starting with . because WS can't
; handle misspelling markers in dot command lines.
; -SPELL did not find hyphenated words if LF had parity bit
; set. Fixed.
;
; 1.0 - August 16, 1982 Michael Adler
; -Released
;
INCLUDE SPELL0.MAC
ERRCHR EQU 0 ;THIS IS THE CHARACTER THAT MARKS
;UNFOUND WORDS. FOR WordStar, IT
;MUST BE 0. IF YOU ARE USING ANOTHER
;EDITOR, SET ERRCHR TO THE ASCII
;EQUIVALENT OF THE CHARACTER THAT
;SHOULD MARK MISSPELLED WORDS.
BOOT EQU 0000H
BDOS EQU BOOT+0005H
IFCB EQU BOOT+005CH
TPA EQU BOOT+0100H
CONOUT EQU 2
STROUT EQU 9
OPEN EQU 15
CLOSE EQU 16
DELETE EQU 19
READ EQU 20
WRITE EQU 21
MAKE EQU 22
RENAME EQU 23
CURDSK EQU 25
SETDMA EQU 26 ;SET DMA ADDRESS
RANREA EQU 33 ;READ RANDOM
LF EQU 10
CR EQU 13
EOF EQU 01AH ;END OF FILE CHARACTER
;
; The following are bit flags for the dictionary
;
ZFLAG EQU 1
YFLAG EQU 2
RFLAG EQU 4
GFLAG EQU 8
DFLAG EQU 16
MFLAG EQU 32
SFLAG EQU 64
HFLAG EQU 256
VFLAG EQU 512
JFLAG EQU 1024
XFLAG EQU 2048
TFLAG EQU 4096
NFLAG EQU 8192
PFLAG EQU 16384
;
; Macros
;
JISIT MACRO CHAR, JMPLOC ;JUMP TO JMPLOC IF (HL) .EQ. CHAR
LD A,CHAR
CP (HL)
JP Z,JMPLOC
ENDM
QISIT MACRO CHAR, JMPLOC ;QUICK FORM OF JISIT. CHARACTER
;TO COMPARE IS ALREADY IN A
CP CHAR
JP Z,JMPLOC
ENDM
ISIT MACRO CHAR ;JUMP TO WRDNOT IF (HL) .NE. CHAR
LD A,CHAR
CP (HL)
JP NZ,WRDNOT
ENDM
START: LD SP,STACK ;CREATE A STACK
LD A,0C9H ;RETURN INSTRUCTION
LD (TPA),A ;DON'T ALLOW SECOND RUN OF PROGRAM
LD DE,WELCOM ;PRINT SIGNON MESSAGE
LD C,STROUT
CALL BDOS
JR START0
WELCOM: DB 'SPELL V2.0 -- December 22, 1982',CR,LF
DB '(C) 1982 Michael C. Adler',CR,LF,'$'
START0: LD A,(BOOT+6CH) ;GET OUTPUT DRIVE
PUSH PSW ;SAVE IT
LD C,OPEN ;OPEN THE DICTIONARY FILE
LD DE,DICFCB
CALL BDOS
CP 0FFH ;FOUND?
JP NZ,SETUP ;JUMP IF FOUND
LD A,1 ;TRY DRIVE A IF NOT ON DEFAULT DRIVE
LD (DICFCB),A
LD C,OPEN
LD DE,DICFCB
CALL BDOS
CP 0FFH ;FOUND?
JP NZ,SETUP
LD C,STROUT ;OUTPUT: "CAN'T FIND DICTIONARY..."
LD DE,DICERR
CALL BDOS
CALL 0
DICERR: DB 'Can''t find dictionary file DICT.DIC',CR,LF,'$'
SETUP: LD DE,OFCB ;COPY INPUT FILENAME TO OUTPUT
LD HL,IFCB
LD BC,9
LDIR
LD DE,P2FCB ;COPY INPUT FILENAME TO PASS 2 FCB
LD HL,IFCB ; (THIS FCB IS FOR INPUT ON PASS 2)
LD BC,12
LDIR
LD DE,FILDIC ;COPY INPUT FILENAME TO FILE.DIC FCB
LD HL,IFCB
LD BC,9
LDIR
LD DE,FILADD ;COPY INPUT FILENAME TO FILE.ADD FCB
LD HL,IFCB
LD BC,9
LDIR
LD DE,FILD$$ ;COPY INPUT FILENAME TO FILE.D$$ FCB
LD HL,IFCB
LD BC,9
LDIR
POP PSW ;GET OUTPUT DRIVE, IF SPECIFIED (FROM
;6CH)
CP 0
JR Z,NODRV
LD HL,BOOT+81H ;SEARCH FOR DRIVE SPECIFICATION IN
;COMMAND LINE
LD A,(BOOT+80H) ;NUMBER OF CHARACTERS IN COMMAND LINE
LD B,0
LD C,A ;COUNTER
ADD HL,BC ;HL POINTS TO LAST CHARACTER
DEC BC ;DON'T TEST FIRST BYTE OF INPUT
LD A,':' ;SEARCHING FOR ":"
CPDR
JR NZ,NODRV ;IF NO DRIVE SPECIFIED, THEN USE
;DEFAULT
LD A,(HL) ;GET DRIVE NAME
AND 5FH ;MAKE UPPER CASE
SUB 40H ;MAKE A=1
LD (OFCB),A ;MAKE OUTPUT FILES ON OUTPUT DRIVE
LD (FILD$$),A
NODRV: LD C,OPEN ;OPEN THE INPUT FILE
LD DE,IFCB
CALL BDOS
LD C,OPEN ;WITH PASS 1 AND PASS 2 FCB'S
LD DE,P2FCB
CALL BDOS
CP 0FFH
JP NZ,SETUP0 ;JUMP IF FOUND
LD C,STROUT ;OUTPUT: "CAN'T FIND INPUT FILE"
LD DE,NOINPT
CALL BDOS
CALL BOOT
NOINPT: DB 'Can''t find input file',CR,LF,'$'
SETUP0: LD C,DELETE ;DELETE OUTPUT FILE (FILE.$$$)
LD DE,OFCB
CALL BDOS
LD C,MAKE ;MAKE A NEW OUTPUT FILE
LD DE,OFCB
CALL BDOS
CP 0FFH
JP NZ,SETUP1 ;JUMP IF SUCCESSFUL
LD C,STROUT ;OUTPUT: "DIRECTORY FULL"
LD DE,NODIR
CALL BDOS
CALL BOOT
NODIR: DB 'Directory full',CR,LF,'$'
SETUP1: CALL CTRLZ ;FILL OUTPUT BUFFER WITH EOF
LD A,EOF ;MARK END OF OUTPUT BUFFER
LD (OBUFF+512),A
LD A,0 ;MARK END OF DICTIONARY BUFFER
LD (DICBUF+256),A
LD (DICBUF+257),A
LD A,(PUTCHR) ;GET THE NORMAL FIRST INSTRUCTION
;AT PUTCHR
LD (SAVPUT),A ;STORE IT FOR LATER
LD A,0C9H ;RETURN INSTRUCTION
LD (PUTCHR),A ;DISABLE OUTPUT
CALL AUXDIC ;LOAD "SPELL.DIC", FILE.DIC AND
; FILE.ADD
LD HL,IFCB ;INPUT FILE FCB
LD (FILPAT),HL ;PATCH GETWRD: TO READ FROM IT
;
; SRTFIL -- Sort the file in memory, alphabetically. Duplicate words are
; discarded. This will save a lot of time during dictionary lookups
; since the words will have to be located only once.
SRTFIL: CALL GETWRD ;GET A WORD INTO SRCWRD:
JP Z,CHECK ;IF EOF THEN START CHECKING
LD HL,(TOTWRD) ;KEEP TRACK OF TOTAL # OF WORDS
INC HL
LD (TOTWRD),HL
CALL CREWRD ;CREATE WORD FROM SRCWRD:
LD A,0 ;IS IT AT LEAST 2 CHARS LONG?
CP C
JR Z,SRTFIL ;FORGET IT IF NOT
LD A,41 ;IF LONGER THAN 40, ALSO FORGET IT
CP C
JR Z,SRTFIL
CALL MEMWRD ;PUT WORD IN MEMORY
AND A ;FULL?
JR Z,SRTFIL ;GET ANOTHER WORD IF NOT
LD HL,(INPTR) ;GET OLD INPUT BUFFER POINTER
LD (OLDPTR),HL ;SAVE IT
LD A,1 ;MARK PASS 1 INCOMPLETE
LD (STOPED),A
LD (MLTPAS),A ;RECORD PERMANENTLY THAT >1 PASS
LD C,36 ;GET RECORD NUMBER OF INPUT FILE
LD DE,IFCB
CALL BDOS
;
; CHECK -- Compare the entries in the sorted table to the dictionary.
; Words that are found are marked.
;
CHECK: LD A,(STOPED) ;IS READ OF SOURCE COMPLETE?
CP 0
JR NZ,CHEC01 ;IF NOT, NO MESSAGE
LD DE,TOTMSG ;OUTPUT: "TOTAL # OF WORDS"
LD C,STROUT
CALL BDOS
LD HL,(TOTWRD) ;PRINT NUMBER
CALL DECOUT
CHEC01: LD HL,(SRTTOP) ;TOP ENTRY IN POINTER TABLE
LD DE,(SRTBOT) ;BOTTOM
XOR A ;CLEAR CARRY
DEC HL ;POINT TO TRUE END, NOT DUMMY RECORD
DEC HL
SBC HL,DE ;HL=NUMBER OF WORDS TIMES 2
JP Z,MAIN ;IF EMPTY, WRITE OUT FILE
RRC L ;DIVIDE HL BY 2
LD A,L
AND 07FH
LD L,A
LD A,0
RRC H
ADC A,0
RRC A
OR L
LD L,A
LD A,H
AND 07FH
LD H,A
LD B,H ;PUT COUNTER IN BC
LD C,L
INC B ;MAKE B WORD WITH DEC B AND JNZ
;COMBINATION
LD A,0
CP C ;IF C 0?
JR NZ,CHEC02
DEC B ;IF C=0 THEN READJUST B BECAUSE
;IT WOULD LOOP 256 TOO MANY TIMES
CHEC02: LD HL,(SRTBOT) ;GET ADDRESS OF FIRST ENTRY
INC HL ;BYPASS DUMMY ENTRY AT BEGINNING
INC HL
CHECK0: LD E,(HL) ;GET ADDRESS OF WORD IN MEMORY
INC HL
LD D,(HL)
INC HL
PUSH BC
PUSH HL
LD HL,WORD
LD C,0FEH ;CHARACTER COUNTER
;AT THE END, C=CHARS-1. START OF WITH
;-2 SO THAT 0 AND 1 CHARACTER NOT
;COUNTED
CHECK1: LD A,(DE) ;GET A CHARACTER
LD (HL),A ;PUT IT IN WORD:
INC C
DEC DE
INC HL
CP 0 ;END OF WORD?
JR NZ,CHECK1 ;LOOP UNTIL END
LD A,C
LD (LASTC),A ;NUMBER OF CHARACTERS
LD HL,(UNQWRD) ;RECORD # OF UNIQUE WORDS
INC HL
LD (UNQWRD),HL
CALL WRDTST ;SEARCH DICTIONARY FOR WORD
CP 1 ;FOUND?
JR NZ,CHECK2 ;LEAVE UNMARKED IF NOT FOUND
INC DE ;POINT TO END MARKER OF WORD
LD A,080H
LD (DE),A ;MARK WORD AS FOUND
JR CHECK3
CHECK2: LD HL,(MISWRD) ;INCREMENT MISSED WORDS COUNTER
INC HL
LD (MISWRD),HL
CHECK3: POP HL
POP BC
DEC C
JP NZ,CHECK0 ;LOOP
DEC B
JP NZ,CHECK0 ;16 BIT LOOP INDEX
LD HL,P2FCB ;SET TO INPUT FROM PASS 2 FCB
LD (FILPAT),HL
LD HL,(P2OPTR) ;SET PASS 2 INPUT POINTER
LD (INPTR),HL
LD A,(SAVPUT) ;GET THE NORMAL FIRST INSTRUCTION
;AT PUTCHR
LD (PUTCHR),A ;PATCH IT INTO THE WRITE ROUTINE
LD HL,P2BUF+512 ;PATCH NEW BUFFER INTO GETCHR
LD (BUFPAT),HL
LD HL,P2BUF
LD (BUFPA0),HL
LD (BUFPA1),HL
INC HL
LD (BUFPA2),HL
LD A,(LASTCH) ;SAVE LAST CHARACTER READ
LD B,A
LD A,(OLSTCH) ;PUT OLD LASTCH BACK
LD (LASTCH),A
LD A,B
LD (OLSTCH),A ;SAVE CURRENT LASTCH
JR MAIN
TOTMSG: DB CR,LF,LF,'Total number of words in document: $'
;
; MAIN -- Do a second pass through the input file. Compare each word to
; the memory buffer to determine whether the words were found.
; If a word was not found, mark it with the ERRCHR.
MAIN: CALL GETWRD ;GET A WORD INTO SRCWRD:
JP Z,DONE ;IF EOF THEN STOP
CALL CREWRD ;CREATE WORD FROM SRCWRD:
LD A,0 ;IS WORD AT LEAST 2 CHARS LONG?
CP C
JR Z,MAIN0 ;ACCEPT IF NOT
LD A,41 ;IF LONGER THAN 40, ALSO ACCEPT
CP C
JR Z,MAIN0
CALL MEMWRD ;SEARCH FOR WORD IN MEMORY
LD A,(DE) ;GET MARKER FOR WORD
CP 080H ;WORD SPELLED CORRECTLY?
JR Z,MAIN0
LD A,ERRCHR ;IF NOT, MARK WORD
CALL PUTCHR
MAIN0: LD HL,SRCWRD ;OUTPUT THE ORIGINAL WORD
MAIN1: LD A,(HL)
CP 0 ;END OF WORD?
JR Z,MAIN2
CALL PUTCHR ;OUTPUT CHARACTER
INC HL ;POINT TO NEXT CHARACTER
JR MAIN1 ;LOOP
MAIN2: LD A,(STOPED) ;WAS PASS 1 INCOMPLETE
AND A
JR Z,MAIN ;GET NEXT WORD IF NOT
LD DE,P2FCB ;COMPUTE CURRENT RECORD NUMBER
LD C,36
CALL BDOS
LD DE,(P2FCB+21H)
LD HL,(IFCB+21H) ;COMPARE STOPPED RECORD NUMBER TO
;CURRENT
XOR A ;CLEAR CARRY
SBC HL,DE
JP NZ,MAIN ;GET NEXT WORD IF NOT THE SAME
LD HL,(OLDPTR) ;GET POSITION IN RECORD
LD DE,P2BUF-INBUF ;OFFSET IT TO COMPARE WITH PASS 2 BUFF
ADD HL,DE
LD DE,(INPTR)
XOR A
SBC HL,DE ;COMPARE STOPPED POS TO CURRENT
JP NZ,MAIN ;GET NEXT WORD IF NOT THE SAME
LD A,0 ;UNMARK PASS 1 INCOMPLETE
LD (STOPED),A
LD HL,(INPTR) ;SAVE PASS 2 INPUT POINTER
LD (P2OPTR),HL
LD HL,(OLDPTR) ;GET OLD POINTER TO BUFFER
LD (INPTR),HL ;RESET BUFFER POINTER
LD HL,IFCB ;PATCH GETCHR: ROUTINE TO READ FROM
;PASS 1 FCB
LD (FILPAT),HL
LD HL,INBUF+512 ;PATCH THE CORRECT BUFFER INTO GETCHR
LD (BUFPAT),HL
LD HL,INBUF
LD (BUFPA0),HL
LD (BUFPA1),HL
INC HL
LD (BUFPA2),HL
LD A,0C9H ;MAKE PUTCHR NOT WORK
LD (PUTCHR),A
LD HL,(SRTBOT) ;RESET MEMORY BUFFER POINTERS
INC HL
INC HL
LD (SRTTOP),HL
LD HL,(BDOS+1) ;GET BDOS VECTOR
DEC HL ;POINT TO FREE MEMORY
LD (FREE),HL ;POINTER TO FREE MEMORY
LD A,(LASTCH) ;SAVE LAST CHARACTER READ
LD B,A
LD A,(OLSTCH) ;PUT OLD LASTCH BACK
LD (LASTCH),A
LD A,B
LD (OLSTCH),A ;SAVE CURRENT LASTCH
JP SRTFIL ;FILL BUFFER AGAIN
;
; DONE -- Write out the rest of the output buffer and then close and rename
; output files.
DONE: LD HL,OBUFF ;WRITE OUT REMAINING DATA IN BUFFER
LD DE,128
DONE1: LD A,EOF ;DONE?
CP (HL)
JR Z,DONE3
PUSH DE
PUSH HL
LD D,H ;SET UP DMA ADDRESS
LD E,L
LD C,SETDMA
CALL BDOS
LD DE,OFCB ;WRITE 128 BYTES TO FILE
LD C,WRITE
CALL BDOS
POP HL
POP DE
CP 0 ;ERROR?
JR NZ,DONE2 ;DISK FULL --> BRANCH
ADD HL,DE ;POINT TO NEXT RECORD
JR DONE1
DONE2: LD C,STROUT ;DISK FULL
LD DE,DSKFUL ;DISK FULL MESSAGE (SEE PUTCHR)
CALL BDOS
CALL BOOT
DONE3: LD DE,OFCB ;CLOSE OUTPUT FILE
LD C,CLOSE
CALL BDOS
LD A,'B'
LD (OFCB+9),A
LD A,'A'
LD (OFCB+10),A
LD A,'K'
LD (OFCB+11),A
LD A,(OFCB) ;REMEMBER OUTPUT DRIVE NAME
PUSH PSW
LD A,(IFCB) ;GET THE DRIVE ON WHICH INPUT WAS DONE
LD (OFCB),A ;TRY TO DELETE .BAK ON IT ONLY
LD DE,OFCB
LD C,DELETE
CALL BDOS
LD DE,IFCB+16 ;RENAME SOURCE TO SOURCE.BAK
LD HL,OFCB
LD BC,12 ;COPY FILENAME FOR RENAME
LDIR
LD DE,IFCB
LD C,RENAME
CALL BDOS
LD HL,IFCB+9 ;RENAME .$$$ TO SOURCE
LD DE,IFCB+25
LD BC,3
LDIR
LD A,'$'
LD (IFCB+9),A
LD (IFCB+10),A
LD (IFCB+11),A
POP PSW ;RESTORE OUTPUT DRIVE NAME
LD (IFCB),A ;AND PUT IT IN FCB
LD DE,IFCB
LD C,RENAME
CALL BDOS
;NOW TAKE CARE OF USER DICTIONARIES
LD DE,FILADD ;DELETE FILE.ADD
LD C,DELETE
CALL BDOS
LD DE,FILDIC ;DELETE FILE.UDC
LD C,DELETE
CALL BDOS
LD A,(MLTPAS) ;WAS MORE THAN 1 PASS REQUIRED?
CP 0
JR Z,DONE30 ;NO MESSAGE IF NOT
LD DE,NOTOUT ;OUTPUT: NO TOTALS PRINTED
LD C,STROUT
CALL BDOS
JR DONE31
DONE30: LD DE,UNQMSG ;OUTPUT: # OF UNIQUE WORDS
LD C,STROUT
CALL BDOS
LD HL,(UNQWRD) ;PRINT # OF WORDS
CALL DECOUT
LD DE,MISMSG ;OUTPUT: # OF "MISSPELLED" WORDS
LD C,STROUT
CALL BDOS
LD HL,(MISWRD) ;PRINT #
CALL DECOUT
DONE31: LD HL,(FILUDC) ;DELETE FILE.D$$ IF IT HAS NO WORDS
LD A,0
CP H
JR NZ,DONE4 ;RENAME IT TO FILE.UDC IF >0 WORDS
CP L
JR NZ,DONE4
LD DE,FILD$$ ;DELETE IT
LD C,DELETE
CALL BDOS
CALL BOOT
DONE4: LD HL,FILDIC ;RENAME FILE.D$$ TO FILE.UDC
LD DE,FILD$$+16
LD BC,12
LDIR
LD DE,FILD$$
LD C,RENAME
CALL BDOS
CALL BOOT
NOTOUT: DB CR,LF,'No subtotals available due to size of document.$'
UNQMSG: DB CR,LF,' Number of unique words: $'
MISMSG: DB CR,LF,' Unique "misspelled" words: $'
;
; GETWRD -- Read a word from an input file. Words are any series of
; alphabetic characters (with or without parity set) and
; apostrophes.
;
; Returns: C <-- number of characters in word
; Z flag set if EOF
GETWRD: CALL GETCHR ;GET A CHARACTER
CP EOF ;END OF FILE?
RET Z
CALL LEGAL ;TEST IF LEGAL
LD (LASTCH),A ;SAVE LAST CHARACTER
JR Z,KILPER ;EXIT LOOP IF LEGAL
CALL PUTCHR ;SEND CHARACTER DIRECTLY OUT
JR GETWRD ;LOOP UNTIL LEGAL
KILPER: LD C,A
AND 07FH ;MASK PARITY
CP '.' ;IS IT A DOT COMMAND?
LD A,C
JR NZ,GETWR0 ;GET WORD IF NOT
CALL PUTCHR
KILPE0: CALL GETCHR ;ERADICATE THIS LINE
CP EOF ;END OF FILE?
RET Z
CALL PUTCHR ;OUTPUT IT
LD (LASTCH),A
AND 07FH ;MASK PARITY
CP LF ;IS IT A LINE TERMINATOR?
JR NZ,KILPE0 ;LOOP THROUGH WHOLE LINE
JR GETWRD ;GET A TEXT WORD NOW
GETWR0: LD C,1 ;ZERO CHARACTER COUNTER
LD HL,SRCWRD+1 ;INITIALIZE POINTER TO SRCWRD:
LD (SRCWRD),A ;STORE FIRST CHARACTER
GETWR1: CALL GETCHR
HYPTST: CP 1FH ;SOFT HYPHEN?
JP Z,ISHYP
CP 1EH ;UNUSED SOFT HYPHEN?
JP NZ,NOTHYP
ISHYP: LD (LASTCH),A ;UPDATE LAST CHARACTER POINTER
LD (HL),A ;STORE HYPHEN
INC HL
INC C
CALL GETCHR
ISHYP0: CP CR OR 80H ;SOFT RETURN AFTER HYPHEN?
JR NZ,HYPTST ;TEST FOR ANOTHER HYPHEN
LD (LASTCH),A ;UPDATE LAST CHARACTER POINTER
LD (HL),A ;STORE RETURN
INC HL
INC C
CALL GETCHR
LD B,A ;SAVE THE CHAR
AND 07FH ;KILL PARITY BIT
CP LF ;SOFT LINE FEED?
LD A,B ;RESTORE CHAR
JR NZ,HYPTST ;TEST FOR ANOTHER HYPHEN
LD (LASTCH),A ;UPDATE LAST CHARACTER POINTER
LD (HL),A ;STORE LF
INC HL
INC C
CALL GETCHR
ISHYP1: CP ' ' OR 80H ;ELIMINATE SOFT SPACES AT START OF LINE
JR NZ,ISHYP0 ;TEST FOR REST OF WORD
LD (LASTCH),A ;UPDATE LAST CHARACTER POINTER
LD (HL),A ;STORE SPACE
INC HL
INC C
CALL GETCHR
JR ISHYP1 ;TEST FOR MORE SPACES
NOTHYP: CALL LEGAL
JR NZ,GETWR2 ;EXIT LOOP WHEN NOT WORD CHARACTER
LD (LASTCH),A
LD (HL),A
INC HL
INC C
JR GETWR1
GETWR2: LD (HL),0 ;MARK END OF WORD
LD HL,(INPTR) ;DECREMENT POINT FOR INPUT BUFFER
;SO FIRST CHAR AFTER WORD IS KEPT
DEC HL
LD (INPTR),HL
RET
;
; LEGAL -- Determines whether character is alphabetic, an apostrophe,
; or period for a dot command.
;
; Returns: Z flag set <-- characters was one of the above
; Z flag clear <-- was not one of the above
LEGAL: LD B,A
AND 05FH ;KILL PARITY AND LOWER CASE
CP 'A' ;MUST BE GREATER THAN "A"
JP C,LEGAL0
CP 'Z'+1 ;GREATER THAN "Z"
JP NC,LEGAL0
LD A,B
CP A ;SET ZERO FLAG
RET
LEGAL0: LD A,B
AND 07FH ;KILL ONLY PARITY
CP 27H ;"'"
JR Z,LEGAL1
LD A,(LASTCH) ;WAS LAST CHARACTER A LF?
AND 7FH ;MASK PARITY
CP LF
JR NZ,LEGAL1
LD A,B
CP '.' ;ACCEPT PERIODS SO DOT COMMAND ACCEPTED
LEGAL1: LD A,B
RET
;
; GETCHR -- Read a character from an input file into a buffer. This routine
; is used for a number of different purposes. The file from which
; to read is patched into FILPAT. The buffer to which writes should
; occur is patched into BUFPAT-BUFPA2.
;
; Returns: A <-- next character from input file
GETCHR: PUSH BC
PUSH DE
PUSH HL
LD HL,(INPTR) ;POINTER FOR INPUT
LD DE,INBUF+512 ;END OF INPUT BUFFER
BUFPAT EQU $-2 ;PATCH FOR INPUT BUFFER
XOR A ;CLEAR CARRY
PUSH HL
SBC HL,DE ;AT END OF BUFFER?
POP HL
JP Z,GETCH0 ;REFILL BUFFER
LD A,(HL) ;GET THE CHARACTER
INC HL ;INCREMENTED POINTER
LD (INPTR),HL
GETRET: POP HL
POP DE
POP BC
RET
GETCH0: LD HL,INBUF
BUFPA0 EQU $-2 ;PATCH FOR INPUT BUFFER
LD DE,128
LD C,4
GETCH2: PUSH BC
PUSH DE
PUSH HL
LD A,EOF ;MARK WITH EOF IN CASE NO RECORD
LD (HL),A
LD D,H ;SET UP DMA ADDRESS FOR READ
LD E,L
LD C,SETDMA
CALL BDOS ;SET DMA ADDRESS
LD DE,IFCB
FILPAT EQU $-2 ;FCB PATCH ADDRESS
LD C,READ ;READ 128 BYTES OF INPUT FILE
CALL BDOS
POP HL
POP DE
POP BC
CP 0 ;SUCCESS?
JR NZ,GETCH3 ;JUMP IF EOF
ADD HL,DE ;POINT TO NEXT RECORD ADDRESS
DEC C
JP NZ,GETCH2 ;LOOP FOR 4 RECORDS
GETCH3: LD A,(INBUF) ;GET FIRST CHARACTER
BUFPA1 EQU $-2 ;ANOTHER BUFFER PATCH
LD (HL),EOF ;PUT EOF AT BEGINNING OF FIRST UNUSED
;RECORD IN MEMORY
LD HL,INBUF+1
BUFPA2 EQU $-2 ;AND YET ANOTHER BUFFER PATCH
LD (INPTR),HL ;SET UP POINTER TO RECORDS
JP GETRET
;
; CREWRD -- Converts the word in SRCWRD to a word in WORD. Parity bits
; are cleared, lower case is converted to upper case, and apostrophes
; that are not imbedded in words are discarded.
CREWRD: LD HL,WORD ;POINTER TO DESINATION BUFFER
LD DE,SRCWRD ;SOURCE BUFFER
LD C,0
CREWR0: LD A,(DE) ;GET A CHARACTER
CP 0 ;END OF WORD?
JP Z,CREWR2
AND 07FH ;MASK PARITY
CP 027H ;"'" CHARACTER?
JR Z,CREWR3
AND 05FH ;MASK PARITY AND CONVERT TO UPPER CASE
CP 'A' ;IS IT ONLY A HYPHEN?
JR C,CREWR1 ;IF SO, SKIP IT
CREADD: INC C
LD (HL),A ;PUT CHARACTER IN WORD BUFFER
LD A,41 ;MAXIMUM OF 40 CHARACTERS IN WORD
CP C
RET Z ;WORD TOO LONG. ACCEPT IT
INC HL
CREWR1: INC DE
JP CREWR0
CREWR2: LD (HL),0 ;MARK END OF WORD
LD HL,LASTC ;PUT NUMBER OF CHARACTERS IN LASTC
DEC C ;C=NUMBER OF CHARACTERS - 1
JP P,CREWS2 ;JUMP IF C WASN'T ALREADY 0
INC C
CREWS2: LD (HL),C
RET
CREWR3: LD A,0 ;IF FIRST CHARACTER IN WORD, IGNORE
;"'"
CP C
JR Z,CREWR1
INC DE
LD A,(DE) ;IF LAST CHAR IN WORD, IGNORE
CP 0
DEC DE
JR Z,CREWR1
LD A,27H ;OTHERWISE, KEEP "'"
JR CREADD
;
; PUTCHR -- write a character to output file
;
; Input: A --> character to output
PUTCHR: PUSH PSW
PUSH BC
PUSH DE
PUSH HL
LD HL,(OPOSS) ;GET CURRENT POSITION IN OBUFF
LD (HL),A ;PUT CHARACTER IN BUFFER
INC HL
LD (OPOSS),HL ;UPDATE POINTER
LD DE,OBUFF+512 ;AT END OF BUFFER?
XOR A ;CLEAR CARRY
SBC HL,DE
JR Z,PUTCH0 ;WRITE OUT DATA IF END OF BUFFER
PUTRET: POP HL
POP DE
POP BC
POP PSW
RET
PUTCH0: LD C,4 ;LOOP COUNTER
LD HL,OBUFF ;ADDRESS OF DATA
LD DE,128 ;LENGTH OF EACH RECORD
PUTCH1: PUSH BC
PUSH DE
PUSH HL
LD D,H ;SET UP DMA ADDRESS
LD E,L
LD C,SETDMA
CALL BDOS
LD DE,OFCB ;WRITE RECORD TO OUTPUT FILE
OUTPAT EQU $-2 ;PATCH ADDRESS FOR OUTPUT FILE FCB
LD C,WRITE
CALL BDOS
CP 0 ;SUCCESS?
JR NZ,PUTCH2 ;JUMP IF DISK FULL
POP HL
POP DE
POP BC
ADD HL,DE ;POINT TO NEXT RECORD
DEC C
JP NZ,PUTCH1 ;LOOP FOR 512 BYTE BUFFER
LD HL,OBUFF ;RESET POINTER
LD (OPOSS),HL
CALL CTRLZ ;FILL BUFFER WITH EOF CHARACTER
JP PUTRET ;RETURN
PUTCH2: LD C,STROUT ;DISK FULL ERROR
LD DE,DSKFUL
CALL BDOS
CALL BOOT ;GIVE UP
DSKFUL: DB CR,LF,'Disk full -- aborting',CR,LF,'$'
;
; CTRLZ -- Fill the output buffer with EOF characters to prepare it for
; writing
CTRLZ: LD HL,OBUFF ;BUFFER ADDRESS
LD B,2 ;LOOP 256 BYTES 2 TIMES
LD C,0
CTRLZ0: LD (HL),EOF ;PUT EOF IN BUFFER
INC HL
DEC C ;FAST COUNTER
JR NZ,CTRLZ0
DEC B ;SLOW COUNTER
JR NZ,CTRLZ0
RET
;
; MEMWRD -- put word in WORD into memory. If word already exists in memory
; then its the address of its status byte is returned in DE. If the
; word is not found, the word is placed in memory and the pointers
; that alphabetize the words are updated. If memory is full, a
; 1 is returned in A. Otherwise 0 is returned in A.
MEMWRD: LD BC,(SRTBOT) ;GET ADDRESS OF BOTTOM OF WORD POINTER
LD DE,(SRTTOP) ;TOP OF WORD POINTER
JR MEMSKP
MEMWR0: POP HL
POP DE
POP BC
JP M,MEMW00
LD D,H ;MOVE HIGH POINTER DOWN
LD E,L
JR MEMSKP
MEMW00: LD B,H ;MOVE LOW POINTER UP
LD C,L
MEMSKP: LD H,D ;HL IS POINTER TO RECORD BETWEEN DE AND
LD L,E ;BC
XOR A ;CLEAR CARRY
SBC HL,BC
LD A,0 ;IS BC+1=HL?
CP H
JP NZ,MEMWR1
LD A,2
CP L
JP NZ,MEMWR1
LD HL,(SRTTOP) ;UPDATE SRTTOP=SRTTOP+2
INC HL
INC HL
LD (SRTTOP),HL
XOR A ;CLEAR CARRY
PUSH HL
SBC HL,DE ;NUMBER OF BYTES IN TABLE TO MOVE
LD B,H ;PUT NUMBER OF COUNTER FOR LDDR
LD C,L
POP HL
PUSH DE ;SAVE THE ADDRESS FOR NEW WORD
LD D,H ;PUT DESTINATION ADDRESS IN DE
LD E,L
DEC HL ;SOURCE IN HL
DEC HL
LDDR ;PUT A SPACE FOR NEW POINTER IN TABLE
POP DE
LD HL,(FREE) ;ADDRESS TO STORE NEW WORD
EX DE,HL
LD (HL),E ;STORE ADDRESS OF WORD IN TABLE
INC HL
LD (HL),D
LD HL,WORD
LD A,(LASTC) ;GET NUMBER OF CHARACTERS
LD B,A
INC B ;B = NUMBER OF CHARACTERS IN WORD + 1
INC B
MEMW01: LD A,(HL) ;GET A CHARACTER
LD (DE),A ;PUT IT IN MEMORY
DEC DE ;STORE WORD BACKWARDS
INC HL ;NEXT CHAR IN WORD
DJNZ MEMW01
LD (FREE),DE ;UPDATE FREE MEMORY POINTER
EX DE,HL
LD DE,45 ;ALLOW ROOM FOR ANOTHER WORD
XOR A ;CLEAR CARRY
SBC HL,DE
LD DE,(SRTTOP) ;POINTER TO TOP OF POINTER TABLE
XOR A
SBC HL,DE ;IS ANY MEMORY LEFT?
JR C,MEMFUL ;IF NOT, NO MORE MEMORY
LD A,0 ;INDICATE MEMORY LEFT
RET
MEMFUL: LD A,1 ;NO MEMORY LEFT
RET
MEMWR1: RRC L ;DIVIDE HL BY 2
LD A,L
AND 07EH ;MAKE IT EVEN
LD L,A
LD A,0
RRC H
ADC A,0 ;IF BIT 0 OF H SET, THEN C SET
RRC A
OR L
LD L,A
LD A,H
AND 07FH
LD H,A
ADD HL,BC ;HL POINTS TO RECORD BETWEEN BC AND
;DE
PUSH BC
PUSH DE
PUSH HL
LD E,(HL) ;DE= ADDRESS OF COMPARISON WORD IN BUFF
INC HL
LD D,(HL)
LD HL,WORD ;HL= ADDRESS OF NEW WORD BUFFER
MEMWR2: LD A,(DE) ;GET A CHARACTER
AND 07FH ;KILL CARRY (FOR 0 BYTE OF CORRECTED
;WORDS)
CP (HL) ;COMPARE THEM
JP NZ,MEMWR0 ;IF .NE. THEN TRY ANOTHER WORD
LD A,0
CP (HL) ;END OF WORD?
DEC DE ;DECREMENT WORD TABLE POINTER (WORDS
;ARE STORED BACKWARDS
INC HL ;INCREMENT POINTER (DON'T AFFECT
;FLAGS)
JR NZ,MEMWR2
POP HL
POP BC ;TRASH OLD DE
POP BC
INC DE ;POINT TO 0 OR 080H AT END OF WORD
; (80H IS CORRECTED AND WORD FOUND)
RET ;DON'T BOTHER TO BUFFER THE SAME WORD
;
; AUXDIC -- Open SPELL.DIC and load it into memory. Open FILE.UDC and load
; it as well. If FILE.ADD exists (wordstar dictionary addition file),
; load it and put its contents in FILE.UDC.
AUXDIC: LD HL,NEXT ;ZERO THE MEMORY DICTIONARY
LD A,0
LD (HL),A
AUXDI2: LD DE,FILD$$ ;MAKE TEMPORARY .DIC OUTPUT FILE
LD C,DELETE
CALL BDOS
LD DE,FILD$$
LD C,MAKE
CALL BDOS
LD HL,FILD$$ ;PATCH THIS FCB INTO OUTPUT ROUTINE
LD (OUTPAT),HL
LD A,(SAVPUT) ;MAKE PUTCHR: WRITE TO A FILE
LD (PUTCHR),A
LD DE,FILDIC ;TRY TO OPEN FILE.UDC
LD C,OPEN
CALL BDOS
CP 0FFH ;FOUND?
JP Z,AUXDI4 ;JUMP IF NOT
LD HL,FILDIC ;PATCH GETCHR: TO READ FROM THIS FILE
LD (FILPAT),HL
LD HL,INBUF+512 ;PATCH INPTR TO READ ON FIRST CALL
LD (INPTR),HL
AUXDI3: CALL GETWRD ;GET A WORD INTO SRCWRD:
JP Z,AUXDI4 ;IF EOF THEN START CHECKING
CALL CREWRD ;CREATE WORD FROM SRCWRD:
LD A,0 ;IS IT AT LEAST 2 CHARS LONG?
CP C
JR Z,AUXDI3 ;FORGET IT IF NOT
LD A,41 ;IF LONGER THAN 40, ALSO FORGET IT
CP C
JR Z,AUXDI3
PUSH HL
LD HL,(FILUDC) ;INCREMENT COUNTER FOR WORDS OUTPUT
INC HL ; TO FILE.UDC
LD (FILUDC),HL
POP HL
CALL SAVWRD ;PUT WORD IN MEMORY
LD HL,WORD ;PUT WORD IN OUTPUT FILE
AUXDH3: LD A,(HL) ;GET A CHARACTER
CP 0 ;END OF WORD?
JR Z,AUXDI3 ;QUIT OUTPUT IF END
CALL PUTCHR ;OUTPUT CHARACTER
INC HL
JR AUXDH3
AUXDI4: LD DE,FILADD ;TRY TO OPEN FILE.ADD
LD C,OPEN
CALL BDOS
CP 0FFH ;FOUND?
JP Z,AUXDI7 ;RETURN IF NOT
LD HL,FILADD ;PATCH GETCHR: TO READ FROM FILE.ADD
LD (FILPAT),HL
LD HL,INBUF+512 ;SET INPTR TO READ ON FIRST CALL
LD (INPTR),HL
LD A,CR ;PUT CR LF IN OUTPUT FILE
CALL PUTCHR
LD A,LF
CALL PUTCHR
AUXDI5: CALL GETCHR ;GET A CHARACTER FROM FILE.ADD
;IGNORE ALL WORD TYPES EXCEPT 0FFH
;(EOF)
CP 0FFH ;END OF WORDS IN CURRENT RECORD?
JR Z,AUXDI5 ;IF YES, LOOP THROUGH RECORD
CP 0 ;ZERO USED AS FILLER CHARACTER?
JP Z,AUXDI5
CP EOF ;EOF?
JP Z,AUXDI7
CALL GETCHR ;THIS CHARACTER IS LENGTH OF WORD
CP 0 ;NO CHARACTERS?
JR Z,AUXDI5
LD B,A
LD HL,WORD ;BUFFER IT IN WORD
AUXDI6: CALL GETCHR ;GET A CHARACTER
LD (HL),A ;SAVE IT IN WORD:
INC HL
DJNZ AUXDI6 ;GET WHOLE WORD
LD A,0 ;MARK END OF WORD
LD (HL),A
CALL SAVWRD ;PUT WORD IN MEMORY
JP Z,AUXDI5 ;IF WORD ALREADY IN MEMORY THEN DON'T
;OUTPUT TO FILE.UDC
PUSH HL
LD HL,(FILUDC) ;INCREMENT COUNTER FOR WORDS OUTPUT
INC HL ; TO FILE.UDC
LD (FILUDC),HL
POP HL
LD HL,WORD ;OUTPUT WORD
AUXDH6: LD A,(HL) ;GET A CHARACTER
INC HL
CP 0 ;END?
JR Z,AUXDK6
CALL PUTCHR
JR AUXDH6
AUXDK6: LD A,CR ;PUT CR LF IN OUTPUT FILE
CALL PUTCHR
LD A,LF
CALL PUTCHR
JP AUXDI5 ;GET ANOTHER CHARACTER
AUXDI7: LD HL,OBUFF ;WRITE OUT REMAINING DATA IN BUFFER
LD DE,128
AUXDI8: LD A,EOF ;DONE?
CP (HL)
JR Z,AUXDJ0
PUSH DE
PUSH HL
LD D,H ;SET UP DMA ADDRESS
LD E,L
LD C,SETDMA
CALL BDOS
LD DE,FILD$$ ;WRITE 128 BYTES TO FILE
LD C,WRITE
CALL BDOS
POP HL
POP DE
CP 0 ;ERROR?
JR NZ,AUXDI9 ;DISK FULL --> BRANCH
ADD HL,DE ;POINT TO NEXT RECORD
JR AUXDI8
AUXDI9: LD C,STROUT ;DISK FULL
LD DE,DSKFUL ;DISK FULL MESSAGE (SEE PUTCHR)
CALL BDOS
CALL BOOT
AUXDJ0: CALL RDICT ;READ SPELL.DIC
LD HL,BOOT+81H ;POINT TO INPUT LINE BUFFER
LD A,(BOOT+80H) ;NUMBER OF CHARACTERS
LD B,A
LD A,' ' ;LOOK FOR A SPACE TO INDICATE END OF
;INPUT FILE NAME
AUXDJ1: CP (HL)
JR Z,AUXDJ2 ;EXIT LOOP IF SPACE
INC HL ;NEXT CHARACTER
DJNZ AUXDJ1 ;LOOP WHILE CHARACTERS LEFT
JR AUXRET ;NOTHING AFTER FILE. JUST RETURN.
AUXDJ2: LD A,'$' ;NOW LOOK FOR $ TO INDICATE MORE DICTS.
AUXDJ3: CP (HL)
JR Z,AUXDJ4 ;EXIT LOOP IF FOUND
INC HL
DJNZ AUXDJ3 ;CONTINUE WHILE CHARACTERS LEFT
JR AUXRET ;NOT FOUND
AUXDJ4: LD A,' ' ;ELMINATE SPACES
INC HL ;SKIP '$'
DJNZ AUXDJ5 ;CONTINUE IF CHAR LEFT
JR AUXRET
AUXDJ5: CP (HL)
JR NZ,AUXDJ6 ;STOP IF NOT SPACE
INC HL
DJNZ AUXDJ5
JR AUXRET ;DONE IF JUST SPACES
AUXDJ6: LD A,0
LD (SPLDIC),A ;USE DEFAULT DRIVE
LD DE,SPLDIC+1 ;FCB BUFFER TO USE
LD A,' ' ;NULL FILE NAME (SPACES)
PUSH BC
LD B,8 ;FIRST DO FILENAME, NOT EXTENSION
AUXDJ7: LD (DE),A
INC DE ;CLEAR ENTIRE FILENAME
DJNZ AUXDJ7
LD DE,SPLDIC+12 ;GET REST OF FCB
LD B,24
LD A,0 ;ZERO REST OF FCB
AUXDJ8: LD (DE),A
INC DE
DJNZ AUXDJ8
POP BC
LD DE,SPLDIC+1 ;POINT TO FCB
LD C,0 ;NO CHARACTERS WRITTEN TO FCB YET
LD A,2 ;ARE AT LEAST 3 CHAR LEFT?
CP B
JP NC,AUXDJ9 ;IF NOT, CAN'T BE A DRIVE SPECIFIED
INC HL
LD A,(HL) ;SEE IF NEXT CHAR IS A ':'
DEC HL
CP ':'
JR NZ,AUXDJ9 ;JUMP IF NO DRIVE SPECIFIED
LD A,(HL) ;GET THE DRIVE
INC HL ;POINT TO NEXT CHAR AFTER :
INC HL
DEC B
DEC B
AND 11011111B ;MAKE SURE IT'S UPPER CASE
SUB 'A'-1 ;MAKE DRIVE NUMBER BASED AT 0
LD (SPLDIC),A ;STORE IT IN FCB
AUXDJ9: LD A,(HL) ;GET A CHARACTER
CP ' ' ;END OF FILENAME?
JR Z,AUXDK2 ;READ THE FILE
CP '.' ;PERIOD? DISCARD EXTENSION IF YES.
JR Z,AUXDK0
LD (DE),A ;FCB IT
INC DE
INC C
LD A,8 ;8 CHARACTERS WRITTEN YET?
CP C
JR Z,AUXDK0
INC HL
DJNZ AUXDJ9
JR AUXDK2 ;READ THE FILE
AUXDK0: LD A,' ' ;SEARCH FOR SPACE TO END FILENAME
;DISCARD EXTRA CHARACTERS
AUXDK1: CP (HL)
JR Z,AUXDK2
INC HL ;TRY NEXT CHAR
DJNZ AUXDK1
AUXDK2: DEC HL ;POINT TO CHAR BEFORE END OR SPACE
INC B ;1 MORE LEFT NOW
CALL RDICT ;READ FROM THE DICTIONARY
JP AUXDJ4 ;TRY FOR ANOTHER DICTIONARY
AUXRET: LD DE,FILD$$ ;CLOSE TEMPORARY FILE
LD C,CLOSE
CALL BDOS
LD HL,OFCB ;PATCH PUTCHR: TO OUTPUT TO FILE.$$$
LD (OUTPAT),HL
LD A,0C9H ;RETURN OPCODE
LD (PUTCHR),A ;MAKE PUTCHR: DO NOT OUTPUT
CALL CTRLZ ;CLEAR OUTPUT BUFFER
LD HL,OBUFF ;CLEAR OUTPUT BUFFER POSITION
LD (OPOSS),HL
LD HL,(FREE) ;GET ADDRESS OF NEXT FREE BYTE
INC HL
LD (SRTBOT),HL ;POINT TO TABLE FOR INPUT FILE INDEX
INC HL
INC HL
LD (SRTTOP),HL ;TOP OF TABLE
LD HL,(BDOS+1) ;GET ADDRESS OF BDOS
DEC HL ;POINT TO FREE MEMORY
LD (FREE),HL
LD HL,INBUF+512
LD (INPTR),HL ;SET INPUT FILE POINTER TO READ RECORD
;ON NEXT GETCHR: CALL
CALL CTRLZ ;CLEAN OUTPUT FILE BUFFER
LD A,LF ;RESET LASTCH FOR INPUT
LD (LASTCH),A
LD DE,UDCMSG ;OUTPUT: "Words read from...FILE.UDC"
LD C,STROUT
CALL BDOS
LD HL,(FILUDC) ;NUMBER OF WORDS WRITTEN
LD B,H ;GOES IN BC
LD C,L
LD HL,FILD$$ ;FCB ADDRESS IN HL
LD A,'U' ;CHANGE 'FILENAME.D$$' TO '.UDC'
LD (FILD$$+9),A
LD A,'D'
LD (FILD$$+10),A
LD A,'C'
LD (FILD$$+11),A
CALL TYPFIL ;WRITE FILENAME AND # TO CONSOLE
LD A,'D' ;CHANGE IT BACK TO '.D$$'
LD (FILD$$+9),A
LD A,'$'
LD (FILD$$+10),A
LD (FILD$$+11),A
RET
UDCMSG: DB CR,LF,'Words written to dictionary $'
;
; RDICT - Read dictionary in SPLDIC FCB
;
RDICT: PUSH PSW
PUSH BC
PUSH DE
PUSH HL
LD HL,INBUF+512
LD (INPTR),HL ;SET INPUT FILE POINTER TO READ RECORD
;ON NEXT GETCHR CALL
LD HL,0
LD (SPLDC),HL ;RESET # WORDS READ
LD DE,SPLDIC ;FCB FOR SPELL.DIC
LD C,OPEN
CALL BDOS
PUSH PSW
LD C,CURDSK ;GET THE DEFAULT DRIVE BEFORE ITS
CALL BDOS ; TOO LATE
INC A
LD (DEFDRV),A
POP PSW
CP 0FFH ;FOUND?
JR NZ,RDICT0 ;YES, READ FROM IT
LD A,(SPLDIC) ;WAS DEFAULT DRIVE TESTED?
CP 0
JR NZ,RDICT2 ;IF SPECIFIC DRIVE TESTED AND NOT
;FOUND THEN GIVE UP
LD A,1 ;TRY DRIVE "A"
LD (SPLDIC),A
LD DE,SPLDIC
LD C,OPEN
CALL BDOS
CP 0FFH ;FOUND?
JP Z,RDICT2 ;IF NOT, GIVE UP
RDICT0: LD HL,SPLDIC ;PATCH FCB ADDRESS INTO GETWRD
LD (FILPAT),HL
RDICT1: CALL GETWRD ;GET A WORD INTO SRCWRD:
JP Z,RDICT3 ;IF EOF THEN START CHECKING
CALL CREWRD ;CREATE WORD FROM SRCWRD:
LD A,0 ;IS IT AT LEAST 2 CHARS LONG?
CP C
JR Z,RDICT1 ;FORGET IT IF NOT
LD A,41 ;IF LONGER THAN 40, ALSO FORGET IT
CP C
JR Z,RDICT1
PUSH HL
LD HL,(SPLDC) ;INCREMENT COUNTER FOR WORDS IN
INC HL ; SPELL.DIC
LD (SPLDC),HL
POP HL
CALL SAVWRD ;PUT WORD IN MEMORY
JR RDICT1
RDICT2: LD DE,RNT ;PRINT CR,LF
LD C,STROUT
CALL BDOS
LD A,(SPLDIC) ;DEFAULT DRIVE USED?
CP 0
JR NZ,RDICU0 ;JUMP IF NOT
LD A,(DEFDRV) ;GET DEFAULT DRIVE
LD (SPLDIC),A ;AND PUT IT IN FCB
RDICU0: LD A,(SPLDIC) ;GET DRIVE
ADD A,'A'-1 ;MAKE IT A LETTER
LD C,CONOUT
LD E,A
CALL BDOS ;PRINT DRIVE NAME
LD E,':'
LD C,CONOUT
CALL BDOS ;AND A COLON
LD DE,SPLDIC+1 ;POINT TO FILENAME
LD HL,SPLDIC+12 ;POINT TO FIRST BYTE AFTER NAME
LD A,'$'
LD (SPLDIC+12),A ;MARK END FOR OUPTUT
LD C,STROUT
CALL BDOS ;PRINT THE FILE NAME
LD DE,RNT0 ;PRINT NOT FOUND
LD C,STROUT
CALL BDOS
JR RDICTR
RNT: DB CR,LF,'$'
RNT0: DB ' not found$'
RDICT3: LD DE,RDIWRD ;FOUND MESSAGE
LD C,STROUT
CALL BDOS
LD HL,(SPLDC) ;STORE # WORDS READ IN BC
LD B,H
LD C,L
LD HL,SPLDIC ;FCB ADDRESS
CALL TYPFIL ;TYPE FILE NAME AND # WORDS IN IT
JR RDICTR
RDIWRD: DB CR,LF,'Words read from dictionary $'
RDICTR: POP HL
POP DE
POP BC
POP PSW
RET
;
; TYPFIL -- print name of dictionary file being read and words found in it
;
; Input: BC --> Number of words found in file
; HL --> Address of file's FCB
;
TYPFIL: PUSH PSW
PUSH BC
PUSH DE
PUSH HL
LD A,(HL) ;DEFAULT DRIVE USED?
CP 0
JR NZ,TYPFI0 ;JUMP IF NOT
LD A,(DEFDRV) ;GET DEFAULT DRIVE
LD (HL),A ;AND PUT IT IN FCB
TYPFI0: LD A,(HL) ;GET DRIVE
ADD A,'A'-1 ;MAKE IT A LETTER
PUSH BC
PUSH HL
LD C,CONOUT
LD E,A
CALL BDOS ;PRINT DRIVE NAME
LD E,':'
LD C,CONOUT
CALL BDOS ;AND A COLON
POP HL
LD D,H
LD E,L
INC DE ;POINT TO FILENAME
LD BC,12
ADD HL,BC ;HL POINTS TO END OF FILENAME
LD A,'$'
LD (HL),A ;MARK END FOR OUPTUT
LD C,STROUT
CALL BDOS ;PRINT THE FILE NAME
LD DE,TYPSPA ;PRINT SPACES AFTER NAME
LD C,STROUT
CALL BDOS
POP BC
LD H,B ;NUMBER OF WORDS FOUND
LD L,C
CALL DECOUT ;PRINT NUMBER
POP HL
POP DE
POP BC
POP PSW
RET
TYPSPA: DB ': $'
;
; SAVWRD -- put WORD in memory dictionary
;
SAVWRD: CALL USRTST ;WAS WORD ALREADY MARKED?
CP 1
RET Z ;RETURN WITH Z SET IF ALREADY MARKED
LD HL,(FREE) ;GET NEXT AVAILABLE BYTE
LD DE,WORD ;POINT TO WORD
SAVWR0: LD A,(DE) ;GET A CHARACTER
LD (HL),A ;STORE IT IN BUFFER
INC DE ;INCREMENT POINTERS
INC HL
CP 0 ;END OF WORD?
JR NZ,SAVWR0
LD (HL),A ;MARK END OF TABLE WITH 0
LD (FREE),HL ;UPDATE FREE MEMORY POINTER
LD A,1
AND A ;MAKE FOR Z NOT SET
RET
;
; WRDTST -- Search for WORD in dictionary. If it is not found, try
; stripping of suffixes and looking for new word with a flag set.
;
; Returns in A: 0 <-- WORD not found
; 1 <-- WORD found
; 2 <-- root word found but necessary
; suffix flag not set. Returned
; only if suffixes were stripped.
WRDTST: PUSH BC
PUSH DE
PUSH HL
LD HL,0
LD (FLAG),HL ;NO FLAGS FOR FIRST LOOKUP
CALL FINDIT ;LOOK FOR WORD IN DICTIONARY
PUSH PSW
LD A,1 ;ASSUME THAT WORD WAS FOUND AND
;INDICATE WORKING IN ALPHABETICAL ORDER
;STILL (DECODING OF CURRENT RECORD
;BY LOOKUP ROUTINE CAN CONTINUE FROM
;CURRENT POSITION)
LD (ALPHA),A
POP PSW
CP 0 ;NOT FOUND?
JP Z,WRDTS0 ;KEEP TRYING IF NOT FOUND
WRDRET: POP HL
POP DE
POP BC
RET
WRDTS0: CALL USRTST ;TEST USER DICTIONARY
CP 1 ;FOUND?
JP Z,WRDRET
LD A,0 ;NOT WORKING IN ALPHABETICAL ORDER
;ANY MORE
LD (ALPHA),A
LD HL,LASTC ;PUT NUMBER OF CHARACTERS -1 IN C
LD C,(HL)
LD B,0 ;MAKE BC AN OFFSET TO LAST CHAR
LD HL,WORD ;POINT TO WORD
ADD HL,BC ;POINT TO LAST CHARACTER IN WORD
LD A,(HL)
QISIT 'E',FLGV ;WORD ENDS IN "E"
QISIT 'H',FLGH ;"H"
QISIT 'Y',FLGY ;"LY"
QISIT 'G',FLGG ;"ING"
QISIT 'N',FLGN ;"TION, "EN"
QISIT 'D',FLGD ;"ED", "IED"
QISIT 'T',FLGT ;"EST", "IEST"
QISIT 'R',FLGR ;"ER", "IER"
QISIT 'S',FLGS ;LOTS OF WORDS ENDING IN "S"
LD A,0
JP WRDRET ;NO FLAGS FIT
FLGV: LD A,2 ;WORD MUST BE 4 CHARS LONG
CP C
JP P,WRDNOT ;NOT FOUND IF TOO SHORT
LD DE,VFLAG ;LOOKING FOR V FLAG
LD (FLAG),DE
DEC HL ;CHARACTER BEFORE
ISIT 'V'
DEC HL
ISIT 'I'
LD (HL),'E' ;GET "CREATIVE"
INC HL
LD (HL),0
CALL FINDIT
CP 0 ;FOUND?
JP NZ,WRDRET ;BRANCH IF FOUND
DEC HL ;POINT TO CHAR BEFORE NEW "E"
DEC HL
JISIT 'E',WRDNOT ;KILL "CREATEIVE"
INC HL
LD (HL),0
CALL FINDIT
JP WRDRET
FLGH: LD A,2 ;MUST BE 4 CHARS LONG
CP C
JP P,WRDNOT
LD DE,HFLAG ;SEEKING WORD WITH H FLAG SET
LD (FLAG),DE
DEC HL
ISIT 'T'
DEC HL
JISIT 'Y',WRDNOT ;KILL "TWENTYTH"
INC HL
LD (HL),0 ;NEW END OF WORD
CALL FINDIT ;GET "HUNDREDTH"
CP 0 ;FOUND?
JP NZ,WRDRET ;RETURN IS FOUND
LD A,4 ;WORDS WITH "IETH" MUST BE 6 CHARS LONG
CP C
JP P,WRDNOT
DEC HL
ISIT 'E'
DEC HL
ISIT 'I'
LD (HL),'Y' ;MODIFY WORD TO END IN "Y"
INC HL
LD (HL),0
CALL FINDIT ;GET "TWENTIETH"
JP WRDRET
FLGY: LD A,2 ;MUST BE 4 CHARACTERS LONG (AT LEAST)
CP C
JP P,WRDNOT
LD DE,YFLAG ;WORDS MUST HAVE Y FLAG SET
LD (FLAG),DE
DEC HL
ISIT 'L'
LD (HL),0 ;MARK NEW END OF WORD
CALL FINDIT ;GET "QUICKLY"
JP WRDRET
FLGG: LD A,2 ;MUST BE AT LEAST 4 CHARS LONG
CP C
JP P,WRDNOT
LD DE,GFLAG ;SET G FLAG
LD (FLAG),DE
FLGGE: DEC HL
ISIT 'N'
DEC HL
ISIT 'I'
LD (HL),'E'
INC HL
LD (HL),0
CALL FINDIT ;GET "FILING"
CP 0
JP NZ,WRDRET ;RETURN IF FOUND
DEC HL
DEC HL
JISIT 'E',WRDNOT ;KILL "FILEING"
INC HL
LD (HL),0
CALL FINDIT ;GET "CROSSING"
JP WRDRET
FLGN: LD A,2 ;MUST BE 4 CHARS LONG
CP C
JP P,WRDNOT
LD DE,NFLAG ;SET N FLAG
LD (FLAG),DE
FLGNEE: DEC HL
JISIT 'O',FLGNO ;WORD WITH "TION"
ISIT 'E' ;IF .EQ. "TION" THEN .EQ. "EN"
DEC HL
JISIT 'E',WRDNOT ;KILL "CREATEEN"
JISIT 'Y',WRDNOT ;KILL "MULTIPLYEN"
INC HL
LD (HL),0
CALL FINDIT ;GET "FALLEN"
JP WRDRET
FLGNO: LD A,3 ;MUST BE 4 CHARS LONG
CP C
JP P,WRDNOT
DEC HL
ISIT 'I'
LD (HL),'E'
INC HL
LD (HL),0
CALL FINDIT ;GET "CREATION"
CP 0
JP NZ,WRDRET ;RETURN IF FOUND
LD A,7 ;MUST BE AT LEAST 9 CHARS LONG
CP C
JP P,WRDNOT
DEC HL
DEC HL
ISIT 'T'
DEC HL
ISIT 'A'
DEC HL
ISIT 'C'
DEC HL
ISIT 'I'
LD (HL),'Y'
INC HL
LD (HL),0
CALL FINDIT
JP WRDRET
FLGD: LD A,2 ;MUST BE 4 CHARACTERS LONG
CP C
JP P,WRDRET
LD DE,DFLAG ;SET D FLAG
LD (FLAG),DE
FLGDE: DEC HL
ISIT 'E'
INC HL
LD (HL),0
CALL FINDIT ;GET "CREATED"
CP 0
JP NZ,WRDRET
DEC HL
DEC HL
JISIT 'E',WRDNOT ;KILL "CREATEED"
LD A,(HL)
CP 'Y' ;IF .NE. "Y" TRY OTHER SUFFIXES
JP NZ,FLGD5
DEC HL
CALL VOWEL ;VOWEL MUST BE BEFORE "Y"
JP NZ,WRDNOT
INC HL
INC HL
LD (HL),0
CALL FINDIT ;GET "CONVEYED"
JP WRDRET
FLGD5: INC HL
LD (HL),0
CALL FINDIT ;GET "CROSSED"
CP 0
JP NZ,WRDRET ;RETURN IF FOUND
DEC HL
ISIT 'I'
DEC HL
CALL VOWEL ;CAN'T BE A VOWEL
JP Z,WRDNOT
INC HL
LD (HL),'Y'
INC HL
LD (HL),0
CALL FINDIT ;GET "IMPLIED"
JP WRDRET
FLGT: LD A,2 ;MUST BE AT LEAST 4 CHARS LONG
CP C
JP P,WRDNOT
LD DE,TFLAG ;T FLAG MUST BE SET
LD (FLAG),DE
DEC HL
JISIT 'S',FLGDE ;SAME RULES AS 'D' FLAG IF ENDS
;IN "ST"
JP WRDNOT ;NOT FOUND IF NOT "ST"
FLGR: LD A,2 ;MUST BE AT LEAST 4 CHARS LONG
CP C
JP P,WRDNOT
LD DE,RFLAG ;SET R FLAG
LD (FLAG),DE
FLGRE: JP FLGDE ;SAME RULES AS D FLAG
FLGS: LD A,2 ;MUST BE 4 CHARS LONG
CP C
JP P,WRDNOT
LD DE,SFLAG ;TRY PURE "S" FLAG FIRST
LD (FLAG),DE
DEC HL
JISIT 'S',FLGP ;"NESS", "INESS"
JISIT 27H,FLGM ;"'S"
CALL SXZH ;IS IT S, X, Z OR H?
JP Z,WRDNOT ;IF YES, THEN ILLEGAL WORD
LD A,(HL) ;PURE "S" LEGAL IF .NE. "Y"
CP 'Y'
JR NZ,FLGS2
DEC HL
CALL VOWEL
JP NZ,WRDNOT ;ILLEGAL WORD IF NOT A VOWEL
INC HL
FLGS2: INC HL
LD (HL),0
CALL FINDIT ;GET "CONVEYS", "BATS"
CP 0
JP NZ,WRDRET ;RETURN IF FOUND
DEC HL
LD A,(HL)
QISIT 'R',FLGZ ;"ERS", "IERS"
QISIT 'N',FLGX ;"IONS", "ICATIONS", "ENS"
QISIT 'G',FLGJ ;"INGS"
ISIT 'E'
DEC HL
CALL SXZH
JP NZ,FLGS5 ;IF LETTER NOT S,X,Z OR H TRY "IES"
INC HL
LD (HL),0
CALL FINDIT ;GET "FIXES"
JP WRDRET
FLGS5: ISIT 'I'
DEC HL
CALL VOWEL
JP Z,WRDNOT ;CAN'T BE A VOWEL
INC HL
LD (HL),'Y'
INC HL
LD (HL),0
CALL FINDIT ;GET "IMPLIES"
JP WRDRET
FLGX: LD DE,XFLAG ;SET X FLAG
LD (FLAG),DE
JP FLGNEE ;SAVE AS "N" FLAG NOW
FLGJ: LD DE,JFLAG ;SET J FLAG
LD (FLAG),DE
JP FLGGE ;SAVE AS "G" FLAG NOW
FLGZ: LD DE,ZFLAG ;SET Z FLAG
LD (FLAG),DE
JP FLGRE ;SAVE AS "R" FLAG NOW
FLGP: LD A,3 ;MUST BE 5 CHARS LONG
CP C
JP P,WRDNOT
LD DE,PFLAG ;SET P FLAG
LD (FLAG),DE
DEC HL
ISIT 'E'
DEC HL
ISIT 'N'
DEC HL
LD A,(HL)
CP 'Y'
JP NZ,FLGP4 ;LEGAL IF .NE. "Y"
DEC HL
CALL VOWEL
JP NZ,WRDNOT ;ILLEGAL IF "Y" AND NO VOWEL
INC HL
FLGP4: INC HL
LD (HL),0
CALL FINDIT ;GET "LATENESS", "GRAYNESS"
CP 0
JP NZ,WRDRET ;RETURN IF FOUND
DEC HL
ISIT 'I'
DEC HL
CALL VOWEL
JP Z,WRDNOT ;CAN'T BE A VOWEL
INC HL
LD (HL),'Y'
INC HL
LD (HL),0
CALL FINDIT ;GET "CLOUDINESS"
JP WRDRET
FLGM: LD DE,MFLAG ;SET M FLAG
LD (FLAG),DE
LD (HL),0
CALL FINDIT ;GET "DOG'S"
JP WRDRET
WRDNOT: LD A,0 ;NOT FOUND
JP WRDRET
;
; VOWEL -- determine whether character in (HL) is a vowel.
;
; Returns: Z flag SET <-- is a vowel
; Z flag CLEAR <-- is not a vowel
VOWEL: PUSH BC
PUSH HL
LD A,(HL) ;GET CHARACTER
LD HL,VOWELS ;POINTER TO VOWELS
LD BC,5 ;5 VOWELS
CPIR ;TEST AGAINST ALL VOWELS
;SET STATUS BITS TO BE USED AFTER RET
POP HL
POP BC
RET
VOWELS: DB 'AEIOU'
;
; SXZH -- same as VOWEL put for the characters S, X, Z and H
SXZH: PUSH BC
PUSH HL
LD A,(HL) ;GET CHARACTER
LD HL,SXZH0 ;POINTER TO CHARACTER LIST
LD BC,4 ;4 POTENTIAL MATCHES
CPIR ;TEST AGAINST S, X, Z, H
POP HL
POP BC
RET
SXZH0: DB 'SXZH'
;
; Determine which record of dictionary would contain WORD. Puts record in
; BC
FINDIT: PUSH BC
PUSH DE
PUSH HL
LD A,(ALPHA) ;STILL IN ALPHABETICAL ORDER?
AND A
JP NZ,FINLO5 ;DON'T DECREMENT POINTERS IF YES
LD HL,(LSTADR) ;GET ADDRESS OF LAST DICTIONARY POINTER
;USED
FINLOW: LD DE,WORD
EX DE,HL
LD BC,4
FINLO1: LD A,(DE) ;MAKE SURE THAT CURRENT RECORD IS
;BEFORE WORD
INC DE
CPI
JP M,FINLO5 ;IF EARLIER, JUMP
JR NZ,FINLO2 ;IF LATER, THEN DECREMENT
JP PE,FINLO1 ;LOOP WHILE BC-1 .NE. 0
FINLO2: LD BC,(LSTREC) ;DECREMENT THE POINTERS
DEC BC
LD HL,0 ;IS IT TOO LOW?
XOR A
SBC HL,BC
JR Z,FINLO5 ;IF SO, THEN FORGET IT
LD (LSTREC),BC
LD HL,(LSTADR)
LD DE,4
XOR A ;CLEAR CARRY
SBC HL,DE
LD (LSTADR),HL
JP FINLOW ;TRY AGAIN
FINLO5: LD BC,(LSTREC) ;GET LAST RECORD NUMBER READ
LD HL,(LSTADR) ;GET ADDRESS OF LAST DICTIONARY POINTER
;USED
FINDI0: PUSH BC
PUSH HL
LD DE,WORD
EX DE,HL
LD BC,4 ;COMPARING UP TO 4 CHARACTERS
FINDI1: LD A,(DE) ;GET A CHARACTER FROM POINTER TABLE
INC DE
CPI ;COMPARE TO WORD:
JR NZ,FINDI2 ;JUMP IF DIFFERENT
JP PO,FINDI3 ;JUMP IF ALL 4 CHARACTERS EQUAL
JR FINDI1 ;TRY ANOTHER
FINDI2: JP P,FINDI3 ;TOO FAR IN DICTIONARY
LD DE,4
POP HL
POP BC
ADD HL,DE ;POINT TO NEXT RECORD INDEX
INC BC
JP FINDI0
FINDI3: POP HL
POP BC
FINDI4: DEC BC
LD DE,4
XOR A ;CLEAR CARRY
SBC HL,DE
LD (LSTREC),BC ;UPDATE POINTERS
LD (LSTADR),HL
;
; LOOKUP -- loop through as many records as it takes to be sure that word is
; not in dictionary
LOOKUP: CALL DICFND
CP 0FFH ;IF RETURN STATUS=0FFH THEN WORD COULD
;BE IN NEXT RECORD
JR NZ,LOOKU0 ;RETURN IF NOT IN NEXT RECORD
INC BC
LD HL,0+(TABBOT-TABTOP)/4 ;MAKE SURE NOT PAST LAST RECORD
XOR A ;CLEAR THE CARRY BIT
SBC HL,BC
JR NZ,LOOKUP ;TRY NEXT RECORD IF NOT AT END
LD A,0
LOOKU0: POP HL ;RESTORE THE STACK
POP DE
POP BC
RET
;
; DICFND -- read record in BC from dictionary. Determine whether WORD is
; in it.
;
; Returns in A: 0 <-- word not found
; 1 <-- word found
; 2 <-- word found but flag not set
; 0FFH<-- word not found but it may be in next
; record
DICFND: PUSH HL
PUSH DE
PUSH BC
RLC B ;MULTIPLY BC BY 2 SO IT POINTS TO
;256 BYTE RECORD
RLC C
LD A,0
ADC A,B ;GET THE CARRY FROM RLC C
LD B,A
LD A,C
AND 0FEH ;KILL BIT 0
LD C,A
LD HL,(DICREC) ;GET THE CURRENT RECORD IN RAM
DEC HL ;POINT TO FIRST 128 BYTES OF 256
;BYTE DICTIONARY RECORDS
XOR A ;CLEAR CARRY BIT
SBC HL,BC ;ATTEMPT TO READ THE SAME RECORD?
JR NZ,DICDSK ;IF NO, READ FROM DISK
LD A,(ALPHA) ;WORKING IN APHABETICAL ORDER?
AND A
JP NZ,DICFO1 ;JUST GET ANOTHER WORD IF YES
LD A,(CURBIT) ;ROTATE CURRENT BYTE SO IT IS THE RIGHT
;POSITION IN CASE SAME RECORD USED
AND 0111B ;IS IT ON CORRECT ROTATION NOW?
JR Z,DICFN0
LD HL,(CURBYT) ;ADDRESS OF CURRENT BYTE
LD B,A
LD A,8 ;MUST COMPLETE 8 ROTATIONS, TOTAL
SUB B ;SUBTRACT NUMBER ALREADY DONE
LD B,A
ROTATE: RLC (HL)
DJNZ ROTATE
JP DICFN0 ;START TESTING WORDS
DICDSK: PUSH BC
LD (DICREC),BC ;NEW RECORD TO READ
LD C,SETDMA ;SET DMA TO DICTIONARY BUFFER
LD DE,DICBUF ;DICIONARY BUFFER IN RAM
CALL BDOS
LD C,RANREA ;BDOS RANDOM READ CODE
LD DE,DICFCB ;FCB OF DICTIONARY
CALL BDOS
POP BC
INC BC ;GET 256 BYTES
LD (DICREC),BC ;NEXT RECORD NUMBER
LD C,SETDMA ;DMA ADDRESS 256 BYTES HIGHER
LD DE,DICBUF+128
CALL BDOS
LD C,RANREA
LD DE,DICFCB
CALL BDOS ;READ NEXT RECORD
DICFN0: LD HL,DICBUF-1 ;INITIALIZE POINTER TO DICTIONARY
;BUFFER
LD (CURBYT),HL
LD A,0 ;CURRENT BIT=0
LD (CURBIT),A
DICFN1: CALL MOVWRD ;GET A WORD
DICFO1: LD HL,WORD ;POINT TO WORD
LD BC,DICWRD ;POINTER TO DICTIONARY WORD
DICFN2: LD A,(BC) ;GET A LETTER FROM DICTIONARY WORD
CP (HL) ;THE SAME?
INC HL ;POINT TO NEXT CHARACTER
INC BC
JR NZ,DICFN4 ;IF NOT, TRY SOME MORE OR END
CP 0 ;END OF WORD?
JR NZ,DICFN2 ;TRY ANOTHER CHARACTER
LD C,1 ;INDICATE WORD FOUND
LD HL,(DICFL)
LD A,(FLAG) ;GET FIRST FLAG
CP 0 ;NO FLAG WANTED?
JR Z,DICFN3 ;TRY NEXT BYTE OF FLAG IF NONE
AND L
CP 0 ;INDICATE WORD FOUND IF .NE. 0
JR NZ,DICRET
LD C,2 ;INDICATE MAIN WORD FOUND/NO FLAG
JR DICRET
DICFN3: LD A,(FLAG+1) ;TRY NEXT FLAG BYTE
CP 0 ;NO FLAG WANTED?
JR Z,DICRET ;IF NONE WANTED THEN WORD FOUND
AND H
CP 0 ;FOUND IF .NE. 0
JR NZ,DICRET
LD C,2 ;INDICATE MAIN WORD FOUND/NO FLAG
JR DICRET
DICFN4: JP M,DICFN1 ;IF TEST WORD IS EARLIER IN ALPHABET,
;TRY NEXT DICTIONARY WORD
LD C,0 ;NOT FOUND
; JP DICRET
DICRET: LD A,C ;PUT STATUS BYTE IN A
POP BC
POP DE
POP HL
RET
;
; MOVWRD -- read a word from the dictionary by decoding the flags.
;
; Returns: DICWRD <-- word from dictionary
MOVWRD: LD A,0
LD B,4
CALL GETBIT ;GET 4 BITS INTO A
LD HL,DICWRD ;POINT TO DICTIONARY WORD BUFFER
LD B,0
LD C,A ;NUMBER OF CHARACTERS TO KEEP
ADD HL,BC
MOVWR1: LD A,0
LD B,3 ;GET FIRST 3 OF 5 BITS FOR CHARACTER
CALL GETBIT ;BITS IN A
CP 0111B ;IS IT AN END OF WORD MARK?
JR Z,GETFLG ;GET THE FLAGS NOW
LD B,2 ;GET REMAINING 2 BITS
CALL GETBIT
CP 0 ;IF ZERO THEN END OF RECORD
JP Z,MOVMOR ;INDICATE MAY BE IN NEXT RECORD
ADD A,40H ;MAKE IT ASCII
CP 'Z'+1 ;IS IT AN ENCODED "'"?
JR NZ,MOVWR4 ;JUMP IF NOT
LD A,027H ;MAKE IT A "'"
MOVWR4: LD (HL),A ;BUFFER THE CHARACTER
INC HL
JR MOVWR1 ;GET ANOTHER CHARACTER
MOVMOR: LD C,0FFH ;INDICATE MAY BE IN NEXT RECORD
POP DE ;GET USELESS RETURN WORD
JP DICRET ;RETURN
;
; GETFLG -- read suffix flags from buffer.
;
; Returns: DICFL <-- 16 byte flag word
GETFLG: LD A,0
LD (HL),A ;MARK THE END OF THE WORD
LD B,4 ;GET 4 BITS FOR NUMBER OF FLAGS VALUE
CALL GETBIT
LD B,A ;NUMBER OF BITS IN B
LD HL,0
CP 0 ;ARE ANY BITS THERE TO COPY?
JP Z,GETFL8 ;RETURN IF NONE
PUSH BC
CP 8 ;MORE THAN 8 BITS?
JP M,GETFL1 ;JUMP IF NOT
LD B,7 ;GET 7 BITS FOR FIRST BYTE
GETFL1: LD A,0
CALL GETBIT
POP BC
LD L,A ;THIS IS THE LOW BYTE OF FLAGS
LD A,B
CP 8 ;GET MORE IF GREATER THAN 8 BITS
JP P,GETFL4
LD A,7 ;COMPUTE NUMBER OF ROTATIONS NECESSARY
;TO PUT IT IN THE RIGHT PLACE
SUB B
JP Z,GETFL8 ;IF EXACTLY 7 THEN DONE
LD B,A ;COUNTER
GETFL3: RLC L
DJNZ GETFL3
JP GETFL8 ;NO RETURN
GETFL4: SUB 7 ;GET NUMBER OF BITS NEEDED FOR BYTE 2
LD B,A
LD A,0
PUSH BC
CALL GETBIT ;GET BIT FOR HIGH BYTE OF STATUS FLAG
POP BC
LD H,A ;SAVE HIGH BYTE
LD A,7 ;COMPUTE NUMBER OF ROTATIONS LEFT
SUB B
JR Z,GETFL8 ;RETURN IF NONE
LD B,A
GETFL6: RLC H
DJNZ GETFL6
GETFL8: LD (DICFL),HL ;SAVE THE FLAG
RET
;
; GETBIT -- read number of bits in B from dictionary buffer.
;
; Returns: A <-- byte value of B bits
GETBIT: PUSH DE
PUSH HL
EX AF,AF' ;A HOLDS DESIRED DECODED BYTE.
;A' HOLD CURRENT BIT VALUE
LD A,(CURBIT)
LD HL,(CURBYT) ;ADDRESS OF CURRENT BYTE FOR OUTPUT
LD D,(HL) ;D = CURRENT BYTE VALUE
EX AF,AF'
GETBI0: EX AF,AF' ;GET CURRENT BIT VALUE
AND 0111B ;MASK 1ST THREE BITS
JR NZ,GETBI1 ;IF .NE. 0 THEN NOT TIME TO INC CURBYT
LD (HL),D ;RESTORE OLD BYTE TO ORIGINAL VALUE
INC HL
LD (CURBYT),HL
LD D,(HL) ;UPDATE D = CURRENT BYTE VALUE
GETBI1: INC A
EX AF,AF' ;BACK TO DECODED BYTE
RLC D ;MOVE BYTE SO NEXT BIT IN RIGHT PLACE
RLC A ;MAKE A READY TO RECEIVE
BIT 0,D ;IS THE BIT ON?
JR Z,GETBI2 ;DON'T SET A IF IT ISN'T
OR 1 ;SET BIT 1 OF A
GETBI2: DJNZ GETBI0 ;LOOP THROUGH DESIRED NUMBER OF BITS
EX AF,AF' ;GET CURRENT BIT
LD (CURBIT),A ;UPDATE IT
EX AF,AF'
LD (HL),D ;UPDATE BYTE FOR NEXT CALL
POP HL
POP DE
RET
;
; USRTST -- test user dictionary in memory for WORD.
;
; Returns in A: 0 <-- word not found
; 1 <-- word found
USRTST: LD HL,NEXT ;BEGINNING OF USER DICTIONARY BUFFER
LD A,0
CP (HL) ;DOES ONE EXIST?
RET Z ;CAN'T FIND ELEMENT OF AN EMPTY SET!
LD DE,WORD ;ADDRESS OF WORD FOR COMPARING
USRTS1: LD A,(DE) ;GET A CHARACTER
CP (HL) ;ARE THEY THE SAME?
JR NZ,USRTS2 ;IF NOT SAME, TRY ANOTHER WORD
INC DE
INC HL
CP 0 ;END OF WORD?
JR NZ,USRTS1 ;LOOP THROUGH WHOLE WORD
LD A,1 ;FOUND IT!
RET
USRTS2: LD A,0
USRTS3: CP (HL) ;LOOK FOR END OF WORD
INC HL
JR NZ,USRTS3
CP (HL) ;IF NEXT BYTE ALSO 0 THEN END OF TABLE
RET Z
LD DE,WORD
JR USRTS1 ;TEST ANOTHER WORD
;
; DECOUT - Output number in HL to console in decimal
;
DECOUT: PUSH PSW
PUSH BC
PUSH DE
PUSH HL
LD B,0 ;B WILL BE 1 ONCE NON-ZERO CHAR OUTPUT
LD DE,10000 ;START BY TRYING 10,000'S
CALL NUMOUT ;OUTPUT A NUMBER
LD DE,1000 ;1000'S
CALL NUMOUT
LD DE,100
CALL NUMOUT
LD DE,10
CALL NUMOUT
LD B,1 ;GUARANTEE THAT 0 WILL PRINT
LD DE,1
CALL NUMOUT
POP HL
POP DE
POP BC
POP PSW
RET
NUMOUT: LD C,0 ;COUNTER FOR NUMBER OF SUBTRACTIONS
NUMOU0: INC C ;COUNT LOOPS THROUGH SUBTRACTION
XOR A ;CLEAR CARRY
SBC HL,DE ;SUBTRACT UNITS UNTIL CARRY
JP NC,NUMOU0
ADD HL,DE ;RESET TO LAST POSITIVE VALUE
DEC C ;DON'T COUNT LAST SUBRTRACTION
JR NZ,NUMOU1 ;IF NOT ZERO, THEN OUTPUT
CP B ;ANYTHING OUTPUT YET?
RET Z ;IF NOT, THEN DON'T PRINT A 0
NUMOU1: LD B,1 ;INDICATE OUTPUT SENT
LD A,C
ADD A,'0' ;CONVERT TO ASCII
LD E,A ;OUTPUT TO CONSOLE
LD C,CONOUT ;CONSOLE OUTPUT CODE
PUSH BC
PUSH HL
CALL BDOS
POP HL
POP BC
RET
INPTR: DW INBUF+512 ;POINTER TO CURRENT BYTE IN INPUT BUFF
OPOSS: DW OBUFF ;POINTER TO CURRENT BYTE IN OUTPUT BUFF
SRTBOT: DW NEXT ;POINTER TO BEGINNING OF MEMORY POINTER
;TABLE OF ALPHABETIZED WORDS FROM INPUT
;FILE
SRTTOP: DW NEXT+2 ;POINTER TO TOP OF MEMORY POINTER TABLE
FREE: DW NEXT ;POINTER TO NEXT FREE BYTE IN TPA
LASTCH: DB LF ;LAST CHARACTER INDICATOR
OLSTCH: DB LF ;BUFFER FOR OLD LAST CHARACTER WHEN
;MULTIPLE INPUT FCB'S IN USE
SAVPUT: DS 1 ;BUFFER FOR NORMAL FIRST INSTRUCTION
;AT PUTCHR. USED WHILE OUTPUT IS
;DISABLED
DICFL: DW 0 ;DICTIONARY FLAG FOR COMPARE
CURBYT: DW DICBUF-1 ;CURRENT BYTE OF DICBUF (FOR GETBIT)
CURBIT: DB 0 ;CURRENT BIT OF BYTE (FOR GETBIT)
ALPHA: DB 0 ;0 IF NOT WORKING IN ALPHABETICAL
;ORDER (DID FLAG SEEK). 1 IF IN ORDER
LSTREC: DW 1 ;RECORD NUMBER OF LAST DICTIONARY
;RECORD READ
LSTADR: DW TABTOP+4 ;POINTER TO TABLE WHERE FIRST 4 BYTES
;OF LAST RECORD READ ARE FOUND
OLDPTR: DW 0 ;POINTER FOR PASS 1 OF INPUT FILE IS
;SAVED HERE DURING PASS 2 IF ALL OF
;FILE DID NOT FIT IN MEMORY
P2OPTR: DW P2BUF+512 ;POINTER FOR PASS2 OF INPUT FILE IS
;SAVED HERE DURING PASS 1 IF ALL OF
;FILE DID NOT FIT IN MEMORY
STOPED: DB 0 ;IS 1 IF ALL OF FILE DID NOT FIT IN
;MEMORY
MLTPAS: DB 0 ;SAME AS STOPED BUT NEVER RESET ONCE
;SET
DEFDRV: DB 0 ;RECEIVES DEFAULT DRIVE VALUE
SPLDC: DW 0 ;STORES NUMBER OF WORDS IN SPELL.DIC
FILUDC: DW 0 ;STORES NUMBER OF WORDS WRITTEN TO
;FILE.UDC
TOTWRD: DW 0 ;RECORDS TOTAL NUMBER OF WORDS IN DOC
UNQWRD: DW 0 ;NUMBER OF UNIQUE WORDS IN DOC
MISWRD: DW 0 ;NUMBER OF MISSPELLED WORDS
OFCB: DB 0,' $$$' ;FCB FOR OUTPUT FILE
DB 0,0,0,0,0,0,0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0,0,0,0,0
P2FCB: DB 0,' ' ;FCB FOR PASS 2 OF INPUT FILE
DB 0,0,0,0,0,0,0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0,0,0,0,0
FILDIC: DB 0,' UDC' ;FCB FILE FILE.UDC
DB 0,0,0,0,0,0,0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0,0,0,0,0
FILADD: DB 0,' ADD' ;FCB FOR FILE.ADD
DB 0,0,0,0,0,0,0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0,0,0,0,0
FILD$$: DB 0,' D$$' ;FCB FOR TEMPORARY FILE FILE.D$$
DB 0,0,0,0,0,0,0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0,0,0,0,0
SPLDIC: DB 0,'SPELL DIC' ;FCB FOR SPELL.DIC
DB 0,0,0,0,0,0,0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0,0,0,0,0
DICFCB: DB 0,'DICT DIC' ;FCB FOR DICTIONARY FILE
DB 0,0,0,0,0,0,0,0,0,0,0,0,0
DB 0,0,0,0,0,0,0,0
DICREC: DB 0,0,0 ;RECORD NUMBER FOR RANDOM READ
;(STILL PART OF FCB)
STACK EQU $+50 ;STACK
SRCWRD EQU STACK ;BUFFER FOR UNFIXED WORD
WORD EQU SRCWRD+100 ;BUFFER FOR FIXED WORD
DICWRD EQU WORD+41 ;BUFFER FOR WORDS READ FROM DICTIONARY
FLAG EQU DICWRD+41 ;BUFFER FOR DESIRED DICTIONARY FLAG
LASTC EQU FLAG+2 ;BUFFER FOR LAST CHARACTER READ
DICBUF EQU LASTC+1 ;BUFFER FOR DICTIONARY RECORD
INBUF EQU DICBUF+258 ;INPUT BUFFER FOR PASS 1
P2BUF EQU INBUF+513 ;INPUT BUFFER FOR PASS 2
OBUFF EQU P2BUF+513 ;OUTPUT BUFFER
NEXT EQU OBUFF+513 ;ADDRESS OF NEXT FREE BYTE
END START