home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
commercial-software
/
programming
/
Z80TOOLS.ZIP
/
DISK3.ZIP
/
SEQFILE1.ANT
next >
Wrap
Text File
|
1998-07-30
|
99KB
|
3,582 lines
name 'FCKPT'
title 'FCKPT -- ensure file data is on disk'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This module ensures that all a file's data is written to disk
; and its directory entries are up to date. Doing so ensures
; that a file-size call will return accurate data and that, if
; the program crashes later, at least the current data will
; exist.
;
; interface: FCHECKPT macro
;
; History
; initial code 23 April 84
;===============================================================
maclib equates
maclib services
maclib smallz80
maclib fcr
extrn @FDOUT,@BDOS
public @FCKPT
@FCKPT:
push psw
push b
push h
push d
xtix
; If the file is really a device, do nothing.
bitx FcrDisk,FcrFlags
jrz exit
; Make sure any modified data is on disk. Afterward, the FCB
; will be positioned to the record following Bufeod (which we
; don't change). That defines the Sequential Input Condition
; so we set NotEof true to signal it.
call @FDOUT ; write any new data
bsetx FcrNotEof,FcrFlags ; not at eof any more
; The buffer is (now) clean, so we can close the file. We set
; attribute f5', which causes CP/M Plus to do a temporary, or
; checkpoint, close. CP/M 2 will ignore it.
clean:
bsetx Bit7,FcrName+4 ; attribute f5'
mvi c,BdosClose
call @BDOS ; (DE still ->FCR)
bresx Bit7,FcrName+4
; That's it, restore regs and exit.
exit:
xtix
pop d
pop h
pop b
pop psw
ret
end
bresx Bit7,FcrName+4
; That's it, restore regs and exit.
exit:
xtix
pop d
pop h
pop b
pop name 'FDIRS'
title 'FDIRS -- do directory search'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This module takes two FCRs. The first, addressed by DE,
; contains a (possibly ambiguous) filespec which is the search
; argument. It is presented to the BDOS with a request for a
; directory search.
;
; If the search is successful, the BDOS returns a disk directory
; entry for a matching file. We move the first 12 bytes of that
; entry to the second FCR, based on HL, and refresh the rest of
; the target FCR so that it may, for instance, be opened for
; input, or its filespec extracted as a string (@FSTSP), etc.
;
; On return, A is nonzero and Z false if a matching file was
; found, or zero and Z true if none was found.
;
; Note this module does not allow the full liberty of the Bdos
; search functions. It limits the results to a single return
; for each file rather than one return for each extent, and it
; blocks the use of the "global" search that extends over all
; types of directory entries and all user numbers.
;
; interface: FDIRBEGN and FDIRCONT macros
;
; History
; initial code 16 April 84
;===============================================================
common /FSWORK/
; work area shared with the decimal-output modules
workstr ds 128 ; used as buffer for file search
cseg
maclib equates
maclib services
maclib smallz80
maclib fcr
dseg
arg dw 0 ; save ->argument FCR
targ dw 0 ; save ->target FCR
cseg
extrn @ADDAH,@BDOS
public @FDIR1,@FDIR2
; The only difference between the two entries is in the number
; of the Bdos service request they issue.
@FDIR1:
push b ; save work register
mvi c,BdosSrch1 ; set service number
jr mutual
@FDIR2:
push b
mvi c,BdosSrchn
mutual:
push b ; save Bdos number for later
shld targ ; save ->target FCR
xchg
shld arg ; and ->argument FCR
; For this module's specified purposes we can't allow the
; global directory search, which returns every kind of entry
; including the disk label, password records, and timestamp
; records as well as filespecs from all user numbers. You
; can program your own if you want those abilities.
mov a,m
cpi '?' ; drivecode of "?" is global
jrnz okspec
mvi m,0 ; force it to default drive
okspec:
; We want only one directory entry for each matching file,
; therefore we set a zero in the extent number of the argument
; file control block.
lxi d,FcrExtent
dad h ; HL->argument extent number
mvi m,0 ; make it zero
; Make our common work area the file buffer -- that's where
; the Bdos will return a block of four directory entries.
lxi d,workstr
mvi c,BdosSetBuffer
call @BDOS
; Try for a match. The Bdos returns A=FFh if there is none.
pop b ; C=search first or search next
lded arg ; DE->argument fcb
call @BDOS
; If there was no match, set Z true and exit
inr a
jz exit
dcr a
; There was a match, and A = 0/1/2/3 depending on which of the
; four directory entries in the block matched. Convert that
; into an offset of 0/32/64/96 bytes and get HL pointing to
; the correct directory entry.
add a ; *2
add a ; *4
add a ; *8
add a ; *16
add a ; *32
lxi h,workstr
call @ADDAH
; Point DE to the target FCR and copy in the filespec from
; the received directory entry. The relevant bytes are the
; filename and filetype -- we copy the user code as well
; because it's simpler, but we'll fix that in a minute.
lded targ
lxi b,12 ; drivecode/user, filename, filetype
ldir
; Clean up the target FCR so it can be opened without error:
; copy the search drivecode from the search FCR, blank the
; password field, and zero the flags.
lded arg ; restore DE->argument FCR
lhld targ
ldax d
mov m,a ; set the drivecode
lxi b,FcrFlags
dad b
mvi m,0 ; zero the flags
lxi b,FcrPassword-FcrFlags
dad b
mvi m,AsciiBlank ; blank password
; Force the Z flag false to reflect our success -- the high
; byte of the target FCR address has to be nonzero.
mov a,h ! ora a
; Restore the registers and exit.
exit:
lded arg
lhld targ
pop b
ret
end
has t name 'FATTRS'
title 'FATTRS -- get/put file attributes'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; These routines either return the important attributes of the
; file represented by DE->FCR, or install a new set of them.
; Attributes are represented as a bit-mask in register A -- this
; is for both convenience and for compatibility with future
; systems. CP/M supports seven file attributes: f1' thru f4'
; (used in MP/M as "compatibility attributes"), plus the RO,
; SYS, and Archive attributes. Names for these -- as they are
; passed in A -- appear in EQUATES.LIB.
;
; interface: FGETATTR and FPUTATTR macros
;
; History
; initial code 11 May 84
;===============================================================
maclib equates
maclib smallz80
maclib services
maclib fcr
extrn @BDOS
; Scan the filespec in DE->FCR, collecting attribute bits.
public @FGATT
@FGATT:
push b
push d
lxi b,(4*256)+0 ; B=4, C=0
gloop1: inx d ; DE->first/next filename byte
ldax d
ral ; attribute bit to carry
ralr c ; push into low end of C
djnz gloop1 ; repeat for f1, f2, f3, f4
; Attributes f5' - f8' can be set and can be checked by getting
; the filespec from the directory, but are not passed at open
; time and are little-used. We ignore them in order to keep the
; number of bits to pass less than 8.
inx d ; DE->f5
inx d ; f6
inx d ; f7
inx d ; f8
mvi b,3 ; set to loop over filetype
gloop2: inx d ; first/next filetype byte
ldax d
ral
ralr c
djnz gloop2
mov a,c ; return bits in A
pop d
pop b
ret
; Reverse the above process, putting bits back into the
; bitfields of the filespec -- then write to directory.
@FPATT:
push psw
push h
push b
push d ; save ->FCR last
mov c,a ; keep bits in C
lxi h,FcrType+2
dad d ; HL->byte t3
mvi b,3
ploop1: mov a,m ; pick up filespec byte
add a ; discard high bit
rarr c ; next bit from C to carry
rar ; and into filespec byte
mov m,a ; and back to filespec
dcx h ; HL->t2, t1, f8
djnz ploop1
dcx h ; HL->f7
dcx h ; f6
dcx h ; f5
mvi b,4
ploop2: dcx h ; HL->f4, 3, 2, 1
mov a,m
add a
rarr c
rar
mov m,a
djnz ploop2
; Setting attributes requires a password if the file is
; protected at any level. Set the FCRPassword field as the file
; buffer so that if it contains a password the Bdos will see it.
; (Makes no difference in CP/M 2.2)
lxi h,FcrPassword
dad d
xchg ; DE->password
mvi c,BdosSetBuffer
call @BDOS
pop d ; restore DE->FCR
push d ; ..and save again
; Errors here include disk i/o, read-only disk, invalid drive
; number, password error, and ambiguous filespec. All will
; cause us to be aborted.
mvi c,BdosSetAttr
call @BDOS
; If the file didn't exist, A=FFh. That is probably a logic
; error, but what should be done about it? It doesn't seem
; serious enough to abort the program, so we'll ignore it.
pop d
pop b
pop h
pop psw
ret
end
name 'FQEOF'
title 'FQEOF -- lookahead test for end of file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Look ahead to see if the next byte input will encounter end
; of file. If the file is at physical eof, return Z true and
; A = 00h. If not, return Z false and A = next byte (so that
; the caller can test for logical eof).
;
; If the file is really a device, return physical eof signal.
; This is allows a program to avoid trying to read binary data
; from a device file which has no true physical eof.
;
; interface: FEOF? macro
;
; History
; initial code 26 June 84
;===============================================================
maclib equates
maclib smallz80
maclib fcr
extrn @FILLB
public @FQEOF
@FQEOF:
push d
xtix
xra a ; anticipate eof-return
; If the file isn't open, or is positioned in a hole, or isn't
; readable (e.g. PUN:), or if eof has already been reported,
; return true.
bitx FcrNotEof,FcrFlags
jrz done
; If the file is a device, even a readable one, return physical
; eof signal.
bitx FcrDisk,FcrFlags
jrz done
; It's a disk capable of input, so load up the buffer pointer
; in DE, and see if it is less than the size of buffered data.
ldbwx d,FcrBufptr
mov a,d
cmpx FcrBufeod+1
jrc havedata ; (high byte is less)
mov a,e
cmpx FcrBufeod
jrc havedata ; (low byte is less)
; The current buffer-load, at least, has been consumed. Try to
; load some more and, if we fail, report physical eof.
call @FILLB ; reads, sets NotEof, resets Bufptr
xra a
bitx FcrNotEof,FcrFlags ; now eof?
jrz done
lxi d,0 ; bufptr is zero now
; There is data remaining in the file and it's in the buffer
; at offset DE. Pick up the next byte (but don't advance the
; buffer pointer -- this isn't an official input). Then force
; Z false by testing NotEof ("ora a" wouldn't be reliable as
; the byte might be a valid null).
havedata:
push h
ldbwx h,FcrBufadr
dad d
mov a,m
pop h
bitx FcrNotEof,FcrFlags
; Z and A are set for exit.
done: xtix
pop d
ret
end
crBufadr
name 'FSGBW'
title 'FSGBW -- read integers from file as decimal'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Read decimal digits from the file represented as DE->FCR as
; a binary word. Method is to skip over whitespace to a
; nonwhite byte, then convert decimal digits to binary until a
; nondigit is seen. Return the binary value in HL, the byte
; that stopped the scan in A, and Z TRUE if that is end of file.
;
; The entry @FSGBW reads an unsigned word. Entry @FSGSW reads
; a word that may be prefixed with a sign character.
;
; Note: no check is made for overflow. If more than five
; significant digits appear, the result will be wrong.
;
; interface: FGETADBW, FGETADSW macros
;
; History
; initial code 16 April 84
;===============================================================
maclib equates
maclib smallz80
extrn @FSKIP,@FSGAC,@CPLBW
public @FSGBW
@FSGBW:
lxi h,0 ; clear cumulative result
call @FSKIP ; get past whitespace
cnz convert ; convert digits if not eof
cpi CpmEof ; set Z for end of file
ret ; and exit
@FSGSW:
lxi h,0 ; clear cumulative result
call @FSKIP ; skip whitespace, get 1st byte
rz ; exit with that if end of file
push b ; save work register
mvi b,'+' ; assume no sign given
cpi '-' ; if it is a minus
jrz issign
cpi '+' ; ..or a plus
jrnz notsign
issign: mov b,a ; ..save it
call @FSKIP ; ..and skip to next nonwhite byte
jrz nomore ; ..and exit with zero on eof
notsign:
call convert ; convert digits starting with [A]
mov c,a ; save last byte
mov a,b ; examine the sign
cpi '-' ; if it's minus,
cz @CPLBW ; complement the result
mov a,c ; recover scan-stopper
nomore:
pop b ; restore work register
cpi CpmEof ; set Z for end of file
ret
; here we convert decimal digits to binary, starting with
; the byte in A (result of calling @FSKIP) and continuing
; until a nondigit or end of file.
convert:
push b ; save work register
Loop:
cpi '9'+1
jrnc done ; (greater than 9)
cpi '0'
jrc done ; (less than 0)
sui '0' ; reduce to binary-coded decimal
mov b,h ! mov c,l ; copy the partial result
dad h
dad h ; result times 4
dad b ; result times 5
dad h ; result times 10
mov c,a
mvi b,0 ; BC=000d
dad b ; add in new digit
call @FSGAC ; get next digit (?)
jnz Loop ; continue if not eof
done: pop b
ret
end
d
dad b ; add in new digit
call @FSGAC ; get next digit (?)
jnz Loop ; continue if not name 'FSGXW'
title 'FSGXW -- read word from file as hex digits'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Read a binary word in the form of hex digits from the file
; represented by DE->FCR. Method is to skip over whitespace
; to a printable character or end of file. Then read and convert
; hex digits until a non-digit is found. Return the resulting
; binary word in HL, the byte that stopped the scan in A, and
; Z TRUE if that was end of file.
;
; Note: there is no check for overflow -- if there are more than
; four hex digits, HL will reflect the least significant four.
;
; interface: FGETAXBW macro
;
; History
; initial code 16 April 84
;===============================================================
maclib equates
extrn @FSKIP,@FSGAC,@CKHEX
public @FSGXW
@FSGXW:
call @FSKIP ; skip whitespace, return 1st nonwhite
lxi h,0 ; initialize result
rz ; quit with that, if eof.
Loop:
call @CKHEX ; see if hex, and make uppercase if so
jnz exit ; not hex, time to stop
cpi 'A' ; separate 0..9 from A..F
jc isnum ; (is numeric digit)
sui 'A'-'9'-1 ; make A..F contiguous with digits
isnum: ani 0fh ; isolate binary value
dad h
dad h
dad h
dad h ; shift HL left by 4 bits
ora l
mov l,a ; and install new digit
call @FSGAC ; read next digit (?)
jnz Loop ; ..and continue if not eof
exit:
cpi CpmEof ; set Z true if end of file
ret
end
; read next digit (?)
jnz Loop name 'FSKIP'
title 'FSKIP -- advance input file to nonwhite byte'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Advance the file represented by DE->FCR until the current byte
; is neither a blank nor a tab. The first nonwhite byte (which
; may be CpmEof) is returned. That byte has been consumed, so a
; following call on a "get" module will return the NEXT byte.
;
; interface: FSKIPWHT macro
;
; History
; initial code 16 April 84
;===============================================================
maclib equates
maclib smallz80
extrn @FSGAC
public @FSKIP
@FSKIP:
loop:
call @FSGAC ; get a byte and
rz ; ..return if it is end of file
cpi AsciiBlank ; no? repeat if it's blank
jrz loop
cpi AsciiTAB ; ..or a tab
jrz loop
ret ; else return it
end
cpi AsciiBla name 'FSGST'
title 'FSGST -- get string from ASCII file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Read a line from a (presumably Ascii) file and format it as a
; string. The macro interface prepares these parameters:
; DE->FCR
; HL->string space
; BC = maximum length
; We return:
; HL->the end of the string
; BC = residual count
; A = last byte processed, which will be one of:
; CR, indicating end of line found
; ^Z, indicating string ended by end of file
; other, when the count is exhausted first
; Z flag reflecting end of file.
;
; interface: FGETSTR macro
;
; History
; initial code 11 April 84
;===============================================================
maclib equates
maclib smallz80
maclib fcr
maclib services
extrn @FSGAC,@BDOS
dseg
crlf db AsciiCR,AsciiLF,'$'
cseg
public @FSGST
@FSGST:
push d
xtix
; First, eliminate the error of a zero count in BC.
mov a,b
ora c
jrz exit
; Decrement the count so that when (if) it goes to zero, there
; will be room for the terminating NUL. If that brings it to
; zero, build a null string and exit.
dcx b
mov a,b
ora c
jrz setnull
; Eliminate the case of immediate end of file.
bitx FcrNotEof,FcrFlags
jnz noteof
mvi a,CpmEof ; set A to terminating byte
jr setnull ; ..and build null string
; The file has data and the count allows at least one byte
; in the string. Choose a method based on the kind of file.
noteof:
bitx FcrDisk,FcrFlags
jrz isdevice
call Disk ; handle disk input
jr setnull ; ..and finish string
isdevice:
bitx FcrAux,FcrFlags
cnz Aux ; handle input from AUX
bitx FcrCon,FcrFlags
cnz Con ; handle input from CON
; The string data (if any) has been moved and HL updated to the
; place for the terminal NUL. A = the last byte processed. The
; count has been decremented. If the last byte read was CR, set
; the LastCR flag. Then finish the string and exit.
setnull:
cpi AsciiCR
jrnz setnull2
bsetx FcrLastCR,FcrFlags
setnull2:
mvi m,0
exit:
cpi CpmEof ; set Z for end of file
xtix
pop d ; restore IX, DE
ret ; and exit
; For AUX and disk input, we can't seem to do any better than
; making repeated calls on @FSGAC, the Ascii byte-getter. In
; the case of AUX we would have to duplicate most of @FSGAC
; here anyway. With a disk file, the complexities of juggling
; buffer-variables, string address, count, and the CR/LF logic
; make things so complicated I couldn't save any time.
AUX:
Disk:
call @FSGAC ; get one byte from file
rz ; if end of file, done
cpi AsciiCR ; if CR, also done
rz
mov m,a ; stow the byte
inx h ; ..step over it
dcx b ; ..count it
mov a,b ! ora c ; test the count
jnz Disk ; continue while room
dcx h ; count exhausted,
mov a,m ; recover last byte
inx h ; aim at place for NUL
ret ; and stop.
; For the CON device, however, we want to allow the keyboard
; operator the full range of CP/M line editing, so we will use
; Bdos function 10, edited line input.
CON:
; We have to tell the Bdos how many bytes to read. The maximum
; is 255. We want to tell it the lesser of: the string-size,
; the file's buffer-size, and 255.
push b ; save string count
push h ; save string address
ldbwx h,FcrBufsize
dcx h ; allow for 2 bytes of
dcx h ; Bdos parameters
push h ; save buffer size
ora a ; (clear carry)
dsbc b ; HL = bufsize - string size
pop h ; (recover buffer size)
jrc useHL ; bufsize < count, use bufsize
mov h,b ! mov l,c ; count <= bufsize, use count
useHL:
mov a,h ! ora a ; if result > 255,
jrz underFF ; (isn't)
mvi l,255 ; use just 255.
underFF:
; Ok, we have in L the number of bytes we can let the Bdos
; read for us. Prepare a Bdos line buffer in the file buffer.
ldbwx d,FcrBufadr ; DE->file buffer
mov a,l
stax d ; set max size for Bdos
push d ; save buffer address
; Read an edited line and, when we get control back, echo
; a CR and LF to the console.
mvi c,BdosLineIn
call @BDOS ; read edited line
lxi d,crlf
mvi c,BdosString
call @BDOS ; write cr, lf
; The buffer contains (length) (count received) data...
; Our job now is to copy that to the string space. However,
; to be true to our definition of an Ascii file, we have to
; examine each byte for CpmEof. If one appears, we have to
; break off copying at that point.
pop h ; HL->buffer
pop d ; DE->string space
inx h ; HL->count of received bytes
mov a,m ! ora a ; guard against null line
jrz nulline ; (yes, just a return)
mov b,a ; set loop-limit
mvi c,0 ; initial count of bytes moved
inx h ; HL->first data byte
copy:
mov a,m ! inx h
cpi CpmEof ; look for eof in typed data
jrz hiteof ; ..and break off if found
stax d ! inx d ; store gotten byte
inr c ; count byte moved
djnz copy ; and repeat till done
nulline:
mvi a,AsciiCR ; fake the ending return
hiteof:
pop h ; HL=size of string space
mvi b,0 ; BC=count of bytes received
ora a
dsbc b ; HL=residual count
mov b,h ! mov c,l ; BC=residual count
xchg ; HL->byte after the last one
ret ; all done
end
; HL=residual count
mov b,h ! mov c,l ; BC=residual count
xchg ; HL->byte after the last o name 'FSGAC'
title 'FSGAC -- get next ascii character'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Return the next character from the file represented by DE->FCR
; This is a logical, textfile get: it respects ^Z as logical
; end of file, and it swallows the LF that follows a CR so that
; CR is the only new-line signal seen.
;
; Special provision is made for nonstandard ascii files that end
; with a ^Z that is NOT preceded by CR/LF. A false CR is
; generated prior to ^Z if one didn't occur naturally.
;
; These two features are simple to explain but very hard to get
; right for all cases. Early versions would swallow all LFs
; after a CR, when only the first should disappear, and if
; called again after reporting eof, would generate an extra CR
; due to mishandling the LastCR flag. The processing for all
; the special cases can be expensive, too, but after lots of
; tuning, the path for ordinary printable chars -- 85-90% of
; all calls -- has been reduced to just 11 instructions.
;
; interface: FGETCHAR macro
;
; History
; complete rewrite for correctness and speed 6/12/85
; don't try for LF after CR from the keyboard 5/11/84
; initial code 28 March 84
;===============================================================
maclib equates
maclib smallz80
maclib fcr
extrn @FSGBY
public @FSGAC
@FSGAC:
push d
xtix
call @FSGBY
; If physical eof is reported, the NotEof flag has been set
; false but we need to convert it to logical eof. Since
; that happens only once per file (usually, although doing
; a seek can reset NotEof true), take it out of line.
jrz PhysEof
; If the input is a control character, we have to check
; for CR, LF, and ^Z. Since control chars are very much
; a minority, take these checks out of line.
cpi ' '
jrc CtlChar
; Reach this point whenever it is known that the byte to be
; delivered is NOT a CR, and there is any doubt that LastCR
; may not reflect that fact properly.
NotaCR:
bresx FcrLastCR,FcrFlags
; Reach this point whenever the state of the Zero flag is not
; known, to set it from the value of the returned byte (note:
; NOT from the NotEof bit as usual, since we might be returning
; a CR after eof has been seen).
SetZFlag:
cpi CpmEof
; Reach this point when the state of the Zero flag is known
; for sure to be correct, and the LastCR flag is known for
; sure to be correct as well.
ZFlagNowSet:
xtix
pop d
ret
;======= end of normal path taken by 80-90% of all calls =======
; Physical eof reported, either because it occurred or because
; somebody -- maybe us, on previously detecting input ^Z -- set
; the NotEof flag false. There's a NUL in A; make it a ^Z and
; go on to test that as a control character, which it is.
PhysEof:
mvi a,CpmEof
; The input is a control character, and has to be checked for
; special cases. First ask, is it a CR?
CtlChar:
cpi AsciiCR
jnz TestEof
; Reach this point when the byte to return is a CR, and the
; LastCR flag may not reflect that. Set it, and exit.
IsaCR:
bsetx FcrLastCR,FcrFlags
jmp SetZFlag
; See if the control character is CpmEof.
TestEof:
cpi CpmEof
jnz TestLF
; It is. Set the NotEof flag, thus preventing further input,
; effectively converting logical eof to physical eof (it may
; have been physical in the first place). Then, if the prior
; input was CR (and LF), the file is over: return the ^Z.
bresx FcrNotEof,FcrFlags
bitx FcrLastCR,FcrFlags
jnz SetZflag
; The end of file was not preceded by a line-end. Invent a
; CR to return. Since NotEof is now false, the next request
; for input will see physical eof, will make that into ^Z,
; will end up taking the jump just above.
mvi a,AsciiCR
jmp IsaCR
; See if the control character is LF. If not, it's just data.
TestLF:
cpi AsciiLF
jnz NotaCR
; It is. If the LastCR flag is NOT set, this is a rogue LF --
; maybe we're reading an MBASIC source file -- but it gets
; treated as data, regardless. Since LF isn't CR and LastCR
; is false, we can go to SetZFlag not NotaCR.
bitx FcrLastCR,FcrFlags
jz SetZFlag
; LastCR IS set, so this LF is following a CR in the normal
; CP/M line-end pattern. We want to swallow it, which means
; reading another byte. We can't clear LastCR just yet, since
; we might get eof, which we'll return, then if we are recalled
; and get eof again, we'd generate a CR...
call @FSGBY
jnz NotPEOF
mvi A,CpmEof ; convert phys. to log. eof
NotPEOF:
; The expected result here is that we now have the first byte
; of the next line, and it's a printable char. Pick off that
; case first so as to save time.
cpi ' ' ; control char?
jnc NotaCR
; Next most likely result is a CR, from CR-LF-CR(-LF), the
; sequence of a null line. LastCR is set correctly for that.
cpi AsciiCR
jz SetZFlag
; Ok, if we DON'T have eof, we have a misc. control char, which
; is just data and not a CR.
cpi CpmEof
jnz NotaCR
; We had CR-LF-^Z, the normal end of file sequence. Ensure
; physical eof and return it (Z flag now set ok), leaving
; LastCR set true so if we're recalled we won't think we
; have to generate a CR.
bresx FcrNotEof,FcrFlags
jmp ZflagNowSet
The end
ue so if we're recalled we won't think we
; have to generate a CR.
bresx FcrNotEof,FcrFlags
jmp ZflagNowSet
The end name 'FSGBK'
title 'FSGBK -- get block of bytes from file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This module reads a block of bytes from a file to an area
; of storage. The file is represented by DE->FCR, the
; starting address of storage is in HL, and BC contains the
; size of the area in bytes.
;
; Data are moved directly from the buffer to storage using the
; Z80 instruction, LDI.
;
; On return, HL has been updated to the next byte in the area,
; and BC contains the count of bytes received (the same as the
; entry value, unless end of file is seen).
;
; Altho this code will handle device files, it isn't a good
; idea to read from a device in blocks. There is no such thing
; as "physical end of file" to tell us when to stop, so we are
; likely to hang, waiting for a next byte that is never coming.
;
; interface: FGETBLOK macro
;
; History
; initial code 11 April 84
;===============================================================
maclib equates
maclib smallz80
maclib fcr
extrn @FILLB,@BDOS
public @FSGBK
@FSGBK:
push d
xtix ; base the FCR
; Make sure input is possible.
bitx FcrNotEof,FcrFlags
jz exit ; oops, eof right now
; Make sure the count is greater than zero -- we don't want to
; write 65,536 bytes into storage.
mov a,b ! ora c
jz exit ; zero count, zero action.
; Separate disks and devices.
bitx FcrDisk,FcrFlags
jrnz isdisk
; For a device, we just make BC-many Bdos input calls.
push b ; save input count for exit
devloop:
push h ; save next byte address
push b ; save current count
ldx c,FcrBdosIn
call @BDOS ; get a byte
pop b
pop h
mov m,a ; store gotten byte
inx h
dcx b ; count it
mov a,b ! ora c ; is that it?
jnz devloop ; (no, continue)
pop b ; restore count of data read
jmp exit
; For a disk file, we have four values to juggle:
; the count of bytes remaining to be read
; the address of the next target byte
; the address of the next buffer byte
; the count of bytes left in the buffer.
; That is one more word than we have registers for, but not to
; worry. Thanks to the XTHL instruction, we can keep a word
; on top of the stack and have it nearly as accessible. What
; we will now set up is
; BC=count to go
; DE->next output byte
; HL->next buffer byte
; top of stack = count left in buffer
; In the loop, the last two values will alternate positions
; as each is needed in HL.
isdisk:
push b ; save initial count for exit
push h ; save initial target addr
ldbwx d,FcrBufptr
ldbwx h,FcrBufeod
ora a
dsbc d ; HL=count left in buffer
xthl ; put that on stack
push h ; re-save target addr
ldbwx h,FcrBufadr
dad d ; HL->next buffer byte
pop d ; DE->next target byte
Loop:
xthl ; HL=count of data in buffer
mov a,h ! ora l ; is it zero?
jnz dataleft ; (no)
; We have used up the file buffer and have to refill it. That
; could result in finding end of file. If not, we have to
; reconstruct our buffer address and buffer count.
call @FILLB
bitx FcrNotEof,FcrFlags
jz hiteof
ldbwx h,FcrBufadr ; new address of next byte
xthl ; ..on the stack
ldbwx h,FcrBufeod ; new count of data left
dataleft:
dcx h ; count down one buffer byte
xthl ; stack it, HL->byte
ldi ; copy from H to D reducing B
jpe Loop ; continue while BC>0
; We have moved all the bytes requested, BC=0000. We must
; reconstruct the correct Bufptr value for the FCR, then
; we can return the original BC value as the count.
pop b ; BC=bytes left in buffer
ldbwx h,FcrBufeod
ora a
dsbc b ; end of data, less bytes left
stbwx h,FcrBufptr ; is new Bufptr
pop b ; BC=original count
jmp diskexit
; We met end of file before moving all (maybe any) requested
; bytes. Since physical end of file was seen, @FILLB set
; Bufptr and Bufeod to zero. However, we have to compute the
; count of bytes actually moved for our exit BC value.
hiteof:
pop h ; discard buffer-byte-count
pop h ; HL=original count
ora a
dsbc b ; HL=bytes actually moved
mov b,h ! mov c,l ; BC=new count
; Regs BC have the count of moved bytes, DE->after last one
; moved. Put that in HL where it should be, set the Z flag
; to reflect end of file, and exit.
diskexit:
xchg
exit:
bitx FcrNotEof,FcrFlags
xtix
pop d
ret
end
e Z flag
; to ref name 'FSG24'
title 'FSG24 -- read 2 or 4 byte items from file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Read a binary word or longword from the (presumably binary)
; file represented by DE->FCR. If end of file intervenes, the
; most-significant bytes are returned as zero. The Z flag
; reflects end of file.
;
; interface: FGETLW macros
;
; History
; initial code 16 April 84
;===============================================================
extrn @FSGBY
; On entry to @FSG4B, HL->space for a longword in storage.
; We will just read 4 bytes regardless, counting on FSGBY
; to return nulls on and following end of file.
public @FSG4B
@FSG4B:
rept 4
call @FSGBY
mov m,a ! inx h
endm
ret ; return with last Z setting
; @FSG2B is similar, except that the word value is returned
; IN HL, not in storage AT HL.
public @FSG2B
@FSG2B:
call @FSGBY
mov l,a
call @FSGBY
mov h,a
ret
end
t name 'FSGBB'
title 'FSGBB -- get next byte from sequential file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Return the next byte from a file represented by DE->FCR. This
; is a binary get: it respects only physical eof (^Z is data),
; and it returns both CR and LF at ends of lines.
;
; If physical eof is seen, return A=NUL and Z true. Else return
; A=the byte and Z false.
;
; interface: FGETBYTE macro
;
; History
; initial code 28 March 84
;===============================================================
maclib smallz80
extrn @FSGBY
public @FSGBB
@FSGBB:
; set up IX for @FSGBY, the physical byte-getter. Call it. If
; it returns Z false (not eof) just return; otherwise set A=NUL.
push d ; stack ->FCR
xtix ; save IX, IX->FCR
call @FSGBY
xtix ; restore IX, stack ->FCR
pop d ; restore DE
rnz ; if not eof, exit w/ Z false
xra a ; when eof, force A=NUL
ret
end
store IX, stac name 'FSGBY'
title 'FSGBY -- get next physical byte'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This is the central sequential-input operation. It returns
; the next byte from the file represented by IX->FCR. It does
; not respect the special features of Ascii files; checking for
; ^Z and swallowing linefeeds is up to the caller. However, if
; we read from CON and receive a CR, we immediately echo an LF.
;
; interface: IX->FCR
; returns A=byte and Z false, or
; returns A unpredictable and Z true after eof
; destroys register DE
;
; History
; initial code 28 March 84
;===============================================================
maclib equates
maclib smallz80
maclib FCR
extrn @FILLB,@BDOS
public @FSGBY
@FSGBY:
bitx FcrNotEof,FcrFlags ; is there any input data?
rz ; (no, quit)
push d
push h
bitx FcrDisk,FcrFlags ; disk, or device?
jrz isDevice
; A disk file. Load up the buffer pointer into DE. If that is
; not less than the size of valid data, we have to fill the
; buffer first.
ldbwx d,FcrBufptr
ldbwx h,FcrBufeod
ora a
dsbc d ; flags := (HL := data left)
jnz havedata
call @FILLB ; fills buffer, resets bufptr
xchg ; (DE=new bufptr=0)
bitx FcrNotEof,FcrFlags ; now end of file?
jrz exit ; (yes, quit)
; The byte we need is Buffer[Bufptr]. Compute its address and
; move it. Then increment the buffer pointer.
havedata:
ldbwx h,FcrBufadr
dad d ; HL-> Buffer[Bufptr]
mov a,m ; got the byte
inx d
stbwx d,FcrBufptr ; save incremented pointer
; for either device or disk files, set Z false (by testing the
; NotEof bit) and exit.
exit: pop h
pop d
bitx FcrNotEof,FcrFlags
ret
; This is a device file. Get one byte by calling the Bdos with
; the appropriate function code. N.B. output only devices have
; NotEof false, they never get this far.
isdevice:
push b
ldx c,FcrBdosIn ; appropriate input request
call @BDOS ; ..gets a byte
pop b
cpi AsciiCR ; got a CR?
jnz exit ; (no, all done)
bitx FcrCon,FcrFlags ; from the keyboard?
jz exit ; (no, all done)
push psw ; yes, save gotten byte
push b ; ..and other regs
mvi c,2 ; (BdosType)
mvi e,AsciiLF ; ..and echo LF for the CR
call @BDOS
pop b
pop psw ; recover gotten byte
jmp exit ; (and exit)
end
i e,AsciiLF ; . name 'FSPBX'
title 'FSPBX -- put byte as hex to file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Write the byte in A to the file represented by DE->FCR, as
; hex digits. Method is to build a string of hex digits and
; to write that.
;
; interface: FPUTBBAX macro
;
; History
; initial code 16 April 84
;===============================================================
common /fswork/
; common work area for file operations
workstr ds 128 ; room for 127-byte string
cseg
extrn @STPBX,@FSPST
public @FSPBX
@FSPBX:
push h
lxi h,workstr
mvi m,0 ; make null string
xchg ; DE->string
call @STPBX ; create string
xchg ; DE->file
lxi h,workstr ; HL->string
call @FSPST ; write it
pop h
ret
end
call @STPBX ; create string
xchg ; DE->file
lxi h,workst name 'FSPWX'
title 'FSPWX -- put word as hex digits to file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Write the word in HL to the file represented by DE->FCR, as
; four hex digits. Method is to convert the word to a string
; and to write the string.
;
; interface: FPUTBWAX macro
;
; History
; initial code 16 April 84
;===============================================================
common /fswork/
; common work area for file operations
workstr ds 128 ; room for 127-byte string
cseg
extrn @STPWX,@FSPST
public @FSPWX
@FSPWX:
push h ; preserve HL for exit
push d ; preserve file address
push h ; save HL for conversion
lxi h,workstr
mvi m,0 ; make null string
xchg ; DE->null string
pop h ; HL=word to convert
call @STPWX ; convert word to string
pop d ; DE->file
lxi h,workstr ; HL->string to write
call @FSPST ; write it
pop h ; restore entry HL
ret
end
ng
pop d ; D name 'FSPLX'
title 'FSPLX -- put longword as hex digits to file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Write HL->longword to the file represented by DE->FCR as
; eight hex digits. Method is to convert the data to a string
; and to write the string.
;
; interface: FPUTLWAX macro
;
; History
; initial code 16 April 84
;===============================================================
common /fswork/
; common work area for file operations
workstr ds 128 ; room for 127-byte string
cseg
extrn @STPLX,@FSPST
public @FSPLX
@FSPLX:
push d ; preserve file address
push h ; save HL for conversion
lxi h,workstr
mvi m,0 ; make null string
xchg ; DE->null string
pop h ; HL=word to convert
call @STPLX ; convert word to string
push h ; save updated HL for exit
pop d ; DE->file
lxi h,workstr ; HL->string to write
call @FSPST ; write it
pop h ; restore updated HL
ret
end
pop name 'FSPBD'
title 'FSPBD -- put byte as decimal to file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Write the byte in A to the file represented by DE->FCR, as
; decimal digits. Method is to build a string of digits and
; write that to the file.
;
; interface: FPUTBBAD macro
;
; History
; initial code 16 April 84
;===============================================================
common /fswork/
; common work area for file operations
workstr ds 128 ; room for 127-byte string
cseg
extrn @STPBB,@FSPST
public @FSPBD
@FSPBD:
push h
lxi h,workstr
mvi m,0 ; make null string
xchg ; DE->string
call @STPBB ; create string
xchg ; DE->file
lxi h,workstr ; HL->string
call @FSPST ; write it
pop h
ret
end
call @STPBB ; create string
xchg ; DE->file
name 'FSPBW'
title 'FSPBW -- put unsigned word as decimal digits to file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Write the word in HL to the file represented by DE->FCR, as
; decimal digits in a field whose minimum size is in A. Method
; is to convert the word to a string and to write the string.
;
; interface: FPUTBWAD macro
;
; History
; initial code 16 April 84
;===============================================================
common /fswork/
; common work area for file operations
workstr ds 128 ; room for 127-byte string
cseg
extrn @STPBW,@FSPST
public @FSPBW
@FSPBW:
push h ; preserve HL for exit
push d ; preserve file address
push h ; save HL for conversion
cpi 128 ; ensure field-width will fit our
jc lenok ; work-string -- no fields over 127
mvi a,127
lenok:
lxi h,workstr
mvi m,0 ; make null string
xchg ; DE->null string
pop h ; HL=word to convert
call @STPBW ; convert word to string
pop d ; DE->file
lxi h,workstr ; HL->string to write
call @FSPST ; write it
pop h ; restore entry HL
ret
end
ng
pop d ; DE->file
lxi h,workstr ; HL->string to write
call @FSPST ; write it
pop h ; restore ent name 'FSPSW'
title 'FSPSW -- put signed word as decimal digits to file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Write the word in HL to the file represented by DE->FCR, as
; decimal digits in a field whose minimum size is in A. Method
; is to convert the word to a string and to write the string.
;
; interface: FPUTBWAD macro
;
; History
; initial code 16 April 84
;===============================================================
common /fswork/
; common work area for file operations
workstr ds 128 ; room for 127-byte string
cseg
extrn @STPSW,@FSPST
public @FSPSW
@FSPSW:
push h ; preserve HL for exit
push d ; preserve file address
push h ; save HL for conversion
cpi 128 ; ensure field-width will fit our
jc lenok ; work-string -- no more than 127
mvi a,127
lenok:
lxi h,workstr
mvi m,0 ; make null string
xchg ; DE->null string
pop h ; HL=word to convert
call @STPSW ; convert word to string
pop d ; DE->file
lxi h,workstr ; HL->string to write
call @FSPST ; write it
pop h ; restore entry HL
ret
end
ng
pop d ; DE->file
lxi h,workstr ; HL->string to write
call @FSPST ; write it
pop h ; restore entry H name 'FSPLD'
title 'FSPLD -- put longword as decimal digits to file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Write HL->longword to the file represented by DE->FCR, as
; decimal digits in a field whose minimum size is in A. Method
; is to convert the word to a string and to write the string.
;
; interface: FPUTLWAD macro
;
; History
; initial code 16 April 84
;===============================================================
common /fswork/
; common work area for file operations
workstr ds 128 ; room for 127-byte string
cseg
extrn @STPLW,@FSPST
public @FSPLD
@FSPLD:
push d ; preserve file address
push h ; save HL for conversion
cpi 128 ; ensure field-width will fit our
jc lenok ; work-string -- no more than 127
mvi a,127
lenok:
lxi h,workstr
mvi m,0 ; make null string
xchg ; DE->null string
pop h ; HL->word to convert
call @STPLW ; convert word to string
pop d ; DE->file
push h ; save updated HL
lxi h,workstr ; HL->string to write
call @FSPST ; write it
pop h ; restore updated HL
ret
end
h ; save updated HL
lxi h,workstr ; HL->string to write
call @FSPST ; write it
pop h ; restore updated HL
ret name 'FSPST'
title 'FSPST -- put string to ASCII file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This module writes HL->string onto the (presumably Ascii) file
; represented by DE->FCR, and returns HL->end of string.
;
; interface: FPUTSTR macro
;
; History
; initial code Friday, 13 April 84
;===============================================================
maclib equates
maclib smallz80
maclib fcr
extrn @FDUMP,@FRFIL,@BDOS
public @FSPST
@FSPST:
push psw
push b
push d
xtix
; Is the file capable of output?
bitx FcrOutput,FcrFlags
jz exit ; (no)
; Is the string null?
mov a,m
ora a
jz exit ; (yes)
; Is the file a disk or a device?
bitx FcrDisk,FcrFlags
jrnz isdisk
; For a device, we just make repetitive calls on the Bdos.
devloop:
ldx c,FcrBdosOut ; output function number
mov e,a ; byte to write
push h ; save ->string
call @BDOS ; (preserves BC, DE)
pop h
mov a,m ; oh, was that a CR?
cpi AsciiCR
jrnz devnotlf ; (no)
mvi e,AsciiLF ; yes, add an LF to it
push h
call @BDOS
pop h
devnotlf:
inx h ; now, the next byte
mov a,m ! ora a ; is it the end of string?
jnz devloop ; (no, continue)
jmp exit ; yes, done.
; Now, for a disk we want to take advantage of the file buffer
; to speed things up. The process is similar to that of @FSPBK
; except that, since the string is delimited, we don't have to
; worry about counting data bytes -- only buffer bytes. We will
; keep the count of buffer space in BC and the buffer address
; in DE. When the buffer fills up, we have to call @FDUMP and
; reconstruct those two values.
isdisk:
push h ; save string addr a mo.
ldbwx d,FcrBufptr ; count of bytes now in buffer
ldbwx h,FcrBufsize ; total space in buffer
dsbc d ; HL=space left in buffer
mov b,h ! mov c,l ; put that in BC
ldbwx h,FcrBufadr
dad d ; HL->next byte in buffer
xchg ; put that in DE
pop h ; HL->string
diskloop:
call checkbfr ; make sure we have room
mov a,m ; pick the byte up
stax d ! inx d ! dcx b ; and store it
cpi AsciiCR ; was it a CR?
jrnz disknotlf ; (no, continue)
call checkbfr ; ensure room for LF
mvi a,AsciiLF
stax d ! inx d ! dcx b ; and put one in
disknotlf:
inx h ; now the next byte
mov a,m ! ora a ; might be the end
jnz diskloop ; (no, continue)
; Adjust the file buffer-pointer to account for what we did.
push h ; save updated string-ptr
ldbwx h,FcrBufSize ; buffer size..
dsbc b ; less buffer space remaining
stbwx h,FcrBufptr ; is new Bufptr (carry clear)
xchg ; save it in DE
ldbwx h,FcrBufeod ; if we wrote past eod,
dsbc d ; (if bufptr > bufeod)
cc @FRFIL ; ..fill out new record
pop h ; recover string-ptr
bsetx FcrDirty,FcrFlags ; buffer now dirty
exit:
xtix
pop d
pop b
pop psw
ret
; This subroutine tests the buffer and, if it is full, dumps
; it and resets BC (room left) and DE (next output byte).
; It's a subroutine because it may have to be done twice for
; a byte (CR and again for LF).
checkbfr:
mov a,b ! ora c ; have we filled the buffer?
rnz ; (no)
ldbwx b,FcrBufsize ; yes, set data quantity for
stbwx b,FcrBufeod ; the buffer-dumper
bsetx FcrDirty,FcrFlags ; ensure buffer marked
call @FDUMP ; clear the buffer
ldbwx d,FcrBufadr ; BC=size of bfr, DE->1st byte
ret
end
ags ; ensure buffer marked
call @FDUMP ; clear the buffer
ldbwx d,FcrBufadr ; BC=size of bfr, DE->1st byte
ret
name 'FSPAC'
title 'FSPAC -- put ascii character to seq. file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Write the byte in A to the file represented by DE->FCR. This
; is an Ascii operation, so if the byte is CR, add an LF to it.
;
; interface: FPUTCHAR macro
;
; History
; initial code 30 March 84
;===============================================================
maclib equates
maclib smallz80
extrn @FSPBY
public @FSPAC
@FSPAC:
push d
xtix
call @FSPBY
cpi AsciiCR
jnz exit
push psw
mvi a,AsciiLF
call @FSPBY
pop psw
exit: xtix
pop d
ret
end
x
call @FSPBY
cpi AsciiCR
jnz exit
push psw
mvi a,AsciiLF
call @FSPBY
name 'FSPBK'
title 'FSPBK -- put block of bytes to file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This module writes a block of bytes from an area of storage
; to a file. The file is represented by DE->FCR, HL has the
; starting address of the block, and BC contains the size of the
; block in bytes (in other words, this is "LDIR to a file").
;
; Data are moved directly from storage to the buffer using the
; Z80 instruction, LDI. See comments later for efficiency.
;
; On return, HL has been updated to the next byte in the
; area. Other registers are unchanged (so that a sequence of
; calls will write a large contiguous area).
;
; interface: FPUTBLOK macro
;
; History
; initial code 11 April 84
;===============================================================
maclib equates
maclib smallz80
maclib fcr
extrn @FDUMP,@FRFIL,@BDOS
public @FSPBK
@FSPBK:
push psw ; save psw
push b ; ..and BC
push d
xtix ; base the FCR
bitx FcrOutput,FcrFlags ; can it do output?
jz exit ; nah, ferget it
mov a,b ! ora c ; is the count > 0?
jz exit ; no, don't write 65KB
bitx FcrDisk,FcrFlags ; is it a disk?
jrnz isdisk ; yes, yes, get on with it
; For device output we just do repetitive Bdos output calls.
devloop:
push b ; save count
push h ; save byte address
mov e,m ; byte to write
ldx c,FcrBdosOut ; Bdos service number
call @BDOS ; write one byte
pop h
pop b
inx h ; next byte
dcx b ; count last one
mov a,b ! ora c ; is that it?
jnz devloop ; (nope, continue)
jmp exit ; (yep, done)
; In disk output we have four values to juggle:
; the count of bytes remaining to be written
; the address of the next source byte
; the address of the next buffer byte
; the count of free bytes left in the buffer.
; That is one more word than we have registers for, but not to
; worry. Thanks to the XTHL instruction, we can keep a word
; on top of the stack and have it nearly as accessible. What
; we will now set up is
; BC=count to go
; DE->next buffer byte
; HL->next source byte
; top of stack = count left in buffer
; In the loop, the last two values will alternate positions
; as each is needed in HL.
isdisk:
push h ; save initial source addr
ldbwx d,FcrBufptr
ldbwx h,FcrBufsize
ora a
dsbc d ; HL=space left in buffer
xthl ; stack that, HL->source
push h ; re-save source address
ldbwx h,FcrBufadr
dad d
xchg ; DE->next buffer byte
pop h ; HL->next source byte
; The point of this elaborate code is to save time relative
; to a sequence of calls to @FSPBY. If we ignore the times
; when the buffer fills up, the following loop costs 88 clocks
; or 22 microseconds per byte moved (4MHZ). That is at least
; four times as fast as fast as @FSPBY, which uses 370 clocks
; per call in the best conditions.
Loop:
xthl ; HL=space left in buffer
mov a,h ! ora l ; is it zero?
jnz roomenuf ; (no)
; We have filled the file buffer and have to dump it. Before
; doing so we have to set things up the way they would be after
; as many calls to FSPBY: FcrDirty true and FcrBufeod equal to
; the size of the buffer. (In one very rare instance we will
; write a buffer-load needlessly, but no harm will result.)
; After the dump, we have to reset our target to the buffer's
; address and our count to its size.
ldbwx h,FcrBufsize ; HL=space in buffer later
stbwx h,FcrBufeod ; ..and data in buffer now
bsetx FcrDirty,FcrFlags ; buffer is (probly) dirty
call @FDUMP
ldbwx d,FcrBufadr ; DE->next buffer byte
roomenuf:
dcx h ; count down one buffer byte
xthl ; stack it, HL->source byte
ldi ; copy from H to D reducing B
jpe Loop ; continue while BC>0
; The data have been moved. Now we have to reconstruct the
; proper value of FcrBufptr and FcrBufeod. We also mark the
; buffer dirty, since we definitely changed it since it was
; last written.
pop d ; DE=space left (and stk clear)
push h ; save updated source addr
ldbwx h,FcrBufsize
ora a
dsbc d ; size - unused gives pointer
stbwx h,FcrBufptr
xchg
ldbwx h,FcrBufeod ; check: did we write past eod?
dsbc d
cc @FRFIL ; if so, fill out new record
bsetx FcrDirty,FcrFlags
; Restore the registers and exit.
pop h ; HL = updated source address
exit:
xtix
pop d
pop b
pop psw
ret
end
Restore name 'FSP24'
title 'FSP24 -- write 2 or 4-byte items to file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Write binary words and longwords into the (presumably binary)
; file represented by DE->FCR.
;
; interface: FPUTWORD and FPUTLW macros
;
; History
; initial code 16 April 84
;===============================================================
extrn @FSPBY
public @FSP2B,@FSP4B
; On entry to @FSP4B, HL->a longword in storage. We just shove
; it out to the file in standard (least significant to most)
; order -- which is how it is laid out in storage.
@FSP4B:
push psw
push h
rept 4
mov a,m ! inx h
call @FSPBY
endm
pop h
pop psw
ret
; @FSP2B is presented with a number IN the HL register to be
; written in low-high order.
@FSP2B:
push psw
mov a,l
call @FSPBY
mov a,h
call @FSPBY
pop psw
ret
end
be
; written in low-high order.
@FSP2B:
push psw
mov a,l
call @ name 'FSPBB'
title 'FSPBB -- put binary byte to sequential file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Writes the byte in A to the file represented by DE->FCR.
; There's hardly anything to do but set up for, and call, the
; FSPBY subroutine.
;
; interface: FPUTBYTE macro
;
; History
; initial code 30 March 84
;===============================================================
maclib smallz80
extrn @FSPBY
public @FSPBB
@FSPBB:
push d
xtix
call @FSPBY
xtix
pop d
ret
end
======
maclib smallz80
extrn @F name 'FSPBY'
title 'FSPBY -- put next physical byte'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This is the central sequential-put routine. It puts the byte
; in A to the file defined by IX->FCR. If the file buffer is
; full it is written to disk first, and if the byte is the first
; of a new record at the end of the file, the new record is
; filled and the end-of-data pointer updated.
;
; interface: IX->FCR, A=byte to write
; updates FcrBufptr, FcrBufeod
;
; History
; initial code 30 March 84
;===============================================================
maclib equates
maclib smallz80
maclib fcr
extrn @FDUMP,@FRFIL,@BDOS
public @FSPBY
@FSPBY:
push h
push d
push psw
bitx FcrOutput,FcrFlags ; output allowed?
jrz exit ; (no)
bitx FcrDisk,FcrFlags ; disk, or device?
jrz isdevice ; (device)
; The buffer-pointer has the offset to the next output byte. If
; it isn't less than the buffer size we have to dump the buffer.
; That resets the buffer-pointer.
ldbwx d,FcrBufptr
ldbwx h,FcrBufsize
ora a
dsbc d ; flags := (HL := size-ptr)
jnz roomenuf ; (ptr is less, carry on)
call @FDUMP ; dump and advance buffer
xchg ; DE=new bufptr of zero
; the new byte may now go at Buffer[DE=Bufptr]
roomenuf:
pop psw ; recover byte to write
push psw ; ..but save caller's flags
ldbwx h,FcrBufadr
dad d ; HL->Buffer[Bufptr]
mov m,a ; deposit byte in buffer
inx d ; increment the bufptr
stbwx d,FcrBufptr ; ..and store it
bsetx FcrDirty,FcrFlags ; buffer is dirty now
; If Bufptr has gotten beyond Bufeod, that byte started
; a new record. In that case we have to fill out the rest
; of the new record with fill bytes and advance Bufeod to
; its end, so that Bufeod always reflects the in-storage
; physical end of the file.
ldbwx h,FcrBufeod
ora a
dsbc d ; bufeod - bufptr
cc @FRFIL ; fill record if eod less
exit: pop psw
pop d
pop h
ret
; the file represents an output device. Write the byte by
; calling the Bdos for the appropriate service.
isdevice:
push b
mov e,a ; byte to be written
ldx c,FcrBdosOut ; Bdos output request number
call @BDOS ; ..writes the byte
pop b
jr exit
end
yte to be written
ldx c,FcrBdosOut ; Bdos output request number
call @BDOS ; ..writes the byte
pop b
jr e name 'FRECDA'
title 'FRECDA -- record-oriented direct access'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Contains 2 functions related to direct access by fixed-length
; records (as opposed to d.a. by byte-offsets as supported by
; FSEEK et. al.):
;
; FRLEN: set HL as the record size of DE->FCR
; FRSEK: seek to start of record number in HL
;
; interface: FRECLEN, FRECSEEK macros
;
; History
; initial code 24 July 84
;===============================================================
maclib environ
maclib fcr
; Stow HL in the FcrRecLen field as the record-length of this
; file. FILEDEF initializes it to 128. We do NOT check for a
; record length of zero or 65535 or other ridiculous value.
public @FRLEN
@FRLEN:
push d
push h
lxi h,FcrRecLen
dad d ; HL->field
pop d ; DE=value
mov m,e
inx h
mov m,d ; stored.
xchg ; restore HL=number
pop d
ret
; Set the file position so that the next access will go to the
; first byte of the record whose number is in HL.
;
; Record numbers are origin-1 for compatibility with BASIC, PL/I
; and most Pascals. In other words, when HL=1, we seek to the
; zeroeth byte of the file.
;
; Method is to multiply the record length by the record number
; less one. That yields a longword which we pass to FSEEK as
; a byte-offset. On exit, the Z flag (set by FSEEK) is TRUE
; if there is NO DATA at that position.
dseg
lw ds 4 ; scratch longword
cseg
public @FRSEK
@FRSEK:
push b
push h
push d
xtix ; address file via IX
ldbwx d,FcrRecLen
xtix ; restore IX
dcx h ; make recno origin-1
mpybw @H,@D ; DEHL=longword offset
lxi b,lw
storelw @B ; put in storage
mov h,b ! mov l,c; HL->RBA longword
pop d ; restore DE->FCR
fseek @D,@Hupdate ; aim the file
pop h
pop b
ret
end
ov h,b ! name 'FSIZE'
title 'FSIZE -- return size of file as longword'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Register HL points to space for a longword in storage, and
; DE->an FCR representing a file which may or may not be open.
; This module computes the size of the file as a 32-bit count
; of bytes.
;
; The count is 128 times the number of records reported by the
; CP/M Bdos -- which may not be an accurate count. An Ascii
; file might be as much as 127 bytes smaller than reported due
; to a ^Z byte in its last record. A file built by direct
; access operations may contain nonexistent holes.
;
; We checkpoint the file before taking the size so as to
; ensure an accurate reading on a new output file.
;
; If the Bdos can't find the file (if it's a device, say)
; a count of zero is returned.
;
; interface: FSIZE macro
;
; History
; preserve RRA when it is important 8/21/84
; initial code 16 April 84
;===============================================================
maclib equates
maclib services
maclib smallz80
maclib fcr
extrn @FCKPT,@BDOS
dseg
lwptr ds 2 ; save ->output longword
cseg
public @FSIZE
@FSIZE:
push psw
push b
push d
xtix ; IX bases the FCR
shld lwptr ; save ->longword
; If this is a presently-open, writable disk file, checkpoint it
; to ensure an accurate size. The test is for a writable file
; because we don't know if any output has been done.
rwdisk equ (1 shl FcrDisk)+(1 shl FcrOutput)
ldx a,FcrFlags
ani rwdisk
cpi rwdisk ; both Disk and Output?
cz @FCKPT ; yes, checkpoint it
; If the file's FCB position is invalid, we must preserve the
; present contents of FcrRRA.
noposn equ (1 shl FcrDisk)+(1 shl FcrNoPosn)
ldx a,FcrFlags
ani noposn
cpi noposn
cz saveRRA
; Zero the FcrRRA field -- if CP/M can't find the file (e.g. if
; it's a device or doesn't exist) it will leave the zero there.
xra a
stx a,FcrRRA
stx a,FcrRRA+1
stx a,FcrRRA+2
; Ask the BDOS to look up this file in its disk directory and
; set FcrRRA to its count of records. A=FFh if no file is
; found. If the drivecode is bad the BDOS will abort us.
mvi c,BdosFileSize
call @BDOS ; DE still ->FCB
; The RRA contains zero or the count of 128-byte records in the
; file. Load that into registers BCDE as a count of bytes by
; loading it into BCD and shifting it right one bit.
ldx d,FcrRRA ; least-sig byte
ldx c,FcrRRA+1
ldx b,FcrRRA+2 ; most-significant
xra a ; A=00, carry cleared
rarr b
rarr c
rarr d
rar ; last bit to A bit 7
mov e,a ; set low byte of longword
; We now have the answer in BCDE. Store it in the longword,
; updating HL to the byte following.
lhld lwptr ; HL->longword
mov m,e ! inx h ; store 4 bytes in
mov m,d ! inx h ; ascending order
mov m,c ! inx h ; of significance
mov m,b ! inx h
; If this was a disk file, restore FcrRRA.
ldx a,FcrFlags
ani noposn
cpi noposn
cz restRRA
xtix
pop d
pop b
pop psw
ret
; out-of-line subroutines to save and restore the file position
; when that is needed (rarely).
dseg
rra ds 3
cseg
saveRRA:
ldx a,FcrRRA
sta rra
ldx a,FcrRRA+1
sta rra+1
ldx a,FcrRRA+2
sta rra+2
ret
restRRA:
lda rra
stx a,FcrRRA
lda rra+1
stx a,FcrRRA+1
lda rra+2
stx a,FcrRRA+2
ret
end
+2
sta rra+2
ret
restRR name 'FNOTE'
title 'FNOTE -- return file position as longword'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This module receives HL->a longword in storage and DE->an FCR.
; It returns in the longword the count of bytes that precede
; the next byte to be read or written in the file. This count,
; a Relative Byte Address (RBA), is the file position in records
; times 128, adjusted for the present file buffer position.
; The RBA may be saved and used later by FSEEK to reposition
; the file to this point.
;
; interface: FNOTE macro
;
; History
; initial code 19 April 84
;===============================================================
maclib smallz80
maclib fcr
extrn @FBRRA,@CPLBW
public @FNOTE
@FNOTE:
push psw
push b
push d
xtix ; base the FCR
push h ; save the longword's address
; If the file is a device (or an unopened file), we return an
; answer of zero.
bitx FcrDisk,FcrFlags
jrnz isdisk
lxi d,0
lxi h,0 ; longword of zero
jmp storeit
; It's a disk file. Find out its current Relative Record
; Address.
isdisk:
call @FBRRA
; The meaning of the RRA depends on whether the file is
; positioned within existing data or beyond the end of file.
bitx FcrNotEof,FcrFlags
jrnz infile
; It's positioned at its end. The RRA describes the first
; record in the buffer and the Bufptr will be added to it.
; Build a positive longword on the stack.
lxi d,0
push d ; high 16 bits
ldbwx d,FcrBufptr
push d ; low 16 bits
jr either
; It's positioned over existing data. The RRA describes the
; next record after Bufeod. We have to subtract from it the
; difference between Bufeod and Bufptr. Build a negative
; longword (or a zero one) of that value on the stack.
infile:
ldbwx h,FcrBufeod
ldbwx d,FcrBufptr
ora a
dsbc d ; HL = Bufeod - Bufptr
lxi d,0 ; (assume difference is 0)
jrz iszero ; (it was)
call @CPLBW ; HL = - (Bufeod - Bufptr)
lxi d,-1 ; complete 32-bit negative
iszero: push d ; push long of -(eod-ptr)
push h
; With that done, get (RRA*128) as a longword. RRA*128 is just
; RRA*256 shifted right 1 bit.
either:
mvi l,0
ldx h,FcrRRA
ldx e,FcrRRA+1
ldx d,FcrRRA+2 ; DEHL = RRA*256
ora a ; (clear carry)
rarr d
rarr e
rarr h
rarr l ; DEHL = RRA * 128
; Now add to that, the longword we prepared earlier.
pop b
dad b ; add low 16 bits
xchg
pop b
dadc b ; add carry, high 16 bits
; Finally, store the result in the given longword.
storeit:
mov b,h ! mov c,l ; save high 16 bits
pop h ; HL->longword
mov m,e ! inx h ; store from least significant
mov m,d ! inx h ; up to most significant.
mov m,c ! inx h
mov m,b ! inx h ; leaving HL->byte after
xtix
pop d
pop b
pop psw
ret
end
t significant.
mov m,c ! inx h
mov m,b ! inx h ; leaving HL->byte af name 'FSEEK'
title 'FSEEK -- reposition file'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This module receives HL->a longword in storage, and DE->an FCR
; describing a file. It repositions the file so that the next
; byte in or out will go to the Relative Byte Address (RBA)
; specified by the longword.
;
; If the file is a device, nothing is done. If the given RBA
; falls into the present file buffer, only the buffer pointer
; is changed. Otherwise the buffer is written (if necessary)
; and the file is pointed to the record that contains the RBA.
; If that exists, the buffer is refilled; otherwise, the file
; is set up for output to a simulated end of file.
;
; On return, the Z flag is TRUE if the file is positioned
; beyond the end of existing data (or within a "hole"). This
; is compatible with the operation of FOPNI and input functions
; in which Z true means end of file.
;
; FRWND functional module
;
; @FRWND is a special entry to FSEEK which seeks to the first
; byte of the file.
;
; interface: FSEEK and FREWIND macros
;
; History
; fix error in positioning within buffer 8/20/84
; allow for ambiguous FCB position on seek outside file 8/4/84
; add FRWND entry 6/28/84
; initial code 19 April 84
;===============================================================
maclib smallz80
maclib fcr
extrn @FBRRA,@FBAIM,@FDOUT,@FBINP,@RAMFL
dseg
ds 1 ; scratch byte
targRRA ds 3 ; target RRA, longword div 128
targptr ds 1 ; target byte, longword mod 128
ds 1
highRRA ds 3 ; RRA of record following Bufeod
ds 1
lowRRA ds 3 ; RRA of record at head of buffer
RBA000 db 0,0,0,0 ; RBA of head of file.
cseg
public @FSEEK,@FRWND
@FRWND:
push h ; save caller's HL
lxi h,RBA000 ; point to first byte of file
call @FSEEK ; seek there
pop h
ret
@FSEEK:
push b
push h
push d
xtix
; If the file is a device, do nothing.
bitx FcrDisk,FcrFlags
jz exit
; Convert the longword into a Relative Record Address as TargRRA
; and a record offset within that record as targptr.
mov e,m ! inx h ; load it into BCDE
mov d,m ! inx h ; from least to most
mov c,m ! inx h ; significant.
mov b,m
mov a,e
ani 7fh ; (note: carry now clear)
sta targptr ; offset within 128-byte record
; "Longword div 128" is the high 3 bytes of longword times 2,
; in other words we shift it left once and drop the low byte.
ralr e
ralr d
ralr c
ralr b
sded targRRA-1 ; store low byte,
sbcd targRRA+1 ; ..middle, hi: targRRA=D,C,B
; Get the current Relative Record Address of the file:
; If FcrNoPosn is false, the FCB accurately reflects the status
; of the buffer vis-a-vis the file while FcrRRA is unknown. If
; FcrNoPosn is true, the FCB is ambiguous but the FcrRRA value
; is one this module set earlier and is correct.
bitx FcrNoPosn,FcrFlags
cz @FBRRA
ldx d,FcrRRA
ldx c,FcrRRA+1
ldx b,FcrRRA+2
; From it, we want to calculate the RRA of the first record in
; the buffer (lowRRA) and the RRA of the record that follows
; Bufeod (highRRA). The number of records in the buffer is
; bits 15..7 of Bufeod. Get that into A.
ldx a,FcrBufeod
ral
ldx a,FcrBufeod+1
ral
; If we are working at end of file, lowRRA should be the file
; position RRA, and highRRA should be that plus A.
bitx FcrNotEof,FcrFlags
jrnz midfile
sded lowRRA-1
sbcd lowRRA+1 ; lowRRA = D,C,B = RRA
add d ! mov d,a ; add #recs in buffer
mov a,c ! aci 0 ! mov c,a ; and propogate carry
mov a,b ! aci 0 ! mov b,a
sded highRRA-1 ; highRRA = RRA + Bufeod div 128
sbcd highRRA+1 ; ..into storage as D, C, B
jr either
; We are working within existing data, so highRRA should be the
; file-position RRA, and lowRRA should be that minus A.
midfile:
sded highRRA-1
sbcd highRRA+1 ; highRRA = D,C,B = RRA
mov e,a
mov a,d ! sub e ! mov d,a
mov a,c ! sbi 0 ! mov c,a
mov a,b ! sbi 0 ! mov b,a
sded lowRRA-1 ; lowRRA = RRA - Bufeod div 128
sbcd lowRRA+1 ; ..as D, C, B
; Whew. Ok, if lowRRA <= targRRA < highRRA, the desired byte
; falls within the buffer and we don't need to do any I/O.
either:
lxi h,lowRRA+2
lxi d,targRRA+2
call compRRA ; compare target :: low
jrc outside ; (lowRRA is greater)
lxi h,highRRA+2
lxi d,targRRA+2
call compRRA ; compare target :: high
jrnc outside ; (highRRA is not greater)
; ..and that is the case. What we do now is to calculate
; targRRA - lowRRA, yielding a count of records into the buffer.
; They can't differ by more than 127 (since we limit buffers
; to 32K) so we only have to subtract the low bytes (and we
; don't care about any borrow, as when LowRRA=01FC and TargRRA
; is, say, 0200, giving 04 and a carry -- 8/20/84)
lda targRRA
lxi h,lowRRA
sub m
; That count, times 128, plus targptr, is our new Bufptr. We
; set it and exit. We do NOT alter the NotEof or NoPosn bits
; since the situations they reflect haven't changed.
ora a ; (clear carry)
rar ; A = new bufptr bits 15..8
stx a,FcrBufptr+1 ; ..and new bit 7 in cy
mvi a,0
rar ; cy moves to A bit 7
lxi h,targptr
add m ; A = new bufptr bits 7..0
stx a,FcrBufptr
jmp exit
; Now then. It seems the desired RRA falls outside the present
; buffer. We will be reloading the buffer but before we do, if
; it has been modified, we have to write it to disk.
outside:
call @FDOUT
; Right, the buffer is clean. Now set the target RRA and try
; to position the file to it by reading at that address.
clean:
lda targRRA
stx a,FcrRRA
lda targRRA+1
stx a,FcrRRA+1
lda targRRA+2
stx a,FcrRRA+2
call @FBAIM
; If FB-AIM returned zero in register A, there does exist file
; data at that RRA. Therefore we are positioned in mid-file,
; and the NotEof bit should reflect that. Also the FCB does
; now match the file condition, so FcrNoPosn should be off.
; Then we can load the buffer with a sequential read.
ora a
jrnz nodata
bsetx FcrNotEof,FcrFlags
bresx FcrNoPosn,FcrFlags
call @FBINP ; sequential read to fill buffer
jr setptr
; No data exists at the target RRA. We set NotEof false to
; prevent attempts to input nonexistent data. If no EXTENT
; exists at this RRA, set NoPosn true because the FCB no longer
; reflects the buffer position.
nodata:
bresx FcrNotEof,FcrFlags
cpi 04h ; 01/no data, or 04/no extent?
jrc nullrec ; (no data, FCB ok)
bsetx FcrNoPosn,FcrFlags
; Since there's no file data, prepare a null record at the head
; of the buffer just so we can point Bufptr at something.
nullrec:
lxi b,128 ; BC=length of 128
stbwx b,FcrBufeod ; so does Bufeod
ldbwx d,FcrBufadr ; DE->buffer
ldx a,FcrFillByte ; A = fill byte
call @RAMFL ; fill first record
; The file has been positioned and the buffer set up so that
; the desired 128-byte record is first in it. Set the Bufptr.
; Also set the LastCR flag, since an ASCII get after a seek
; should reveal eof immediately.
setptr:
lda targptr
stx a,FcrBufptr
mvix 0,FcrBufptr+1
bsetx FcrLastCR,FcrFlags
; Exit, setting Z from the NotEof flag.
exit:
bitx FcrNotEof,FcrFlags
xtix
pop d
pop h
pop b
ret
; Here compare two RRAs. DE points to the most-significant byte
; of the first, HL to the msb of the second. Return the flags
; for the first inequality working from high back to low.
compRRA:
ldax d ! dcx d
cmp m ! dcx h ; compare high bytes
rnz
ldax d ! dcx d
cmp m ! dcx h ; ..middle bytes
rnz
ldax d
cmp m ; ..low bytes
ret
end
are high bytes
rnz
ldax d ! dcx d
cmp m ! dcx name 'FRFIL'
title 'FRFIL -- fill new record'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; One or more bytes have been placed in a file buffer past
; Bufeod. This routine is called to get Bufeod out past Bufptr
; and a multiple of 128, like it spozed to be. We fill from
; the present Bufptr to the next 128-byte boundary with the
; the chosen end-of-file-fill byte, and adjust Bufeod. The logic
; is: Bufeod := Bufptr
; while (0 <> Bufeod mod 128)
; Buffer[Bufeod] := fill byte
; Bufeod := Bufeod + 1
; end while
;
; interface: IX->FCR, Bufeod < Bufptr
; Bufeod updated to 128-multiple after Bufptr
;
; History
; initial code 19 April 84
;===============================================================
maclib smallz80
maclib fcr
public @FRFIL
@FRFIL:
push psw
push b
push d
push h
ldbwx d,FcrBufptr ; Bufeod = Bufptr
mov a,e
ani 127
jrz exit ; No filling needed
mov b,a ; number needed is
mvi a,128 ; 128 minus Bufptr mod 128
sub b
mov b,a ; B = loop count
ldbwx h,FcrBufadr
dad d ; HL->first fill-spot
ldx a,FcrFillByte
loop: mov m,a
inx h
djnz loop
mov a,e
ori 127
mov e,a
inx d ; get new Bufeod
exit stbwx d,FcrBufeod
pop h
pop d
pop b
pop psw
ret
end
mov a,e
ori 127
mov e,a
inx d ; get new Bufeod
ex name 'FBADV'
title 'FBADV -- advance file buffer'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This module controls the internal subroutines concerned with
; buffer management of the file defined by IX->FCR...
;
; @FDOUT is called to ensure that all buffered data is on disk.
; It calls either FBOUT or FBUPD, depending on the condition
; of the file, to write the buffer. It is used by FSEEK, FCLOS,
; FCKPT, and by...
;
; @FDUMP is called when someone wants to write a byte but the
; file buffer is full (Bufptr = Bufsize). It advances the
; buffer to cover the next portion of the file (if any).
;
; @FILLB is called when someone wants to read a byte but there
; is no data (Bufptr = Bufeod). It advances the buffer to
; cover the next portion of the file (if any).
;
; Is there an echo in here? These 2 entries are in fact one,
; for historical reasons.
;
; History
; rewrite for simplicity 8/21/84
; modify for FcrNoPosn status 8/4/84
; initial code 18 April 84
;===============================================================
maclib smallz80
maclib fcr
extrn @FBOUT,@FBUPD,@FBINP
; If the buffer is dirty (has new data), make it clean. Do
; that with an update-write if we are in mid-file or if we
; have lost our FCB position by a seek to nonexistent data.
; In either case, DO NOT reset Bufptr or Bufeod. That's for
; our caller to do according to its circumstances.
upbits equ (1 shl FcrNotEof)+(1 shl FcrNoPosn)
public @FDOUT
@FDOUT:
bitx FcrDirty,FcrFlags
rz ; not dirty? do nothing.
push psw
ldx a,FcrFlags
ani upbits ; if NotEof or NoPosn
cnz @FBUPD ; ..do an update-write
cz @FBOUT ; ..else append-write
pop psw
ret
; Advance the buffer for sequential I/O. If it is dirty,
; write it (see above). Reset Bufptr to zero. If not at
; eof, read the next buffer-load of data.
public @FDUMP,@FILLB
@FILLB:
@FDUMP:
call @FDOUT ; make sure the buffer is clean
xra a
stx a,FcrBufptr
stx a,FcrBufptr+1 ; Bufptr := 0
bitx FcrNotEof,FcrFlags ; if no more data in file,
jnz noteof
stx a,FcrBufeod ; make Bufeod := 0 too
stx a,FcrBufeod+1
ret ; and return Z true
noteof: ; else there may be more data
call @FBINP ; try to read it (sets Bufptr)
ret ; and return INP's Zero flag
end
: ; else there may be more data
cal name 'FBOUT'
title 'FBOUT -- sequential write of file buffer'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This subroutine of the file operations writes the to disk the
; buffer contents of the file represented by IX->FCR. The
; active buffer contents run from Bufadr to Bufadr+Bufeod-1.
; The "Sequential Output Condition" holds, i.e. the CP/M file
; position is correct for the first record of the buffer. Our
; caller is responsible for this.
;
; Bufptr and Bufeod are not changed (that's up to our caller)
; but as a result of the write, the Sequential Input Condition
; may be said to hold -- the FCB is positioned at the record
; that logically follows Bufeod. We do NOT reset FcrNotEof
; (which usually tracks the SIC). Only FBINP does that, when
; it finds physical eof.
;
; The CP/M Plus feature of multi-record I/O enables us to write
; the whole buffer in one request regardless of its size.
;
; Errors are possible, and are handled thus:
; no data/directory space: close file, abort with msg
; media change or invalid FCB: abort with msg
;
; interface: IX->FCR; Bufeod is a multiple of 128.
; output: FcrDirty bit reset.
;
; History
; initial code 30 March 84
;===============================================================
maclib equates
maclib smallz80
maclib fcr
maclib services
extrn @FABTX,@BDOS
if CpmLevel eq 22h
extrn @ADDAH
endif
public @FBOUT
@FBOUT:
push psw
push b
push d
push h
; Bufeod is a count of active bytes in the buffer, and is
; maintained at a multiple of 128 by all file routines. Figure
; out how many 128-byte records we have to write (Bufeod bits
; 15..7). If that turns out to be zero, we shouldn't have
; been called, but that's ok.
ldx a,FcrBufeod ; low byte of count..
ral ; ..high bit to carry
ldx a,FcrBufeod+1 ; high byte of count..
ral ; shifted left, plus carry
ora a ; is count zero?
jrz exit ; if so, skip the write
if CpmLevel gt 22h ; HOW WE DO IT IN CP/M PLUS...
; Set the count of buffered records as the CP/M Plus multi-
; record count.
mov e,a
mvi c,BdosSetMulti
call @BDOS
; Set the buffer as the CP/M data transfer address.
ldbwx d,FcrBufadr
mvi c,BdosSetBuffer
call @BDOS
; Now we can do the write.
pushix
pop d ; DE->FCB
mvi c,BdosWrite
call @BDOS
else ; HOW WE DO IT IN CP/M 2.2...
; Save the count of records as a loop-count. Set up HL->data
; and DE->FCB.
mov b,a
ldbwx h,FcrBufadr
pushix
pop d
; Loop writing each 128-byte record.
Wloop:
xchg ; DE->data, HL->FCB
mvi c,BdosSetBuffer
push h
call @BDOS ; (saves BC,DE)
pop h
xchg ; DE->FCB, HL->data
mvi c,BdosWrite
push h
call @BDOS
pop h
ora a ; did the write work?
jrnz exit ; (no, quit with A=retcode)
mvi a,128
call @ADDAH ; advance data pointer
djnz Wloop
xra a ; did them all ok
endif ; END OF CP/M 2.2 WRITE
; Save the Bdos's return code, and if it is "success," reset
; the dirty bit and exit.
exit:
stx a,FcrRetcode
ora a
jrnz error
bresx FcrDirty,FcrFlags
pop h
pop d
pop b
pop psw
ret
; Four errors are possible. Two (media change and bad FCB,
; both unique to CP/M Plus) are disasters during output.
error:
cpi 03h ; 01=no directory, 02=no data space
jnc @FABTX ; higher than 2, just die
; The other two errors (no directory or no data space) mean that
; some of the present buffer-load did not make it to disk and
; won't ever. That too calls for aborting the program, but in
; those cases we should close the file in hopes of preserving
; the data that was written previously.
; The error 01h, no directory space, is reported as 05h after
; a direct-access write, and that's how our error-message
; handler expects it.
cpi 01h
jrnz close
mvix 05h,FcrRetcode
close:
pushix
pop d
mvi c,BdosClose
call @BDOS
jmp @FABTX
end
.
cpi 01h
jrnz close
mvix 05h,FcrRetcode
close:
pushix
pop d
mvi c,BdosClose
call @BDOS name 'FBUPD'
title 'FBUPD -- update-write file buffer'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This routine is called when the file is positioned at other
; than its end, the buffer contains modified data, and it is
; time to move to another part of the file. It does a direct-
; access write of the first record in the buffer, setting the
; first buffered record as the CP/M file position, then does a
; sequential write of Bufeod bytes (via FBOUT).
;
; If FcrNotEof is true, the file FCB is positioned at the record
; that logically follows BufEod (the Sequential Input Condition)
; and the direct-access address in FcrRRA is invalid, we have to
; set it before aiming the file.
;
; If FcrNotEof is false, FcrNoPosn will be true (we wouldn't be
; called otherwise). A seek was done to a nonexistent extent
; and the file FCB is not positioned to anything in particular,
; but the FcrRRA is valid for the first record in the buffer.
; After we aim the file, we can do a sequential write.
;
; On our return, the S.I.C. will hold -- the FCB will point to
; the next record after BufEod (which likely doesn't exist).
;
; interface: IX->FCR, either FcrNotEof OR FcrNoPosn true
; output: file updated on disk
; Sequential Input Condition established.
;
; History
; add test for 2nd condition, 8/4/84
; initial code 19 April 84
;===============================================================
maclib equates
maclib smallz80
maclib services
maclib fcr
extrn @FBRRA,@FBOUT,@BDOS
public @FBUPD
@FBUPD:
push psw
push b
push d
push h
; If the SIC holds, the FCB position is valid and FcrRRA is not,
; so get it updated. Otherwise use the RRA set by FSEEK.
bitx FcrNotEof,FcrFlags
jrz HaveRRA
call @FBRRA
; Now FcrRRA reflects the record that follows BufEod. We want
; to position the file at the record at the head of the buffer.
; Its RRA is the present RRA less Bufeod div 128. "Bufeod div
; 128" is just bits 15..7 of Bufeod.
pushix
pop h
lxi d,FcrRRA
dad d ; HL->RRA (low, mid, high bytes)
ldx a,FcrBufeod
ral ; carry = Bufeod bit 7
ldx a,FcrBufeod+1
ral ; A = Bufeod bits 15..7
mov d,a ; save that in D
; Ad-hoc subtract of a byte from a 24-bit number: subtract
; from the low byte and propogate a possible carry.
mov a,m
sub d ; subtract from low byte
mov m,a
inx h
mov a,m
sbi 0 ; propogate the borrow
mov m,a
inx h
mov a,m
sbi 0 ; ..to the third byte
mov m,a
; Ok, FcrRRA addresses the 1st record in the buffer, so now we
; can re-aim the CP/M file position to that record -- i.e., make
; the Sequential Output Condition hold. We do it with a direct-
; access write of that first record.
haveRRA:
ldbwx d,FcrBufAdr
mvi c,BdosSetBuffer
call @BDOS ; set buffer address
if CpmLevel gt 22h ; in CP/M Plus don't forget to
mvi e,1
mvi c,BdosSetMulti
call @BDOS ; ..set record count of 1
endif
pushix
pop d
mvi c,BdosDirWrite
call @BDOS ; write one record
bresx FcrNoPosn,FcrFlags ; FCB matches file now
; That set up the FCB such that the next sequential operation
; will go to the record now at BufAdr (the Sequential Output
; Condition) so it is valid to call @FBOUT to write the entire
; active contents of the buffer (and thereby establish the
; Sequential Input Condition).
bresx FcrNoPosn,FcrFlags ; FCB position valid now
call @FBOUT
pop h
pop d
pop b
pop psw
ret
end
ndition).
bresx FcrNoPosn,FcrFlags name 'FBINP'
title 'FBINP -- sequential read to file buffer'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This subroutine is called to read from the file represented
; by IX->FCR into its buffer. It assumes that the "Sequential
; Input Condition" holds, i.e. that a sequential read will
; return the record that logically follows the present Bufeod
; position. The SIC continues to hold afterward.
;
; The CP/M Plus multi-record feature allows the entire buffer
; to be filled in one request regardless of its size.
;
; Bufeod is set to reflect the amount of data received. If no
; data is received, the NotEof bit is set false. (At that
; time, the Sequential Output condition begins to hold, i.e.
; a write will add new data to the file.)
;
; Three errors are possible. 01/unwritten data is the end
; of file condition and indicates that not all of the records
; requested (perhaps none of them) were received. It is
; handled as noted above. 09/invalid fcb and 10/media change
; are disasters and we cause an abort.
;
; interface: IX->FCR
; output: Bufeod set to byte after last one read,
; NotEof set false when no more data exists
;
; History
; initial code 30 March 84
;===============================================================
maclib equates
maclib services
maclib smallz80
maclib fcr
extrn @BDOS
if CpmLevel gt 22h ; we could get killing errors
extrn @FABTX
else ; in 2.2 we need to add A to HL
extrn @ADDAH
endif
public @FBINP
@FBINP:
push psw
push b
push d
push h
; Figure out how many records will fit in the buffer: bits 15..7
; of the buffersize.
ldx a,FcrBufsize
ral ; bit 7 to carry
ldx a,FcrBufsize+1 ; bits 16..8 in A
ral ; bits 15..7 in A
if CpmLevel gt 22h ; HOW WE DO IT IN CP/M PLUS....
; Set the CP/M Plus multi-record count.
mov e,a ; count of records to read
mvi c,BdosSetMulti
call @BDOS
; Set the buffer address for the read.
ldbwx d,FcrBufadr ; DE->buffer
mvi c,BdosSetBuffer
call @BDOS
; Attempt a read of that many records. Count of records
; gotten is returned in register H.
pushix
pop d ; DE->file control block
mvi c,BdosRead
call @BDOS
else ; HOW WE DO IT IN CP/M 2.2...
; Save the count of possible records and set it as a loop count.
stx a,FcrBufEod ; using BufEod as scratch space
mov b,a
; Set up the addresses of the buffer and the FCB.
pushix
pop d ; DE->FCB
ldbwx h,FcrBufadr ; HL->data
; Read B records into the buffer or until physical eof.
Rloop:
xchg ; DE->data, HL->fcb
mvi c,BdosSetBuffer
push h
call @BDOS ; (preserves BC, DE)
pop h
xchg ; DE->fcb, HL->data
mvi c,BdosRead
push h
call @BDOS
pop h
ora a ; did we get that one?
jrnz RloopZ ; (no, stop)
mvi a,128
call @ADDAH ; yes, advance bufptr
djnz Rloop ; ..and continue
RloopZ:
; B was decremented for each record received. Set H=count of
; records received as CP/M Plus would have done.
ldx a,FcrBufEod ; original count
sub b ; less residual count
mov h,a ; ..into H
mov a,b ; A=00 if not eof, else A>00
endif ; BACK TO COMMON CODE, INPUT COUNT IN L, RETCODE IN A
if CpmLevel gt 22h ; need to save retcode in CP/M 3
; Save the Bdos's return code
stx a,FcrRetcode
endif
; If we had no error, then we filled the buffer and Bufeod
; should equal Bufsize. Assume that's going to be the case.
ldbwx b,FcrBufsize
ora a
jrz seteod
if CpmLevel gt 22h ; must check error code in CP/M 3
; We had an error. If it was 9/bad fcb or 10/media change,
; abort with a file error message.
cpi 01h ; if it isn't "unwritten data"
jnz @FABTX ; ..then abort.
endif
; We encountered physical end of file, so we didn't get all the
; records we asked for. If we got no records at all, mark true
; end of file in the FCR and make Bufeod=zero
mov a,h ; count of records moved
ora a ; was it zero?
jrnz gotsome ; (no)
bresx FcrNotEof,FcrFlags
mov b,a ! mov c,a ; make Bufeod equal zero
jr seteod
; We read some data. Compute how many bytes we got and set
; that as Bufeod: a 16-bit number whose bits 15..7 are the
; count of records. Note: carry is clear at this point.
gotsome:
rar ; A=bits 16..8, bit 7 to carry
mov b,a
mvi a,0
rar ; A=bits 7..0
mov c,a
; Set the new value of Bufeod
seteod:
stbwx b,FcrBufeod
pop h
pop d
pop b
pop psw
ret
end
..0
mov c,a
; Set the new value of Bufeod
seteod:
stbwx b,FcrBufeod
pop h
pop name 'FBRRA'
title 'FBRRA -- get direct address of next record'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This routine is called to learn the present position of a
; file. It asks CP/M to store the Relative Record Address (RRA)
; at which the next sequential operation will affect.
;
; If the FcrNoPosn flag is on, then the present RRA of the file
; was set by FSEEK and it is accurate. Moreover, it is our only
; valid position info and we mustn't change it.
;
; Otherwise, if the file is positioned at its end (NotEof false)
; the RRA will be the address at which the first record in the
; buffer would be written (the Sequential Output condition).
;
; When NotEof is true, the file is positioned somewhere in its
; middle and the RRA will be the address of the record that
; follows Bufeod (the Sequential Input condition).
;
; interface: IX->FCR
; FcrRRA updated (unless FcrNoPosn)
;
; History
; Update for FcrNoPosn condition 8/20/84
; initial code 19 April 84
;===============================================================
maclib equates
maclib services
maclib smallz80
maclib fcr
extrn @BDOS
public @FBRRA
@FBRRA:
bitx FcrNoPosn,FcrFlags
rnz
push psw
push b
push d
push h
pushix
pop d ; DE->file control block
mvi c,BdosGetRecNo
call @BDOS
pop h
pop d
pop b
pop psw
ret
end
pushix
pop d ; DE->file control block
mvi c,BdosGetRecNo
call @BDOS
name 'FBAIM'
title 'FBAIM -- aim file at specific RRA'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; This subroutine of FSEEK is called to reposition a file to a
; particular Relative Record Address now in FcrRRA.
;
; The method is to do a single direct-access read of the file
; into a scratch buffer. After a d.a. read (provided it is
; successful), the next sequential access will begin with that
; record. Doing a read may seem like a waste of time. However,
; there isn't any other way, and the waste is not all that great
; since that sector will be buffered by the Bdos and the next
; sequential operation will do no actual I/O.
;
; A number of errors are possible. 01/unwritten data means that
; the requested file extent exists but there is no data block at
; that RRA. 04/unwritten extent means the RRA fell into a non-
; existent extent. These we pass back to our caller. Other
; errors are disasters and cause an abort.
;
; interface: IX->FCR with FcrRRA initialized
; A=The Bdos's return code from the read
;
; History
; initial code 19 April 84
;===============================================================
maclib equates
maclib services
maclib smallz80
maclib fcr
extrn @FABTX,@BDOS
common /fswork/
scratch ds 128
cseg
public @FBAIM
@FBAIM:
push b
push d
push h
; Set the buffer address to the common scratch area.
lxi d,scratch
mvi c,BdosSetBuffer
call @BDOS
if CpmLevel gt 22h ; under CP/M Plus don't forget to...
; Set the record count to 1.
mvi e,1
mvi c,BdosSetMulti
call @BDOS
endif
; Attempt the direct read.
pushix
pop d ; DE->fcb
mvi c,BdosDirRead
call @BDOS
; Note the return code and, if it is 0/ok, 1/unwritten data,
; or 4/unwritten extent, exit.
stx a,FcrRetcode
pop h ; prepare to exit
pop d
pop b
ora a ; expected result is 0/ok
rz ; (yes)
cpi 01h ; 1 and 4 are ok as well
rz
cpi 04h
rz
; We have one of: 3/can't close current extent, 6/invalid RRA,
; or 10/media change. Abort the program.
jmp @FABTX
end
We have one o name 'FEROR'
title 'FEROR -- Abort with file-error message'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Builds a file-error message string from the contents of an FCR
; addressed by DE or IX, and aborts the program with that msg.
; The message is
;
; File error on FRUMMAGE -- (descriptive phrase)
;
; The (descriptive phrase) is selected based on FcrRetCode from
; the following list:
;
; 0 (null)
; 1 reading unwritten data
; 2 no space for data
; 3 cannot close current extent
; 4 reading nonexistent extent
; 5 no directory space
; 6 invalid record address
; 7 (null)
; 8 (null)
; 9 invalid file control block
; 10 unexpected media change
; 11 file not found
; 12 disk I/O error
; 13 disk is read-only
; 14 file is read-only
; 15 invalid drive letter
; 16 (null)
; 17 (null)
; 18 password error
; 19 file already exists (on a Make request)
; 20 ambiguous filespec
;
; Return codes 11-20 are the Bdos "extended" errors, only seen
; when extended-error mode is on (almost never, in this code).
; The return codes from CP/M are 11 less; the caller must add
; 11 to them to avoid confusion with normal error codes.
;
; Since the string is meant for error display, it begins with
; CR, LF. However, terminal punctuation such as a dollarsign
; to delimit the string to the Bdos is up to the caller.
;
; interface: To @FABRT, DE->Fcr
; To @FABTX, IX->Fcr
; History
; initial code 2 April 84
;===============================================================
maclib equates
maclib strings
maclib smallz80
maclib fcr
extrn @FSTSP,@STAPP,@STPUT,@ERROR
dseg
msga strconst <AsciiCR,AsciiLF,'File error on '>,64
msgb strconst ' -- '
msg0 db 0 ; null string
msg1 strconst 'reading unwritten data'
msg2 strconst 'no space for data'
msg3 strconst 'cannot close current extent'
msg4 strconst 'reading nonexistent extent'
msg5 strconst 'no directory space'
msg6 strconst 'invalid record address'
msg7 equ msg0
msg8 equ msg0
msg9 strconst 'invalid file control block'
msg10 strconst 'unexpected media change'
msg11 strconst 'file not found'
msg12 strconst 'disk I/O error'
msg13 strconst 'disk is read-only'
msg14 strconst 'file is read-only'
msg15 strconst 'invalid drive-letter'
msg16 equ msg0
msg17 equ msg0
msg18 strconst 'password error'
msg19 strconst 'file already exists'
msg20 strconst 'ambiguous filespec'
maxerror equ 20
mtab dw msg0,msg1,msg2
dw msg3,msg4,msg5
dw msg6,msg7,msg8
dw msg9,msg10,msg11
dw msg12,msg13,msg14
dw msg15,msg16,msg17
dw msg18,msg19,msg20
cseg
public @FABRT,@FABTX
@FABTX: ; from file routines with IX->FCR
pushix
pop d ; get DE->FCR
@FABRT: ; from user (?) code with DE->FCR
; We will be aborting, so the register contents don't really
; matter anymore. However, if debugging is going on we may
; be trapped at @ERROR, so we will make sure the registers
; are meaningful there.
push h ; push h first, pop it last
push psw
push b
push d ; save ->Fcr on top
; Begin the message by appending the full filespec to the msg
lxi h,msga ; HL->start of message string
mvi a,0111b ; want drive, name, type
call @FSTSP ; append filespec as string
xchg ; DE->end of string
; Append the m-dash to the filespec
lxi h,msgb ; HL->append data
call @STAPP ; append, updating DE
; Pick up the FcrRetcode in A and make sure it is one we
; can handle.
pop h ; HL->FCR
push h ; (and saved again)
lxi b,FcrRetcode
dad b
mov a,m ; A := BDOS return code
cpi maxerror+1
jrc retok
xra a ; hmmm- make it zero
retok:
; Index the table by the retcode and pick up the address
; of the appropriate phrase
add a ; double the retcode
mov c,a ; BC=00xx
lxi h,mtab
dad b ; HL->(word)->string
mov a,m
inx h
mov h,m
mov l,a ; HL->phrase-string
; Append the explanatory phrase and a dollar-sign
call @STAPP
mvi a,'$'
call @STPUT
; Perform the abort with the input registers restored.
pop d ; restore DE->fcr
pop b ; restore entry BC and AF
pop psw
lxi h,msga
xthl ; restore HL, ->msg on stack
call @ERROR
end
ore DE->fcr
name 'FSTSP'
title 'FSTSP -- convert filespec to string'
;===============================================================
; Copyright (C) 1985 M&T Publishing Co. all rights reserved
;
; Convert the filespec in DE->FCR into a string and append it to
; HL->string. Return HL updated to end of new string. Register
; A contains a bit-mask that controls what parts of the filespec
; are returned:
; bit 0 = return drivecode (if not default)
; bit 1 = return filename
; bit 2 = return .filetype (if not null)
; Bit 3, which could cause return of the password field, is not
; supported for security reasons.
;
; interface: FGETSPEC macro
;
; History
; revise for bit-mask 6 August 84
; clear attribute bits 9 April 84
; initial code 2 April 84
;===============================================================
maclib equates
maclib smallz80
maclib integers
extrn @STEND
public @FSTSP
@FSTSP:
push psw
push b
mov c,a ; carry bit-map in C
xchg ; DE->output string, HL->File
call @STEND ; DE->end of output string
rarr c ; bit 0 to carry
cc dodrive ; ..drive wanted
rarr c ; check bit 1
cc doname ; ..name wanted
rarr c ; and bit 2
cc dotype ; ..type wanted
xchg ; DE->file again, HL->end of string
mvi m,0 ; put a null on it
pop b
pop psw
ret
; The drivecode is wanted, put X: in DE->string if a nondefault
; drive was used.
dodrive:
mov a,m ! ora a ; explicit drivecode, or default?
rz ; (default, do nothing)
; the fcb specifies a particular drive-letter. Convert it
; to a letter and append that and a colon to the string.
adi 'A'-1 ; drivecode 01=A, 02=B, etc
stax d ! inx d ; store the letter
mvi a,':'
stax d ! inx d ; ..and a colon
ret
; The filename is wanted. Copy up to 8 bytes, but no trailing
; blanks, from the filename field of the FCB
doname:
push h
inx h ; HL->2nd FCB byte, 1st filename byte
mvi b,8 ; #bytes to copy
call copy
pop h
ret
; The filetype is wanted. If there is none, do nothing. Else
; append a dot to the string and copy up to 3 bytes.
dotype:
push h
mvi a,9
addhla ; HL->1st filetype byte
mov a,m
cpi ' ' ; is there a filetype?
jrz notype ; (no, don't copy dot either)
mvi a,'.'
stax d ! inx d
mvi b,3 ; copy up to 3 bytes
call copy
notype: pop h
ret
; Copy up to B bytes, but no trailing blanks, sans any
; attribute bits, from HL->text to DE->string.
copy:
mov a,m
ani 07Fh ; clear attribute bits
cpi ' ' ; reached a blank?
rz ; (done if so)
inx h ; no, use this byte
stax d ! inx d ; and store it
djnz copy ; ..and continue
ret
end
?
rz ; (done if so)
inx h ; no, use this byte