.TITLE "A poor person's spelling checker" .PAGE 96,84 ; .EPOP .ZOP .PABS .PHEX .XLINK ASEG ; ; version 1.1 (04/30/82) (Jim Byram) ; Changed last instruction of GBYTE1 from OR A to AND 7FH ; to clear high bit of text character as well as to reset ; carry. Necessary to scan WordStar files. ; Changed all unconditional JR instructions to JP to speed ; execution. Moved BDOS calls in-line. ; Added file output using routines from SD-42.ASM. Words ; not matched are written to console and (optionally) to ; the printer and/or to a file named SPELL.LEX. The file ; is created on the default drive if it did not previously ; exist. If it did exist, the new list of unmatched words ; is appended to the file. This feature allows generation ; of word lists which can be sorted and edited and then ; added to your MASTER.LEX. ; Added command line options for file and printer output. ; ; version 1.0 (Alan Bomberger) ; ; Bomberger, Alan. 1982. A poor person's spelling ; checker. Dr. Dobb's Journal 7(4):42-53. (DDJ #66) ; ; Released for NON COMMERCIAL USE ONLY ; (c) 1981 Alan Bomberger ; ; USAGE: [d:]spell [d:]filename.typ [fp] ; ; spell filename.typ --> output to console ; ; spell filename.typ f --> ..and to file ; ; spell filename.typ p --> ..or to printer ; ; spell filename.typ fp --> output to all three ; ; The input file is checked using the lexicon files and ; misspelled words (i.e., unmatched words) are printed in ; the order they appear in the text. ; ; The input file is broken down into a word list and the ; user is prompted to enter the name of each lexicon to ; be scanned. ; ; Note -- a lexicon is a list of words usually separated ; by . The words comprising a lexicon may be in ; any order, but program execution is much faster if all ; lexicon words are UPPER CASE. ; ; The word list will fill all available memory so only ; very large documents will require more than one pass ; of the lexicons. ; BOOT EQU 0 BDOS EQU 5 ; PCHAR EQU 2 LISTC EQU 5 PSTRING EQU 9 RSTRING EQU 10 OPENF EQU 15 CLOSEF EQU 16 SRCHF EQU 17 READF EQU 20 WRITEF EQU 21 MAKEF EQU 22 SETDMA EQU 26 ; FCB EQU 5CH FCB2 EQU 6CH BUFF1 EQU 80H ; CR EQU 0DH LF EQU 0AH BELL EQU 7 ; ORG 100H ; SPELIT: LD SP,STACK ; a new stack pointer LD A,(FCB2+1) ; check for output options CP " " ; any options? JR Z,NOOPT CALL CHKOPT ; yes, determine which LD A,(FCB2+2) ; check for second option CP " " ; another option? JR Z,NOOPT CALL CHKOPT ; yes, determine which NOOPT: LD DE,COPYR LD C,PSTRING CALL BDOS CALL OPENIN ; open input files CALL ZCHN ; zap chains BUILDL: CALL GWORD ; get the next word of text JR C,ENDIN ; no more left, check spelling CALL SEARCH ; see if in word list JR NC,BUILDL ; yes, it is CALL ADDW ; no, so add it in ; ; "WORK" contains the address of the last word put into ; the word list. See if this word is past the threshold ; of memory. ; CALL COMPARE JR NC,BUILDL ; no, so continue LD HL,NUMWDC+2 ; mark as incomplete LD (HL),"*" CALL SPELL ; check the current list CALL PTABLE ; print the misspelled words LD IX,COMWDL ; the last of the common words SET WFLGSL,(IX+WFLGS) ; mark this as the last in list LD C,6 LD B,0 LD DE,NUMWD LD HL,ZCOUNT ; zero counter LDIR CALL ZCHN ; zap chains JP BUILDL ; and get next word ENDIN: CALL SPELL ; check spelling of words in list LD DE,FCB LD C,CLOSEF CALL BDOS ; close up input file CALL OUTPUT ; print the words not in lexicon JP CLZOUT ; close output file and exit ; ; chkopt ; ; determine whether file and/or printer output selected ; any unrecognized options will be ignored ; CHKOPT: CP "F" ; file output wanted? JR NZ,NOTF ; no, what about printer? LD A,0 LD (FOPFLG),A ; set flag RET NOTF: CP "P" ; printer output wanted? RET NZ ; no LD A,0 LD (POPFLG),A ; set flag RET ; ; compare ; ; compare the value in work with "endmem" ; COMPARE: LD HL,WORK ; address of last word LD A,(ENDMEM) ; end of memory SUB (HL) LD A,(ENDMEM+1) INC HL SBC A,(HL) ; double precision subtract RET ; ; openin ; ; open input file and locate end of memory ; OPENIN: LD DE,FCB ; input file LD C,OPENF CALL BDOS LD DE,NINPUT ; in case not there INC A JR Z,FAILED ; no file LD A,128 LD (IBP),A ; set so 1st call gets disk record ; ; find end of memory ; LD HL,(6) ; address of bdos LD BC,64 ; a margin OR A ; clear carry SBC HL,BC ; subtract margin LD (ENDMEM),HL RET FAILED: LD C,PSTRING CALL BDOS JP BOOT ; quit now ; ; gword ; ; get next word in text into cword ; carry flag on means end of input ; GWORD: LD A,128 LD (CFLAGS),A ; set this word as last LD DE,0 ; length of word GWORDL: CALL GBYTE ; get next byte of text JR C,GWORDE ; end of input LD BC,(DELIML) ; length of delimiter table LD HL,DELIMT ; the table CPIR ; is it a delimiter? JR Z,DELIM ; yes LD BC,(ALPHAL) LD HL,ALPHA ; is it alphabetic? CPIR JR NZ,GWORDL ; no, skip it CP "a" ; is it lower case JR C,GWORDU ; no CP "{" ; lower JR NC,GWORDU AND 5FH ; make all upper case GWORDU: LD HL,CWORD+4 ; place to build word ADD HL,DE LD (HL),A ; put byte in word INC E ; new length LD A,E LD (CLEN),A ; update in word entry CP 30 ; how long is word? JR Z,GWORDT ; too long a word JP GWORDL ; loop DELIM: LD A,E ; current length CP 0 JR Z,GWORDL ; skip leading delimiters OR A ; zero carry GWORDE: RET GWORDT: LD DE,LNGWD1 ; first part of text LD C,PSTRING CALL BDOS LD DE,CWORD+4 LD C,PSTRING CALL BDOS LD DE,LNGLX2 ; second part LD C,PSTRING CALL BDOS OR A JP GWORDE ; ; getbyte ; ; get next byte of text ; carry flag on for end of file ; GBYTE: PUSH DE LD A,(IBP) CP 128 ; do we need another buffer full? JR NZ,GBYTE1 ; no LD DE,FCB LD C,READF CALL BDOS ; read a block CP 0 ; did it ok? SCF ; in case not JR NZ,GBYTER ; end of file return GBYTE1: LD E,A ; has current byte index to fetch LD D,0 ; double precision LD HL,BUFF1 ADD HL,DE INC A ; next index LD (IBP),A LD A,(HL) ; get byte CP 1AH ; check for end SCF ; in case it is JR Z,GBYTER ; yes AND 7FH ; clear carry and set bit 7 to 0 GBYTER: POP DE RET ; ; search ; ; search word list for match with cword ; ; on return ix will point to matched entry or last in list ; carry on if no match ; ; searc1 is the entry when searching on a chain ; SEARCH: LD IX,WORDS ; start of list SEARC1: ; entry if starting with chain SLOOP: LD A,(CLEN) ; length of current word CP (IX+WLEN) ; must be same as list entry JR NZ,NEXTW ; try next entry CALL CLC ; compare JR Z,MATCH ; it is a match NEXTW: BIT WFLGSL,(IX+WFLGS) ; is this the last entry? JR NZ,NMATCH ; yes, then no match LD A,(IX+WCHN) ; get chain pointer LD (WORK),A LD A,(IX+WCHN1) ; both parts LD (WORK+1),A CP 0 ; this is high order (zero only if end) JR Z,NMATCH ; end of chain LD IX,(WORK) JP SLOOP MATCH: OR A ; clear carry JP SRET NMATCH: SCF ; set carry SRET: RET ; ; clc ; ; compare logical character ; cword with list entry pointed to by ix ; a contains length ; CLC: PUSH IX LD C,A ; length for down count LD HL,CWORD+4 ; compare here CLCL: LD A,(IX+WORD) ; first character CP (HL) ; is it? JR NZ,CLCE ; no, stop INC HL INC IX DEC C JR NZ,CLCL ; not end so continue CLCE: POP IX RET ; ; addw ; ; add word to list ; word is in cword and ix points to last entry ; ADDW: LD (WORK),IX ; save LD IY,(WORK) ; old position LD A,0 LD (CCHN),A LD (CCHN1),A ; zero chain pointer RES WFLGSL,(IX+WFLGS) ; clear this is last entry flag LD B,0 LD A,(IX+WLEN) ; get length of last word ADD A,4 LD C,A ; include chain and stuff ADD IX,BC ; skip over last entry LD (WORK),IX LD A,(WORK) ; get low byte LD (IY+WCHN),A ; to chain LD A,(WORK+1) LD (IY+WCHN1),A ; to chain LD A,(CLEN) ADD A,4 LD C,A LD HL,CWORD ; source LD (WORK),IX LD DE,(WORK) ; can't get there from here LDIR ; move it CALL COUNTW ; bump count RET ; ; spell ; ; check each lexicon word with list entries ; mark correct (found) words in list ; SPELL: LD DE,NUMWD LD C,PSTRING CALL BDOS ; inform of number of words CALL SETCHN ; set up chains LD DE,BUFF2 ; switch buffers LD C,SETDMA CALL BDOS NEXTLEX: CALL GETLEX ; get a lexicon file JR C,SPELLR ; none, so return LD DE,LFCB ; get lexicon file LD C,OPENF CALL BDOS LD DE,NOLEX ; in case not there INC A JR NZ,GOTLEX ; it is a valid lexicon LD C,PSTRING CALL BDOS ; it is not a valid lexicon JP NEXTLEX ; try again GOTLEX: LD DE,LFCB ; lexicon fcb LD C,READF CALL BDOS ; read first record CP 0 ; did it JR NZ,ENDL ; quick exit LD DE,CHECKM ; tell customer LD C,PSTRING CALL BDOS ; that we begin LD A,0 LD (IBPL),A LD (COMP),A ; say not compacted LD A,(BUFF2) ; first of compacted CP 0FFH JR NZ,SPELLL LD A,1 LD (COMP),A ; set compacted LD (IBPL),A ; skip ff SPELLL: CALL LWORD ; get a word in cword JR C,ENDL ; end of lexicon LD IX,CWORD CALL GETCHN ; get correct chain for this word LD E,(HL) ; low order byte INC HL LD D,(HL) ; high order byte LD (WORK),DE ; get first word in list LD IX,(WORK) ; place to start LD A,(WORK+1) CP 0 JR Z,SPELLL ; if zero no words this letter CALL SEARC1 ; look for word in chain JR C,SPELLL ; did not find it SET WFLGSC,(IX+WFLGS) ; mark spelled correctly JP SPELLL ; and loop ENDL: LD DE,LFCB ; close LD C,CLOSEF CALL BDOS JP NEXTLEX ; get another lexicon SPELLR: LD DE,BUFF1 ; reset dma LD C,SETDMA CALL BDOS ; in case more input RET ; ; getlex ; ; get a lexicon file from the customer ; if none requested (null input) return with carry flag on ; GETLEX: LD DE,ASKLEX LD C,PSTRING CALL BDOS ; type prompt CALL ANSWER ; get answer JR C,GETLXR ; return, no lexicon CALL BLDFCB ; build a new fcb OR A ; clear carry GETLXR: RET ; ; answer ; ; get answer to question in buff2 ; ANSWER: LD DE,BUFF2 LD A,80 LD (BUFF2),A LD C,RSTRING CALL BDOS ; get answer LD A,(BUFF2+1) ; get length of answer CP 0 ; see if any SCF ; none JR Z,ANSWRT ; quit now OR A ; clear carry ANSWRT: RET ; ; bldfcb ; ; build an fcb from information in buff2 ; assumes file type of .LEX ; BLDFCB: LD HL,DEFFCB ; the default fcb LD DE,LFCB ; goes here LD BC,16 ; move this much LDIR ; move it XOR A ; get a zero LD (LFCBCR),A ; zero this as well LD HL,BUFF2+2 LD A,(BUFF2+1) ; get number of bytes in name LD C,A ; b is zero from block above BLLOOP: LD A,(HL) ; get a byte CP " " ; is it a blank? JR NZ,NOBLK ; no INC HL DEC C JR NZ,BLLOOP ; skip leading blanks JP BLDRET ; return with bad fcb NOBLK: INC HL ; skip disk name if present LD A,(HL) ; get suspected ":" DEC HL ; back to first character CP ":" ; is it a disk name? JR NZ,NODSK ; no, just a name LD A,(HL) ; get disk name AND 0FH ; to cp/m standards LD (LFCBDN),A ; to fcb INC HL INC HL ; skip name and ":" DEC C JR Z,BLDRET ; quit with bad fcb DEC C JR Z,BLDRET ; quit with bad fcb NODSK: LD DE,LFCBFN ; place for name LD A,8 ; max length at this point CP C ; are we ok? JR Z,BLDRET ; no, so leave blank FILELP: LD A,(HL) CP "." ; this is end (we ignore) JR Z,BLDRET CP " " ; also end JR Z,BLDRET ; and this CP "a" ; lower case alpha? JR C,FILEL1 ; no AND 5FH ; make upper FILEL1: LD (DE),A ; put in fcb INC DE INC HL DEC C JR NZ,FILELP ; loop BLDRET: RET ; ; lword ; ; get a lexicon word ; carry flag on if end of lexicon ; LWORD: LD DE,0 ; length of word LWORDL: CALL LCHAR ; get char from file JR C,LWORDR ; if end CP LF ; skip these if present JR Z,LWORDL CP " " JR Z,LWORDL ; skip blanks in lexicon CP CR ; end of word JR Z,LWORDE ; done CP 1AH ; end JR Z,LWORDF ; set carry and return CP "a" ; lower case? JR C,LWORDU ; no, upper CP "{" JR NC,LWORDU AND 5FH ; make sure upper case LWORDU: LD HL,CWORD+4 ; place to put it ADD HL,DE LD (HL),A ; build word INC E ; bump count LD A,E LD (CLEN),A CP 30 ; how long? JR Z,LWORDT ; too long JP LWORDL ; get more bytes LWORDE: LD A,E ; check for null word CP 0 ; any so far? JR Z,LWORDL ; no, so continue OR A ; clear carry LWORDR: RET LWORDF: SCF JP LWORDR ; return LWORDT: LD DE,LNGLX1 ; first part LD C,PSTRING CALL BDOS LD DE,CWORD+4 LD C,PSTRING CALL BDOS LD DE,LNGLX2 ; second part LD C,PSTRING CALL BDOS OR A JP LWORDR ; ; lchar ; ; get a character from lexicon (compacted or not) ; LCHAR: LD A,(COMP) ; is it a compacted lexicon? CP 0 ; well? JR NZ,LCHARC ; yes CALL LBYTE ; no, get a byte RET ; and return ; LCHARC: CALL GNIB ; get a nibble JR C,LCHARE ; end already CP 0FH ; is it a flag? JR Z,LCHARS ; yes, second set of letters LD C,16 ; size of table LD HL,T1 ; in table one LCHAR1: CP C JR NC,LCHARE ; too big LD B,0 LD C,A ADD HL,BC JP LCHARG ; got it LCHARE: LD DE,BADLEX LD C,PSTRING CALL BDOS SCF RET ; say end of lexicon LCHARS: CALL GNIB JR C,LCHARE LD C,14 ; search length LD HL,T2 JP LCHAR1 ; loop here LCHARG: LD A,(HL) OR A ; clear carry RET ; ; gnib ; ; get a nibble from compacted lexicon ; GNIB: LD A,(LRNIB) CP 1 ; left or right? JR Z,GNIBR ; right LD A,1 LD (LRNIB),A CALL LBYTE ; get a byte JR C,GNIBR ; report carry LD (BYTE),A SRL A SRL A SRL A SRL A ; put left in lower OR A ; clear carry RET GNIBR: LD A,0 LD (LRNIB),A LD A,(BYTE) AND 0FH RET ; ; lbyte ; ; get a byte from lexicon file ; carry flag on for end of file ; LBYTE: PUSH DE LD A,(IBPL) ; get buffer pointer CP 128 ; at end? JR NZ,LBYTE1 ; no LD DE,LFCB ; fcb for lexicon LD C,READF CALL BDOS CP 0 ; did it work? SCF ; in case not JR NZ,LBYTER ; return with carry if end LBYTE1: LD E,A ; position in buffer LD D,0 LD HL,BUFF2 ADD HL,DE ; correct byte INC A ; for next time LD (IBPL),A LD A,(HL) ; get the byte OR A ; clear carry LBYTER: POP DE RET ; ; count words ; COUNTW: LD HL,NUMWDC ; get lowest byte LD A,":" ; a test for too large COUNTL: INC (HL) CP (HL) ; see if too big RET NZ ; no LD (HL),"0" ; yes, set to 0 DEC HL JP COUNTL ; backup and try again ; ; zchn ; ; zero chain headers ; ZCHN: LD A,0 ; get a zero LD C,54 ; number LD HL,ALPHC ; place ZCHNL: LD (HL),0 INC HL DEC C JR NZ,ZCHNL RET ; ; getchn ; ; get address of chain head of word pointed to by ix ; GETCHN: LD A,(IX+WORD) ; first char LD B,0 LD HL,ALPHC ; first chain head CP "A" ; first JR C,CHNOTH ; lower use other CP "[" JR NC,CHNOTH ; greater use other GETCHA: AND 1FH ; mask DEC A SLA A ; double it LD C,A ; displacement ADD HL,BC RET CHNOTH: LD A,"[" JP GETCHA ; use last chain ; ; setchn ; ; scans word list and rechains it by letter ; SETCHN: LD IX,WORDS ; place to start SETCH0: CALL GETCHN ; get the correct header LD A,0 ; get a zero SETCHL: INC HL ; to high order byte CP (HL) JR NZ,NXTCHN ; not this one LD (WORK),IX ; goes here LD DE,(WORK) ; get it LD (HL),d DEC HL LD (HL),E LD (IX+WCHN),A ; zero forward LD (IX+WCHN1),A JP SETCHW ; next word NXTCHN: LD D,(HL) DEC HL LD E,(HL) EX DE,HL INC HL INC HL ; to chain portion of word JP SETCHL SETCHW: BIT WFLGSL,(IX+WFLGS) JR NZ,SETCHR ; return LD A,(IX+WLEN) ADD A,4 LD C,A LD B,0 ADD IX,BC JP SETCH0 SETCHR: RET ; ; output ; ; create or open output file for unmatched words ; OUTPUT: LD A,(FOPFLG) ; is file output active? OR A JP NZ,PTABLE ; no, begin console output LD DE,OUTBUF ; set dma for output buffer LD C,SETDMA CALL BDOS ; ; first pass on file append ; prepare SPELL.LEX to receive new or appended output ; LD DE,OUTFCB ; does file already exist? LD C,SRCHF PUSH DE CALL BDOS POP DE INC A JR NZ,OPENIT ; yes, open it for processing LD C,MAKEF CALL BDOS ; no, create the output file ; INC A JP NZ,PTABLE ; continue if open successful ; ; if make or open fails, declare error ; OPNERR: CALL ERXIT DB CR,LF,"OPEN$" ; WRTERR: CALL ERXIT DB CR,LF,"WRITE$" ; ; openit ; ; output file already exists - open it and position to ; the last record of the last extent ; OPENIT: LD C,OPENF PUSH DE CALL BDOS ; open 1st extent of output file POP DE INC A JR Z,OPNERR ; bad deal if 1st won't open OPNMOR: LD A,(OUTFCB+15) CP 128 JR C,RDLAST ; if rc <128, this is last extent LD HL,OUTFCB+12 INC (HL) ; else, bump to next extent LD C,OPENF PUSH DE PUSH HL CALL BDOS ; and try to open it POP HL POP DE INC A JR NZ,OPNMOR ; open extents until no more DEC (HL) ; then, reopen preceding extent LD C,OPENF PUSH DE CALL BDOS POP DE LD A,(OUTFCB+15) ; get rc for the last extent ; ; rdlast ; ; at this point, outfcb is opened to the last extent of ; the file, so read in the last record of the last extent ; RDLAST: OR A ; is this extent empty? JR Z,PTABLE ; yes, start a clean slate DEC A ; normalize record count LD (OUTFCB+32),A ; set record number to read LD C,READF PUSH DE CALL BDOS ; and read last record of file POP DE OR A ; was read successful? JR Z,RDOK ; yes, go scan for eof mark ; ; if read or append fails, declare error ; APERR: CALL ERXIT DB CR,LF,"APPEND$" ; ; rdok ; ; we now have the last record of the file in our buffer ; ; scan the last record for the eof mark, indicating where ; we can start adding data ; RDOK: LD HL,OUTBUF ; point to start of output buffer LD B,128 ; get length of output buffer SCAN: LD A,(HL) CP "Z"-40H ; have we found end of file? JR Z,RESCR ; yes, save pointers and reset cr INC HL DEC B JR NZ,SCAN ; no, keep looking til end of buffer ; ; rescr reset current record ; ; if we find an explicit eof mark in the last buffer (or an ; implied eof if the last record is full), move the fcb record ; and extent pointers back to correct for the read operation ; so that our first write operation will effectively replace ; the last record of the spell.lex file ; RESCR: PUSH HL ; save eof buffer pointer PUSH BC ; save eof buffer remaining LD HL,OUTFCB+32 ; get current record again DEC (HL) ; dock it JP P,SAMEXT ; if cr >=0, still in same extent LD HL,OUTFCB+12 ; else, move to previous extent DEC (HL) LD C,OPENF CALL BDOS ; then, reopen the previous extent INC A JR Z,APERR ; append error if we can't reopen LD A,(OUTFCB+15) ; position to last record of extent DEC A LD (OUTFCB+32),A SAMEXT: POP AF ; recall where eof is in buffer LD (BUFCNT),A ; and set buffer counter POP HL ; recall next buffer pointer LD (BUFPNT),HL ; set pointer for first addition ; ; ptable ; ; print misspelled words from list ; PTABLE: LD B,0 LD IX,WORDS ; start PTLOOP: BIT WFLGSC,(IX+WFLGS) ; is this one correct? JR NZ,PNEXT ; yes, don't print it CALL PWORD ; print the word PNEXT: BIT WFLGSL,(IX+WFLGS) JR NZ,PTABR LD A,(IX+WLEN) ; get length this entry ADD A,4 LD C,A ADD IX,BC JP PTLOOP ; try again PTABR: RET ; ; pword ; ; print word pointed to by ix ; PWORD: PUSH IX LD B,(IX+WLEN) PWLOOP: LD E,(IX+WORD) ; a character CALL TYPE DEC B JR Z,CRLF INC IX ; next character JP PWLOOP CRLF: LD E,CR CALL TYPE LD E,LF CALL TYPE POP IX RET ; ; type ; ; output character in e to console and (optionally) to ; output file and/or to printer ; TYPE: PUSH BC PUSH DE ; save the character to output LD C,PCHAR CALL BDOS ; send it to console POP DE ; restore the output character LD B,E ; save character to b LD A,(FOPFLG) ; is file output active? OR A JR NZ,NOWRIT ; no, bypass file output ; ; file output mode active ; ; make sure we have room in buffer to add next character ; ; if buffer full, write out current record first and then ; start a new record with current character ; LD HL,(BUFPNT) ; get current buffer pointer LD A,(BUFCNT) ; get buffer capacity remaining OR A JR NZ,PUTBUF ; continue if buffer not full LD DE,OUTFCB ; otherwise, write current buffer LD C,WRITEF PUSH BC CALL BDOS ; (call must save character in b) POP BC OR A JP NZ,WRTERR ; error exit if disk full or r/o LD HL,OUTBUF ; reset buffer pointer LD A,128 ; reset buffer capacity ; PUTBUF: LD (HL),B ; shove char to next buffer position INC HL ; bump buffer pointer LD (BUFPNT),HL ; and save it DEC A ; dock count of chars left in buffer LD (BUFCNT),A ; and save it NOWRIT: LD E,B LD C,LISTC ; set up list output call LD A,(POPFLG) ; is printer output active? OR A CALL Z,BDOS ; yes, list character on printer POP BC RET ; ; clzout ; ; we've finished all of our outputting ; flush the remainder of the output buffer and close the ; file before making our exit ; CLZOUT: LD A,(FOPFLG) ; is file output active? OR A JP NZ,BOOT ; no, exit from program LD HL,BUFCNT LD A,(HL) ; get # of unflushed chars in buffer OR A ; if bufcnt=128, empty so set sign bit JP M,CLOZE ; close spell.lex if buffer is empty JR Z,FLUSH ; write last record if buffer full ; LD HL,(BUFPNT) ; else, pad unused buffer with ctrl-zs PUTAGN: LD (HL),"Z"-40H INC HL DEC A JR NZ,PUTAGN ; continue until buffer filled out ; FLUSH: LD DE,OUTFCB ; flush the last output buffer LD C,WRITEF CALL BDOS OR A JP NZ,WRTERR CLOZE: LD DE,OUTFCB ; close the output file LD C,CLOSEF CALL BDOS JP BOOT ; exit ; ; erxit ; ; abort program on output file error and define error ; ERXIT: POP DE ; get pointer to message string LD C,PSTRING CALL BDOS ; print it LD DE,DSKERR ; print " ERROR" LD C,PSTRING CALL BDOS JP BOOT ; exit ; ; DS 64 STACK: DS 1 ENDMEM: DS 2 DEFFCB: DB 0," LEX",0,0,0,0 LFCB: DS 33 LFCBCR EQU LFCB+32 LFCBEX EQU LFCB+12 LFCBS1 EQU LFCB+13 LFCBS2 EQU LFCB+14 LFCBRC EQU LFCB+15 LFCBDN EQU LFCB+0 LFCBFN EQU LFCB+1 LFCBFT EQU LFCB+9 IBP: DS 1 IBPL: DS 1 WORK: DS 2 BYTE: DS 1 LRNIB: DB 0 COMP: DB 0 BUFF2: DS 128 ZCOUNT: DB "0000 " NUMWD: DB "0000 distinct words in text.",CR,LF,"$" NUMWDC EQU NUMWD+3 LNGLX1: DB "Lexicon word '$" LNGLX2: DB "' longer than 29 characters.",CR,LF,"$" LNGWD1: DB "Text word '$" BADLEX: DB "Error in compacted lexicon.",CR,LF,"$" NINPUT: DB "Input file not specified or non-existant.",CR,LF,"$" NOLEX: DB CR,LF,"Lexicon file not specified or non-existant." DB CR,LF,"$" CHECKM: DB CR,LF,"Begin spelling check pass...",CR,LF,"$" ASKLEX: DB "Enter lexicon file name (.LEX assumed) or 'return' " DB BELL,CR,LF,"$" COPYR: DB CR,LF,"Poor Person Speller (c) 1981, Alan Bomberger" DB CR,LF,CR,LF,"$" CWORD: DS 34 DB "$" CFLAGS EQU CWORD CLEN EQU CWORD+1 CCHN EQU CWORD+2 CCHN1 EQU CWORD+3 WFLGS EQU 0 WLEN EQU 1 WCHN EQU 2 WCHN1 EQU 3 WORD EQU 4 WFLGSL EQU 7 WFLGSC EQU 6 WFLGSP EQU 5 ; FOPFLG: DB "F" ; file output option flag POPFLG: DB "P" ; printer output option flag ; BUFPNT: DW OUTBUF ; next location in output buffer BUFCNT: DB 128 ; number bytes left in output buffer OUTFCB: DB 0,"SPELL LEX" DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 OUTBUF: DS 128 ; output file buffer DSKERR: DB " ERROR",CR,LF,"$" ; DELIMT: DB " .,:;'""-?!/()[]{}",CR,LF,9 DB 0,0,0,0,0,0,0,0 DELIML: DB DELIML-DELIMT-8,0 ALPHA: DB "ABCDEFGHIJKLMNOPQRSTUVWXYZ" DB "abcdefghijklmnopqrstuvwxyz" DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ALPHAL: DB ALPHAL-ALPHA-20,0 T1: DB "EISNATR" DB "OLDCUGP",CR T2: DB "MHBYFVW" DB "KZXQJ",1AH ALPHC: DS 54 WORDS: COMWDL: DB 192,1,0,0,"A" END 100H