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 >
Text File  |  1998-07-30  |  99KB  |  3,582 lines

  1.     name    'FCKPT'
  2.     title    'FCKPT -- ensure file data is on disk'
  3. ;===============================================================
  4. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  5. ;
  6. ; This module ensures that all a file's data is written to disk
  7. ; and its directory entries are up to date.  Doing so ensures
  8. ; that a file-size call will return accurate data and that, if
  9. ; the program crashes later, at least the current data will
  10. ; exist.
  11. ;
  12. ; interface: FCHECKPT macro
  13. ;
  14. ; History
  15. ; initial code 23 April 84
  16. ;===============================================================
  17.     maclib    equates
  18.     maclib    services
  19.     maclib    smallz80
  20.     maclib    fcr
  21.     extrn    @FDOUT,@BDOS
  22.  
  23.     public    @FCKPT
  24. @FCKPT:
  25.     push    psw
  26.     push    b
  27.     push    h
  28.     push    d
  29.     xtix
  30.  
  31. ; If the file is really a device, do nothing.
  32.     bitx    FcrDisk,FcrFlags
  33.     jrz    exit
  34.  
  35. ; Make sure any modified data is on disk.  Afterward, the FCB
  36. ; will be positioned to the record following Bufeod (which we
  37. ; don't change).  That defines the Sequential Input Condition
  38. ; so we set NotEof true to signal it.
  39.     call    @FDOUT        ; write any new data
  40.     bsetx    FcrNotEof,FcrFlags ; not at eof any more
  41.  
  42. ; The buffer is (now) clean, so we can close the file.    We set
  43. ; attribute f5', which causes CP/M Plus to do a temporary, or
  44. ; checkpoint, close.  CP/M 2 will ignore it.
  45.  
  46. clean:
  47.     bsetx    Bit7,FcrName+4        ; attribute f5'
  48.     mvi    c,BdosClose
  49.     call    @BDOS            ; (DE still ->FCR)
  50.     bresx    Bit7,FcrName+4
  51.  
  52. ; That's it, restore regs and exit.
  53.  
  54. exit:
  55.     xtix
  56.     pop    d
  57.     pop    h
  58.     pop    b
  59.     pop    psw
  60.     ret
  61.  
  62.     end
  63. 
  64.     bresx    Bit7,FcrName+4
  65.  
  66. ; That's it, restore regs and exit.
  67.  
  68. exit:
  69.     xtix
  70.     pop    d
  71.     pop    h
  72.     pop    b
  73.     pop        name    'FDIRS'
  74.     title    'FDIRS -- do directory search'
  75. ;===============================================================
  76. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  77. ;
  78. ; This module takes two FCRs.  The first, addressed by DE,
  79. ; contains a (possibly ambiguous) filespec which is the search
  80. ; argument.  It is presented to the BDOS with a request for a
  81. ; directory search.
  82. ;
  83. ; If the search is successful, the BDOS returns a disk directory
  84. ; entry for a matching file.  We move the first 12 bytes of that
  85. ; entry to the second FCR, based on HL, and refresh the rest of
  86. ; the target FCR so that it may, for instance, be opened for
  87. ; input, or its filespec extracted as a string (@FSTSP), etc.
  88. ;
  89. ; On return, A is nonzero and Z false if a matching file was
  90. ; found, or zero and Z true if none was found.
  91. ;
  92. ; Note this module does not allow the full liberty of the Bdos
  93. ; search functions.  It limits the results to a single return
  94. ; for each file rather than one return for each extent, and it
  95. ; blocks the use of the "global" search that extends over all
  96. ; types of directory entries and all user numbers.
  97. ;
  98. ; interface: FDIRBEGN and FDIRCONT macros
  99. ;
  100. ; History
  101. ; initial code 16 April 84
  102. ;===============================================================
  103.     common    /FSWORK/
  104. ; work area shared with the decimal-output modules
  105. workstr ds    128    ; used as buffer for file search
  106.     cseg
  107.     maclib    equates
  108.     maclib    services
  109.     maclib    smallz80
  110.     maclib    fcr
  111.     dseg
  112. arg    dw    0    ; save ->argument FCR
  113. targ    dw    0    ; save ->target FCR
  114.     cseg
  115.     extrn    @ADDAH,@BDOS
  116.  
  117.     public    @FDIR1,@FDIR2
  118.  
  119. ; The only difference between the two entries is in the number
  120. ; of the Bdos service request they issue.
  121. @FDIR1:
  122.     push    b    ; save work register
  123.     mvi    c,BdosSrch1    ; set service number
  124.     jr    mutual
  125.  
  126. @FDIR2:
  127.     push    b
  128.     mvi    c,BdosSrchn
  129.  
  130. mutual:
  131.     push    b    ; save Bdos number for later
  132.     shld    targ    ; save ->target FCR
  133.     xchg
  134.     shld    arg    ; and  ->argument FCR
  135.  
  136. ; For this module's specified purposes we can't allow the
  137. ; global directory search, which returns every kind of entry
  138. ; including the disk label, password records, and timestamp
  139. ; records as well as filespecs from all user numbers.  You
  140. ; can program your own if you want those abilities.
  141.     mov    a,m
  142.     cpi    '?'    ; drivecode of "?" is global
  143.     jrnz    okspec
  144.     mvi    m,0    ; force it to default drive
  145. okspec:
  146.  
  147. ; We want only one directory entry for each matching file,
  148. ; therefore we set a zero in the extent number of the argument
  149. ; file control block.
  150.     lxi    d,FcrExtent
  151.     dad    h    ; HL->argument extent number
  152.     mvi    m,0    ; make it zero
  153.  
  154. ; Make our common work area the file buffer -- that's where
  155. ; the Bdos will return a block of four directory entries.
  156.     lxi    d,workstr
  157.     mvi    c,BdosSetBuffer
  158.     call    @BDOS
  159.  
  160. ; Try for a match.  The Bdos returns A=FFh if there is none.
  161.     pop    b    ; C=search first or search next
  162.     lded    arg    ; DE->argument fcb
  163.     call    @BDOS
  164.  
  165. ; If there was no match, set Z true and exit
  166.     inr    a
  167.     jz    exit
  168.     dcr    a
  169.  
  170. ; There was a match, and A = 0/1/2/3 depending on which of the
  171. ; four directory entries in the block matched.    Convert that
  172. ; into an offset of 0/32/64/96 bytes and get HL pointing to
  173. ; the correct directory entry.
  174.     add    a    ; *2
  175.     add    a    ; *4
  176.     add    a    ; *8
  177.     add    a    ; *16
  178.     add    a    ; *32
  179.     lxi    h,workstr
  180.     call    @ADDAH
  181.  
  182. ; Point DE to the target FCR and copy in the filespec from
  183. ; the received directory entry.  The relevant bytes are the
  184. ; filename and filetype -- we copy the user code as well
  185. ; because it's simpler, but we'll fix that in a minute.
  186.     lded    targ
  187.     lxi    b,12    ; drivecode/user, filename, filetype
  188.     ldir
  189.  
  190. ; Clean up the target FCR so it can be opened without error:
  191. ; copy the search drivecode from the search FCR, blank the
  192. ; password field, and zero the flags.
  193.     lded    arg    ; restore DE->argument FCR
  194.     lhld    targ
  195.     ldax    d
  196.     mov    m,a    ; set the drivecode
  197.     lxi    b,FcrFlags
  198.     dad    b
  199.     mvi    m,0    ; zero the flags
  200.     lxi    b,FcrPassword-FcrFlags
  201.     dad    b
  202.     mvi    m,AsciiBlank ; blank password
  203.  
  204. ; Force the Z flag false to reflect our success -- the high
  205. ; byte of the target FCR address has to be nonzero.
  206.     mov a,h ! ora a
  207.  
  208. ; Restore the registers and exit.
  209. exit:
  210.     lded    arg
  211.     lhld    targ
  212.     pop    b
  213.     ret
  214.  
  215.     end
  216. has t    name    'FATTRS'
  217.     title    'FATTRS -- get/put file attributes'
  218. ;===============================================================
  219. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  220. ;
  221. ; These routines either return the important attributes of the
  222. ; file represented by DE->FCR, or install a new set of them.
  223. ; Attributes are represented as a bit-mask in register A -- this
  224. ; is for both convenience and for compatibility with future
  225. ; systems.  CP/M supports seven file attributes: f1' thru f4'
  226. ; (used in MP/M as "compatibility attributes"), plus the RO,
  227. ; SYS, and Archive attributes.    Names for these -- as they are
  228. ; passed in A -- appear in EQUATES.LIB.
  229. ;
  230. ; interface: FGETATTR and FPUTATTR macros
  231. ;
  232. ; History
  233. ; initial code 11 May 84
  234. ;===============================================================
  235.     maclib equates
  236.     maclib smallz80
  237.     maclib services
  238.     maclib fcr
  239.     extrn    @BDOS
  240.  
  241. ; Scan the filespec in DE->FCR, collecting attribute bits.
  242.     public    @FGATT
  243. @FGATT:
  244.     push    b
  245.     push    d
  246.     lxi    b,(4*256)+0 ; B=4, C=0
  247. gloop1: inx    d    ; DE->first/next filename byte
  248.     ldax    d
  249.     ral        ; attribute bit to carry
  250.     ralr    c    ; push into low end of C
  251.     djnz    gloop1    ; repeat for f1, f2, f3, f4
  252.  
  253. ; Attributes f5' - f8' can be set and can be checked by getting
  254. ; the filespec from the directory, but are not passed at open
  255. ; time and are little-used.  We ignore them in order to keep the
  256. ; number of bits to pass less than 8.
  257.     inx    d    ; DE->f5
  258.     inx    d    ; f6
  259.     inx    d    ; f7
  260.     inx    d    ; f8
  261.     mvi    b,3    ; set to loop over filetype
  262.  
  263. gloop2: inx    d    ; first/next filetype byte
  264.     ldax    d
  265.     ral
  266.     ralr    c
  267.     djnz    gloop2
  268.  
  269.     mov    a,c    ; return bits in A
  270.     pop    d
  271.     pop    b
  272.     ret
  273.  
  274. ; Reverse the above process, putting bits back into the
  275. ; bitfields of the filespec -- then write to directory.
  276. @FPATT:
  277.     push    psw
  278.     push    h
  279.     push    b
  280.     push    d    ; save ->FCR last
  281.  
  282.     mov    c,a    ; keep bits in C
  283.     lxi    h,FcrType+2
  284.     dad    d    ; HL->byte t3
  285.     mvi    b,3
  286.  
  287. ploop1: mov    a,m    ; pick up filespec byte
  288.     add    a    ; discard high bit
  289.     rarr    c    ; next bit from C to carry
  290.     rar        ; and into filespec byte
  291.     mov    m,a    ; and back to filespec
  292.     dcx    h    ; HL->t2, t1, f8
  293.     djnz    ploop1
  294.  
  295.     dcx    h    ; HL->f7
  296.     dcx    h    ; f6
  297.     dcx    h    ; f5
  298.     mvi    b,4
  299.  
  300. ploop2: dcx    h    ; HL->f4, 3, 2, 1
  301.     mov    a,m
  302.     add    a
  303.     rarr    c
  304.     rar
  305.     mov    m,a
  306.     djnz    ploop2
  307.  
  308. ; Setting attributes requires a password if the file is
  309. ; protected at any level.  Set the FCRPassword field as the file
  310. ; buffer so that if it contains a password the Bdos will see it.
  311. ; (Makes no difference in CP/M 2.2)
  312.     lxi    h,FcrPassword
  313.     dad    d
  314.     xchg            ; DE->password
  315.     mvi    c,BdosSetBuffer
  316.     call    @BDOS
  317.     pop    d        ; restore DE->FCR
  318.     push    d        ; ..and save again
  319.  
  320. ; Errors here include disk i/o, read-only disk, invalid drive
  321. ; number, password error, and ambiguous filespec.  All will
  322. ; cause us to be aborted.
  323.     mvi    c,BdosSetAttr
  324.     call    @BDOS
  325.  
  326. ; If the file didn't exist, A=FFh.  That is probably a logic
  327. ; error, but what should be done about it?  It doesn't seem
  328. ; serious enough to abort the program, so we'll ignore it.
  329.     pop    d
  330.     pop    b
  331.     pop    h
  332.     pop    psw
  333.     ret
  334.  
  335.     end
  336.     name    'FQEOF'
  337.     title    'FQEOF -- lookahead test for end of file'
  338. ;===============================================================
  339. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  340. ;
  341. ; Look ahead to see if the next byte input will encounter end
  342. ; of file.  If the file is at physical eof, return Z true and
  343. ; A = 00h.  If not, return Z false and A = next byte (so that
  344. ; the caller can test for logical eof).
  345. ;
  346. ; If the file is really a device, return physical eof signal.
  347. ; This is allows a program to avoid trying to read binary data
  348. ; from a device file which has no true physical eof.
  349. ;
  350. ; interface: FEOF? macro
  351. ;
  352. ; History
  353. ; initial code 26 June 84
  354. ;===============================================================
  355.     maclib    equates
  356.     maclib    smallz80
  357.     maclib    fcr
  358.     extrn    @FILLB
  359.  
  360.     public    @FQEOF
  361. @FQEOF:
  362.     push    d
  363.     xtix
  364.     xra    a    ; anticipate eof-return
  365.  
  366. ; If the file isn't open, or is positioned in a hole, or isn't
  367. ; readable (e.g. PUN:), or if eof has already been reported,
  368. ; return true.
  369.     bitx    FcrNotEof,FcrFlags
  370.     jrz    done
  371.  
  372. ; If the file is a device, even a readable one, return physical
  373. ; eof signal.
  374.     bitx    FcrDisk,FcrFlags
  375.     jrz    done
  376.  
  377. ; It's a disk capable of input, so load up the buffer pointer
  378. ; in DE, and see if it is less than the size of buffered data.
  379.     ldbwx    d,FcrBufptr
  380.     mov    a,d
  381.     cmpx    FcrBufeod+1
  382.     jrc    havedata    ; (high byte is less)
  383.     mov    a,e
  384.     cmpx    FcrBufeod
  385.     jrc    havedata    ; (low byte is less)
  386.  
  387. ; The current buffer-load, at least, has been consumed.  Try to
  388. ; load some more and, if we fail, report physical eof.
  389.     call    @FILLB    ; reads, sets NotEof, resets Bufptr
  390.     xra    a
  391.     bitx    FcrNotEof,FcrFlags ; now eof?
  392.     jrz    done
  393.     lxi    d,0    ; bufptr is zero now
  394.  
  395. ; There is data remaining in the file and it's in the buffer
  396. ; at offset DE.  Pick up the next byte (but don't advance the
  397. ; buffer pointer -- this isn't an official input).  Then force
  398. ; Z false by testing NotEof ("ora a" wouldn't be reliable as
  399. ; the byte might be a valid null).
  400. havedata:
  401.     push    h
  402.     ldbwx    h,FcrBufadr
  403.     dad    d
  404.     mov    a,m
  405.     pop    h
  406.     bitx    FcrNotEof,FcrFlags
  407.  
  408. ; Z and A are set for exit.
  409. done:    xtix
  410.     pop    d
  411.     ret
  412.  
  413.     end
  414. crBufadr
  415.     name    'FSGBW'
  416.     title    'FSGBW -- read integers from file as decimal'
  417. ;===============================================================
  418. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  419. ;
  420. ; Read decimal digits from the file represented as DE->FCR as
  421. ; a binary word.  Method is to skip over whitespace to a
  422. ; nonwhite byte, then convert decimal digits to binary until a
  423. ; nondigit is seen.  Return the binary value in HL, the byte
  424. ; that stopped the scan in A, and Z TRUE if that is end of file.
  425. ;
  426. ; The entry @FSGBW reads an unsigned word.  Entry @FSGSW reads
  427. ; a word that may be prefixed with a sign character.
  428. ;
  429. ; Note: no check is made for overflow.    If more than five
  430. ; significant digits appear, the result will be wrong.
  431. ;
  432. ; interface: FGETADBW, FGETADSW macros
  433. ;
  434. ; History
  435. ; initial code 16 April 84
  436. ;===============================================================
  437.     maclib    equates
  438.     maclib    smallz80
  439.     extrn    @FSKIP,@FSGAC,@CPLBW
  440.  
  441.     public    @FSGBW
  442. @FSGBW:
  443.     lxi    h,0    ; clear cumulative result
  444.     call    @FSKIP    ; get past whitespace
  445.     cnz    convert ; convert digits if not eof
  446.     cpi    CpmEof    ; set Z for end of file
  447.     ret        ; and exit
  448.  
  449. @FSGSW:
  450.     lxi    h,0    ; clear cumulative result
  451.     call    @FSKIP    ; skip whitespace, get 1st byte
  452.     rz        ; exit with that if end of file
  453.     push    b    ; save work register
  454.     mvi    b,'+'    ; assume no sign given
  455.     cpi    '-'    ; if it is a minus
  456.     jrz    issign
  457.     cpi    '+'    ; ..or a plus
  458.     jrnz    notsign
  459. issign: mov    b,a    ; ..save it
  460.     call    @FSKIP    ; ..and skip to next nonwhite byte
  461.     jrz    nomore    ; ..and exit with zero on eof
  462. notsign:
  463.     call    convert ; convert digits starting with [A]
  464.     mov    c,a    ; save last byte
  465.     mov    a,b    ; examine the sign
  466.     cpi    '-'    ; if it's minus,
  467.     cz    @CPLBW    ; complement the result
  468.     mov    a,c    ; recover scan-stopper
  469. nomore:
  470.     pop    b    ; restore work register
  471.     cpi    CpmEof    ; set Z for end of file
  472.     ret
  473.  
  474. ; here we convert decimal digits to binary, starting with
  475. ; the byte in A (result of calling @FSKIP) and continuing
  476. ; until a nondigit or end of file.
  477.  
  478. convert:
  479.     push    b    ; save work register
  480. Loop:
  481.     cpi    '9'+1
  482.     jrnc    done    ; (greater than 9)
  483.     cpi    '0'
  484.     jrc    done    ; (less than 0)
  485.     sui    '0'    ; reduce to binary-coded decimal
  486.     mov b,h ! mov c,l ; copy the partial result
  487.     dad    h
  488.     dad    h    ; result times 4
  489.     dad    b    ; result times 5
  490.     dad    h    ; result times 10
  491.     mov    c,a
  492.     mvi    b,0    ; BC=000d
  493.     dad    b    ; add in new digit
  494.     call    @FSGAC    ; get next digit (?)
  495.     jnz    Loop    ; continue if not eof
  496.  
  497. done:    pop    b
  498.     ret
  499.  
  500.     end
  501. d
  502.     dad    b    ; add in new digit
  503.     call    @FSGAC    ; get next digit (?)
  504.     jnz    Loop    ; continue if not    name    'FSGXW'
  505.     title    'FSGXW -- read word from file as hex digits'
  506. ;===============================================================
  507. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  508. ;
  509. ; Read a binary word in the form of hex digits from the file
  510. ; represented by DE->FCR.  Method is to skip over whitespace
  511. ; to a printable character or end of file. Then read and convert
  512. ; hex digits until a non-digit is found. Return the resulting
  513. ; binary word in HL, the byte that stopped the scan in A, and
  514. ; Z TRUE if that was end of file.
  515. ;
  516. ; Note: there is no check for overflow -- if there are more than
  517. ; four hex digits, HL will reflect the least significant four.
  518. ;
  519. ; interface: FGETAXBW macro
  520. ;
  521. ; History
  522. ; initial code 16 April 84
  523. ;===============================================================
  524.     maclib    equates
  525.     extrn    @FSKIP,@FSGAC,@CKHEX
  526.  
  527.     public    @FSGXW
  528. @FSGXW:
  529.     call    @FSKIP    ; skip whitespace, return 1st nonwhite
  530.     lxi    h,0    ; initialize result
  531.     rz        ; quit with that, if eof.
  532. Loop:
  533.     call    @CKHEX    ; see if hex, and make uppercase if so
  534.     jnz    exit    ; not hex, time to stop
  535.     cpi    'A'    ; separate 0..9 from A..F
  536.     jc    isnum    ; (is numeric digit)
  537.     sui    'A'-'9'-1 ; make A..F contiguous with digits
  538. isnum:    ani    0fh    ; isolate binary value
  539.     dad    h
  540.     dad    h
  541.     dad    h
  542.     dad    h    ; shift HL left by 4 bits
  543.     ora    l
  544.     mov    l,a    ; and install new digit
  545.     call    @FSGAC    ; read next digit (?)
  546.     jnz    Loop    ; ..and continue if not eof
  547.  
  548. exit:
  549.     cpi    CpmEof    ; set Z true if end of file
  550.     ret
  551.  
  552.     end
  553.     ; read next digit (?)
  554.     jnz    Loop    name    'FSKIP'
  555.     title    'FSKIP -- advance input file to nonwhite byte'
  556. ;===============================================================
  557. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  558. ;
  559. ; Advance the file represented by DE->FCR until the current byte
  560. ; is neither a blank nor a tab.  The first nonwhite byte (which
  561. ; may be CpmEof) is returned.  That byte has been consumed, so a
  562. ; following call on a "get" module will return the NEXT byte.
  563. ;
  564. ; interface: FSKIPWHT macro
  565. ;
  566. ; History
  567. ; initial code 16 April 84
  568. ;===============================================================
  569.     maclib    equates
  570.     maclib    smallz80
  571.     extrn    @FSGAC
  572.  
  573.     public    @FSKIP
  574. @FSKIP:
  575. loop:
  576.     call    @FSGAC        ; get a byte and
  577.     rz            ; ..return if it is end of file
  578.     cpi    AsciiBlank    ; no?  repeat if it's blank
  579.     jrz    loop
  580.     cpi    AsciiTAB    ; ..or a tab
  581.     jrz    loop
  582.     ret            ; else return it
  583.  
  584.     end
  585. cpi    AsciiBla    name    'FSGST'
  586.     title    'FSGST -- get string from ASCII file'
  587. ;===============================================================
  588. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  589. ;
  590. ; Read a line from a (presumably Ascii) file and format it as a
  591. ; string.  The macro interface prepares these parameters:
  592. ;    DE->FCR
  593. ;    HL->string space
  594. ;    BC = maximum length
  595. ; We return:
  596. ;    HL->the end of the string
  597. ;    BC = residual count
  598. ;    A = last byte processed, which will be one of:
  599. ;        CR, indicating end of line found
  600. ;        ^Z, indicating string ended by end of file
  601. ;        other, when the count is exhausted first
  602. ;    Z flag reflecting end of file.
  603. ;
  604. ; interface: FGETSTR macro
  605. ;
  606. ; History
  607. ; initial code 11 April 84
  608. ;===============================================================
  609.     maclib    equates
  610.     maclib    smallz80
  611.     maclib    fcr
  612.     maclib    services
  613.     extrn    @FSGAC,@BDOS
  614.     dseg
  615. crlf    db    AsciiCR,AsciiLF,'$'
  616.     cseg
  617.  
  618.     public    @FSGST
  619. @FSGST:
  620.     push    d
  621.     xtix
  622.  
  623. ; First, eliminate the error of a zero count in BC.
  624.     mov    a,b
  625.     ora    c
  626.     jrz    exit
  627.  
  628. ; Decrement the count so that when (if) it goes to zero, there
  629. ; will be room for the terminating NUL.  If that brings it to
  630. ; zero, build a null string and exit.
  631.     dcx    b
  632.     mov    a,b
  633.     ora    c
  634.     jrz    setnull
  635.  
  636. ; Eliminate the case of immediate end of file.
  637.     bitx    FcrNotEof,FcrFlags
  638.     jnz    noteof
  639.     mvi    a,CpmEof    ; set A to terminating byte
  640.     jr    setnull     ; ..and build null string
  641.  
  642. ; The file has data and the count allows at least one byte
  643. ; in the string.  Choose a method based on the kind of file.
  644. noteof:
  645.     bitx    FcrDisk,FcrFlags
  646.     jrz    isdevice
  647.     call    Disk        ; handle disk input
  648.     jr    setnull     ; ..and finish string
  649. isdevice:
  650.     bitx    FcrAux,FcrFlags
  651.     cnz    Aux        ; handle input from AUX
  652.     bitx    FcrCon,FcrFlags
  653.     cnz    Con        ; handle input from CON
  654.  
  655. ; The string data (if any) has been moved and HL updated to the
  656. ; place for the terminal NUL.  A = the last byte processed. The
  657. ; count has been decremented.  If the last byte read was CR, set
  658. ; the LastCR flag.  Then finish the string and exit.
  659. setnull:
  660.     cpi    AsciiCR
  661.     jrnz    setnull2
  662.     bsetx    FcrLastCR,FcrFlags
  663. setnull2:
  664.     mvi    m,0
  665. exit:
  666.     cpi    CpmEof        ; set Z for end of file
  667.     xtix
  668.     pop    d        ; restore IX, DE
  669.     ret            ; and exit
  670.  
  671. ; For AUX and disk input, we can't seem to do any better than
  672. ; making repeated calls on @FSGAC, the Ascii byte-getter.  In
  673. ; the case of AUX we would have to duplicate most of @FSGAC
  674. ; here anyway.    With a disk file, the complexities of juggling
  675. ; buffer-variables, string address, count, and the CR/LF logic
  676. ; make things so complicated I couldn't save any time.
  677. AUX:
  678. Disk:
  679.     call    @FSGAC        ; get one byte from file
  680.     rz            ; if end of file, done
  681.     cpi    AsciiCR     ; if CR, also done
  682.     rz
  683.     mov    m,a        ; stow the byte
  684.     inx    h        ; ..step over it
  685.     dcx    b        ; ..count it
  686.     mov a,b ! ora c     ; test the count
  687.     jnz    Disk        ; continue while room
  688.  
  689.     dcx    h        ; count exhausted,
  690.     mov    a,m        ; recover last byte
  691.     inx    h        ; aim at place for NUL
  692.     ret            ; and stop.
  693.  
  694. ; For the CON device, however, we want to allow the keyboard
  695. ; operator the full range of CP/M line editing, so we will use
  696. ; Bdos function 10, edited line input.
  697. CON:
  698.  
  699. ; We have to tell the Bdos how many bytes to read.  The maximum
  700. ; is 255.  We want to tell it the lesser of: the string-size,
  701. ; the file's buffer-size, and 255.
  702.     push    b        ; save string count
  703.     push    h        ; save string address
  704.     ldbwx    h,FcrBufsize
  705.     dcx    h        ; allow for 2 bytes of
  706.     dcx    h        ; Bdos parameters
  707.     push    h        ; save buffer size
  708.     ora    a        ; (clear carry)
  709.     dsbc    b        ; HL = bufsize - string size
  710.     pop    h        ; (recover buffer size)
  711.     jrc    useHL        ; bufsize < count, use bufsize
  712.     mov h,b ! mov l,c    ; count <= bufsize, use count
  713. useHL:
  714.     mov a,h ! ora a     ; if result > 255,
  715.     jrz    underFF     ; (isn't)
  716.     mvi    l,255        ; use just 255.
  717. underFF:
  718.  
  719. ; Ok, we have in L the number of bytes we can let the Bdos
  720. ; read for us.    Prepare a Bdos line buffer in the file buffer.
  721.     ldbwx    d,FcrBufadr    ; DE->file buffer
  722.     mov    a,l
  723.     stax    d        ; set max size for Bdos
  724.     push    d        ; save buffer address
  725.  
  726. ; Read an edited line and, when we get control back, echo
  727. ; a CR and LF to the console.
  728.     mvi    c,BdosLineIn
  729.     call    @BDOS        ; read edited line
  730.     lxi    d,crlf
  731.     mvi    c,BdosString
  732.     call    @BDOS        ; write cr, lf
  733.  
  734. ; The buffer contains (length) (count received) data...
  735. ; Our job now is to copy that to the string space.  However,
  736. ; to be true to our definition of an Ascii file, we have to
  737. ; examine each byte for CpmEof.  If one appears, we have to
  738. ; break off copying at that point.
  739.     pop    h        ; HL->buffer
  740.     pop    d        ; DE->string space
  741.     inx    h        ; HL->count of received bytes
  742.     mov a,m ! ora a     ; guard against null line
  743.     jrz    nulline     ; (yes, just a return)
  744.     mov    b,a        ; set loop-limit
  745.     mvi    c,0        ; initial count of bytes moved
  746.     inx    h        ; HL->first data byte
  747. copy:
  748.     mov a,m ! inx h
  749.     cpi    CpmEof        ; look for eof in typed data
  750.     jrz    hiteof        ; ..and break off if found
  751.     stax d ! inx d        ; store gotten byte
  752.     inr    c        ; count byte moved
  753.     djnz    copy        ; and repeat till done
  754. nulline:
  755.     mvi    a,AsciiCR    ; fake the ending return
  756. hiteof:
  757.     pop    h        ; HL=size of string space
  758.     mvi    b,0        ; BC=count of bytes received
  759.     ora    a
  760.     dsbc    b        ; HL=residual count
  761.     mov b,h ! mov c,l    ; BC=residual count
  762.     xchg            ; HL->byte after the last one
  763.     ret            ; all done
  764.  
  765.     end
  766. ; HL=residual count
  767.     mov b,h ! mov c,l    ; BC=residual count
  768.     xchg            ; HL->byte after the last o    name    'FSGAC'
  769.     title    'FSGAC -- get next ascii character'
  770. ;===============================================================
  771. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  772. ;
  773. ; Return the next character from the file represented by DE->FCR
  774. ; This is a logical, textfile get: it respects ^Z as logical
  775. ; end of file, and it swallows the LF that follows a CR so that
  776. ; CR is the only new-line signal seen.
  777. ;
  778. ; Special provision is made for nonstandard ascii files that end
  779. ; with a ^Z that is NOT preceded by CR/LF.  A false CR is
  780. ; generated prior to ^Z if one didn't occur naturally.
  781. ;
  782. ; These two features are simple to explain but very hard to get
  783. ; right for all cases.    Early versions would swallow all LFs
  784. ; after a CR, when only the first should disappear, and if
  785. ; called again after reporting eof, would generate an extra CR
  786. ; due to mishandling the LastCR flag.  The processing for all
  787. ; the special cases can be expensive, too, but after lots of
  788. ; tuning, the path for ordinary printable chars -- 85-90% of
  789. ; all calls -- has been reduced to just 11 instructions.
  790. ;
  791. ; interface: FGETCHAR macro
  792. ;
  793. ; History
  794. ; complete rewrite for correctness and speed 6/12/85
  795. ; don't try for LF after CR from the keyboard 5/11/84
  796. ; initial code 28 March 84
  797. ;===============================================================
  798.     maclib    equates
  799.     maclib    smallz80
  800.     maclib    fcr
  801.     extrn    @FSGBY
  802.  
  803.     public    @FSGAC
  804. @FSGAC:
  805.     push    d
  806.     xtix
  807.     call    @FSGBY
  808.  
  809. ; If physical eof is reported, the NotEof flag has been set
  810. ; false but we need to convert it to logical eof.  Since
  811. ; that happens only once per file (usually, although doing
  812. ; a seek can reset NotEof true), take it out of line.
  813.  
  814.     jrz    PhysEof
  815.  
  816. ; If the input is a control character, we have to check
  817. ; for CR, LF, and ^Z.  Since control chars are very much
  818. ; a minority, take these checks out of line.
  819.  
  820.     cpi    ' '
  821.     jrc    CtlChar
  822.  
  823. ; Reach this point whenever it is known that the byte to be
  824. ; delivered is NOT a CR, and there is any doubt that LastCR
  825. ; may not reflect that fact properly.
  826. NotaCR:
  827.     bresx    FcrLastCR,FcrFlags
  828.  
  829. ; Reach this point whenever the state of the Zero flag is not
  830. ; known, to set it from the value of the returned byte (note:
  831. ; NOT from the NotEof bit as usual, since we might be returning
  832. ; a CR after eof has been seen).
  833. SetZFlag:
  834.     cpi    CpmEof
  835.  
  836. ; Reach this point when the state of the Zero flag is known
  837. ; for sure to be correct, and the LastCR flag is known for
  838. ; sure to be correct as well.
  839. ZFlagNowSet:
  840.     xtix
  841.     pop    d
  842.     ret
  843. ;======= end of normal path taken by 80-90% of all calls =======
  844.  
  845. ; Physical eof reported, either because it occurred or because
  846. ; somebody -- maybe us, on previously detecting input ^Z -- set
  847. ; the NotEof flag false.  There's a NUL in A; make it a ^Z and
  848. ; go on to test that as a control character, which it is.
  849. PhysEof:
  850.     mvi    a,CpmEof
  851.  
  852. ; The input is a control character, and has to be checked for
  853. ; special cases.  First ask, is it a CR?
  854. CtlChar:
  855.     cpi    AsciiCR
  856.     jnz    TestEof
  857.  
  858. ; Reach this point when the byte to return is a CR, and the
  859. ; LastCR flag may not reflect that.  Set it, and exit.
  860. IsaCR:
  861.     bsetx    FcrLastCR,FcrFlags
  862.     jmp    SetZFlag
  863.  
  864. ; See if the control character is CpmEof.
  865. TestEof:
  866.     cpi    CpmEof
  867.     jnz    TestLF
  868.  
  869. ; It is.  Set the NotEof flag, thus preventing further input,
  870. ; effectively converting logical eof to physical eof (it may
  871. ; have been physical in the first place).  Then, if the prior
  872. ; input was CR (and LF), the file is over: return the ^Z.
  873.  
  874.     bresx    FcrNotEof,FcrFlags
  875.     bitx    FcrLastCR,FcrFlags
  876.     jnz    SetZflag
  877.  
  878. ; The end of file was not preceded by a line-end.  Invent a
  879. ; CR to return.  Since NotEof is now false, the next request
  880. ; for input will see physical eof, will make that into ^Z,
  881. ; will end up taking the jump just above.
  882.  
  883.     mvi    a,AsciiCR
  884.     jmp    IsaCR
  885.  
  886. ; See if the control character is LF.  If not, it's just data.
  887. TestLF:
  888.     cpi    AsciiLF
  889.     jnz    NotaCR
  890.  
  891. ; It is.  If the LastCR flag is NOT set, this is a rogue LF --
  892. ; maybe we're reading an MBASIC source file -- but it gets
  893. ; treated as data, regardless.    Since LF isn't CR and LastCR
  894. ; is false, we can go to SetZFlag not NotaCR.
  895.  
  896.     bitx    FcrLastCR,FcrFlags
  897.     jz    SetZFlag
  898.  
  899. ; LastCR IS set, so this LF is following a CR in the normal
  900. ; CP/M line-end pattern.  We want to swallow it, which means
  901. ; reading another byte.  We can't clear LastCR just yet, since
  902. ; we might get eof, which we'll return, then if we are recalled
  903. ; and get eof again, we'd generate a CR...
  904.  
  905.     call    @FSGBY
  906.     jnz    NotPEOF
  907.     mvi    A,CpmEof    ; convert phys. to log. eof
  908. NotPEOF:
  909.  
  910. ; The expected result here is that we now have the first byte
  911. ; of the next line, and it's a printable char.  Pick off that
  912. ; case first so as to save time.
  913.  
  914.     cpi    ' '        ; control char?
  915.     jnc    NotaCR
  916.  
  917. ; Next most likely result is a CR, from CR-LF-CR(-LF), the
  918. ; sequence of a null line.  LastCR is set correctly for that.
  919.  
  920.     cpi    AsciiCR
  921.     jz    SetZFlag
  922.  
  923. ; Ok, if we DON'T have eof, we have a misc. control char, which
  924. ; is just data and not a CR.
  925.  
  926.     cpi    CpmEof
  927.     jnz    NotaCR
  928.  
  929. ; We had CR-LF-^Z, the normal end of file sequence.  Ensure
  930. ; physical eof and return it (Z flag now set ok), leaving
  931. ; LastCR set true so if we're recalled we won't think we
  932. ; have to generate a CR.
  933.  
  934.     bresx    FcrNotEof,FcrFlags
  935.     jmp    ZflagNowSet
  936.  
  937. The    end
  938. ue so if we're recalled we won't think we
  939. ; have to generate a CR.
  940.  
  941.     bresx    FcrNotEof,FcrFlags
  942.     jmp    ZflagNowSet
  943.  
  944. The    end    name    'FSGBK'
  945.     title    'FSGBK -- get block of bytes from file'
  946. ;===============================================================
  947. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  948. ;
  949. ; This module reads a block of bytes from a file to an area
  950. ; of storage.  The file is represented by DE->FCR, the
  951. ; starting address of storage is in HL, and BC contains the
  952. ; size of the area in bytes.
  953. ;
  954. ; Data are moved directly from the buffer to storage using the
  955. ; Z80 instruction, LDI.
  956. ;
  957. ; On return, HL has been updated to the next byte in the area,
  958. ; and BC contains the count of bytes received (the same as the
  959. ; entry value, unless end of file is seen).
  960. ;
  961. ; Altho this code will handle device files, it isn't a good
  962. ; idea to read from a device in blocks.  There is no such thing
  963. ; as "physical end of file" to tell us when to stop, so we are
  964. ; likely to hang, waiting for a next byte that is never coming.
  965. ;
  966. ; interface: FGETBLOK macro
  967. ;
  968. ; History
  969. ; initial code 11 April 84
  970. ;===============================================================
  971.     maclib    equates
  972.     maclib    smallz80
  973.     maclib    fcr
  974.     extrn    @FILLB,@BDOS
  975.  
  976.     public    @FSGBK
  977. @FSGBK:
  978.     push    d
  979.     xtix            ; base the FCR
  980.  
  981. ; Make sure input is possible.
  982.     bitx    FcrNotEof,FcrFlags
  983.     jz    exit        ; oops, eof right now
  984.  
  985. ; Make sure the count is greater than zero -- we don't want to
  986. ; write 65,536 bytes into storage.
  987.     mov a,b ! ora c
  988.     jz    exit        ; zero count, zero action.
  989.  
  990. ; Separate disks and devices.
  991.     bitx    FcrDisk,FcrFlags
  992.     jrnz    isdisk
  993.  
  994. ; For a device, we just make BC-many Bdos input calls.
  995.     push    b        ; save input count for exit
  996. devloop:
  997.     push    h        ; save next byte address
  998.     push    b        ; save current count
  999.     ldx    c,FcrBdosIn
  1000.     call    @BDOS        ; get a byte
  1001.     pop    b
  1002.     pop    h
  1003.     mov    m,a        ; store gotten byte
  1004.     inx    h
  1005.     dcx    b        ; count it
  1006.     mov a,b ! ora c     ; is that it?
  1007.     jnz    devloop     ; (no, continue)
  1008.     pop    b        ; restore count of data read
  1009.     jmp    exit
  1010.  
  1011. ; For a disk file, we have four values to juggle:
  1012. ;    the count of bytes remaining to be read
  1013. ;    the address of the next target byte
  1014. ;    the address of the next buffer byte
  1015. ;    the count of bytes left in the buffer.
  1016. ; That is one more word than we have registers for, but not to
  1017. ; worry.  Thanks to the XTHL instruction, we can keep a word
  1018. ; on top of the stack and have it nearly as accessible.  What
  1019. ; we will now set up is
  1020. ;    BC=count to go
  1021. ;    DE->next output byte
  1022. ;    HL->next buffer byte
  1023. ;    top of stack = count left in buffer
  1024. ; In the loop, the last two values will alternate positions
  1025. ; as each is needed in HL.
  1026.  
  1027. isdisk:
  1028.     push    b        ; save initial count for exit
  1029.     push    h        ; save initial target addr
  1030.     ldbwx    d,FcrBufptr
  1031.     ldbwx    h,FcrBufeod
  1032.     ora    a
  1033.     dsbc    d        ; HL=count left in buffer
  1034.     xthl            ; put that on stack
  1035.     push    h        ; re-save target addr
  1036.     ldbwx    h,FcrBufadr
  1037.     dad    d        ; HL->next buffer byte
  1038.     pop    d        ; DE->next target byte
  1039.  
  1040. Loop:
  1041.     xthl            ; HL=count of data in buffer
  1042.     mov a,h ! ora l     ; is it zero?
  1043.     jnz    dataleft    ; (no)
  1044.  
  1045. ; We have used up the file buffer and have to refill it.  That
  1046. ; could result in finding end of file.    If not, we have to
  1047. ; reconstruct our buffer address and buffer count.
  1048.     call    @FILLB
  1049.     bitx    FcrNotEof,FcrFlags
  1050.     jz    hiteof
  1051.     ldbwx    h,FcrBufadr    ; new address of next byte
  1052.     xthl            ; ..on the stack
  1053.     ldbwx    h,FcrBufeod    ; new count of data left
  1054.  
  1055. dataleft:
  1056.     dcx    h        ; count down one buffer byte
  1057.     xthl            ; stack it, HL->byte
  1058.     ldi            ; copy from H to D reducing B
  1059.     jpe    Loop        ; continue while BC>0
  1060.  
  1061. ; We have moved all the bytes requested, BC=0000.  We must
  1062. ; reconstruct the correct Bufptr value for the FCR, then
  1063. ; we can return the original BC value as the count.
  1064.     pop    b        ; BC=bytes left in buffer
  1065.     ldbwx    h,FcrBufeod
  1066.     ora    a
  1067.     dsbc    b        ; end of data, less bytes left
  1068.     stbwx    h,FcrBufptr    ; is new Bufptr
  1069.     pop    b        ; BC=original count
  1070.     jmp    diskexit
  1071.  
  1072. ; We met end of file before moving all (maybe any) requested
  1073. ; bytes.  Since physical end of file was seen, @FILLB set
  1074. ; Bufptr and Bufeod to zero.  However, we have to compute the
  1075. ; count of bytes actually moved for our exit BC value.
  1076. hiteof:
  1077.     pop    h        ; discard buffer-byte-count
  1078.     pop    h        ; HL=original count
  1079.     ora    a
  1080.     dsbc    b        ; HL=bytes actually moved
  1081.     mov b,h ! mov c,l    ; BC=new count
  1082.  
  1083. ; Regs BC have the count of moved bytes, DE->after last one
  1084. ; moved.  Put that in HL where it should be, set the Z flag
  1085. ; to reflect end of file, and exit.
  1086. diskexit:
  1087.     xchg
  1088. exit:
  1089.     bitx    FcrNotEof,FcrFlags
  1090.     xtix
  1091.     pop    d
  1092.     ret
  1093.  
  1094.     end
  1095. e Z flag
  1096. ; to ref    name    'FSG24'
  1097.     title    'FSG24 -- read 2 or 4 byte items from file'
  1098. ;===============================================================
  1099. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1100. ;
  1101. ; Read a binary word or longword from the (presumably binary)
  1102. ; file represented by DE->FCR.    If end of file intervenes, the
  1103. ; most-significant bytes are returned as zero.    The Z flag
  1104. ; reflects end of file.
  1105. ;
  1106. ; interface: FGETLW macros
  1107. ;
  1108. ; History
  1109. ; initial code 16 April 84
  1110. ;===============================================================
  1111.     extrn    @FSGBY
  1112.  
  1113. ; On entry to @FSG4B, HL->space for a longword in storage.
  1114. ; We will just read 4 bytes regardless, counting on FSGBY
  1115. ; to return nulls on and following end of file.
  1116.     public    @FSG4B
  1117. @FSG4B:
  1118.  rept 4
  1119.     call    @FSGBY
  1120.     mov m,a ! inx h
  1121.  endm
  1122.     ret        ; return with last Z setting
  1123.  
  1124. ; @FSG2B is similar, except that the word value is returned
  1125. ; IN HL, not in storage AT HL.
  1126.     public    @FSG2B
  1127. @FSG2B:
  1128.     call    @FSGBY
  1129.     mov    l,a
  1130.     call    @FSGBY
  1131.     mov    h,a
  1132.     ret
  1133.  
  1134.     end
  1135. t    name    'FSGBB'
  1136.     title    'FSGBB -- get next byte from sequential file'
  1137. ;===============================================================
  1138. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1139. ;
  1140. ; Return the next byte from a file represented by DE->FCR.  This
  1141. ; is a binary get: it respects only physical eof (^Z is data),
  1142. ; and it returns both CR and LF at ends of lines.
  1143. ;
  1144. ; If physical eof is seen, return A=NUL and Z true.  Else return
  1145. ; A=the byte and Z false.
  1146. ;
  1147. ; interface: FGETBYTE macro
  1148. ;
  1149. ; History
  1150. ; initial code 28 March 84
  1151. ;===============================================================
  1152.     maclib    smallz80
  1153.     extrn    @FSGBY
  1154.  
  1155.     public    @FSGBB
  1156. @FSGBB:
  1157. ; set up IX for @FSGBY, the physical byte-getter.  Call it.  If
  1158. ; it returns Z false (not eof) just return; otherwise set A=NUL.
  1159.     push    d    ; stack ->FCR
  1160.     xtix        ; save IX, IX->FCR
  1161.     call    @FSGBY
  1162.     xtix        ; restore IX, stack ->FCR
  1163.     pop    d    ; restore DE
  1164.     rnz        ; if not eof, exit w/ Z false
  1165.     xra    a    ; when eof, force A=NUL
  1166.     ret
  1167.  
  1168.     end
  1169. store IX, stac    name    'FSGBY'
  1170.     title    'FSGBY -- get next physical byte'
  1171. ;===============================================================
  1172. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1173. ;
  1174. ; This is the central sequential-input operation.  It returns
  1175. ; the next byte from the file represented by IX->FCR.  It does
  1176. ; not respect the special features of Ascii files; checking for
  1177. ; ^Z and swallowing linefeeds is up to the caller.  However, if
  1178. ; we read from CON and receive a CR, we immediately echo an LF.
  1179. ;
  1180. ; interface:    IX->FCR
  1181. ;        returns A=byte and Z false, or
  1182. ;        returns A unpredictable and Z true after eof
  1183. ;        destroys register DE
  1184. ;
  1185. ; History
  1186. ; initial code 28 March 84
  1187. ;===============================================================
  1188.     maclib    equates
  1189.     maclib    smallz80
  1190.     maclib    FCR
  1191.     extrn    @FILLB,@BDOS
  1192.  
  1193.     public    @FSGBY
  1194. @FSGBY:
  1195.     bitx    FcrNotEof,FcrFlags ; is there any input data?
  1196.     rz        ; (no, quit)
  1197.     push    d
  1198.     push    h
  1199.  
  1200.     bitx    FcrDisk,FcrFlags    ; disk, or device?
  1201.     jrz    isDevice
  1202.  
  1203. ; A disk file.    Load up the buffer pointer into DE.  If that is
  1204. ; not less than the size of valid data, we have to fill the
  1205. ; buffer first.
  1206.     ldbwx    d,FcrBufptr
  1207.     ldbwx    h,FcrBufeod
  1208.     ora    a
  1209.     dsbc    d        ; flags := (HL := data left)
  1210.     jnz    havedata
  1211.     call    @FILLB        ; fills buffer, resets bufptr
  1212.     xchg            ; (DE=new bufptr=0)
  1213.     bitx    FcrNotEof,FcrFlags    ; now end of file?
  1214.     jrz    exit        ; (yes, quit)
  1215.  
  1216. ; The byte we need is Buffer[Bufptr].  Compute its address and
  1217. ; move it.  Then increment the buffer pointer.
  1218. havedata:
  1219.     ldbwx    h,FcrBufadr
  1220.     dad    d        ; HL-> Buffer[Bufptr]
  1221.     mov    a,m        ; got the byte
  1222.     inx    d
  1223.     stbwx    d,FcrBufptr    ; save incremented pointer
  1224.  
  1225. ; for either device or disk files, set Z false (by testing the
  1226. ; NotEof bit) and exit.
  1227.  
  1228. exit:    pop    h
  1229.     pop    d
  1230.     bitx    FcrNotEof,FcrFlags
  1231.     ret
  1232.  
  1233. ; This is a device file.  Get one byte by calling the Bdos with
  1234. ; the appropriate function code.  N.B. output only devices have
  1235. ; NotEof false, they never get this far.
  1236. isdevice:
  1237.     push    b
  1238.     ldx    c,FcrBdosIn    ; appropriate input request
  1239.     call    @BDOS        ; ..gets a byte
  1240.     pop    b
  1241.     cpi    AsciiCR     ; got a CR?
  1242.     jnz    exit        ; (no, all done)
  1243.     bitx    FcrCon,FcrFlags ; from the keyboard?
  1244.     jz    exit        ; (no, all done)
  1245.     push    psw        ; yes, save gotten byte
  1246.     push    b        ; ..and other regs
  1247.     mvi    c,2        ; (BdosType)
  1248.     mvi    e,AsciiLF    ; ..and echo LF for the CR
  1249.     call    @BDOS
  1250.     pop    b
  1251.     pop    psw        ; recover gotten byte
  1252.     jmp    exit        ; (and exit)
  1253.  
  1254.     end
  1255. i    e,AsciiLF    ; .    name    'FSPBX'
  1256.     title    'FSPBX -- put byte as hex to file'
  1257. ;===============================================================
  1258. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1259. ;
  1260. ; Write the byte in A to the file represented by DE->FCR, as
  1261. ; hex digits.  Method is to build a string of hex digits and
  1262. ; to write that.
  1263. ;
  1264. ; interface: FPUTBBAX macro
  1265. ;
  1266. ; History
  1267. ; initial code 16 April 84
  1268. ;===============================================================
  1269.     common    /fswork/
  1270. ; common work area for file operations
  1271. workstr ds    128    ; room for 127-byte string
  1272.     cseg
  1273.     extrn    @STPBX,@FSPST
  1274.  
  1275.     public    @FSPBX
  1276. @FSPBX:
  1277.     push    h
  1278.     lxi    h,workstr
  1279.     mvi    m,0        ; make null string
  1280.     xchg            ; DE->string
  1281.     call    @STPBX        ; create string
  1282.     xchg            ; DE->file
  1283.     lxi    h,workstr    ; HL->string
  1284.     call    @FSPST        ; write it
  1285.     pop    h
  1286.     ret
  1287.  
  1288.     end
  1289. call    @STPBX        ; create string
  1290.     xchg            ; DE->file
  1291.     lxi    h,workst    name    'FSPWX'
  1292.     title    'FSPWX -- put word as hex digits to file'
  1293. ;===============================================================
  1294. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1295. ;
  1296. ; Write the word in HL to the file represented by DE->FCR, as
  1297. ; four hex digits.  Method is to convert the word to a string
  1298. ; and to write the string.
  1299. ;
  1300. ; interface: FPUTBWAX macro
  1301. ;
  1302. ; History
  1303. ; initial code 16 April 84
  1304. ;===============================================================
  1305.     common    /fswork/
  1306. ; common work area for file operations
  1307. workstr ds    128    ; room for 127-byte string
  1308.     cseg
  1309.     extrn    @STPWX,@FSPST
  1310.  
  1311.     public    @FSPWX
  1312. @FSPWX:
  1313.     push    h    ; preserve HL for exit
  1314.     push    d    ; preserve file address
  1315.     push    h    ; save HL for conversion
  1316.  
  1317.     lxi    h,workstr
  1318.     mvi    m,0    ; make null string
  1319.     xchg        ; DE->null string
  1320.     pop    h    ; HL=word to convert
  1321.     call    @STPWX    ; convert word to string
  1322.     pop    d    ; DE->file
  1323.     lxi    h,workstr ; HL->string to write
  1324.     call    @FSPST    ; write it
  1325.     pop    h    ; restore entry HL
  1326.     ret
  1327.  
  1328.     end
  1329. ng
  1330.     pop    d    ; D    name    'FSPLX'
  1331.     title    'FSPLX -- put longword as hex digits to file'
  1332. ;===============================================================
  1333. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1334. ;
  1335. ; Write HL->longword to the file represented by DE->FCR as
  1336. ; eight hex digits.  Method is to convert the data to a string
  1337. ; and to write the string.
  1338. ;
  1339. ; interface: FPUTLWAX macro
  1340. ;
  1341. ; History
  1342. ; initial code 16 April 84
  1343. ;===============================================================
  1344.     common    /fswork/
  1345. ; common work area for file operations
  1346. workstr ds    128    ; room for 127-byte string
  1347.     cseg
  1348.     extrn    @STPLX,@FSPST
  1349.  
  1350.     public    @FSPLX
  1351. @FSPLX:
  1352.     push    d    ; preserve file address
  1353.     push    h    ; save HL for conversion
  1354.  
  1355.     lxi    h,workstr
  1356.     mvi    m,0    ; make null string
  1357.     xchg        ; DE->null string
  1358.     pop    h    ; HL=word to convert
  1359.     call    @STPLX    ; convert word to string
  1360.     push    h    ; save updated HL for exit
  1361.     pop    d    ; DE->file
  1362.     lxi    h,workstr ; HL->string to write
  1363.     call    @FSPST    ; write it
  1364.     pop    h    ; restore updated HL
  1365.     ret
  1366.  
  1367.     end
  1368. 
  1369.     pop    name    'FSPBD'
  1370.     title    'FSPBD -- put byte as decimal to file'
  1371. ;===============================================================
  1372. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1373. ;
  1374. ; Write the byte in A to the file represented by DE->FCR, as
  1375. ; decimal digits.  Method is to build a string of digits and
  1376. ; write that to the file.
  1377. ;
  1378. ; interface: FPUTBBAD macro
  1379. ;
  1380. ; History
  1381. ; initial code 16 April 84
  1382. ;===============================================================
  1383.     common    /fswork/
  1384. ; common work area for file operations
  1385. workstr ds    128    ; room for 127-byte string
  1386.     cseg
  1387.     extrn    @STPBB,@FSPST
  1388.  
  1389.     public    @FSPBD
  1390. @FSPBD:
  1391.     push    h
  1392.     lxi    h,workstr
  1393.     mvi    m,0        ; make null string
  1394.     xchg            ; DE->string
  1395.     call    @STPBB        ; create string
  1396.     xchg            ; DE->file
  1397.     lxi    h,workstr    ; HL->string
  1398.     call    @FSPST        ; write it
  1399.     pop    h
  1400.     ret
  1401.  
  1402.     end
  1403. call    @STPBB        ; create string
  1404.     xchg            ; DE->file
  1405.     name    'FSPBW'
  1406.   title   'FSPBW -- put unsigned word as decimal digits to file'
  1407. ;===============================================================
  1408. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1409. ;
  1410. ; Write the word in HL to the file represented by DE->FCR, as
  1411. ; decimal digits in a field whose minimum size is in A.  Method
  1412. ; is to convert the word to a string and to write the string.
  1413. ;
  1414. ; interface: FPUTBWAD macro
  1415. ;
  1416. ; History
  1417. ; initial code 16 April 84
  1418. ;===============================================================
  1419.     common    /fswork/
  1420. ; common work area for file operations
  1421. workstr ds    128    ; room for 127-byte string
  1422.     cseg
  1423.     extrn    @STPBW,@FSPST
  1424.  
  1425.     public    @FSPBW
  1426. @FSPBW:
  1427.     push    h    ; preserve HL for exit
  1428.     push    d    ; preserve file address
  1429.     push    h    ; save HL for conversion
  1430.  
  1431.     cpi    128    ; ensure field-width will fit our
  1432.     jc    lenok    ; work-string -- no fields over 127
  1433.     mvi    a,127
  1434. lenok:
  1435.     lxi    h,workstr
  1436.     mvi    m,0    ; make null string
  1437.     xchg        ; DE->null string
  1438.     pop    h    ; HL=word to convert
  1439.     call    @STPBW    ; convert word to string
  1440.     pop    d    ; DE->file
  1441.     lxi    h,workstr ; HL->string to write
  1442.     call    @FSPST    ; write it
  1443.     pop    h    ; restore entry HL
  1444.     ret
  1445.  
  1446.     end
  1447. ng
  1448.     pop    d    ; DE->file
  1449.     lxi    h,workstr ; HL->string to write
  1450.     call    @FSPST    ; write it
  1451.     pop    h    ; restore ent    name    'FSPSW'
  1452.   title   'FSPSW -- put signed word as decimal digits to file'
  1453. ;===============================================================
  1454. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1455. ;
  1456. ; Write the word in HL to the file represented by DE->FCR, as
  1457. ; decimal digits in a field whose minimum size is in A.  Method
  1458. ; is to convert the word to a string and to write the string.
  1459. ;
  1460. ; interface: FPUTBWAD macro
  1461. ;
  1462. ; History
  1463. ; initial code 16 April 84
  1464. ;===============================================================
  1465.     common    /fswork/
  1466. ; common work area for file operations
  1467. workstr ds    128    ; room for 127-byte string
  1468.     cseg
  1469.     extrn    @STPSW,@FSPST
  1470.  
  1471.     public    @FSPSW
  1472. @FSPSW:
  1473.     push    h    ; preserve HL for exit
  1474.     push    d    ; preserve file address
  1475.     push    h    ; save HL for conversion
  1476.  
  1477.     cpi    128    ; ensure field-width will fit our
  1478.     jc    lenok    ; work-string -- no more than 127
  1479.     mvi    a,127
  1480. lenok:
  1481.     lxi    h,workstr
  1482.     mvi    m,0    ; make null string
  1483.     xchg        ; DE->null string
  1484.     pop    h    ; HL=word to convert
  1485.     call    @STPSW    ; convert word to string
  1486.     pop    d    ; DE->file
  1487.     lxi    h,workstr ; HL->string to write
  1488.     call    @FSPST    ; write it
  1489.     pop    h    ; restore entry HL
  1490.     ret
  1491.  
  1492.     end
  1493. ng
  1494.     pop    d    ; DE->file
  1495.     lxi    h,workstr ; HL->string to write
  1496.     call    @FSPST    ; write it
  1497.     pop    h    ; restore entry H    name    'FSPLD'
  1498.   title   'FSPLD -- put longword as decimal digits to file'
  1499. ;===============================================================
  1500. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1501. ;
  1502. ; Write HL->longword to the file represented by DE->FCR, as
  1503. ; decimal digits in a field whose minimum size is in A.  Method
  1504. ; is to convert the word to a string and to write the string.
  1505. ;
  1506. ; interface: FPUTLWAD macro
  1507. ;
  1508. ; History
  1509. ; initial code 16 April 84
  1510. ;===============================================================
  1511.     common    /fswork/
  1512. ; common work area for file operations
  1513. workstr ds    128    ; room for 127-byte string
  1514.     cseg
  1515.     extrn    @STPLW,@FSPST
  1516.  
  1517.     public    @FSPLD
  1518. @FSPLD:
  1519.     push    d    ; preserve file address
  1520.     push    h    ; save HL for conversion
  1521.  
  1522.     cpi    128    ; ensure field-width will fit our
  1523.     jc    lenok    ; work-string -- no more than 127
  1524.     mvi    a,127
  1525. lenok:
  1526.     lxi    h,workstr
  1527.     mvi    m,0    ; make null string
  1528.     xchg        ; DE->null string
  1529.     pop    h    ; HL->word to convert
  1530.     call    @STPLW    ; convert word to string
  1531.     pop    d    ; DE->file
  1532.     push    h    ; save updated HL
  1533.     lxi    h,workstr ; HL->string to write
  1534.     call    @FSPST    ; write it
  1535.     pop    h    ; restore updated HL
  1536.     ret
  1537.  
  1538.     end
  1539. h    ; save updated HL
  1540.     lxi    h,workstr ; HL->string to write
  1541.     call    @FSPST    ; write it
  1542.     pop    h    ; restore updated HL
  1543.     ret    name    'FSPST'
  1544.     title    'FSPST -- put string to ASCII file'
  1545. ;===============================================================
  1546. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1547. ;
  1548. ; This module writes HL->string onto the (presumably Ascii) file
  1549. ; represented by DE->FCR, and returns HL->end of string.
  1550. ;
  1551. ; interface: FPUTSTR macro
  1552. ;
  1553. ; History
  1554. ; initial code Friday, 13 April 84
  1555. ;===============================================================
  1556.     maclib    equates
  1557.     maclib    smallz80
  1558.     maclib    fcr
  1559.     extrn    @FDUMP,@FRFIL,@BDOS
  1560.  
  1561.     public    @FSPST
  1562. @FSPST:
  1563.     push    psw
  1564.     push    b
  1565.     push    d
  1566.     xtix
  1567.  
  1568. ; Is the file capable of output?
  1569.     bitx    FcrOutput,FcrFlags
  1570.     jz    exit    ; (no)
  1571.  
  1572. ; Is the string null?
  1573.     mov    a,m
  1574.     ora    a
  1575.     jz    exit    ; (yes)
  1576.  
  1577. ; Is the file a disk or a device?
  1578.     bitx    FcrDisk,FcrFlags
  1579.     jrnz    isdisk
  1580.  
  1581. ; For a device, we just make repetitive calls on the Bdos.
  1582. devloop:
  1583.     ldx    c,FcrBdosOut    ; output function number
  1584.     mov    e,a        ; byte to write
  1585.     push    h        ; save ->string
  1586.     call    @BDOS        ; (preserves BC, DE)
  1587.     pop    h
  1588.     mov    a,m        ; oh, was that a CR?
  1589.     cpi    AsciiCR
  1590.     jrnz    devnotlf    ; (no)
  1591.     mvi    e,AsciiLF    ; yes, add an LF to it
  1592.     push    h
  1593.     call    @BDOS
  1594.     pop    h
  1595. devnotlf:
  1596.     inx    h        ; now, the next byte
  1597.     mov a,m ! ora a     ; is it the end of string?
  1598.     jnz    devloop     ; (no, continue)
  1599.     jmp    exit        ; yes, done.
  1600.  
  1601. ; Now, for a disk we want to take advantage of the file buffer
  1602. ; to speed things up.  The process is similar to that of @FSPBK
  1603. ; except that, since the string is delimited, we don't have to
  1604. ; worry about counting data bytes -- only buffer bytes.  We will
  1605. ; keep the count of buffer space in BC and the buffer address
  1606. ; in DE.  When the buffer fills up, we have to call @FDUMP and
  1607. ; reconstruct those two values.
  1608.  
  1609. isdisk:
  1610.     push    h        ; save string addr a mo.
  1611.     ldbwx    d,FcrBufptr    ; count of bytes now in buffer
  1612.     ldbwx    h,FcrBufsize    ; total space in buffer
  1613.     dsbc    d        ; HL=space left in buffer
  1614.     mov b,h ! mov c,l    ; put that in BC
  1615.     ldbwx    h,FcrBufadr
  1616.     dad    d        ; HL->next byte in buffer
  1617.     xchg            ; put that in DE
  1618.     pop    h        ; HL->string
  1619.  
  1620. diskloop:
  1621.     call    checkbfr    ; make sure we have room
  1622.     mov    a,m        ; pick the byte up
  1623.     stax d ! inx d ! dcx b    ; and store it
  1624.     cpi    AsciiCR     ; was it a CR?
  1625.     jrnz    disknotlf    ; (no, continue)
  1626.     call    checkbfr    ; ensure room for LF
  1627.     mvi    a,AsciiLF
  1628.     stax d ! inx d ! dcx b    ; and put one in
  1629. disknotlf:
  1630.     inx    h        ; now the next byte
  1631.     mov a,m ! ora a     ; might be the end
  1632.     jnz    diskloop    ; (no, continue)
  1633.  
  1634. ; Adjust the file buffer-pointer to account for what we did.
  1635.  
  1636.     push    h        ; save updated string-ptr
  1637.     ldbwx    h,FcrBufSize    ; buffer size..
  1638.     dsbc    b        ; less buffer space remaining
  1639.     stbwx    h,FcrBufptr    ; is new Bufptr (carry clear)
  1640.     xchg            ; save it in DE
  1641.     ldbwx    h,FcrBufeod    ; if we wrote past eod,
  1642.     dsbc    d        ; (if bufptr > bufeod)
  1643.     cc    @FRFIL        ; ..fill out new record
  1644.     pop    h        ; recover string-ptr
  1645.     bsetx    FcrDirty,FcrFlags ; buffer now dirty
  1646. exit:
  1647.     xtix
  1648.     pop    d
  1649.     pop    b
  1650.     pop    psw
  1651.     ret
  1652.  
  1653. ; This subroutine tests the buffer and, if it is full, dumps
  1654. ; it and resets BC (room left) and DE (next output byte).
  1655. ; It's a subroutine because it may have to be done twice for
  1656. ; a byte (CR and again for LF).
  1657.  
  1658. checkbfr:
  1659.     mov a,b ! ora c     ; have we filled the buffer?
  1660.     rnz            ; (no)
  1661.     ldbwx    b,FcrBufsize    ; yes, set data quantity for
  1662.     stbwx    b,FcrBufeod    ; the buffer-dumper
  1663.     bsetx    FcrDirty,FcrFlags ; ensure buffer marked
  1664.     call    @FDUMP        ; clear the buffer
  1665.     ldbwx    d,FcrBufadr    ; BC=size of bfr, DE->1st byte
  1666.     ret
  1667.  
  1668.     end
  1669. ags ; ensure buffer marked
  1670.     call    @FDUMP        ; clear the buffer
  1671.     ldbwx    d,FcrBufadr    ; BC=size of bfr, DE->1st byte
  1672.     ret
  1673.  
  1674.     name    'FSPAC'
  1675.     title    'FSPAC -- put ascii character to seq. file'
  1676. ;===============================================================
  1677. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1678. ;
  1679. ; Write the byte in A to the file represented by DE->FCR.  This
  1680. ; is an Ascii operation, so if the byte is CR, add an LF to it.
  1681. ;
  1682. ; interface: FPUTCHAR macro
  1683. ;
  1684. ; History
  1685. ; initial code 30 March 84
  1686. ;===============================================================
  1687.     maclib    equates
  1688.     maclib    smallz80
  1689.     extrn    @FSPBY
  1690.  
  1691.     public    @FSPAC
  1692. @FSPAC:
  1693.     push    d
  1694.     xtix
  1695.     call    @FSPBY
  1696.     cpi    AsciiCR
  1697.     jnz    exit
  1698.     push    psw
  1699.     mvi    a,AsciiLF
  1700.     call    @FSPBY
  1701.     pop    psw
  1702. exit:    xtix
  1703.     pop    d
  1704.     ret
  1705.  
  1706.     end
  1707. x
  1708.     call    @FSPBY
  1709.     cpi    AsciiCR
  1710.     jnz    exit
  1711.     push    psw
  1712.     mvi    a,AsciiLF
  1713.     call    @FSPBY
  1714.     name    'FSPBK'
  1715.     title    'FSPBK -- put block of bytes to file'
  1716. ;===============================================================
  1717. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1718. ;
  1719. ; This module writes a block of bytes from an area of storage
  1720. ; to a file.  The file is represented by DE->FCR, HL has the
  1721. ; starting address of the block, and BC contains the size of the
  1722. ; block in bytes (in other words, this is "LDIR to a file").
  1723. ;
  1724. ; Data are moved directly from storage to the buffer using the
  1725. ; Z80 instruction, LDI.  See comments later for efficiency.
  1726. ;
  1727. ; On return, HL has been updated to the next byte in the
  1728. ; area.  Other registers are unchanged (so that a sequence of
  1729. ; calls will write a large contiguous area).
  1730. ;
  1731. ; interface: FPUTBLOK macro
  1732. ;
  1733. ; History
  1734. ; initial code 11 April 84
  1735. ;===============================================================
  1736.     maclib    equates
  1737.     maclib    smallz80
  1738.     maclib    fcr
  1739.     extrn    @FDUMP,@FRFIL,@BDOS
  1740.  
  1741.     public    @FSPBK
  1742. @FSPBK:
  1743.     push    psw        ; save psw
  1744.     push    b        ; ..and BC
  1745.     push    d
  1746.     xtix            ; base the FCR
  1747.  
  1748.     bitx    FcrOutput,FcrFlags ; can it do output?
  1749.     jz    exit        ; nah, ferget it
  1750.  
  1751.     mov a,b ! ora c     ; is the count > 0?
  1752.     jz    exit        ; no, don't write 65KB
  1753.  
  1754.     bitx    FcrDisk,FcrFlags ; is it a disk?
  1755.     jrnz    isdisk        ; yes, yes, get on with it
  1756.  
  1757. ; For device output we just do repetitive Bdos output calls.
  1758.  
  1759. devloop:
  1760.     push    b        ; save count
  1761.     push    h        ; save byte address
  1762.     mov    e,m        ; byte to write
  1763.     ldx    c,FcrBdosOut    ; Bdos service number
  1764.     call    @BDOS        ; write one byte
  1765.     pop    h
  1766.     pop    b
  1767.     inx    h        ; next byte
  1768.     dcx    b        ; count last one
  1769.     mov a,b ! ora c     ; is that it?
  1770.     jnz    devloop     ; (nope, continue)
  1771.     jmp    exit        ; (yep, done)
  1772.  
  1773. ; In disk output we have four values to juggle:
  1774. ;    the count of bytes remaining to be written
  1775. ;    the address of the next source byte
  1776. ;    the address of the next buffer byte
  1777. ;    the count of free bytes left in the buffer.
  1778. ; That is one more word than we have registers for, but not to
  1779. ; worry.  Thanks to the XTHL instruction, we can keep a word
  1780. ; on top of the stack and have it nearly as accessible.  What
  1781. ; we will now set up is
  1782. ;    BC=count to go
  1783. ;    DE->next buffer byte
  1784. ;    HL->next source byte
  1785. ;    top of stack = count left in buffer
  1786. ; In the loop, the last two values will alternate positions
  1787. ; as each is needed in HL.
  1788.  
  1789. isdisk:
  1790.     push    h        ; save initial source addr
  1791.     ldbwx    d,FcrBufptr
  1792.     ldbwx    h,FcrBufsize
  1793.     ora    a
  1794.     dsbc    d        ; HL=space left in buffer
  1795.     xthl            ; stack that, HL->source
  1796.     push    h        ; re-save source address
  1797.     ldbwx    h,FcrBufadr
  1798.     dad    d
  1799.     xchg            ; DE->next buffer byte
  1800.     pop    h        ; HL->next source byte
  1801.  
  1802. ; The point of this elaborate code is to save time relative
  1803. ; to a sequence of calls to @FSPBY.  If we ignore the times
  1804. ; when the buffer fills up, the following loop costs 88 clocks
  1805. ; or 22 microseconds per byte moved (4MHZ).  That is at least
  1806. ; four times as fast as fast as @FSPBY, which uses 370 clocks
  1807. ; per call in the best conditions.
  1808. Loop:
  1809.     xthl            ; HL=space left in buffer
  1810.     mov a,h ! ora l     ; is it zero?
  1811.     jnz    roomenuf    ; (no)
  1812.  
  1813. ; We have filled the file buffer and have to dump it.  Before
  1814. ; doing so we have to set things up the way they would be after
  1815. ; as many calls to FSPBY: FcrDirty true and FcrBufeod equal to
  1816. ; the size of the buffer.  (In one very rare instance we will
  1817. ; write a buffer-load needlessly, but no harm will result.)
  1818. ; After the dump, we have to reset our target to the buffer's
  1819. ; address and our count to its size.
  1820.     ldbwx    h,FcrBufsize    ; HL=space in buffer later
  1821.     stbwx    h,FcrBufeod    ; ..and data in buffer now
  1822.     bsetx    FcrDirty,FcrFlags ; buffer is (probly) dirty
  1823.     call    @FDUMP
  1824.     ldbwx    d,FcrBufadr    ; DE->next buffer byte
  1825.  
  1826. roomenuf:
  1827.     dcx    h        ; count down one buffer byte
  1828.     xthl            ; stack it, HL->source byte
  1829.     ldi            ; copy from H to D reducing B
  1830.     jpe    Loop        ; continue while BC>0
  1831.  
  1832. ; The data have been moved.  Now we have to reconstruct the
  1833. ; proper value of FcrBufptr and FcrBufeod. We also mark the
  1834. ; buffer dirty, since we definitely changed it since it was
  1835. ; last written.
  1836.     pop    d        ; DE=space left (and stk clear)
  1837.     push    h        ; save updated source addr
  1838.     ldbwx    h,FcrBufsize
  1839.     ora    a
  1840.     dsbc    d        ; size - unused gives pointer
  1841.     stbwx    h,FcrBufptr
  1842.     xchg
  1843.     ldbwx    h,FcrBufeod    ; check: did we write past eod?
  1844.     dsbc    d
  1845.     cc    @FRFIL        ; if so, fill out new record
  1846.     bsetx    FcrDirty,FcrFlags
  1847.  
  1848. ; Restore the registers and exit.
  1849.     pop    h        ; HL = updated source address
  1850. exit:
  1851.     xtix
  1852.     pop    d
  1853.     pop    b
  1854.     pop    psw
  1855.     ret
  1856.  
  1857.     end
  1858.  Restore    name    'FSP24'
  1859.     title    'FSP24 -- write 2 or 4-byte items to file'
  1860. ;===============================================================
  1861. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1862. ;
  1863. ; Write binary words and longwords into the (presumably binary)
  1864. ; file represented by DE->FCR.
  1865. ;
  1866. ; interface: FPUTWORD and FPUTLW macros
  1867. ;
  1868. ; History
  1869. ; initial code 16 April 84
  1870. ;===============================================================
  1871.     extrn    @FSPBY
  1872.     public    @FSP2B,@FSP4B
  1873.  
  1874. ; On entry to @FSP4B, HL->a longword in storage.  We just shove
  1875. ; it out to the file in standard (least significant to most)
  1876. ; order -- which is how it is laid out in storage.
  1877. @FSP4B:
  1878.     push    psw
  1879.     push    h
  1880.  rept 4
  1881.     mov a,m ! inx h
  1882.     call    @FSPBY
  1883.  endm
  1884.     pop    h
  1885.     pop    psw
  1886.     ret
  1887.  
  1888. ; @FSP2B is presented with a number IN the HL register to be
  1889. ; written in low-high order.
  1890. @FSP2B:
  1891.     push    psw
  1892.     mov    a,l
  1893.     call    @FSPBY
  1894.     mov    a,h
  1895.     call    @FSPBY
  1896.     pop    psw
  1897.     ret
  1898.  
  1899.     end
  1900.  be
  1901. ; written in low-high order.
  1902. @FSP2B:
  1903.     push    psw
  1904.     mov    a,l
  1905.     call    @    name    'FSPBB'
  1906.     title    'FSPBB -- put binary byte to sequential file'
  1907. ;===============================================================
  1908. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1909. ;
  1910. ; Writes the byte in A to the file represented by DE->FCR.
  1911. ; There's hardly anything to do but set up for, and call, the
  1912. ; FSPBY subroutine.
  1913. ;
  1914. ; interface: FPUTBYTE macro
  1915. ;
  1916. ; History
  1917. ; initial code 30 March 84
  1918. ;===============================================================
  1919.     maclib    smallz80
  1920.  
  1921.     extrn    @FSPBY
  1922.     public    @FSPBB
  1923. @FSPBB:
  1924.     push    d
  1925.     xtix
  1926.     call    @FSPBY
  1927.     xtix
  1928.     pop    d
  1929.     ret
  1930.  
  1931.     end
  1932. ======
  1933.     maclib    smallz80
  1934.  
  1935.     extrn    @F    name    'FSPBY'
  1936.     title    'FSPBY -- put next physical byte'
  1937. ;===============================================================
  1938. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1939. ;
  1940. ; This is the central sequential-put routine.  It puts the byte
  1941. ; in A to the file defined by IX->FCR.    If the file buffer is
  1942. ; full it is written to disk first, and if the byte is the first
  1943. ; of a new record at the end of the file, the new record is
  1944. ; filled and the end-of-data pointer updated.
  1945. ;
  1946. ; interface:    IX->FCR, A=byte to write
  1947. ;        updates FcrBufptr, FcrBufeod
  1948. ;
  1949. ; History
  1950. ; initial code 30 March 84
  1951. ;===============================================================
  1952.     maclib    equates
  1953.     maclib    smallz80
  1954.     maclib    fcr
  1955.     extrn    @FDUMP,@FRFIL,@BDOS
  1956.  
  1957.     public    @FSPBY
  1958. @FSPBY:
  1959.     push    h
  1960.     push    d
  1961.     push    psw
  1962.  
  1963.     bitx    FcrOutput,FcrFlags    ; output allowed?
  1964.     jrz    exit            ; (no)
  1965.     bitx    FcrDisk,FcrFlags    ; disk, or device?
  1966.     jrz    isdevice    ; (device)
  1967.  
  1968. ; The buffer-pointer has the offset to the next output byte. If
  1969. ; it isn't less than the buffer size we have to dump the buffer.
  1970. ; That resets the buffer-pointer.
  1971.     ldbwx    d,FcrBufptr
  1972.     ldbwx    h,FcrBufsize
  1973.     ora    a
  1974.     dsbc    d        ; flags := (HL := size-ptr)
  1975.     jnz    roomenuf    ; (ptr is less, carry on)
  1976.     call    @FDUMP        ; dump and advance buffer
  1977.     xchg            ; DE=new bufptr of zero
  1978.  
  1979. ; the new byte may now go at Buffer[DE=Bufptr]
  1980. roomenuf:
  1981.     pop    psw        ; recover byte to write
  1982.     push    psw        ; ..but save caller's flags
  1983.     ldbwx    h,FcrBufadr
  1984.     dad    d        ; HL->Buffer[Bufptr]
  1985.     mov    m,a        ; deposit byte in buffer
  1986.     inx    d        ; increment the bufptr
  1987.     stbwx    d,FcrBufptr    ; ..and store it
  1988.     bsetx    FcrDirty,FcrFlags ; buffer is dirty now
  1989.  
  1990. ; If Bufptr has gotten beyond Bufeod, that byte started
  1991. ; a new record.  In that case we have to fill out the rest
  1992. ; of the new record with fill bytes and advance Bufeod to
  1993. ; its end, so that Bufeod always reflects the in-storage
  1994. ; physical end of the file.
  1995.     ldbwx    h,FcrBufeod
  1996.     ora    a
  1997.     dsbc    d        ; bufeod - bufptr
  1998.     cc    @FRFIL        ; fill record if eod less
  1999.  
  2000. exit:    pop    psw
  2001.     pop    d
  2002.     pop    h
  2003.     ret
  2004.  
  2005. ; the file represents an output device.  Write the byte by
  2006. ; calling the Bdos for the appropriate service.
  2007. isdevice:
  2008.     push    b
  2009.     mov    e,a        ; byte to be written
  2010.     ldx    c,FcrBdosOut    ; Bdos output request number
  2011.     call    @BDOS        ; ..writes the byte
  2012.     pop    b
  2013.     jr    exit
  2014.  
  2015.     end
  2016. yte to be written
  2017.     ldx    c,FcrBdosOut    ; Bdos output request number
  2018.     call    @BDOS        ; ..writes the byte
  2019.     pop    b
  2020.     jr    e    name    'FRECDA'
  2021.     title    'FRECDA -- record-oriented direct access'
  2022. ;===============================================================
  2023. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  2024. ;
  2025. ; Contains 2 functions related to direct access by fixed-length
  2026. ; records (as opposed to d.a. by byte-offsets as supported by
  2027. ; FSEEK et. al.):
  2028. ;
  2029. ;    FRLEN: set HL as the record size of DE->FCR
  2030. ;    FRSEK: seek to start of record number in HL
  2031. ;
  2032. ; interface: FRECLEN, FRECSEEK macros
  2033. ;
  2034. ; History
  2035. ; initial code 24 July 84
  2036. ;===============================================================
  2037.     maclib    environ
  2038.     maclib    fcr
  2039.  
  2040. ; Stow HL in the FcrRecLen field as the record-length of this
  2041. ; file.  FILEDEF initializes it to 128.  We do NOT check for a
  2042. ; record length of zero or 65535 or other ridiculous value.
  2043.     public    @FRLEN
  2044. @FRLEN:
  2045.     push    d
  2046.     push    h
  2047.     lxi    h,FcrRecLen
  2048.     dad    d        ; HL->field
  2049.     pop    d        ; DE=value
  2050.     mov    m,e
  2051.     inx    h
  2052.     mov    m,d        ; stored.
  2053.     xchg            ; restore HL=number
  2054.     pop    d
  2055.     ret
  2056.  
  2057. ; Set the file position so that the next access will go to the
  2058. ; first byte of the record whose number is in HL.
  2059. ;
  2060. ; Record numbers are origin-1 for compatibility with BASIC, PL/I
  2061. ; and most Pascals.  In other words, when HL=1, we seek to the
  2062. ; zeroeth byte of the file.
  2063. ;
  2064. ; Method is to multiply the record length by the record number
  2065. ; less one.  That yields a longword which we pass to FSEEK as
  2066. ; a byte-offset.  On exit, the Z flag (set by FSEEK) is TRUE
  2067. ; if there is NO DATA at that position.
  2068.  
  2069.     dseg
  2070. lw    ds    4    ; scratch longword
  2071.     cseg
  2072.     public    @FRSEK
  2073. @FRSEK:
  2074.     push    b
  2075.     push    h
  2076.     push    d
  2077.  
  2078.     xtix    ; address file via IX
  2079.     ldbwx    d,FcrRecLen
  2080.     xtix    ; restore IX
  2081.  
  2082.     dcx    h    ; make recno origin-1
  2083.     mpybw    @H,@D    ; DEHL=longword offset
  2084.     lxi    b,lw
  2085.     storelw @B    ; put in storage
  2086.     mov h,b ! mov l,c; HL->RBA longword
  2087.     pop    d    ; restore DE->FCR
  2088.     fseek    @D,@Hupdate ; aim the file
  2089.     pop    h
  2090.     pop    b
  2091.     ret
  2092.  
  2093.     end
  2094. ov h,b !    name    'FSIZE'
  2095.     title    'FSIZE -- return size of file as longword'
  2096. ;===============================================================
  2097. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  2098. ;
  2099. ; Register HL points to space for a longword in storage, and
  2100. ; DE->an FCR representing a file which may or may not be open.
  2101. ; This module computes the size of the file as a 32-bit count
  2102. ; of bytes.
  2103. ;
  2104. ; The count is 128 times the number of records reported by the
  2105. ; CP/M Bdos -- which may not be an accurate count.  An Ascii
  2106. ; file might be as much as 127 bytes smaller than reported due
  2107. ; to a ^Z byte in its last record.  A file built by direct
  2108. ; access operations may contain nonexistent holes.
  2109. ;
  2110. ; We checkpoint the file before taking the size so as to
  2111. ; ensure an accurate reading on a new output file.
  2112. ;
  2113. ; If the Bdos can't find the file (if it's a device, say)
  2114. ; a count of zero is returned.
  2115. ;
  2116. ; interface: FSIZE macro
  2117. ;
  2118. ; History
  2119. ; preserve RRA when it is important 8/21/84
  2120. ; initial code 16 April 84
  2121. ;===============================================================
  2122.     maclib    equates
  2123.     maclib    services
  2124.     maclib    smallz80
  2125.     maclib    fcr
  2126.     extrn    @FCKPT,@BDOS
  2127.  
  2128.     dseg
  2129. lwptr    ds    2    ; save ->output longword
  2130.     cseg
  2131.  
  2132.     public    @FSIZE
  2133. @FSIZE:
  2134.     push    psw
  2135.     push    b
  2136.     push    d
  2137.     xtix        ; IX bases the FCR
  2138.     shld    lwptr    ; save ->longword
  2139.  
  2140. ; If this is a presently-open, writable disk file, checkpoint it
  2141. ; to ensure an accurate size.  The test is for a writable file
  2142. ; because we don't know if any output has been done.
  2143. rwdisk    equ    (1 shl FcrDisk)+(1 shl FcrOutput)
  2144.     ldx    a,FcrFlags
  2145.     ani    rwdisk
  2146.     cpi    rwdisk    ; both Disk and Output?
  2147.     cz    @FCKPT    ; yes, checkpoint it
  2148.  
  2149. ; If the file's FCB position is invalid, we must preserve the
  2150. ; present contents of FcrRRA.
  2151. noposn    equ    (1 shl FcrDisk)+(1 shl FcrNoPosn)
  2152.     ldx    a,FcrFlags
  2153.     ani    noposn
  2154.     cpi    noposn
  2155.     cz    saveRRA
  2156.  
  2157. ; Zero the FcrRRA field -- if CP/M can't find the file (e.g. if
  2158. ; it's a device or doesn't exist) it will leave the zero there.
  2159.     xra    a
  2160.     stx    a,FcrRRA
  2161.     stx    a,FcrRRA+1
  2162.     stx    a,FcrRRA+2
  2163.  
  2164. ; Ask the BDOS to look up this file in its disk directory and
  2165. ; set FcrRRA to its count of records.  A=FFh if no file is
  2166. ; found.  If the drivecode is bad the BDOS will abort us.
  2167.     mvi    c,BdosFileSize
  2168.     call    @BDOS    ; DE still ->FCB
  2169.  
  2170. ; The RRA contains zero or the count of 128-byte records in the
  2171. ; file.  Load that into registers BCDE as a count of bytes by
  2172. ; loading it into BCD and shifting it right one bit.
  2173.     ldx    d,FcrRRA    ; least-sig byte
  2174.     ldx    c,FcrRRA+1
  2175.     ldx    b,FcrRRA+2    ; most-significant
  2176.     xra    a        ; A=00, carry cleared
  2177.     rarr    b
  2178.     rarr    c
  2179.     rarr    d
  2180.     rar            ; last bit to A bit 7
  2181.     mov    e,a        ; set low byte of longword
  2182.  
  2183. ; We now have the answer in BCDE.  Store it in the longword,
  2184. ; updating HL to the byte following.
  2185.     lhld    lwptr        ; HL->longword
  2186.     mov m,e ! inx h     ; store 4 bytes in
  2187.     mov m,d ! inx h     ; ascending order
  2188.     mov m,c ! inx h     ; of significance
  2189.     mov m,b ! inx h
  2190.  
  2191. ; If this was a disk file, restore FcrRRA.
  2192.     ldx    a,FcrFlags
  2193.     ani    noposn
  2194.     cpi    noposn
  2195.     cz    restRRA
  2196.  
  2197.     xtix
  2198.     pop    d
  2199.     pop    b
  2200.     pop    psw
  2201.     ret
  2202.  
  2203. ; out-of-line subroutines to save and restore the file position
  2204. ; when that is needed (rarely).
  2205.     dseg
  2206. rra    ds    3
  2207.     cseg
  2208. saveRRA:
  2209.     ldx    a,FcrRRA
  2210.     sta    rra
  2211.     ldx    a,FcrRRA+1
  2212.     sta    rra+1
  2213.     ldx    a,FcrRRA+2
  2214.     sta    rra+2
  2215.     ret
  2216. restRRA:
  2217.     lda    rra
  2218.     stx    a,FcrRRA
  2219.     lda    rra+1
  2220.     stx    a,FcrRRA+1
  2221.     lda    rra+2
  2222.     stx    a,FcrRRA+2
  2223.     ret
  2224.     end
  2225. +2
  2226.     sta    rra+2
  2227.     ret
  2228. restRR    name    'FNOTE'
  2229.     title    'FNOTE -- return file position as longword'
  2230. ;===============================================================
  2231. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  2232. ;
  2233. ; This module receives HL->a longword in storage and DE->an FCR.
  2234. ; It returns in the longword the count of bytes that precede
  2235. ; the next byte to be read or written in the file.  This count,
  2236. ; a Relative Byte Address (RBA), is the file position in records
  2237. ; times 128, adjusted for the present file buffer position.
  2238. ; The RBA may be saved and used later by FSEEK to reposition
  2239. ; the file to this point.
  2240. ;
  2241. ; interface: FNOTE macro
  2242. ;
  2243. ; History
  2244. ; initial code 19 April 84
  2245. ;===============================================================
  2246.     maclib    smallz80
  2247.     maclib    fcr
  2248.     extrn    @FBRRA,@CPLBW
  2249.  
  2250.     public    @FNOTE
  2251. @FNOTE:
  2252.     push    psw
  2253.     push    b
  2254.     push    d
  2255.     xtix        ; base the FCR
  2256.     push    h    ; save the longword's address
  2257.  
  2258. ; If the file is a device (or an unopened file), we return an
  2259. ; answer of zero.
  2260.     bitx    FcrDisk,FcrFlags
  2261.     jrnz    isdisk
  2262.     lxi    d,0
  2263.     lxi    h,0        ; longword of zero
  2264.     jmp    storeit
  2265.  
  2266. ; It's a disk file.  Find out its current Relative Record
  2267. ; Address.
  2268. isdisk:
  2269.     call    @FBRRA
  2270.  
  2271. ; The meaning of the RRA depends on whether the file is
  2272. ; positioned within existing data or beyond the end of file.
  2273.     bitx    FcrNotEof,FcrFlags
  2274.     jrnz    infile
  2275.  
  2276. ; It's positioned at its end.  The RRA describes the first
  2277. ; record in the buffer and the Bufptr will be added to it.
  2278. ; Build a positive longword on the stack.
  2279.     lxi    d,0
  2280.     push    d        ; high 16 bits
  2281.     ldbwx    d,FcrBufptr
  2282.     push    d        ; low 16 bits
  2283.     jr    either
  2284.  
  2285. ; It's positioned over existing data.  The RRA describes the
  2286. ; next record after Bufeod.  We have to subtract from it the
  2287. ; difference between Bufeod and Bufptr.  Build a negative
  2288. ; longword (or a zero one) of that value on the stack.
  2289. infile:
  2290.     ldbwx    h,FcrBufeod
  2291.     ldbwx    d,FcrBufptr
  2292.     ora    a
  2293.     dsbc    d        ; HL = Bufeod - Bufptr
  2294.     lxi    d,0        ; (assume difference is 0)
  2295.     jrz    iszero        ; (it was)
  2296.     call    @CPLBW        ; HL = - (Bufeod - Bufptr)
  2297.     lxi    d,-1        ; complete 32-bit negative
  2298. iszero: push    d        ; push long of -(eod-ptr)
  2299.     push    h
  2300.  
  2301. ; With that done, get (RRA*128) as a longword.    RRA*128 is just
  2302. ; RRA*256 shifted right 1 bit.
  2303. either:
  2304.     mvi    l,0
  2305.     ldx    h,FcrRRA
  2306.     ldx    e,FcrRRA+1
  2307.     ldx    d,FcrRRA+2    ; DEHL = RRA*256
  2308.     ora    a        ; (clear carry)
  2309.     rarr    d
  2310.     rarr    e
  2311.     rarr    h
  2312.     rarr    l        ; DEHL = RRA * 128
  2313.  
  2314. ; Now add to that, the longword we prepared earlier.
  2315.     pop    b
  2316.     dad    b        ; add low 16 bits
  2317.     xchg
  2318.     pop    b
  2319.     dadc    b        ; add carry, high 16 bits
  2320.  
  2321. ; Finally, store the result in the given longword.
  2322. storeit:
  2323.     mov b,h ! mov c,l    ; save high 16 bits
  2324.     pop    h        ; HL->longword
  2325.     mov m,e ! inx h     ; store from least significant
  2326.     mov m,d ! inx h     ; up to most significant.
  2327.     mov m,c ! inx h
  2328.     mov m,b ! inx h     ; leaving HL->byte after
  2329.  
  2330.     xtix
  2331.     pop    d
  2332.     pop    b
  2333.     pop    psw
  2334.     ret
  2335.  
  2336.     end
  2337. t significant.
  2338.     mov m,c ! inx h
  2339.     mov m,b ! inx h     ; leaving HL->byte af    name    'FSEEK'
  2340.     title    'FSEEK -- reposition file'
  2341. ;===============================================================
  2342. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  2343. ;
  2344. ; This module receives HL->a longword in storage, and DE->an FCR
  2345. ; describing a file.  It repositions the file so that the next
  2346. ; byte in or out will go to the Relative Byte Address (RBA)
  2347. ; specified by the longword.
  2348. ;
  2349. ; If the file is a device, nothing is done.  If the given RBA
  2350. ; falls into the present file buffer, only the buffer pointer
  2351. ; is changed.  Otherwise the buffer is written (if necessary)
  2352. ; and the file is pointed to the record that contains the RBA.
  2353. ; If that exists, the buffer is refilled; otherwise, the file
  2354. ; is set up for output to a simulated end of file.
  2355. ;
  2356. ; On return, the Z flag is TRUE if the file is positioned
  2357. ; beyond the end of existing data (or within a "hole").  This
  2358. ; is compatible with the operation of FOPNI and input functions
  2359. ; in which Z true means end of file.
  2360. ;
  2361. ;        FRWND functional module
  2362. ;
  2363. ; @FRWND is a special entry to FSEEK which seeks to the first
  2364. ; byte of the file.
  2365. ;
  2366. ; interface: FSEEK and FREWIND macros
  2367. ;
  2368. ; History
  2369. ; fix error in positioning within buffer 8/20/84
  2370. ; allow for ambiguous FCB position on seek outside file 8/4/84
  2371. ; add FRWND entry 6/28/84
  2372. ; initial code 19 April 84
  2373. ;===============================================================
  2374.     maclib    smallz80
  2375.     maclib    fcr
  2376.     extrn    @FBRRA,@FBAIM,@FDOUT,@FBINP,@RAMFL
  2377.  
  2378.     dseg
  2379.     ds    1    ; scratch byte
  2380. targRRA ds    3    ; target RRA, longword div 128
  2381. targptr ds    1    ; target byte, longword mod 128
  2382.     ds    1
  2383. highRRA ds    3    ; RRA of record following Bufeod
  2384.     ds    1
  2385. lowRRA    ds    3    ; RRA of record at head of buffer
  2386.  
  2387. RBA000    db    0,0,0,0 ; RBA of head of file.
  2388.  
  2389.     cseg
  2390.  
  2391.     public    @FSEEK,@FRWND
  2392.  
  2393. @FRWND:
  2394.     push    h    ; save caller's HL
  2395.     lxi    h,RBA000 ; point to first byte of file
  2396.     call    @FSEEK    ; seek there
  2397.     pop    h
  2398.     ret
  2399.  
  2400. @FSEEK:
  2401.     push    b
  2402.     push    h
  2403.     push    d
  2404.     xtix
  2405.  
  2406. ; If the file is a device, do nothing.
  2407.     bitx    FcrDisk,FcrFlags
  2408.     jz    exit
  2409.  
  2410. ; Convert the longword into a Relative Record Address as TargRRA
  2411. ; and a record offset within that record as targptr.
  2412.     mov e,m ! inx h     ; load it into BCDE
  2413.     mov d,m ! inx h     ; from least to most
  2414.     mov c,m ! inx h     ; significant.
  2415.     mov b,m
  2416.  
  2417.     mov    a,e
  2418.     ani    7fh        ; (note: carry now clear)
  2419.     sta    targptr     ; offset within 128-byte record
  2420.  
  2421. ; "Longword div 128" is the high 3 bytes of longword times 2,
  2422. ; in other words we shift it left once and drop the low byte.
  2423.     ralr    e
  2424.     ralr    d
  2425.     ralr    c
  2426.     ralr    b
  2427.     sded    targRRA-1    ; store low byte,
  2428.     sbcd    targRRA+1    ; ..middle, hi: targRRA=D,C,B
  2429.  
  2430. ; Get the current Relative Record Address of the file:
  2431. ; If FcrNoPosn is false, the FCB accurately reflects the status
  2432. ; of the buffer vis-a-vis the file while FcrRRA is unknown. If
  2433. ; FcrNoPosn is true, the FCB is ambiguous but the FcrRRA value
  2434. ; is one this module set earlier and is correct.
  2435.     bitx    FcrNoPosn,FcrFlags
  2436.     cz    @FBRRA
  2437.     ldx    d,FcrRRA
  2438.     ldx    c,FcrRRA+1
  2439.     ldx    b,FcrRRA+2
  2440.  
  2441. ; From it, we want to calculate the RRA of the first record in
  2442. ; the buffer (lowRRA) and the RRA of the record that follows
  2443. ; Bufeod (highRRA).  The number of records in the buffer is
  2444. ; bits 15..7 of Bufeod.  Get that into A.
  2445.     ldx    a,FcrBufeod
  2446.     ral
  2447.     ldx    a,FcrBufeod+1
  2448.     ral
  2449.  
  2450. ; If we are working at end of file, lowRRA should be the file
  2451. ; position RRA, and highRRA should be that plus A.
  2452.     bitx    FcrNotEof,FcrFlags
  2453.     jrnz    midfile
  2454.     sded    lowRRA-1
  2455.     sbcd    lowRRA+1    ; lowRRA = D,C,B = RRA
  2456.     add d ! mov d,a     ; add #recs in buffer
  2457.     mov a,c ! aci 0 ! mov c,a ; and propogate carry
  2458.     mov a,b ! aci 0 ! mov b,a
  2459.     sded    highRRA-1    ; highRRA = RRA + Bufeod div 128
  2460.     sbcd    highRRA+1    ; ..into storage as D, C, B
  2461.     jr    either
  2462.  
  2463. ; We are working within existing data, so highRRA should be the
  2464. ; file-position RRA, and lowRRA should be that minus A.
  2465. midfile:
  2466.     sded    highRRA-1
  2467.     sbcd    highRRA+1    ; highRRA = D,C,B = RRA
  2468.     mov    e,a
  2469.     mov a,d ! sub e ! mov d,a
  2470.     mov a,c ! sbi 0 ! mov c,a
  2471.     mov a,b ! sbi 0 ! mov b,a
  2472.     sded    lowRRA-1    ; lowRRA = RRA - Bufeod div 128
  2473.     sbcd    lowRRA+1    ; ..as D, C, B
  2474.  
  2475. ; Whew.  Ok, if lowRRA <= targRRA < highRRA, the desired byte
  2476. ; falls within the buffer and we don't need to do any I/O.
  2477. either:
  2478.     lxi    h,lowRRA+2
  2479.     lxi    d,targRRA+2
  2480.     call    compRRA     ; compare target :: low
  2481.     jrc    outside     ; (lowRRA is greater)
  2482.     lxi    h,highRRA+2
  2483.     lxi    d,targRRA+2
  2484.     call    compRRA     ; compare target :: high
  2485.     jrnc    outside     ; (highRRA is not greater)
  2486.  
  2487. ; ..and that is the case.  What we do now is to calculate
  2488. ; targRRA - lowRRA, yielding a count of records into the buffer.
  2489. ; They can't differ by more than 127 (since we limit buffers
  2490. ; to 32K) so we only have to subtract the low bytes (and we
  2491. ; don't care about any borrow, as when LowRRA=01FC and TargRRA
  2492. ; is, say, 0200, giving 04 and a carry -- 8/20/84)
  2493.     lda    targRRA
  2494.     lxi    h,lowRRA
  2495.     sub    m
  2496.  
  2497. ; That count, times 128, plus targptr, is our new Bufptr.  We
  2498. ; set it and exit.  We do NOT alter the NotEof or NoPosn bits
  2499. ; since the situations they reflect haven't changed.
  2500.     ora    a        ; (clear carry)
  2501.     rar            ; A = new bufptr bits 15..8
  2502.     stx    a,FcrBufptr+1    ; ..and new bit 7 in cy
  2503.     mvi    a,0
  2504.     rar            ; cy moves to A bit 7
  2505.     lxi    h,targptr
  2506.     add    m        ; A = new bufptr bits 7..0
  2507.     stx    a,FcrBufptr
  2508.     jmp    exit
  2509.  
  2510. ; Now then.  It seems the desired RRA falls outside the present
  2511. ; buffer.  We will be reloading the buffer but before we do, if
  2512. ; it has been modified, we have to write it to disk.
  2513. outside:
  2514.     call    @FDOUT
  2515. ; Right, the buffer is clean.  Now set the target RRA and try
  2516. ; to position the file to it by reading at that address.
  2517. clean:
  2518.     lda    targRRA
  2519.     stx    a,FcrRRA
  2520.     lda    targRRA+1
  2521.     stx    a,FcrRRA+1
  2522.     lda    targRRA+2
  2523.     stx    a,FcrRRA+2
  2524.     call    @FBAIM
  2525.  
  2526. ; If FB-AIM returned zero in register A, there does exist file
  2527. ; data at that RRA.  Therefore we are positioned in mid-file,
  2528. ; and the NotEof bit should reflect that.  Also the FCB does
  2529. ; now match the file condition, so FcrNoPosn should be off.
  2530. ; Then we can load the buffer with a sequential read.
  2531.     ora    a
  2532.     jrnz    nodata
  2533.     bsetx    FcrNotEof,FcrFlags
  2534.     bresx    FcrNoPosn,FcrFlags
  2535.     call    @FBINP    ; sequential read to fill buffer
  2536.     jr    setptr
  2537.  
  2538. ; No data exists at the target RRA.  We set NotEof false to
  2539. ; prevent attempts to input nonexistent data.  If no EXTENT
  2540. ; exists at this RRA, set NoPosn true because the FCB no longer
  2541. ; reflects the buffer position.
  2542. nodata:
  2543.     bresx    FcrNotEof,FcrFlags
  2544.     cpi    04h    ; 01/no data, or 04/no extent?
  2545.     jrc    nullrec ; (no data, FCB ok)
  2546.     bsetx    FcrNoPosn,FcrFlags
  2547.  
  2548. ; Since there's no file data, prepare a null record at the head
  2549. ; of the buffer just so we can point Bufptr at something.
  2550. nullrec:
  2551.     lxi    b,128        ; BC=length of 128
  2552.     stbwx    b,FcrBufeod    ; so does Bufeod
  2553.     ldbwx    d,FcrBufadr    ; DE->buffer
  2554.     ldx    a,FcrFillByte    ; A = fill byte
  2555.     call    @RAMFL        ; fill first record
  2556.  
  2557. ; The file has been positioned and the buffer set up so that
  2558. ; the desired 128-byte record is first in it.  Set the Bufptr.
  2559. ; Also set the LastCR flag, since an ASCII get after a seek
  2560. ; should reveal eof immediately.
  2561. setptr:
  2562.     lda    targptr
  2563.     stx    a,FcrBufptr
  2564.     mvix    0,FcrBufptr+1
  2565.     bsetx    FcrLastCR,FcrFlags
  2566.  
  2567. ; Exit, setting Z from the NotEof flag.
  2568. exit:
  2569.     bitx    FcrNotEof,FcrFlags
  2570.     xtix
  2571.     pop    d
  2572.     pop    h
  2573.     pop    b
  2574.     ret
  2575.  
  2576. ; Here compare two RRAs.  DE points to the most-significant byte
  2577. ; of the first, HL to the msb of the second. Return the flags
  2578. ; for the first inequality working from high back to low.
  2579. compRRA:
  2580.     ldax d ! dcx d
  2581.     cmp  m ! dcx h        ; compare high bytes
  2582.     rnz
  2583.     ldax d ! dcx d
  2584.     cmp  m ! dcx h        ; ..middle bytes
  2585.     rnz
  2586.     ldax d
  2587.     cmp m            ; ..low bytes
  2588.     ret
  2589.  
  2590.     end
  2591. are high bytes
  2592.     rnz
  2593.     ldax d ! dcx d
  2594.     cmp  m ! dcx     name    'FRFIL'
  2595.     title    'FRFIL -- fill new record'
  2596. ;===============================================================
  2597. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  2598. ;
  2599. ; One or more bytes have been placed in a file buffer past
  2600. ; Bufeod.  This routine is called to get Bufeod out past Bufptr
  2601. ; and a multiple of 128, like it spozed to be.    We fill from
  2602. ; the present Bufptr to the next 128-byte boundary with the
  2603. ; the chosen end-of-file-fill byte, and adjust Bufeod. The logic
  2604. ; is:    Bufeod := Bufptr
  2605. ;    while (0 <> Bufeod mod 128)
  2606. ;        Buffer[Bufeod] := fill byte
  2607. ;        Bufeod := Bufeod + 1
  2608. ;    end while
  2609. ;
  2610. ; interface:    IX->FCR, Bufeod < Bufptr
  2611. ;        Bufeod updated to 128-multiple after Bufptr
  2612. ;
  2613. ; History
  2614. ; initial code 19 April 84
  2615. ;===============================================================
  2616.     maclib    smallz80
  2617.     maclib    fcr
  2618.  
  2619.     public    @FRFIL
  2620. @FRFIL:
  2621.     push    psw
  2622.     push    b
  2623.     push    d
  2624.     push    h
  2625.  
  2626.     ldbwx    d,FcrBufptr    ; Bufeod = Bufptr
  2627.     mov    a,e
  2628.     ani    127
  2629.     jrz    exit        ; No filling needed
  2630.  
  2631.     mov    b,a        ; number needed is
  2632.     mvi    a,128        ; 128 minus Bufptr mod 128
  2633.     sub    b
  2634.     mov    b,a        ; B = loop count
  2635.     ldbwx    h,FcrBufadr
  2636.     dad    d        ; HL->first fill-spot
  2637.     ldx    a,FcrFillByte
  2638.  
  2639. loop:    mov    m,a
  2640.     inx    h
  2641.     djnz    loop
  2642.  
  2643.     mov    a,e
  2644.     ori    127
  2645.     mov    e,a
  2646.     inx    d        ; get new Bufeod
  2647. exit    stbwx    d,FcrBufeod
  2648.     pop    h
  2649.     pop    d
  2650.     pop    b
  2651.     pop    psw
  2652.     ret
  2653.  
  2654.     end
  2655. mov    a,e
  2656.     ori    127
  2657.     mov    e,a
  2658.     inx    d        ; get new Bufeod
  2659. ex    name    'FBADV'
  2660.     title    'FBADV -- advance file buffer'
  2661. ;===============================================================
  2662. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  2663. ;
  2664. ; This module controls the internal subroutines concerned with
  2665. ; buffer management of the file defined by IX->FCR...
  2666. ;
  2667. ; @FDOUT is called to ensure that all buffered data is on disk.
  2668. ; It calls either FBOUT or FBUPD, depending on the condition
  2669. ; of the file, to write the buffer.  It is used by FSEEK, FCLOS,
  2670. ; FCKPT, and by...
  2671. ;
  2672. ; @FDUMP is called when someone wants to write a byte but the
  2673. ; file buffer is full (Bufptr = Bufsize).  It advances the
  2674. ; buffer to cover the next portion of the file (if any).
  2675. ;
  2676. ; @FILLB is called when someone wants to read a byte but there
  2677. ; is no data (Bufptr = Bufeod).  It advances the buffer to
  2678. ; cover the next portion of the file (if any).
  2679. ;
  2680. ; Is there an echo in here? These 2 entries are in fact one,
  2681. ; for historical reasons.
  2682. ;
  2683. ; History
  2684. ; rewrite for simplicity 8/21/84
  2685. ; modify for FcrNoPosn status 8/4/84
  2686. ; initial code 18 April 84
  2687. ;===============================================================
  2688.     maclib    smallz80
  2689.     maclib    fcr
  2690.     extrn    @FBOUT,@FBUPD,@FBINP
  2691.  
  2692. ; If the buffer is dirty (has new data), make it clean.  Do
  2693. ; that with an update-write if we are in mid-file or if we
  2694. ; have lost our FCB position by a seek to nonexistent data.
  2695. ; In either case, DO NOT reset Bufptr or Bufeod.  That's for
  2696. ; our caller to do according to its circumstances.
  2697.  
  2698. upbits    equ    (1 shl FcrNotEof)+(1 shl FcrNoPosn)
  2699.  
  2700.     public    @FDOUT
  2701. @FDOUT:
  2702.     bitx    FcrDirty,FcrFlags
  2703.     rz        ; not dirty?  do nothing.
  2704.     push    psw
  2705.     ldx    a,FcrFlags
  2706.     ani    upbits    ; if NotEof or NoPosn
  2707.     cnz    @FBUPD    ; ..do an update-write
  2708.     cz    @FBOUT    ; ..else append-write
  2709.     pop    psw
  2710.     ret
  2711.  
  2712. ; Advance the buffer for sequential I/O.  If it is dirty,
  2713. ; write it (see above).  Reset Bufptr to zero.    If not at
  2714. ; eof, read the next buffer-load of data.
  2715.  
  2716.     public    @FDUMP,@FILLB
  2717. @FILLB:
  2718. @FDUMP:
  2719.     call    @FDOUT        ; make sure the buffer is clean
  2720.     xra    a
  2721.     stx    a,FcrBufptr
  2722.     stx    a,FcrBufptr+1    ; Bufptr := 0
  2723.     bitx    FcrNotEof,FcrFlags ; if no more data in file,
  2724.     jnz    noteof
  2725.     stx    a,FcrBufeod    ; make Bufeod := 0 too
  2726.     stx    a,FcrBufeod+1
  2727.     ret            ; and return Z true
  2728. noteof:             ; else there may be more data
  2729.     call    @FBINP        ; try to read it (sets Bufptr)
  2730.     ret            ; and return INP's Zero flag
  2731.  
  2732.     end
  2733. :             ; else there may be more data
  2734.     cal    name    'FBOUT'
  2735.     title    'FBOUT -- sequential write of file buffer'
  2736. ;===============================================================
  2737. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  2738. ;
  2739. ; This subroutine of the file operations writes the to disk the
  2740. ; buffer contents of the file represented by IX->FCR.  The
  2741. ; active buffer contents run from Bufadr to Bufadr+Bufeod-1.
  2742. ; The "Sequential Output Condition" holds, i.e. the CP/M file
  2743. ; position is correct for the first record of the buffer.  Our
  2744. ; caller is responsible for this.
  2745. ;
  2746. ; Bufptr and Bufeod are not changed (that's up to our caller)
  2747. ; but as a result of the write, the Sequential Input Condition
  2748. ; may be said to hold -- the FCB is positioned at the record
  2749. ; that logically follows Bufeod.  We do NOT reset FcrNotEof
  2750. ; (which usually tracks the SIC).  Only FBINP does that, when
  2751. ; it finds physical eof.
  2752. ;
  2753. ; The CP/M Plus feature of multi-record I/O enables us to write
  2754. ; the whole buffer in one request regardless of its size.
  2755. ;
  2756. ; Errors are possible, and are handled thus:
  2757. ;    no data/directory space: close file, abort with msg
  2758. ;    media change or invalid FCB: abort with msg
  2759. ;
  2760. ; interface:    IX->FCR; Bufeod is a multiple of 128.
  2761. ; output:    FcrDirty bit reset.
  2762. ;
  2763. ; History
  2764. ; initial code 30 March 84
  2765. ;===============================================================
  2766.     maclib    equates
  2767.     maclib    smallz80
  2768.     maclib    fcr
  2769.     maclib    services
  2770.     extrn    @FABTX,@BDOS
  2771.  if CpmLevel eq 22h
  2772.     extrn    @ADDAH
  2773.  endif
  2774.  
  2775.     public    @FBOUT
  2776. @FBOUT:
  2777.     push    psw
  2778.     push    b
  2779.     push    d
  2780.     push    h
  2781.  
  2782. ; Bufeod is a count of active bytes in the buffer, and is
  2783. ; maintained at a multiple of 128 by all file routines. Figure
  2784. ; out how many 128-byte records we have to write (Bufeod bits
  2785. ; 15..7).  If that turns out to be zero, we shouldn't have
  2786. ; been called, but that's ok.
  2787.     ldx    a,FcrBufeod    ; low byte of count..
  2788.     ral            ; ..high bit to carry
  2789.     ldx    a,FcrBufeod+1    ; high byte of count..
  2790.     ral            ; shifted left, plus carry
  2791.     ora    a        ; is count zero?
  2792.     jrz    exit        ; if so, skip the write
  2793.  
  2794.  if CpmLevel gt 22h ; HOW WE DO IT IN CP/M PLUS...
  2795.  
  2796. ; Set the count of buffered records as the CP/M Plus multi-
  2797. ; record count.
  2798.     mov    e,a
  2799.     mvi    c,BdosSetMulti
  2800.     call    @BDOS
  2801.  
  2802. ; Set the buffer as the CP/M data transfer address.
  2803.     ldbwx    d,FcrBufadr
  2804.     mvi    c,BdosSetBuffer
  2805.     call    @BDOS
  2806.  
  2807. ; Now we can do the write.
  2808.     pushix
  2809.     pop    d        ; DE->FCB
  2810.     mvi    c,BdosWrite
  2811.     call    @BDOS
  2812.  
  2813.  else ; HOW WE DO IT IN CP/M 2.2...
  2814.  
  2815. ; Save the count of records as a loop-count.  Set up HL->data
  2816. ; and DE->FCB.
  2817.     mov    b,a
  2818.     ldbwx    h,FcrBufadr
  2819.     pushix
  2820.     pop    d
  2821.  
  2822. ; Loop writing each 128-byte record.
  2823. Wloop:
  2824.     xchg        ; DE->data, HL->FCB
  2825.     mvi    c,BdosSetBuffer
  2826.     push    h
  2827.     call    @BDOS    ; (saves BC,DE)
  2828.     pop    h
  2829.     xchg        ; DE->FCB, HL->data
  2830.     mvi    c,BdosWrite
  2831.     push    h
  2832.     call    @BDOS
  2833.     pop    h
  2834.     ora    a    ; did the write work?
  2835.     jrnz    exit    ; (no, quit with A=retcode)
  2836.     mvi    a,128
  2837.     call    @ADDAH    ; advance data pointer
  2838.     djnz    Wloop
  2839.     xra    a    ; did them all ok
  2840.  
  2841.  endif ; END OF CP/M 2.2 WRITE
  2842.  
  2843. ; Save the Bdos's return code, and if it is "success," reset
  2844. ; the dirty bit and exit.
  2845. exit:
  2846.     stx    a,FcrRetcode
  2847.     ora    a
  2848.     jrnz    error
  2849.  
  2850.     bresx    FcrDirty,FcrFlags
  2851.     pop    h
  2852.     pop    d
  2853.     pop    b
  2854.     pop    psw
  2855.     ret
  2856.  
  2857. ; Four errors are possible.  Two (media change and bad FCB,
  2858. ; both unique to CP/M Plus) are disasters during output.
  2859. error:
  2860.     cpi    03h    ; 01=no directory, 02=no data space
  2861.     jnc    @FABTX    ; higher than 2, just die
  2862.  
  2863. ; The other two errors (no directory or no data space) mean that
  2864. ; some of the present buffer-load did not make it to disk and
  2865. ; won't ever.  That too calls for aborting the program, but in
  2866. ; those cases we should close the file in hopes of preserving
  2867. ; the data that was written previously.
  2868.  
  2869. ; The error 01h, no directory space, is reported as 05h after
  2870. ; a direct-access write, and that's how our error-message
  2871. ; handler expects it.
  2872.  
  2873.     cpi    01h
  2874.     jrnz    close
  2875.     mvix    05h,FcrRetcode
  2876. close:
  2877.     pushix
  2878.     pop    d
  2879.     mvi    c,BdosClose
  2880.     call    @BDOS
  2881.     jmp    @FABTX
  2882.  
  2883.     end
  2884. .
  2885.  
  2886.     cpi    01h
  2887.     jrnz    close
  2888.     mvix    05h,FcrRetcode
  2889. close:
  2890.     pushix
  2891.     pop    d
  2892.     mvi    c,BdosClose
  2893.     call    @BDOS    name    'FBUPD'
  2894.     title    'FBUPD -- update-write file buffer'
  2895. ;===============================================================
  2896. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  2897. ;
  2898. ; This routine is called when the file is positioned at other
  2899. ; than its end, the buffer contains modified data, and it is
  2900. ; time to move to another part of the file.  It does a direct-
  2901. ; access write of the first record in the buffer, setting the
  2902. ; first buffered record as the CP/M file position, then does a
  2903. ; sequential write of Bufeod bytes (via FBOUT).
  2904. ;
  2905. ; If FcrNotEof is true, the file FCB is positioned at the record
  2906. ; that logically follows BufEod (the Sequential Input Condition)
  2907. ; and the direct-access address in FcrRRA is invalid, we have to
  2908. ; set it before aiming the file.
  2909. ;
  2910. ; If FcrNotEof is false, FcrNoPosn will be true (we wouldn't be
  2911. ; called otherwise).  A seek was done to a nonexistent extent
  2912. ; and the file FCB is not positioned to anything in particular,
  2913. ; but the FcrRRA is valid for the first record in the buffer.
  2914. ; After we aim the file, we can do a sequential write.
  2915. ;
  2916. ; On our return, the S.I.C. will hold -- the FCB will point to
  2917. ; the next record after BufEod (which likely doesn't exist).
  2918. ;
  2919. ; interface:    IX->FCR, either FcrNotEof OR FcrNoPosn true
  2920. ; output:    file updated on disk
  2921. ;        Sequential Input Condition established.
  2922. ;
  2923. ; History
  2924. ; add test for 2nd condition, 8/4/84
  2925. ; initial code 19 April 84
  2926. ;===============================================================
  2927.     maclib    equates
  2928.     maclib    smallz80
  2929.     maclib    services
  2930.     maclib    fcr
  2931.     extrn    @FBRRA,@FBOUT,@BDOS
  2932.  
  2933.     public    @FBUPD
  2934. @FBUPD:
  2935.     push    psw
  2936.     push    b
  2937.     push    d
  2938.     push    h
  2939.  
  2940. ; If the SIC holds, the FCB position is valid and FcrRRA is not,
  2941. ; so get it updated.  Otherwise use the RRA set by FSEEK.
  2942.     bitx    FcrNotEof,FcrFlags
  2943.     jrz    HaveRRA
  2944.     call    @FBRRA
  2945.  
  2946. ; Now FcrRRA reflects the record that follows BufEod.  We want
  2947. ; to position the file at the record at the head of the buffer.
  2948. ; Its RRA is the present RRA less Bufeod div 128.  "Bufeod div
  2949. ; 128" is just bits 15..7 of Bufeod.
  2950.     pushix
  2951.     pop    h
  2952.     lxi    d,FcrRRA
  2953.     dad    d    ; HL->RRA (low, mid, high bytes)
  2954.     ldx    a,FcrBufeod
  2955.     ral        ; carry = Bufeod bit 7
  2956.     ldx    a,FcrBufeod+1
  2957.     ral        ; A = Bufeod bits 15..7
  2958.     mov    d,a    ; save that in D
  2959.  
  2960. ; Ad-hoc subtract of a byte from a 24-bit number: subtract
  2961. ; from the low byte and propogate a possible carry.
  2962.     mov    a,m
  2963.     sub    d    ; subtract from low byte
  2964.     mov    m,a
  2965.     inx    h
  2966.     mov    a,m
  2967.     sbi    0    ; propogate the borrow
  2968.     mov    m,a
  2969.     inx    h
  2970.     mov    a,m
  2971.     sbi    0    ; ..to the third byte
  2972.     mov    m,a
  2973.  
  2974. ; Ok, FcrRRA addresses the 1st record in the buffer, so now we
  2975. ; can re-aim the CP/M file position to that record -- i.e., make
  2976. ; the Sequential Output Condition hold.  We do it with a direct-
  2977. ; access write of that first record.
  2978. haveRRA:
  2979.     ldbwx    d,FcrBufAdr
  2980.     mvi    c,BdosSetBuffer
  2981.     call    @BDOS        ; set buffer address
  2982.  if CpmLevel gt 22h ; in CP/M Plus don't forget to
  2983.     mvi    e,1
  2984.     mvi    c,BdosSetMulti
  2985.     call    @BDOS        ; ..set record count of 1
  2986.  endif
  2987.     pushix
  2988.     pop    d
  2989.     mvi    c,BdosDirWrite
  2990.     call    @BDOS        ; write one record
  2991.     bresx    FcrNoPosn,FcrFlags ; FCB matches file now
  2992.  
  2993. ; That set up the FCB such that the next sequential operation
  2994. ; will go to the record now at BufAdr (the Sequential Output
  2995. ; Condition) so it is valid to call @FBOUT to write the entire
  2996. ; active contents of the buffer (and thereby establish the
  2997. ; Sequential Input Condition).
  2998.     bresx    FcrNoPosn,FcrFlags ; FCB position valid now
  2999.     call    @FBOUT
  3000.  
  3001.     pop    h
  3002.     pop    d
  3003.     pop    b
  3004.     pop    psw
  3005.     ret
  3006.  
  3007.     end
  3008. ndition).
  3009.     bresx    FcrNoPosn,FcrFlags    name    'FBINP'
  3010.     title    'FBINP -- sequential read to file buffer'
  3011. ;===============================================================
  3012. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  3013. ;
  3014. ; This subroutine is called to read from the file represented
  3015. ; by IX->FCR into its buffer.  It assumes that the "Sequential
  3016. ; Input Condition" holds, i.e. that a sequential read will
  3017. ; return the record that logically follows the present Bufeod
  3018. ; position.  The SIC continues to hold afterward.
  3019. ;
  3020. ; The CP/M Plus multi-record feature allows the entire buffer
  3021. ; to be filled in one request regardless of its size.
  3022. ;
  3023. ; Bufeod is set to reflect the amount of data received. If no
  3024. ; data is received, the NotEof bit is set false.  (At that
  3025. ; time, the Sequential Output condition begins to hold, i.e.
  3026. ; a write will add new data to the file.)
  3027. ;
  3028. ; Three errors are possible.  01/unwritten data is the end
  3029. ; of file condition and indicates that not all of the records
  3030. ; requested (perhaps none of them) were received.  It is
  3031. ; handled as noted above.  09/invalid fcb and 10/media change
  3032. ; are disasters and we cause an abort.
  3033. ;
  3034. ; interface:    IX->FCR
  3035. ; output:    Bufeod set to byte after last one read,
  3036. ;        NotEof set false when no more data exists
  3037. ;
  3038. ; History
  3039. ; initial code 30 March 84
  3040. ;===============================================================
  3041.     maclib    equates
  3042.     maclib    services
  3043.     maclib    smallz80
  3044.     maclib    fcr
  3045.     extrn    @BDOS
  3046.  if CpmLevel gt 22h ; we could get killing errors
  3047.     extrn    @FABTX
  3048.  else ; in 2.2 we need to add A to HL
  3049.     extrn    @ADDAH
  3050.  endif
  3051.  
  3052.     public    @FBINP
  3053. @FBINP:
  3054.     push    psw
  3055.     push    b
  3056.     push    d
  3057.     push    h
  3058.  
  3059. ; Figure out how many records will fit in the buffer: bits 15..7
  3060. ; of the buffersize.
  3061.     ldx    a,FcrBufsize
  3062.     ral            ; bit 7 to carry
  3063.     ldx    a,FcrBufsize+1    ; bits 16..8 in A
  3064.     ral            ; bits 15..7 in A
  3065.  
  3066.  if CpmLevel gt 22h ; HOW WE DO IT IN CP/M PLUS....
  3067.  
  3068. ; Set the CP/M Plus multi-record count.
  3069.     mov    e,a        ; count of records to read
  3070.     mvi    c,BdosSetMulti
  3071.     call    @BDOS
  3072.  
  3073. ; Set the buffer address for the read.
  3074.     ldbwx    d,FcrBufadr    ; DE->buffer
  3075.     mvi    c,BdosSetBuffer
  3076.     call    @BDOS
  3077.  
  3078. ; Attempt a read of that many records.    Count of records
  3079. ; gotten is returned in register H.
  3080.  
  3081.     pushix
  3082.     pop    d        ; DE->file control block
  3083.     mvi    c,BdosRead
  3084.     call    @BDOS
  3085.  
  3086.  else ; HOW WE DO IT IN CP/M 2.2...
  3087.  
  3088. ; Save the count of possible records and set it as a loop count.
  3089.     stx    a,FcrBufEod    ; using BufEod as scratch space
  3090.     mov    b,a
  3091.  
  3092. ; Set up the addresses of the buffer and the FCB.
  3093.     pushix
  3094.     pop    d        ; DE->FCB
  3095.     ldbwx    h,FcrBufadr    ; HL->data
  3096.  
  3097. ; Read B records into the buffer or until physical eof.
  3098. Rloop:
  3099.     xchg            ; DE->data, HL->fcb
  3100.     mvi    c,BdosSetBuffer
  3101.     push    h
  3102.     call    @BDOS        ; (preserves BC, DE)
  3103.     pop    h
  3104.     xchg            ; DE->fcb, HL->data
  3105.     mvi    c,BdosRead
  3106.     push    h
  3107.     call    @BDOS
  3108.     pop    h
  3109.     ora    a        ; did we get that one?
  3110.     jrnz    RloopZ        ; (no, stop)
  3111.     mvi    a,128
  3112.     call    @ADDAH        ; yes, advance bufptr
  3113.     djnz    Rloop        ; ..and continue
  3114. RloopZ:
  3115. ; B was decremented for each record received.  Set H=count of
  3116. ; records received as CP/M Plus would have done.
  3117.     ldx    a,FcrBufEod    ; original count
  3118.     sub    b        ; less residual count
  3119.     mov    h,a        ; ..into H
  3120.     mov    a,b        ; A=00 if not eof, else A>00
  3121.  
  3122.  endif ; BACK TO COMMON CODE, INPUT COUNT IN L, RETCODE IN A
  3123.  
  3124.  if CpmLevel gt 22h ; need to save retcode in CP/M 3
  3125. ; Save the Bdos's return code
  3126.     stx    a,FcrRetcode
  3127.  endif
  3128.  
  3129. ; If we had no error, then we filled the buffer and Bufeod
  3130. ; should equal Bufsize.  Assume that's going to be the case.
  3131.     ldbwx    b,FcrBufsize
  3132.     ora    a
  3133.     jrz    seteod
  3134.  
  3135.  if CpmLevel gt 22h ; must check error code in CP/M 3
  3136. ; We had an error.  If it was 9/bad fcb or 10/media change,
  3137. ; abort with a file error message.
  3138.     cpi    01h    ; if it isn't "unwritten data"
  3139.     jnz    @FABTX    ; ..then abort.
  3140.  endif
  3141.  
  3142. ; We encountered physical end of file, so we didn't get all the
  3143. ; records we asked for.  If we got no records at all, mark true
  3144. ; end of file in the FCR and make Bufeod=zero
  3145.  
  3146.     mov    a,h    ; count of records moved
  3147.     ora    a    ; was it zero?
  3148.     jrnz    gotsome ; (no)
  3149.     bresx    FcrNotEof,FcrFlags
  3150.     mov b,a ! mov c,a ; make Bufeod equal zero
  3151.     jr    seteod
  3152.  
  3153. ; We read some data.  Compute how many bytes we got and set
  3154. ; that as Bufeod: a 16-bit number whose bits 15..7 are the
  3155. ; count of records.  Note: carry is clear at this point.
  3156. gotsome:
  3157.     rar        ; A=bits 16..8, bit 7 to carry
  3158.     mov    b,a
  3159.     mvi    a,0
  3160.     rar        ; A=bits 7..0
  3161.     mov    c,a
  3162.  
  3163. ; Set the new value of Bufeod
  3164. seteod:
  3165.     stbwx    b,FcrBufeod
  3166.  
  3167.     pop    h
  3168.     pop    d
  3169.     pop    b
  3170.     pop    psw
  3171.     ret
  3172.  
  3173.     end
  3174. ..0
  3175.     mov    c,a
  3176.  
  3177. ; Set the new value of Bufeod
  3178. seteod:
  3179.     stbwx    b,FcrBufeod
  3180.  
  3181.     pop    h
  3182.     pop    name    'FBRRA'
  3183.     title    'FBRRA -- get direct address of next record'
  3184. ;===============================================================
  3185. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  3186. ;
  3187. ; This routine is called to learn the present position of a
  3188. ; file.  It asks CP/M to store the Relative Record Address (RRA)
  3189. ; at which the next sequential operation will affect.
  3190. ;
  3191. ; If the FcrNoPosn flag is on, then the present RRA of the file
  3192. ; was set by FSEEK and it is accurate.    Moreover, it is our only
  3193. ; valid position info and we mustn't change it.
  3194. ;
  3195. ; Otherwise, if the file is positioned at its end (NotEof false)
  3196. ; the RRA will be the address at which the first record in the
  3197. ; buffer would be written (the Sequential Output condition).
  3198. ;
  3199. ; When NotEof is true, the file is positioned somewhere in its
  3200. ; middle and the RRA will be the address of the record that
  3201. ; follows Bufeod (the Sequential Input condition).
  3202. ;
  3203. ; interface:    IX->FCR
  3204. ;        FcrRRA updated (unless FcrNoPosn)
  3205. ;
  3206. ; History
  3207. ; Update for FcrNoPosn condition 8/20/84
  3208. ; initial code 19 April 84
  3209. ;===============================================================
  3210.     maclib    equates
  3211.     maclib    services
  3212.     maclib    smallz80
  3213.     maclib    fcr
  3214.     extrn    @BDOS
  3215.  
  3216.     public    @FBRRA
  3217. @FBRRA:
  3218.     bitx    FcrNoPosn,FcrFlags
  3219.     rnz
  3220.  
  3221.     push    psw
  3222.     push    b
  3223.     push    d
  3224.     push    h
  3225.  
  3226.     pushix
  3227.     pop    d    ; DE->file control block
  3228.     mvi    c,BdosGetRecNo
  3229.     call    @BDOS
  3230.  
  3231.     pop    h
  3232.     pop    d
  3233.     pop    b
  3234.     pop    psw
  3235.     ret
  3236.  
  3237.     end
  3238. 
  3239.     pushix
  3240.     pop    d    ; DE->file control block
  3241.     mvi    c,BdosGetRecNo
  3242.     call    @BDOS
  3243.     name    'FBAIM'
  3244.     title    'FBAIM -- aim file at specific RRA'
  3245. ;===============================================================
  3246. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  3247. ;
  3248. ; This subroutine of FSEEK is called to reposition a file to a
  3249. ; particular Relative Record Address now in FcrRRA.
  3250. ;
  3251. ; The method is to do a single direct-access read of the file
  3252. ; into a scratch buffer.  After a d.a. read (provided it is
  3253. ; successful), the next sequential access will begin with that
  3254. ; record.  Doing a read may seem like a waste of time.    However,
  3255. ; there isn't any other way, and the waste is not all that great
  3256. ; since that sector will be buffered by the Bdos and the next
  3257. ; sequential operation will do no actual I/O.
  3258. ;
  3259. ; A number of errors are possible.  01/unwritten data means that
  3260. ; the requested file extent exists but there is no data block at
  3261. ; that RRA.  04/unwritten extent means the RRA fell into a non-
  3262. ; existent extent.  These we pass back to our caller.  Other
  3263. ; errors are disasters and cause an abort.
  3264. ;
  3265. ; interface:    IX->FCR with FcrRRA initialized
  3266. ;        A=The Bdos's return code from the read
  3267. ;
  3268. ; History
  3269. ; initial code 19 April 84
  3270. ;===============================================================
  3271.     maclib    equates
  3272.     maclib    services
  3273.     maclib    smallz80
  3274.     maclib    fcr
  3275.     extrn    @FABTX,@BDOS
  3276.  
  3277.     common    /fswork/
  3278. scratch ds    128
  3279.     cseg
  3280.  
  3281.     public    @FBAIM
  3282. @FBAIM:
  3283.     push    b
  3284.     push    d
  3285.     push    h
  3286.  
  3287. ; Set the buffer address to the common scratch area.
  3288.     lxi    d,scratch
  3289.     mvi    c,BdosSetBuffer
  3290.     call    @BDOS
  3291.  
  3292.  if CpmLevel gt 22h ; under CP/M Plus don't forget to...
  3293. ; Set the record count to 1.
  3294.     mvi    e,1
  3295.     mvi    c,BdosSetMulti
  3296.     call    @BDOS
  3297.  endif
  3298.  
  3299. ; Attempt the direct read.
  3300.     pushix
  3301.     pop    d    ; DE->fcb
  3302.     mvi    c,BdosDirRead
  3303.     call    @BDOS
  3304.  
  3305. ; Note the return code and, if it is 0/ok, 1/unwritten data,
  3306. ; or 4/unwritten extent, exit.
  3307.     stx    a,FcrRetcode
  3308.     pop    h        ; prepare to exit
  3309.     pop    d
  3310.     pop    b
  3311.     ora    a        ; expected result is 0/ok
  3312.     rz            ; (yes)
  3313.     cpi    01h        ; 1 and 4 are ok as well
  3314.     rz
  3315.     cpi    04h
  3316.     rz
  3317.  
  3318. ; We have one of: 3/can't close current extent, 6/invalid RRA,
  3319. ; or 10/media change.  Abort the program.
  3320.     jmp    @FABTX
  3321.  
  3322.     end
  3323.  We have one o    name    'FEROR'
  3324.     title    'FEROR -- Abort with file-error message'
  3325. ;===============================================================
  3326. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  3327. ;
  3328. ; Builds a file-error message string from the contents of an FCR
  3329. ; addressed by DE or IX, and aborts the program with that msg.
  3330. ; The message is
  3331. ;
  3332. ;    File error on FRUMMAGE -- (descriptive phrase)
  3333. ;
  3334. ; The (descriptive phrase) is selected based on FcrRetCode from
  3335. ; the following list:
  3336. ;
  3337. ;  0 (null)
  3338. ;  1 reading unwritten data
  3339. ;  2 no space for data
  3340. ;  3 cannot close current extent
  3341. ;  4 reading nonexistent extent
  3342. ;  5 no directory space
  3343. ;  6 invalid record address
  3344. ;  7 (null)
  3345. ;  8 (null)
  3346. ;  9 invalid file control block
  3347. ; 10 unexpected media change
  3348. ; 11 file not found
  3349. ; 12 disk I/O error
  3350. ; 13 disk is read-only
  3351. ; 14 file is read-only
  3352. ; 15 invalid drive letter
  3353. ; 16 (null)
  3354. ; 17 (null)
  3355. ; 18 password error
  3356. ; 19 file already exists (on a Make request)
  3357. ; 20 ambiguous filespec
  3358. ;
  3359. ; Return codes 11-20 are the Bdos "extended" errors, only seen
  3360. ; when extended-error mode is on (almost never, in this code).
  3361. ; The return codes from CP/M are 11 less; the caller must add
  3362. ; 11 to them to avoid confusion with normal error codes.
  3363. ;
  3364. ; Since the string is meant for error display, it begins with
  3365. ; CR, LF.  However, terminal punctuation such as a dollarsign
  3366. ; to delimit the string to the Bdos is up to the caller.
  3367. ;
  3368. ; interface:    To @FABRT, DE->Fcr
  3369. ;        To @FABTX, IX->Fcr
  3370. ; History
  3371. ; initial code 2 April 84
  3372. ;===============================================================
  3373.     maclib    equates
  3374.     maclib    strings
  3375.     maclib    smallz80
  3376.     maclib    fcr
  3377.     extrn    @FSTSP,@STAPP,@STPUT,@ERROR
  3378.  
  3379.     dseg
  3380. msga    strconst <AsciiCR,AsciiLF,'File error on '>,64
  3381. msgb    strconst ' -- '
  3382. msg0    db    0    ; null string
  3383. msg1    strconst 'reading unwritten data'
  3384. msg2    strconst 'no space for data'
  3385. msg3    strconst 'cannot close current extent'
  3386. msg4    strconst 'reading nonexistent extent'
  3387. msg5    strconst 'no directory space'
  3388. msg6    strconst 'invalid record address'
  3389. msg7    equ    msg0
  3390. msg8    equ    msg0
  3391. msg9    strconst 'invalid file control block'
  3392. msg10    strconst 'unexpected media change'
  3393. msg11    strconst 'file not found'
  3394. msg12    strconst 'disk I/O error'
  3395. msg13    strconst 'disk is read-only'
  3396. msg14    strconst 'file is read-only'
  3397. msg15    strconst 'invalid drive-letter'
  3398. msg16    equ    msg0
  3399. msg17    equ    msg0
  3400. msg18    strconst 'password error'
  3401. msg19    strconst 'file already exists'
  3402. msg20    strconst 'ambiguous filespec'
  3403.  
  3404. maxerror equ    20
  3405.  
  3406. mtab    dw    msg0,msg1,msg2
  3407.     dw    msg3,msg4,msg5
  3408.     dw    msg6,msg7,msg8
  3409.     dw    msg9,msg10,msg11
  3410.     dw    msg12,msg13,msg14
  3411.     dw    msg15,msg16,msg17
  3412.     dw    msg18,msg19,msg20
  3413.  
  3414.     cseg
  3415.     public    @FABRT,@FABTX
  3416. @FABTX: ; from file routines with IX->FCR
  3417.     pushix
  3418.     pop    d    ; get DE->FCR
  3419.  
  3420. @FABRT: ; from user (?) code with DE->FCR
  3421.  
  3422. ; We will be aborting, so the register contents don't really
  3423. ; matter anymore.  However, if debugging is going on we may
  3424. ; be trapped at @ERROR, so we will make sure the registers
  3425. ; are meaningful there.
  3426.     push    h    ; push h first, pop it last
  3427.     push    psw
  3428.     push    b
  3429.     push    d    ; save ->Fcr on top
  3430.  
  3431. ; Begin the message by appending the full filespec to the msg
  3432.     lxi    h,msga    ; HL->start of message string
  3433.     mvi    a,0111b ; want drive, name, type
  3434.     call    @FSTSP    ; append filespec as string
  3435.     xchg        ; DE->end of string
  3436.  
  3437. ; Append the m-dash to the filespec
  3438.     lxi    h,msgb    ; HL->append data
  3439.     call    @STAPP    ; append, updating DE
  3440.  
  3441. ; Pick up the FcrRetcode in A and make sure it is one we
  3442. ; can handle.
  3443.     pop    h    ; HL->FCR
  3444.     push    h    ; (and saved again)
  3445.     lxi    b,FcrRetcode
  3446.     dad    b
  3447.     mov    a,m    ; A := BDOS return code
  3448.     cpi    maxerror+1
  3449.     jrc    retok
  3450.     xra    a    ; hmmm- make it zero
  3451. retok:
  3452.  
  3453. ; Index the table by the retcode and pick up the address
  3454. ; of the appropriate phrase
  3455.     add    a    ; double the retcode
  3456.     mov    c,a    ; BC=00xx
  3457.     lxi    h,mtab
  3458.     dad    b    ; HL->(word)->string
  3459.     mov    a,m
  3460.     inx    h
  3461.     mov    h,m
  3462.     mov    l,a    ; HL->phrase-string
  3463.  
  3464. ; Append the explanatory phrase and a dollar-sign
  3465.     call    @STAPP
  3466.     mvi    a,'$'
  3467.     call    @STPUT
  3468.  
  3469. ; Perform the abort with the input registers restored.
  3470.     pop    d    ; restore DE->fcr
  3471.     pop    b    ; restore entry BC and AF
  3472.     pop    psw
  3473.     lxi    h,msga
  3474.     xthl        ; restore HL, ->msg on stack
  3475.     call    @ERROR
  3476.     end
  3477. ore DE->fcr
  3478.         name    'FSTSP'
  3479.     title    'FSTSP -- convert filespec to string'
  3480. ;===============================================================
  3481. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  3482. ;
  3483. ; Convert the filespec in DE->FCR into a string and append it to
  3484. ; HL->string.  Return HL updated to end of new string.    Register
  3485. ; A contains a bit-mask that controls what parts of the filespec
  3486. ; are returned:
  3487. ;    bit 0 = return drivecode (if not default)
  3488. ;    bit 1 = return filename
  3489. ;    bit 2 = return .filetype (if not null)
  3490. ; Bit 3, which could cause return of the password field, is not
  3491. ; supported for security reasons.
  3492. ;
  3493. ; interface: FGETSPEC macro
  3494. ;
  3495. ; History
  3496. ; revise for bit-mask 6 August 84
  3497. ; clear attribute bits 9 April 84
  3498. ; initial code 2 April 84
  3499. ;===============================================================
  3500.     maclib    equates
  3501.     maclib    smallz80
  3502.     maclib    integers
  3503.     extrn    @STEND
  3504.  
  3505.     public    @FSTSP
  3506. @FSTSP:
  3507.     push    psw
  3508.     push    b
  3509.     mov    c,a    ; carry bit-map in C
  3510.     xchg        ; DE->output string, HL->File
  3511.     call    @STEND    ; DE->end of output string
  3512.  
  3513.     rarr    c    ; bit 0 to carry
  3514.     cc    dodrive ; ..drive wanted
  3515.     rarr    c    ; check bit 1
  3516.     cc    doname    ; ..name wanted
  3517.     rarr    c    ; and bit 2
  3518.     cc    dotype    ; ..type wanted
  3519.  
  3520.     xchg        ; DE->file again, HL->end of string
  3521.     mvi    m,0    ; put a null on it
  3522.     pop    b
  3523.     pop    psw
  3524.     ret
  3525.  
  3526. ; The drivecode is wanted, put X: in DE->string if a nondefault
  3527. ; drive was used.
  3528. dodrive:
  3529.     mov a,m ! ora a ; explicit drivecode, or default?
  3530.     rz        ; (default, do nothing)
  3531.  
  3532. ; the fcb specifies a particular drive-letter.    Convert it
  3533. ; to a letter and append that and a colon to the string.
  3534.     adi    'A'-1    ; drivecode 01=A, 02=B, etc
  3535.     stax d ! inx d    ; store the letter
  3536.     mvi a,':'
  3537.     stax d ! inx d    ; ..and a colon
  3538.     ret
  3539.  
  3540. ; The filename is wanted.  Copy up to 8 bytes, but no trailing
  3541. ; blanks, from the filename field of the FCB
  3542. doname:
  3543.     push    h
  3544.     inx    h    ; HL->2nd FCB byte, 1st filename byte
  3545.     mvi    b,8    ; #bytes to copy
  3546.     call    copy
  3547.     pop    h
  3548.     ret
  3549.  
  3550. ; The filetype is wanted.  If there is none, do nothing.  Else
  3551. ; append a dot to the string and copy up to 3 bytes.
  3552. dotype:
  3553.     push    h
  3554.     mvi    a,9
  3555.     addhla        ; HL->1st filetype byte
  3556.     mov    a,m
  3557.     cpi    ' '    ; is there a filetype?
  3558.     jrz    notype    ; (no, don't copy dot either)
  3559.     mvi    a,'.'
  3560.     stax d ! inx d
  3561.     mvi    b,3    ; copy up to 3 bytes
  3562.     call    copy
  3563. notype: pop    h
  3564.     ret
  3565.  
  3566. ; Copy up to B bytes, but no trailing blanks, sans any
  3567. ; attribute bits, from HL->text to DE->string.
  3568. copy:
  3569.     mov    a,m
  3570.     ani    07Fh    ; clear attribute bits
  3571.     cpi    ' '    ; reached a blank?
  3572.     rz        ; (done if so)
  3573.     inx    h    ; no, use this byte
  3574.     stax d ! inx d    ; and store it
  3575.     djnz    copy    ; ..and continue
  3576.     ret
  3577.  
  3578.     end
  3579. ?
  3580.     rz        ; (done if so)
  3581.     inx    h    ; no, use this byte
  3582.