home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
cpm
/
programs
/
ppspell
/
spell11.aqm
/
SPELL11.ASM
Wrap
Assembly Source File
|
1985-02-09
|
24KB
|
1,145 lines
.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 <crlf>. 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