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