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 / SEQFILE2.ANT < prev    next >
Text File  |  1998-07-30  |  40KB  |  1,423 lines

  1.     name    'FOPNI'
  2.     title    'FOPNI -- open file for input'
  3. ;===============================================================
  4. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  5. ;
  6. ; Prepare an existing file for use: assign a buffer (if needed),
  7. ; check for a device file, open a disk file.
  8. ;
  9. ; interface: FRESET macro
  10. ;
  11. ; History
  12. ; initial code 2 April 84
  13. ;===============================================================
  14.     maclib    equates
  15.     maclib    smallz80
  16.     maclib    fcr
  17.     extrn    @FBUFR,@FOPNC,@FOPNE,@FILLB
  18.  
  19.     public    @FOPNI
  20. @FOPNI:
  21.     push    b
  22.     push    h
  23.     push    d
  24.     xtix        ; IX->FCR
  25.  
  26. ; First off, get a buffer for the file if it doesn't have one.
  27.     call    @FBUFR
  28.  
  29. ; See if the file is really a device.  If so, we are done.
  30.     call    @FOPNC
  31.     bitx    FcrDisk,FcrFlags
  32.     jrz    exit
  33.  
  34. ; It isn't a device, so let's try to open it as a disk file.  If
  35. ; we can't, return Z true (instant eof).
  36.     mvi    a,FilePMread    ; want only to read it
  37.     call    @FOPNE
  38.     bitx    FcrNotEof,FcrFlags
  39.     jrz    exit
  40.  
  41. ; The file exists and is open to CP/M, at least for input (it
  42. ; may be writeable as well, but we don't check that).  Fill the
  43. ; buffer with the first load of data from disk, setting Bufptr
  44. ; and Bufeod -- and maybe finding end of file if it's empty.
  45.     call    @FILLB
  46.  
  47. ; Exit, setting the Z flag FALSE if the file is capable of
  48. ; input (a device file might not be, and a disk file might
  49. ; have had no data in it).
  50. exit:
  51.     bitx    FcrNotEof,FcrFlags
  52.     xtix
  53.     pop    d
  54.     pop    h
  55.     pop    b
  56.     ret
  57.  
  58.     end
  59. d a disk file might
  60. ; have had no data in it).
  61. exit:    name    'FOPNO'
  62.     title    'FOPNO -- open file for sequential output'
  63. ;===============================================================
  64. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  65. ;
  66. ; Open for sequential output the file represented by DE->FCR.
  67. ; If it is a device, all we have to do is give it a buffer and
  68. ; check that it supports output.
  69. ;
  70. ; For disk files, we implement an elaborate method that (a)
  71. ; ensures data integrity and (b) allows a utility to take the
  72. ; same filespec as both its input and its output.  We set aside
  73. ; the present filetype and replace it with "$$$"  We also save
  74. ; the password.  Any file "name.$$$" is erased and a new file of
  75. ; that name is created.  It will receive the output.  See FCLOS
  76. ; for the rest of the story.
  77. ;
  78. ; interface: FREWRITE macro
  79. ;
  80. ; History
  81. ; initial code 05 April 84
  82. ;===============================================================
  83.     maclib    equates
  84.     maclib    smallz80
  85.     maclib    services
  86.     maclib    fcr
  87.     extrn    @FBUFR,@FOPNC,@FABTX,@FOPNE,@FMAKE,@BDOS
  88.  
  89.     public    @FOPNO
  90. @FOPNO:
  91.     push    psw
  92.     push    b
  93.     push    h
  94.     push    d
  95.     xtix        ; IX->FCR
  96.  
  97. ; Save the last-rec-fill byte passed in register A
  98.     stx    a,FcrFillbyte
  99.  
  100. ; Get a buffer if we need one.
  101.     call    @FBUFR
  102.  
  103. ; Use FOPNC to see if the file is a device-name, setting it up
  104. ; for use if so.
  105.     call    @FOPNC
  106.     bitx    FcrDisk,FcrFlags
  107.     jrnz    isdisk
  108.  
  109. ; It appears to be a device.  Some devices (KBD:, AXI:) are not
  110. ; allowed to be output files.  If that's the case here, somebody
  111. ; has goofed, and we will abort with "read-only file."
  112.     bitx    FcrOutput,FcrFlags
  113.     jnz    exit        ; (it's ok)
  114.     mvix    3+11,FcrRetcode ; fake CP/M 3 "r/o file" return
  115.     jmp    @FABTX
  116.  
  117. ; It's to be a disk file.  Does a file of this name exist now,
  118. ; and if so is it protected by a password, and if so, do we have
  119. ; a password for it?  @FOPNE will try to answer these questions.
  120. isdisk:
  121.     mvi    a,FilePMread+FilePMwrite+FilePMdelete
  122.     call    @FOPNE    ; any protect level is bad news
  123.  
  124. ; If the file doesn't exist, we can just make it right now
  125. ; and have less to do at close time.
  126.     bitx    FcrNotEof,FcrFlags
  127.     jrnz    doesexist
  128.     call    @FMAKE    ; right, make it
  129.     jmp    exit    ; ..and done.
  130.  
  131. ; "Filename.typ" does exist, so we will create our output as
  132. ; "filename.$$$" instead.  Save the desired filetype for close
  133. ; time and move in the dollarsigns.
  134. doesexist:
  135.     ldx    a,FcrType
  136.     stx    a,FcrOtype
  137.     ldx    a,FcrType+1
  138.     stx    a,FcrOtype+1
  139.     ldx    a,FcrType+2
  140.     stx    a,FcrOtype+2
  141.  
  142.     mvix    '$',FcrType
  143.     mvix    '$',FcrType+1
  144.     mvix    '$',FcrType+2
  145.  
  146. ; Now erase any existing "name.$$$" file.  We already know
  147. ; (from the success of @FOPNE) that the filename is unambiguous.
  148. ; If "name.$$$" does exist and is r/o or password-protected, or
  149. ; if the disk is r/o, we will crash right here.
  150.     pushix
  151.     pop    d
  152.     mvi    c,BdosErase
  153.     call    @BDOS
  154.  
  155. ; That done, we can make "name.$$$."  FMAKE would apply a
  156. ; password to the file, but we don't want that here, since
  157. ; (a) it would give us two passwords to juggle at close time and
  158. ; (b) if we crash later, we'd leave behind a protected scratch
  159. ; file.  So we'll do the make from here.
  160.     pushix
  161.     pop    d
  162.     mvi    c,BdosMake
  163.     call    @BDOS
  164.  
  165. ; The scratch file has been created and is ready for output.
  166. ; Record its status in the FCR flags.
  167. nf equ (1 shl FcrOutput)+(1 shl FcrDisk)+(1 shl FcrBaktype)
  168.     mvix    nf,FcrFlags    ; disk output at end
  169. exit:
  170.     xtix
  171.     pop    d
  172.     pop    h
  173.     pop    b
  174.     pop    psw
  175.     ret
  176.  
  177.     end
  178. isk)+(1 shl FcrBaktype)
  179.     mvix    nf,FcrFl    name    'FOPNA'
  180.     title    'FOPNA -- open file for append-output'
  181. ;===============================================================
  182. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  183. ;
  184. ; Opens the file represented by DE->FCR for output to the end of
  185. ; the file.  If the file is a device, just set it up as usual
  186. ; and return -- device output is always "appended."
  187. ;
  188. ; For a disk file, open it as for input.  Then if it doesn't
  189. ; exist, make a new file of this name.
  190. ;
  191. ; If it does exist, make sure it is read-write.  Then read the
  192. ; last record of the file and try to find the correct byte
  193. ; within the last record to set under the buffer-pointer.
  194. ;
  195. ; interface: FAPPEND macro
  196. ;
  197. ; History
  198. ; initial code 06 April 84
  199. ;===============================================================
  200.     maclib    equates
  201.     maclib    services
  202.     maclib    smallz80
  203.     maclib    fcr
  204.     extrn    @FBUFR,@FOPNC,@FOPNE,@FABTX,@FMAKE,@BDOS
  205.  
  206.     public    @FOPNA
  207. @FOPNA:
  208.     push    psw
  209.     push    b
  210.     push    h
  211.     push    d
  212.     xtix
  213.  
  214. ; Save the last-record-fill byte passed in A
  215.     stx    a,FcrFillByte
  216.  
  217. ; assign a buffer to the file and reset the buffer pointer.
  218.     call    @FBUFR
  219.  
  220. ; check the filespec for a device name.  If it is a device,
  221. ; set it up for use.
  222.     call    @FOPNC
  223.     bitx    FcrDisk,FcrFlags
  224.     jrnz    isdisk
  225.  
  226. ; Some device files aren't allowed to do output.  If this is one
  227. ; such, abort with a "read-only file."    Otherwise we are done.
  228.     bitx    FcrOutput,FcrFlags
  229.     jnz    exit
  230.     mvix    3+11,FcrRetcode ; fake CP/M 3 "r/o file" return
  231.     jmp    @FABTX
  232.  
  233. ; 'Pears to be a disk.  Try try to open it as for input. The
  234. ; NotEof bit will be set if the file exists, the Output bit if
  235. ; it is read-write.
  236. isdisk:
  237.     mvi    a,FilePMread+FilePMwrite
  238.     call    @FOPNE
  239.     bitx    FcrNotEof,FcrFlags
  240.     jrnz    itexists
  241.  
  242. ; The file wasn't found.  All we have to do is make a new
  243. ; file of this name.  Output will be to end of new, empty file.
  244.     call    @FMAKE
  245.     jmp    exit        ; that's it
  246.  
  247. ; The file exists, is open, and is (@FOPNE says) writable.  Now
  248. ; we have to position ourselves to its end.  The first step is
  249. ; to get the file size, which is the relative record address
  250. ; of the last existing record, plus one.
  251. itexists:
  252.     pushix
  253.     pop    d    ; DE->FCB again..
  254.     mvi    c,BdosFileSize
  255.     call    @BDOS    ; updates FcrRRA (and saves DE)
  256.  
  257. ; We want the last record itself, so we have to decrement the
  258. ; 3-byte number the BDOS set for us.
  259.     lxi    h,FcrRRA
  260.     dad    d        ; HL->low byte of number
  261.     mov a,m ! sui 1     ; 1 from low byte
  262.     mov m,a ! inx h
  263.     mov a,m ! sbi 0     ; carry from middle byte
  264.     mov m,a ! inx h
  265.     mov a,m ! sbi 0     ; carry from high byte
  266.     mov    m,a
  267.  
  268. ; Now we can read that last 128-byte record into the buffer.
  269. ; Such a direct read sets the FCB so that a later sequential
  270. ; write will go to the same record (establishing the Sequential
  271. ; Output Condition).
  272.     push    d
  273.  if CpmLevel gt 22h    ; under CP/M Plus we have to
  274.     mvi    e,1
  275.     mvi    c,BdosSetMulti    ; set multi-record count to 1
  276.     call    @BDOS
  277.  endif            ; under all systems we have to
  278.     ldbwx    d,FcrBufadr
  279.     mvi    c,BdosSetBuffer
  280.     call    BdosJump    ; set buffer address
  281.     pop    d
  282.  
  283. ; on this read, only two returns are reasonably possible,
  284. ; 0=success, and 10=media change.  The latter is so unlikely
  285. ; I think we can ignore it.
  286.     mvi    c,BdosDirRead
  287.     call    @BDOS        ; read last record
  288.  
  289. ; The last record is in the buffer.  Now we have to set the
  290. ; buffer pointer to address the byte after the last good one
  291. ; in that record.  There's two ways to do it.
  292.     ldbwx    h,FcrBufadr    ; HL->data to scan
  293.     lxi    b,128        ; count of bytes to check
  294.     ldx    a,FcrFillByte
  295.     cpi    CpmEof        ; Ascii file, ending in ^Z?
  296.     jrnz    binary        ; (no, binary file)
  297.  
  298. ; This is an Ascii file, or at least we aren't told otherwise.
  299. ; Now, some programs fill all unused bytes in the last record
  300. ; with ^Zs, while others make do with just one, leaving garbage
  301. ; after it.  And of course the last record might be exactly full
  302. ; and have no ^Z in it at all.    So we will scan left to right
  303. ; and stop at the first ^Z we see or at the end of the record.
  304. ; The Z80 CPIR instruction fits our needs precisely.
  305.     cpir            ; if no hit at all,
  306.     jrnz    nohit        ; ..BC reduced to zero
  307.     inr    c        ; else reduced by 1 extra
  308. nohit:    mvi    a,128
  309.     sub    c        ; A := # bytes preceding ^Z
  310.     stx    a,FcrBufptr
  311.     jr    exit
  312.  
  313. ; This is a non-Ascii file, so we will assume that its last
  314. ; record was filled to the end with the fillbyte value.  We
  315. ; want to scan right to left for the rightmost byte that is
  316. ; NOT equal to the fill value.    The Z80 instruction CPD will
  317. ; do it, with some effort.
  318. binary:
  319.     dad    b        ; HL->byte following record
  320.     dcx    h        ; HL->last byte of record
  321. binloop:
  322.     cpd            ; Z:=(A == *HL), HL--, BC--
  323.     jrnz    hitx        ; (found a non-fill byte)
  324.     jpo    binloop     ; (BC not down to zero)
  325.     jr    nohitx        ; (BC=0000, no hit)
  326. hitx:    inr    c        ; BC was reduced too far
  327. nohitx: stx    c,FcrBufptr
  328.  
  329. ; Well, the file is ready for use.  Load up the registers
  330. ; and exit.
  331. exit:    xtix
  332.     pop    d
  333.     pop    h
  334.     pop    b
  335.     pop    psw
  336.     ret
  337.  
  338.     end
  339. l, the file is ready for use.  Load up the re    name    'FOPNU'
  340.     title    'FOPNU -- open file for update'
  341. ;===============================================================
  342. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  343. ;
  344. ; Prepare an existing file for use when the user plans to update
  345. ; it, that is, skip around and write in mid-file.  Device files
  346. ; are not allowed -- we abort for "file not found."  Read-only
  347. ; files are not allowed, they cause an abort (from @FOPNE).
  348. ;
  349. ; If the file does not exist, it is created.  If it does exist,
  350. ; its first buffer of data is read.  On exit, the Z flag is
  351. ; TRUE if the file is empty.
  352. ;
  353. ; interface: FUPDATE macro
  354. ;
  355. ; History
  356. ; initial code 23 April 84
  357. ;===============================================================
  358.     maclib    equates
  359.     maclib    smallz80
  360.     maclib    fcr
  361.  
  362.     extrn    @FBUFR,@FOPNC,@FABTX,@FOPNE,@FMAKE,@FILLB
  363.     public    @FOPNU
  364.  
  365. @FOPNU:
  366.     push    b
  367.     push    h
  368.     push    d
  369.     xtix        ; IX->FCR
  370.  
  371. ; First off, get a buffer for the file.
  372.     call    @FBUFR
  373.  
  374. ; See if the file is really a device.  If so, abort for "file
  375. ; not found," since a device can't be updated.
  376.     call    @FOPNC
  377.     bitx    FcrDisk,FcrFlags
  378.     jrnz    isdisk
  379.     mvix    0+11,FcrRetcode ; fake CP/M 3 "not-found" error
  380.     jmp    @FABTX
  381.  
  382. ; The filespec isn't the name of a device, so try to open it
  383. ; as a disk file.
  384. isdisk:
  385.     mvi    a,FilePMread+FilePMwrite ; want rd/wrt access
  386.     call    @FOPNE        ; aborts on r/o file
  387.     bitx    FcrNotEof,FcrFlags
  388.     jrnz    exists
  389.  
  390. ; The file doesn't exist.  We will make a file of that name
  391. ; and exit with FcrNotEof false (and hence Z true).
  392.     call    @FMAKE
  393.     jr    exit
  394.  
  395. ; The file exists and is open to CP/M for input and output.
  396. ; Now fill the buffer with the first load of data from disk,
  397. ; setting Bufptr and Bufeod, and maybe finding end of file if
  398. ; it's empty.
  399. exists:
  400.     call    @FILLB
  401.  
  402. ; Exit, setting the Z flag FALSE if there is data to be read
  403. ; from the file.
  404. exit:
  405.     bitx    FcrNotEof,FcrFlags
  406.     xtix
  407.     pop    d
  408.     pop    h
  409.     pop    b
  410.     ret
  411.  
  412.     end
  413. E if there is data to be read
  414. ; from the file.
  415. exit:
  416.     bitx    FcrNotEof,FcrFlags
  417.     xtix
  418.     pop    d
  419.     po    name    'FOPNC'
  420.     title    'FOPNC -- check filespec for device name'
  421. ;===============================================================
  422. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  423. ;
  424. ; This subroutine of the file-open operations tests the filespec
  425. ; in IX->FCR to see if it names a device or not.  As a byproduct
  426. ; it also clears the extent and current-record bytes of the FCB
  427. ; and the FcrFlags byte.
  428. ;
  429. ; If the file is NOT a device, the FcrDisk flag is set on.  If
  430. ; it IS a device, the FcrFlags are set to reflect which device
  431. ; it is, the Output and NotEof flags are set to reflect whether
  432. ; that device can do output and/or input, and the Bdos request
  433. ; numbers for byte input and output are set in the FCR.
  434. ;
  435. ; History
  436. ; initial code 30 March 84
  437. ;===============================================================
  438.     maclib    equates
  439.     maclib    strings
  440.     maclib    smallz80
  441.     maclib    fcr
  442.  
  443.     dseg
  444. ; These equates are used to assemble static FcrFlags bytes.
  445. xCon    equ    1 shl FcrCon
  446. xAux    equ    1 shl FcrAux
  447. xLst    equ    1 shl FcrLst
  448. xLCR    equ    1 shl FcrLastCR
  449. xOut    equ    1 shl FcrOutput
  450. xInp    equ    1 shl FcrNotEof
  451. ; These equates are the BDOS service numbers for device I/O
  452. ICon    equ    1
  453. Ocon    equ    2
  454. IAux    equ    3
  455. OAux    equ    4
  456. OLst    equ    5
  457.  
  458. colon:    strconst ':       '    ; comparand for e.g. CON:
  459. devtab: strtable 9    ; lookup table for device names
  460. ; In the table entries, each string is followed by a flag byte
  461. ; and two Bdos request numbers.  The entries are ordered by
  462. ; approximate frequency of use.
  463.     strentry 'CON'        ; CON can do input, output
  464.     db    xCon+xOut+xInp+xLCR,ICon,OCon
  465.     strentry 'LST'        ; LST is only output
  466.     db    xLst+xOut,0,OLst
  467.     strentry 'AUX'        ; AUX is in or out
  468.     db    xAux+xOut+xInp+xLCR,IAux,OAux
  469.     strentry 'RDR'        ; RDR is the old name for AXI
  470.     db    xAux+xInp+xLCR,IAux,0
  471.     strentry 'PUN'        ; PUT is the old name for AXO
  472.     db    xAux+xOut,0,OAux
  473.     strentry 'AXI'        ; AXI is input only
  474.     db    xAux+xInp+xLCR,IAux,0
  475.     strentry 'AXO'        ; AXO is output only
  476.     db    xAux+xOut,0,OAux
  477.     strentry 'KBD'        ; KBD is console-input only
  478.     db    xCon+xInp+xLCR,ICon,0
  479.     strentry 'CRT'        ; CRT is console-output only
  480.     db    xCon+xOut,0,OCon
  481.  
  482.     cseg
  483.     extrn    @STCMP,@STLOK
  484.     public    @FOPNC
  485. @FOPNC:
  486.     push    psw
  487.     push    h
  488.     push    d
  489.  
  490. ; clear the flags, current record, and extent bytes.
  491.     xra    a
  492.     stx    a,FcrExtent
  493.     stx    a,FcrCurrec
  494.     stx    a,FcrFlags
  495.  
  496. ; To be a device, the FCB must have a default (zero) drivecode.
  497. ; We treat a filespec like "B:CON:" as a disk file.
  498.     cmpx    Fcrdrive
  499.     jnz    isdisk
  500.  
  501. ; For a device, the "filenametyp" bytes look like "XXX:bbbbbbb"
  502. ; Since FcrExtent is zero, we can check for the colon and seven
  503. ; blanks with one string comparison.
  504.     pushix
  505.     pop    d
  506.     lxi    h,FcrName+3
  507.     dad    d
  508.     xchg            ; DE->":bbbbbbb" if present
  509.     lxi    h,colon     ; HL->comparand string
  510.     call    @STCMP
  511.     jnz    isdisk
  512.  
  513. ; Now to check the XXX part.  We will replace the colon with
  514. ; a NUL, then use a string table-search to look for the name.
  515. ; On a successful lookup, HL points to the NUL at the end of the
  516. ; matching entry, where we have assembled the proper FcrFlags
  517. ; bits and BDOS I/O service numbers.
  518.     mvix    0,FcrName+3
  519.     pushix
  520.     pop    d
  521.     inx    d        ; DE->"XXX",0
  522.     lxi    h,DevTab    ; HL->lookup table
  523.     call    @STLOK
  524.     mvix    ':',FcrName+3    ; restore colon first
  525.     jnz    isdisk        ; (no match, not a device)
  526.  
  527. ; The filespec is that of a device.  Set it up for use.
  528.     inx h ! mov a,m     ; A = flags
  529.     stx    a,FcrFlags
  530.     inx h ! mov a,m     ; A = Bdos input number
  531.     stx    a,FcrBdosIn
  532.     inx h ! mov a,m     ; A = Bdos output number
  533.     stx    a,FcrBdosOut
  534.     jr    exit
  535.  
  536. ; The filespec is not valid for a device, ergo it will be
  537. ; treated as a disk.  Set that flag and return.
  538. isdisk:
  539.     bsetx    FcrDisk,FcrFlags
  540. exit:
  541.     pop    d
  542.     pop    h
  543.     pop    psw
  544.     ret
  545.  
  546.     end
  547. reated as a disk.  Set that flag and return.
  548. isdisk:
  549.     bsetx    FcrDisk,FcrFlags
  550. exit:
  551.     pop    d
  552.     pop    h
  553.     pop    psw
  554.     ret    name    'FOPNE'
  555.     title    'FOPNE -- open existing disk file'
  556. ;===============================================================
  557. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  558. ;
  559. ; This inner subroutine of the file-open operations has several
  560. ; functions.  It:
  561. ;
  562. ; 1. ensures that the filespec in IX->FCR is not ambiguous,
  563. ; 2. ensures that its drivecode is valid,
  564. ; 3. determines if that file exists and if it does not, signals
  565. ;    that by leaving FcrNotEof false,
  566. ; 4. if the file is protected by a password against what the
  567. ;    caller plans to do to it, ensures that a password (not
  568. ;    necessarily the right one) is available in the FCR,
  569. ; 5. if the file is read-only for any cause, signals that fact
  570. ;    by leaving the FcrOutput flag bit false.
  571. ;
  572. ; interface:    IX->FCR
  573. ;        reg A = password-mode bits of interest,
  574. ;        FcrFlags(NotEof,Output) presumed zero on entry,
  575. ;        FCB extent and CR bytes assumed zero on entry,
  576. ;
  577. ; output:    Sets FcrNotEof if file exists.
  578. ;        Sets FcrOutput if file can be written.
  579. ;        File-attribute bits set in FCB by CP/M
  580. ;
  581. ; History
  582. ; initial code 9 April 84
  583. ;===============================================================
  584.     maclib    equates
  585.     maclib    services
  586.     maclib    smallz80
  587.     maclib    fcr
  588.     extrn    @FABTX,@BDOS
  589.  if CpmLevel gt 22h
  590.     extrn    @FPASS
  591.  endif
  592.     dseg
  593. Pmode    db    0    ; desired password mode
  594.     cseg
  595.     public    @FOPNE
  596. @FOPNE:
  597.     push    psw
  598.     push    b
  599.     push    d
  600.     push    h
  601.  
  602. ; Save the password-modes of interest
  603.     sta    Pmode
  604.  
  605.  if CpmLevel gt 22h ; in CP/M Plus only...
  606.  
  607. ; Ask the Bdos to return the file's password mode.  If the file
  608. ; doesn't exist, it returns A=FFh.  If the drivecode is invalid
  609. ; or the filespec is ambiguous, we will be cancelled with a
  610. ; "CP/M Error" message right here.
  611.     pushix
  612.     pop    d    ; DE->file control block
  613.     mvi    c,BdosFileInfo
  614.     call    @BDOS
  615.  
  616. ; If the file doesn't exist, we can exit right now.
  617.     inr    a    ; A=FF goes to A=00
  618.     jz    exit    ; (no such file, all done)
  619.     bsetx    FcrNotEof,FcrFlags ; note it does exist
  620.  
  621. ; The file exists, and its level of password protection is now
  622. ; in FcrExtent.  We AND that with the byte we were given.  If
  623. ; the result is nonzero, we require a password for this file.
  624.     lda    Pmode
  625.     andx    FcrExtent
  626.     jrz    nopassprobs
  627.  
  628. ; We do need a password.  If the first byte of FcrPassword is
  629. ; blank, we don't have one.  @FPASS will get one for us.
  630.     mvi    a,AsciiBlank
  631.     cmpx    FcrPassword
  632.     cz    @FPASS
  633.  
  634. ; Now we have a password (or don't need one), so we can try to
  635. ; open the file.  First point the Bdos at our password field.
  636. nopassprobs:
  637.     pushix
  638.     pop    h
  639.     lxi    d,FcrPassword
  640.     dad    d
  641.     xchg        ; DE->password field
  642.     mvi    c,BdosSetBuffer
  643.     call    @BDOS
  644.  endif ; CP/M Plus password preparation
  645.  
  646. ; In CP/M Plus we have eliminated most of the errors that could
  647. ; occur on the open (if the password is wrong and the file
  648. ; is protected at the read level, that would crash us).  In CP/M
  649. ; 2.2 we don't know anything yet.
  650.     pushix
  651.     pop    d
  652.     mvi    c,BdosOpen
  653.     call    @BDOS
  654.  if CpmLevel lt 30h ; see if file exists
  655.     inr    a    ; A=FFh goes to 00h
  656.     jz    exit    ; file doesn't exist, all done
  657.     bsetx    FcrNotEof, FcrFlags ; note it does exist
  658.  endif
  659.  
  660. ; Now the file is open we can find out if it is read-only.  That
  661. ; could be true for 3 reasons.    (1) If it has been set read-only
  662. ; then attribute t1' is on -- in either version of CP/M.
  663.     bitx    Bit7,FcrType
  664.     jrnz    readonly
  665.  
  666. ; (2) if it is a system file, stored under user 0, opened from a
  667. ; different user number, attribute f8' is on (CP/M Plus only).
  668.     bitx    Bit7,FcrName+7
  669.     jrnz    readonly
  670.  
  671. ; (3) if it has a write-level password and we presented the
  672. ; wrong (or no) password, attribute f7' is on.  That may not
  673. ; be an error -- our caller may intend only to read.
  674.     bitx    Bit7,FcrName+6
  675.     jrnz    readonly
  676.  
  677. ; Writing is permitted on this file.  Note that altho the file
  678. ; can be written, it could still have a delete-level password
  679. ; that we didn't match, which would prevent erase/renaming.
  680.     bsetx    FcrOutput,FcrFlags
  681.     jmp    exit
  682.  
  683. ; The file is read-only for some reason.  If our caller intended
  684. ; to write or delete it (as signalled by the byte passed) then
  685. ; we want to abort right now.
  686. readonly:
  687.     lda    Pmode    ; caller plans to..
  688.     ani    FilePMwrite+FilePMdelete ; ..write or delete?
  689.     jz    exit    ; (no, only plans reading)
  690.  
  691. ; Our caller means to write, and the file won't allow it.  Abort
  692. ; the program with an appropriate file error message.
  693.     mvi    a,3+11    ; assume extended error code of r/o
  694.     bitx    Bit7,FcrName+6
  695.     jrz    aborting; (yes, "read-only file")
  696.     mvi    a,7+11    ; set error code for "password error"
  697. aborting:
  698.     stx    a,FcrRetcode    ; set fake Bdos return code
  699.     jmp    @FABTX    ; ..and abort the program
  700.  
  701. ; The file doesn't exist, or exists and is amenable to actions
  702. ; the caller wants to do.  FcrFlags are set appropriately,
  703. ; except for FcrLastCr which should be initially on in since
  704. ; eof on the very first read should be reported immediately.
  705. exit:
  706.     bsetx    FcrLastCr,FcrFlags
  707.     pop    h
  708.     pop    d
  709.     pop    b
  710.     pop    psw
  711.     ret
  712.  
  713.     end
  714.  first read should be reported immediately.
  715. exit:
  716.     bsetx    FcrLastCr,FcrFlags
  717.     pop    h
  718.     pop    d
  719.     pop    b
  720.     pop    psw
  721.     ret
  722.     name    'FBUFR'
  723.     title    'FBUFR -- get buffer for a file'
  724. ;===============================================================
  725. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  726. ;
  727. ; This subroutine of the file-open operations acquires a buffer
  728. ; for a file if it doesn't already have one (due to having been
  729. ; opened before).  The buffer is allocated dynamically from the
  730. ; high end of the address space.
  731. ;
  732. ; It also clears the Bufptr and Bufeod fields so that when an
  733. ; FCR is reused these will be initialized.  This code is called
  734. ; by all flavors of open, so the clearing will always be done.
  735. ;
  736. ; interface:    IX->FCR
  737. ; output:    FcrBufadr set.
  738. ;
  739. ; History
  740. ; add code to zero Bufeod and Bufptr 8/22/84
  741. ; initial code 06 April 84
  742. ;===============================================================
  743.     maclib    smallz80
  744.     maclib    fcr
  745.     extrn    @RAMHI
  746.  
  747.     public    @FBUFR
  748. @FBUFR:
  749.     xra    a        ; get a zero
  750.     stx    a,FcrBufeod
  751.     stx    a,FcrBufeod+1    ; zero end-of-data ptr
  752.     stx    a,FcrBufptr
  753.     stx    a,FcrBufptr+1    ; ..and next-byte ptr
  754.     ldx    a,FcrBufadr
  755.     orx    FcrBufadr+1    ; nonzero means a buffer exists
  756.     rnz
  757.     push    b
  758.     push    h
  759.     ldbwx    b,FcrBufsize
  760.     call    @RAMHI
  761.     stbwx    h,FcrBufadr
  762.     pop    h
  763.     pop    b
  764.     ret
  765.  
  766.     end
  767.  a buffer exists
  768.     rnz
  769.     push    b
  770.     push    h
  771.     ldbwx    b,FcrBufsize
  772.     call    @R    name    'FMAKE'
  773.     title    'FMAKE -- create new output file'
  774. ;===============================================================
  775. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  776. ;
  777. ; This subroutine of the output-open operations creates a new
  778. ; disk file, applying a password to it if one was supplied.  The
  779. ; password is applied at the Read level.
  780. ;
  781. ; interface:    IX->FCR
  782. ;
  783. ; History
  784. ; initial code 09 April 84
  785. ;===============================================================
  786.     maclib    equates
  787.     maclib    services
  788.     maclib    smallz80
  789.     maclib    fcr
  790.     extrn    @BDOS
  791.  
  792.     public    @FMAKE
  793. @FMAKE:
  794.     push    psw
  795.     push    b
  796.     push    d
  797.     push    h
  798.  
  799. ; If a password has been assigned, set file attribute bit f6'
  800. ; and set the buffer address to the password data.  This will
  801. ; have no effect in CP/M 2.2.
  802.     ldx    a,FcrPassword
  803.     cpi    AsciiBlank    ; any password given?
  804.     jrz    nopass
  805.     mvix    FilePMread,FcrPMmake ; yes, set level
  806.     bsetx    Bit7,FcrName+5    ; and set attribute f6'
  807.  
  808.     pushix
  809.     pop    h
  810.     lxi    d,FcrPassword
  811.     dad    d
  812.     xchg            ; DE->password+mode
  813.     mvi    c,BdosSetbuffer ; point Bdos to our password
  814.     call    @BDOS
  815.  
  816. ; Make the new file and set its flags to indicate it exists,
  817. ; has no data, and can do output.
  818. nopass:
  819.     pushix
  820.     pop    d        ; DE->fcb
  821.     mvi    c,BdosMake
  822.     call    @BDOS        ; create the file
  823.     mvix    <(1 shl FcrDisk)+(1 shl FcrOutput)>,FcrFlags
  824.     bresx    Bit7,FcrName+5    ; clear make-pass attribute
  825.  
  826.     pop    h
  827.     pop    d
  828.     pop    b
  829.     pop    psw
  830.     ret
  831.  
  832.     end
  833. shl FcrOutput)>,FcrFlags
  834.     bresx    Bit7,FcrName+5    ; clear make-pass attribute
  835.  
  836.     pop    h
  837.     pop    d
  838.     po    name    'FCLOS'
  839.     title    'FCLOS -- close a file'
  840. ;===============================================================
  841. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  842. ;
  843. ; This modules completes the processing of DE->FCR, as follows:
  844. ;
  845. ; Device files: merely disable input and output.
  846. ;
  847. ; Disk files: write any final buffered data and issue a CP/M
  848. ; close request.  If the file was opened under a scratch ($$$)
  849. ; filetype, erase the existing file, rename the scratch file to
  850. ; the proper type and if a password was given in the FCR, apply
  851. ; it to the new file.
  852. ;
  853. ; interface: FCLOSE macro
  854. ;
  855. ; History:
  856. ; initial code 06 April 84
  857. ;===============================================================
  858.     maclib    equates
  859.     maclib    services
  860.     maclib    smallz80
  861.     maclib    fcr
  862.     extrn    @FDOUT,@BDOS
  863.  
  864.     public    @FCLOS
  865. @FCLOS:
  866.     push    psw
  867.     push    b
  868.     push    h
  869.     push    d
  870.     xtix
  871.  
  872. ; Separate disk and device files
  873.     bitx    FcrDisk,FcrFlags
  874.     jz    exit    ; all the work done at the end
  875.  
  876. ; Disk files: start by writing any modified data to disk.
  877.     call    @FDOUT
  878.  
  879. ; Close all disk files, because with seeking allowed, we
  880. ; can't tell what may have been modified.
  881.     pushix
  882.     pop    d
  883.     mvi    c,BdosClose
  884.     call    @BDOS
  885.  
  886. ; If the file was opened input, append, or update, or was a new
  887. ; output file, that's all we have to do.
  888.     bitx    FcrBakType,FcrFlags
  889.     jz    exit
  890.  
  891. ; We've just closed an output file under its scratch name of
  892. ; "name.$$$," and there does exist a previous file "name.typ"
  893. ; which we have to erase.  To do so, put back the correct type
  894. ; in the FCR.
  895.     call    fixtype
  896.  
  897. ; Before the erase we set up the one password we know, the one
  898. ; in the FCR.  If the existing file is protected at the read or
  899. ; write level, this will be the correct password for the
  900. ; existing file, as verified at open time.  If the existing file
  901. ; is protected only at the delete level, the password may not
  902. ; have been given or may be wrong, in which case we will be
  903. ; aborted on the erase.  (And under CP/M 2.2 it doesn't matter
  904. ; one way or the other.)
  905.     pushix
  906.     pop    d
  907.     lxi    h,FcrPassword
  908.     dad    d
  909.     xchg        ; DE->new file's given password
  910.     mvi    c,BdosSetbuffer
  911.     call    @BDOS
  912.  
  913.     pushix
  914.     pop    d
  915.     mvi    c,BdosErase    ; now try the erase
  916.     call    @BDOS
  917.  
  918. ; Ok, any existing file "name.typ" is gone.  In order to rename
  919. ; the new one from "name.$$$" to "name.typ" we have to put the
  920. ; desired name in the FCB+16 and put the $$$ back in the type.
  921.     pushix
  922.     pop    d        ; DE->FCB+0
  923.     lxi    h,FcrDatamap
  924.     dad    d        ; HL->FCB+16
  925.     xchg            ; (reverse the above)
  926.     lxi    b,12        ; length to move
  927.     ldir            ; "name.typ" to FCB+16
  928.     mvix    '$',FcrType    ; put back the darn dollars
  929.     mvix    '$',FcrType+1
  930.     mvix    '$',Fcrtype+2    ; "name.$$$" to FCB+0
  931.     pushix
  932.     pop    d        ; DE->FCB
  933.     mvi    c,BdosRename
  934.     call    @BDOS
  935.  
  936. ; Fix the type one more time, so that the FCR can be reopened.
  937.     call    fixtype
  938.  
  939.  if CpmLevel gt 22h ; UNDER CP/M PLUS...
  940. ; We have but one thing left to do: if a password was given as
  941. ; part of the filespec, we have to apply it to the new file
  942. ; (we didn't do it when we made the file).
  943.     mvi    a,AsciiBlank
  944.     cmpx    FcrPassword    ; was one given?
  945.     jrz    exit        ; (no, forget it)
  946.  
  947. ; We have a choice of protection levels, but (in the absence of
  948. ; other information, and we have none) the only safe thing to
  949. ; do from a security standpoint is to set the read level, the
  950. ; most restrictive.  The set-password (write XFCB) function
  951. ; wants the new password at the buffer-address plus 8.
  952.  
  953.     pushix
  954.     pop    d
  955.     lxi    h,FcrPassword-8
  956.     dad    d
  957.     xchg        ; (DE+8)->given password
  958.     mvi    c,BdosSetBuffer
  959.     call    @BDOS
  960.     mvix    FilePMread+1,FcrExtent ; assign at read level
  961.     pushix
  962.     pop    d
  963.     mvi    c,BdosPutXFCB
  964.     call    @BDOS
  965.  endif ; continue for both systems...
  966. ; Clear the FcrFlags to prevent further I/O until an open is
  967. ; done again.
  968.  
  969. exit:    mvix    0,FcrFlags    ; eof, no output, etc.
  970.     xtix
  971.     pop    d
  972.     pop    h
  973.     pop    b
  974.     pop    psw
  975.     ret
  976.  
  977. ; Subroutine to put back the old type in a scratch filespec.
  978.  
  979. fixtype:
  980.     ldx    a,FcrOtype
  981.     stx    a,FcrType
  982.     ldx    a,FcrOtype+1
  983.     stx    a,FcrType+1
  984.     ldx    a,FcrOtype+2
  985.     stx    a,FcrType+2
  986.     ret
  987.  
  988.     end
  989. ype:
  990.     ldx    a,FcrOtype    name    'FPASS'
  991.     title    'FPASS -- read password from keyboard'
  992. ;===============================================================
  993. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  994. ;
  995. ; This inner subroutine of the file-open operations is called
  996. ; when we expect to need a password and none was given in the
  997. ; filespec assigned to IX->FCR.  We prompt for a new password
  998. ; and read as many as 8 bytes into the FcrPassword field of
  999. ; IX->FCR.  Lowercase letters are converted to uppercase.
  1000. ;
  1001. ; In order not to expose the password on the screen we read it
  1002. ; using Bdos function 6, Raw Console Input, and echo only dots.
  1003. ;
  1004. ; If the user has set a default password that will apply, he
  1005. ; or she need only reply with a single return.
  1006. ;
  1007. ; interface:    IX->FCR
  1008. ; output:    new FcrPassword value on return
  1009. ;
  1010. ; History
  1011. ; initial code 2 April 84
  1012. ;===============================================================
  1013.     maclib    equates
  1014.     maclib    services
  1015.     maclib    smallz80
  1016.     maclib    fcr
  1017.     extrn    @FSTSP,@TOUPR,@BDOS
  1018.  
  1019.     dseg
  1020. prompt    db    AsciiCR,AsciiLF,'Enter password for '
  1021. fspec    ds    16 ; room for "x:filename.typ ", "$"
  1022. bspace    db    AsciiBS,AsciiBlank,AsciiBS,'$'
  1023. finis    db    AsciiCR,AsciiLF,'$'
  1024.     cseg
  1025.     public    @FPASS
  1026. @FPASS:
  1027.     push    psw
  1028.     push    b
  1029.     push    d
  1030.     push    h
  1031.  
  1032. ; blank the password field
  1033.     pushix
  1034.     pop    d
  1035.     lxi    h,FcrPassword
  1036.     dad    d        ; HL->password field of FCR
  1037.     push    h        ; save ->password
  1038.     mvi    b,8
  1039. loop1    mvi    m,AsciiBlank
  1040.     inx    h
  1041.     djnz    loop1
  1042.  
  1043. ; get the filespec converted and inserted into the prompt
  1044. ; string.
  1045.     lxi    h,fspec
  1046.     mvi    m,0    ; HL-> a null string
  1047.     mvi    a,0111b ; want drive, name, type
  1048.     call    @FSTSP    ; fspecstr @D,@Hupdate,111b
  1049.     mvi    m,AsciiBlank
  1050.     inx    h    ; one space after name
  1051.     mvi    m,'$'    ; delimit message for the Bdos
  1052.  
  1053. ; display prompt "Enter password for x:filename.typ "
  1054.     lxi    d,prompt
  1055.     mvi    c,BdosString
  1056.     call    @BDOS
  1057.  
  1058. ; We will now accept as many as 8 bytes from the console,
  1059. ; echoing each as a dot.  Two control characters are
  1060. ; also permitted: backspace means back up one in order to
  1061. ; strike over, and CR means to quit.  We stop reading
  1062. ; when a CR comes, or when a ninth byte is typed.
  1063.     pop    h    ; HL->current password byte
  1064.     mvi    b,0    ; B counts bytes received
  1065.  
  1066. Loop2:    call    get1    ; get one byte and store
  1067.     cpi    AsciiCR ; was it CR?
  1068.     jrz    done    ; (yes)
  1069.     mov    a,b
  1070.     cpi    9    ; was it the ninth?
  1071.     jrnz    Loop2    ; (no)
  1072.  
  1073. done:    lxi    d,finis
  1074.     mvi    c,BdosString
  1075.     call    @BDOS    ; display CR, LF
  1076.  
  1077.     pop    h
  1078.     pop    d
  1079.     pop    b
  1080.     pop    psw
  1081.     ret
  1082.  
  1083. ; in this subroutine, read and handle one keystroke.  It gets
  1084. ; complicated: ignoring most control characters, handling
  1085. ; backspaces, etc.
  1086. get1:
  1087.     push b ! push h
  1088.     mvi    c,BdosRawkbd
  1089.     mvi    e,0fdh    ; un-echoed input operation
  1090.     call    @BDOS
  1091.     pop h ! pop b
  1092.     cpi    AsciiCR ; if CR, just return
  1093.     rz
  1094.     cpi    AsciiBS ; if BS,
  1095.     jrnz    notBS    ; (wasn't)
  1096.     mov a,b ! ora a ; ..and if not at left margin,
  1097.     rz        ; (were, ignore)
  1098.     dcr    b    ; then count one less byte
  1099.     dcx    h    ; point back to prior byte
  1100.     mvi    m,AsciiBlank ; and blank it out
  1101.     push b ! push h
  1102.     mvi    c,BdosString
  1103.     lxi    d,bspace; blank dot on screen, too
  1104.     call    @BDOS
  1105.     pop h ! pop b
  1106.     ret        ; and continue loop
  1107. notBS:    cpi    AsciiBlank ; not BS, not CR -- was it a
  1108.     rc        ; control character? if so, ignore
  1109.     call    @TOUPR    ; printable -- ensure uppercase
  1110.     mov    m,a
  1111.     inr    b    ; and count it
  1112.     inx    h    ; and point to next slot
  1113.     push b ! push h
  1114.     mvi    e,'.'    ; now echo a dot
  1115.     mvi    c,BdosType
  1116.     call    @BDOS
  1117.     pop  h ! pop b
  1118.     ret
  1119.  
  1120.     end
  1121. oint to next slot
  1122.     push b    name    'FASGN'
  1123.     title    'FASGN -- process filespec string into FCR'
  1124. ;===============================================================
  1125. ; Copyright (C) 1985 M&T Publishing Co. all rights reserved
  1126. ;
  1127. ; This module parses a character string as a filespec, putting
  1128. ; its various parts in the fields of a file control record to
  1129. ; be opened and used.  Its input and output are more complicated
  1130. ; than most:
  1131. ;    HL->string to be parsed
  1132. ;    DE->FCR to be updated
  1133. ;    BC=0000, or BC->default FCR
  1134. ;    A = bitmap of parts to be assigned
  1135. ;        bit 0 = assign drivecode
  1136. ;        bit 1 = assign filename
  1137. ;        bit 2 = assign filetype
  1138. ;        bit 3 = assign password
  1139. ;
  1140. ; Normally, A=01111b and HL->a full filespec as given in the
  1141. ; command line.  The bitmap, however, allows a program to assign
  1142. ; a full filespec and then override only its filetype, leaving
  1143. ; the drive, filename, password as they were.
  1144. ;
  1145. ; After parsing a full (A=01111b), valid, filespec the module
  1146. ; checks to see if the drivecode, filename, a/o filetype were
  1147. ; omitted.  If any were, and if a default FCR was passed, and if
  1148. ; the new filespec can't be a device name, the missing fields
  1149. ; are copied from the default FCR.
  1150. ;
  1151. ; Upon return, DE->updated FCR and HL->the last string byte
  1152. ; examined.  Register A reflects the filespec status:
  1153. ;    A = 00h = filespec valid and unambiguous
  1154. ;    A = question mark = filespec was valid but ambiguous
  1155. ;    A = exclamation = filespec was found invalid at HL->byte
  1156. ; The Z flag is TRUE if an error occurred and FALSE if not.
  1157. ;
  1158. ; CP/M Plus contains a system function that parses filespecs
  1159. ; into file control blocks, but I chose to write this one for
  1160. ; several reasons: the CP/M operation won't handle a device
  1161. ; filespec like "CON:"; it doesn't return the address of a byte
  1162. ; that caused an error; it doesn't signal an ambiguous spec.
  1163. ;
  1164. ; interface: FASSIGN macro
  1165. ;
  1166. ; History
  1167. ; don't require passwd bit for deflt in 2.2 10/17/85
  1168. ; rewrite to use bitmap scheme 6 August 84
  1169. ; don't do defaults when filename is XXX: (eg CON:) 12/4/84
  1170. ; initial code 11 April 84
  1171. ;===============================================================
  1172.     maclib    environ
  1173.     maclib    fcr
  1174.  
  1175.     dseg
  1176. errbyte ds    1    ; 00, ?, or bang
  1177. bitmap    ds    1    ; input value of bitmap
  1178.     cseg
  1179.  
  1180.     public    @FASGN
  1181. @FASGN:
  1182.     push    b
  1183.     mov    c,a    ; carry bitmap in C
  1184.     sta    bitmap    ; ..and save it for later
  1185.     xra    a
  1186.     sta    ErrByte ; clear exit-flag value
  1187.     xchg        ; set up DE->string
  1188.     strskip @Dupdate; skip leading whitespace
  1189.     xchg        ; DE->file, HL->nonwhite (or null?)
  1190.  
  1191.     rarr    c    ; check bit 0
  1192.     jrnc    tryfn    ; (drivecode not wanted)
  1193.     call    dodrive ; deal with drivecode
  1194.     jrnz    exit    ; (error in drive)
  1195. tryfn:
  1196.     rarr    c    ; bit 1
  1197.     jrnc    tryft    ; (filename not wanted)
  1198.     call    doname    ; deal with filename
  1199.     jrnz    exit    ; (error in name)
  1200. tryft:
  1201.     rarr    c    ; bit 2
  1202.     jrnc    trypw    ; (filetype not wanted)
  1203.     call    dotype    ; deal with filetype
  1204.     jrnz    exit    ; (error in type)
  1205. trypw:
  1206.     rarr    c    ; and bit 3
  1207.     jrnc    trydflt ; (password not wanted)
  1208.     call    dopass    ; deal with password
  1209.     jrnz    exit    ; (error in password)
  1210. trydflt:
  1211.     lda    bitmap
  1212.  if CpmLevel eq 30h
  1213.     cpi    01111b    ; full spec handled?
  1214.  else
  1215.     cpi    00111b    ; all of drive, name, type done?
  1216.  endif
  1217.     jrc    exit    ; (part spec, no default)
  1218.     pop b ! push b    ; restore BC=0000 or ->default
  1219.     mov a,b ! ora c
  1220.     cnz    default
  1221. exit:
  1222.     pop    b    ; BC restored
  1223.     lda    errbyte ; A = 00, query or bang
  1224.     cpi    '!'    ; set Z true if error
  1225.     ret
  1226.  
  1227. ; Error: subroutines exit through here when they note an error.
  1228. Error:
  1229.     mvi    a,'!'
  1230.     sta    errbyte
  1231.     ora    a    ; for not-Z
  1232.     ret
  1233.  
  1234. ; Ambig: called when a subroutine notices * or ?
  1235. Ambig:
  1236.     mvi    a,'?'
  1237.     sta    errbyte
  1238.     ret
  1239.  
  1240. ; Dodrive: set 00=default drive in FCB.  Then if HL->X:,
  1241. ; check X and set it as the drivecode.    We have to keep in
  1242. ; mind that HL->null string is valid.
  1243. dodrive:
  1244.     mov a,m ! ora a ; HL->null string?
  1245.     jrnz    tstcolon ; (no)
  1246.     stax    d    ; yes, set default drive = 00
  1247.     ret        ; ..and exit ok.
  1248. tstcolon:
  1249.     inx    h
  1250.     mov    a,m
  1251.     dcx    h
  1252.     cpi    ':'    ; are we looking at X:?
  1253.     jrz    colon    ; (yes, continue)
  1254.     xra    a    ; no, set default drive
  1255.     stax    d
  1256.     ret        ; (and return w/ Z true)
  1257. colon:
  1258.     mov    a,m    ; A = driveletter
  1259.     toupper @A
  1260.     cpi    'P'+1    ; letter < P?
  1261.     jnc    Error    ; (no, bail out)
  1262.     sui    'A'    ; letter >= A?
  1263.     jc    Error    ; (no, bail out)
  1264.     inr    a    ; FCB code is A=1, B=2, etc
  1265.     stax    d    ; set it
  1266.     inx h ! inx h    ; and step over X:
  1267.     xra    a
  1268.     ret
  1269.  
  1270. ; Doname: move up to 8 bytes of filename, making them uppercase,
  1271. ; to the FCB.  Pad the filename field with blanks.
  1272. doname:
  1273.     mvi    b,8    ; count of bytes in field
  1274.     mvi    a,FcrName ; offset to field
  1275.     call    copy    ; name, type are the same
  1276.     ret
  1277.  
  1278. ; Dotype: move up to 3 bytes of filetype, making them uppercase,
  1279. ; to the FCB.  Pad the field with blanks.
  1280. dotype:
  1281.     mov    a,m    ; we may be looking at
  1282.     cpi    '.'    ; a dot...
  1283.     jrnz    nodot
  1284.     inx    h    ; if so, step over it
  1285. nodot:    mvi    b,3    ; size of field
  1286.     mvi    a,FcrType ; offset to it
  1287.  
  1288. ; Copy: handle filling the filename, filetype fields, allowing
  1289. ; for "*" meaning "fill with ?s" and "?" meaning an ambiguous
  1290. ; reference.  At end, check that we advanced in the string to
  1291. ; a filespec delimiter.
  1292. copy:    push    d    ; save base of file
  1293.     xchg        ; DE->string
  1294.     addhla        ; HL->field of file structure
  1295. coploop:
  1296.     ldax    d
  1297.     fdelim? @A    ; reached a delimiter?
  1298.     jrnz    notdlm    ; (no)
  1299.     mvi    a,' '    ; yes, store a blank
  1300.     jr    store
  1301. notdlm: cpi    '*'    ; found an asterisk?
  1302.     jrnz    notast    ; (no)
  1303.     call    Ambig    ; yes, note and set A='?'
  1304.     jr    store
  1305. notast: toupper @A
  1306.     cpi    '?'    ; ambiguous single letter?
  1307.     cz    Ambig    ; (yes, note it)
  1308.     inx    d    ; X or ?, step on in string
  1309. store:    mov    m,a    ; store blank, X, or ?
  1310.     inx    h
  1311.     djnz    coploop ; continue for size of field
  1312.     xchg        ; restore HL->string
  1313.     pop    d    ; restore DE->file
  1314.     mov    a,m
  1315.     cpi    '*'    ; did we hang on an asterisk?
  1316.     jrnz    ckdlm    ; (no)
  1317.     inx    h    ; yes, step over it now
  1318. ckdlm    mov    a,m    ; now make sure we advanced to
  1319.     fdelim? @A    ; ..a filespec delimiter
  1320.     rz        ; (yes)
  1321.     jmp    Error    ; no, input field too long
  1322.  
  1323. ; Dopass: copy a password -- if any -- into the FcrPassword
  1324. ; field for use during open.  The * and ? have no special
  1325. ; meaning in a password.
  1326. dopass:
  1327.     push    d
  1328.     xchg        ; HL->file, DE->string
  1329.     mvi    a,FcrPassword
  1330.     addhla        ; HL->FcrPassword
  1331.     mvi    b,8    ; field size
  1332.  
  1333.     ldax    d    ; we could be looking at a ";"
  1334.     cpi    ';'    ; are we?
  1335.     jrnz    pcopy
  1336.     inx    d    ; if so, step over it
  1337. pcopy:
  1338.     ldax    d    ; take byte,
  1339.     inx    d    ; ..step past it
  1340.     toupper @A    ; ..make uppercase
  1341.     fdelim? @A    ; is it a filespec delimiter?
  1342.     jrnz    pstore    ; (no)
  1343.     mvi    a,' '    ; if so, store a blank instead
  1344.     dcx    d    ; ..and don't go past the delim.
  1345. pstore: mov    m,a    ; store blank or X
  1346.     inx    h
  1347.     djnz    pcopy    ; continue for size of field
  1348.     xchg        ; restore HL->string
  1349.     pop    d    ; and DE->file
  1350.     mov    a,m    ; we should now be looking at a
  1351.     fdelim? @A    ; filespec delimiter
  1352.     rz        ; (yes)
  1353.     jmp    Error    ; nope, password too long
  1354.  
  1355. ; Default: A full filespec has been parsed (correctly) and
  1356. ; a default FCR was passed.  Take any omitted parts of the spec
  1357. ; from the default FCR.
  1358. default:
  1359.     push    h    ; save string
  1360.     push    d    ; and file-base
  1361.  
  1362. ; We don't do a default if the fourth byte of the filename is
  1363. ; a colon.  If we did there would be a conflict between an
  1364. ; omitted filetype and a device-name which has no filetype.
  1365.     lxi    h,FcrName+3
  1366.     dad    d
  1367.     mov    a,m
  1368.     cpi    ':'
  1369.     jz    endefault
  1370.  
  1371. ; Defaults are to be done.  Get HL->default FCR.
  1372.     mov h,b ! mov l,c
  1373.  
  1374. ; If the drivecode was omitted, default it.
  1375.     ldax    d
  1376.     ora    a
  1377.     jrnz    havedrive
  1378.     mov    a,m
  1379.     stax    d
  1380.  
  1381. ; If the filename was omitted, do likewise.
  1382. havedrive:
  1383.     inx    d    ; DE->output FcrName
  1384.     inx    h    ; HL->default FcrName
  1385.     lxi    b,8    ; BC=length of field
  1386.     ldax    d
  1387.     cpi    ' '    ; name given?
  1388.     jrz    dfltname; (no, copy it)
  1389.     dad    b    ; yes, advance to type
  1390.     xchg
  1391.     dad    b    ; ..in both pointers
  1392.     xchg
  1393.     jr    havename ; ..and don't copy
  1394. dfltname:
  1395.     ldir        ; copy name, advancing ptrs
  1396.  
  1397. ; Likewise with the filetype
  1398. havename:
  1399.     lxi    b,3    ; BC=length of field
  1400.     ldax    d    ; was it omitted?
  1401.     cpi    ' '
  1402.     jrz    dflttype ; (yes, copy it)
  1403.     dad    b    ; no, advance ptrs
  1404.     xchg
  1405.     dad    b
  1406.     xchg
  1407.     jr    havetype ; ..and don't copy
  1408. dflttype:
  1409.     ldir        ; copy type, advancing ptrs
  1410.  
  1411. ; So much for defaults.  We could actually do a default for
  1412. ; the password as well, but in general it seems better not to.
  1413. havetype:
  1414. endefault:
  1415.     pop    d
  1416.     pop    h
  1417.     ret
  1418.  
  1419.     end
  1420. ault for
  1421. ; the password as well, but in general it seems better not to.
  1422. havetype:
  1423. endefa