home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / cpm / zcpr33 / z33tss-c.lbr / ZCPR33.ZZ0 / ZCPR33.Z8°
Encoding:
Text File  |  1987-10-30  |  124.1 KB  |  4,074 lines

  1. ; PROGRAM:    ZCPR
  2. ; VERSION:    3.3
  3. ; DERIVATION:    ZCPR30
  4. ; AUTHOR:    Jay Sage
  5. ; DATE:        May 28, 1987
  6.  
  7. ; ZCPR33 is copyright 1987 by Echelon, Inc.  All rights reserved.  End-user
  8. ; distribution and duplication permitted for non-commercial purposes only.
  9. ; Any commercial use of ZCPR33, defined as any situation where the duplicator
  10. ; recieves revenue by duplicating or distributing ZCPR33 by itself or in
  11. ; conjunction with any hardware or software product, is expressly prohibited
  12. ; unless authorized in writing by Echelon.
  13. ;
  14. ; Echelon specifically disclaims any warranties, expressed or implied,
  15. ; including but not limited to implied warranties of merchantability and
  16. ; fitness for a particular purpose.  In no event will Echelon be liable for
  17. ; any loss of profit or any other commercial damage, including but not limited
  18. ; to special, incidental, consequential, or other damages.
  19. ;
  20. ; Echelon can be contacted at:
  21. ;      Echelon, Inc.
  22. ;      885 N. San Antonio Road
  23. ;      Los Altos, California  USA  94022
  24. ;      (415) 948-3820
  25.  
  26.  
  27. ;-----------------------------------------------------------------------------
  28. ;
  29. ;           A C K N O W L E D G M E N T S
  30. ;
  31. ;-----------------------------------------------------------------------------
  32.  
  33. ; Many people have played a role in the development of ZCPR in general and
  34. ; ZCPR33 in particular.  It all started when "The CCP Group," including
  35. ; Richard Conn, Ron Fowler, Keith Petersen, Frank Wancho, Charlie Strom, and
  36. ; Bob Mathias decided that by rewriting the CP/M command processor to take
  37. ; advantage of Zilog-specific opcodes they could save enough code to enhance
  38. ; some of the features.   Richard Conn then extended that development through
  39. ; ZCPR2 to ZCPR3 (3.0).  Just a little over two years ago, I took the first
  40. ; step to enhance ZCPR3 by making it get the maximum drive and user values
  41. ; from the environment instead of hard coding them in.   This version was
  42. ; distributed privately as ZCPR31.  Along the way to what is now ZCPR
  43. ; version 3.3 a number of others have made valuable contributions: Steve
  44. ; Kitahata, Michael Bate, Bruce Morgen, Roger Warren, Dreas Nielsen, Bob Freed,
  45. ; Al Hawley, Howard Goldstein, and many others who have stimulated developments
  46. ; by pointing out problems or asking questions.
  47.  
  48. ; I would like particularly to acknowledge two people who have played a very
  49. ; significant role in these developments.  One is Al Hawley.  He introduced
  50. ; the idea of having the DUOK flag in the environment control how the CPR
  51. ; would respond to the DU: form of directory reference.  He also originated
  52. ; the idea of using the high bit of the first character of each command name
  53. ; to control whether or not it would be wheel restricted.  Finally, he
  54. ; contributed the basic structure of the highly efficient, elegant, and more
  55. ; general number evaluation routines in the code.
  56.  
  57. ; My biggest debt of gratitude is to Howard Goldstein.  His role in the
  58. ; development of ZCPR33 goes back about a year, when he contributed the first
  59. ; correct implementation of the minpath feature.  More recently, during the
  60. ; period of intense development since Echelon expressed its interest in my
  61. ; writing the official 3.3 version, he and I have shared an especially
  62. ; enjoyable and fruitful relationship.  Most of the newest ideas have been
  63. ; worked out jointly, and Howard has done a great deal to keep my code and
  64. ; concepts on track.  He discovered many ways to pare the code down and, more
  65. ; importantly, uncovered numerous subtle bugs.  He recoded the SAVE command
  66. ; to make it more compact and reliable.
  67. ;
  68. ;                        Jay Sage
  69. ;                        May 28,1987
  70.  
  71. ;-----------------------------------------------------------------------------
  72. ;
  73. ;        U S E R    C O N F I G U R A T I O N
  74. ;
  75. ;-----------------------------------------------------------------------------
  76.  
  77. ; The following MACLIB statements load all the user-selected equates
  78. ; which are used to customize ZCPR33 for the user's working environment.
  79. ; NOTE -- TRUE & FALSE are defined in Z3BASE.
  80.  
  81.     maclib    z3base.lib
  82.     maclib    z33hdr.lib
  83.  
  84. ; Check that the configuration includes the required facilities
  85.  
  86. ; A ZCPR33 system is assumed to include certain minimal features, including
  87. ; an external file control block, external path, shell stack, message buffer,
  88. ; external environment descriptor, multiple command line, and external stack.
  89. ; If wheel checking is enabled in the Z33HDR.LIB file, then there must be
  90. ; an address defined for the wheel byte in the Z3BASE.LIB file.
  91.  
  92. errflag    defl    extfcb eq 0            ; External command FCB
  93. errflag    defl    errflag or [ expath eq 0 ]    ; Symbolic path
  94. errflag    defl    errflag or [ shstk  eq 0 ]    ; Shell stack
  95. errflag    defl    errflag or [ z3msg  eq 0 ]    ; Message buffer
  96. errflag    defl    errflag or [ z3env  eq 0 ]    ; Environment descriptor
  97. errflag    defl    errflag or [ z3cl   eq 0 ]    ; Multiple command line
  98. errflag    defl    errflag or [ extstk eq 0 ]    ; External stack
  99.      if    wheel or wdu or wpass or wprefix or whldir
  100. errflag    defl    errflag or [ z3whl  eq 0 ]    ; Wheel byte
  101.      endif    ;wheel or wdu or wpass or wprefix or whldir
  102.  
  103.      if    errflag
  104.  
  105.     *** NOT ALL REQUIRED ZCPR3 FACILITIES ARE SUPPORTED ***
  106.  
  107.      else    ; go ahead with the assembly
  108.  
  109.  
  110. ;-----------------------------------------------------------------------------
  111. ;
  112. ;        D E F I N I T I O N S    S E C T I O N
  113. ;
  114. ;-----------------------------------------------------------------------------
  115.  
  116.  
  117. ; ----------   Macro definitions
  118.  
  119.     maclib    z33mac.lib        ; Library of macros for ZCPR33
  120.  
  121.  
  122. ; ----------   ASCII definitions
  123.  
  124. ctrlc    equ    03h
  125. bell    equ    07h
  126. tab    equ    09h
  127. lf    equ    0ah
  128. cr    equ    0dh
  129.  
  130.  
  131. ; ----------   Operating system addresses
  132.  
  133. wboot    equ    base+0000h    ; CP/M warm boot address
  134. udflag    equ    base+0004h    ; User number in high nybble, disk in low
  135. bdos    equ    base+0005h    ; BDOS function call entry point
  136. tfcb    equ    base+005ch    ; Default FCB buffer
  137. tfcb2    equ    tfcb+16        ; 2nd FCB
  138. tbuff    equ    base+0080h    ; Default disk I/O buffer
  139. tpa    equ    base+0100h    ; Base of TPA
  140. bios    equ    ccp+0800h+0e00h    ; BIOS location
  141.  
  142.  
  143. ; ----------   Error codes
  144.  
  145. ; ZCPR33 uses the error byte at the beginning of the message buffer as a flag
  146. ; to show what kind of error occurred.  Advanced error handlers will be able
  147. ; to help the user further by suggesting the possible cause of the error.
  148. ; Error code 6 for an ECP error is determined by the code and cannot be
  149. ; changed (without increasing code length).
  150.  
  151. ecduchg        equ    1    ; Directory change error -- attempt to change
  152.                 ; ..logged directory when under control of
  153.                 ; ..wheel byte and wheel is off
  154.  
  155. ecbaddir    equ    2    ; Bad directory specification -- logging of
  156.                 ; ..user number beyond legal range,
  157.                 ; ..nonexistent named directory
  158.  
  159. ecbadpass    equ    3    ; Bad password -- incorrect password entered
  160.  
  161.  
  162. ecbadcmd    equ    5    ; Bad command form -- wildcard or file type
  163.                 ; ..present in command verb
  164.  
  165. ececperr    equ    6    ; ECP error --  command could not be executed
  166.                 ; ..by ECP, error handling was forced by a
  167.                 ; ..transient for its own reasons
  168.                 ; (DO NOT CHANGE FROM 6)
  169.  
  170. ecnocmd        equ    7    ; Command file not found -- command that skips
  171.                 ; ..ECP could not be executed, GET could not
  172.                 ; ..find file to load
  173.  
  174. ecambig        equ    8    ; Ambiguous file specification where not
  175.                 ; ..allowed (SAVE, GET, REN)
  176.  
  177. ecbadnum    equ    9    ; Bad numerical value -- not a number where
  178.                 ; ..number expected, number out of range
  179.  
  180. ecnofile    equ    10    ; File not found -- REN, TYPE, LIST could not
  181.                 ; ..find a specified file
  182.  
  183. ecdiskfull    equ    11    ; Disk directory or data area full
  184.  
  185. ectpafull    equ    12    ; TPA overflow error
  186.  
  187.  
  188. ; ----------   Multiple command line equates
  189.  
  190. ; The multiple command line buffer is located in a protected area in memory so
  191. ; that it is not overwritten during warm boots.  It includes some pointers so
  192. ; that when ZCPR33 starts it can tell where to start reading the command line.
  193. ; BUFSIZ and CHRCNT are not used by ZCPR33 but are provided so that the BDOS
  194. ; line input function can be used to read in a command line.
  195.  
  196. nxtchr    equ    z3cl        ; Address where pointer to next command to
  197.                 ; ..process is kept
  198. bufsiz    equ    z3cl+2        ; Address where size of buffer is kept
  199. chrcnt    equ    z3cl+3        ; Address where length of string actually in
  200.                 ; ..the buffer is kept (not always reliable)
  201. cmdlin    equ    z3cl+4        ; Address of beginning of command line buffer
  202. buflen    equ    z3cls        ; Length of command line buffer
  203.  
  204.  
  205. ; ----------   Command file control block
  206.  
  207. ; In ZCPR33 the file control block for commands must be located in protected
  208. ; memory.  This not only frees up valuable space in the command processor for
  209. ; code but also makes it possible for programs to determine by what name they
  210. ; were invoked.
  211.  
  212. cmdfcb    equ    extfcb
  213.  
  214.  
  215. ; ----------   External CPR stack
  216.  
  217. stack    equ    extstk+48    ; Set top-of-stack address
  218. pwlin    equ    extstk        ; Place line at bottom of stack
  219.  
  220.  
  221. ; ----------  Environment
  222.  
  223. quietfl    equ    z3env+28h    ; Quiet flag
  224. maxdrenv equ    z3env+2ch    ; Maximum drive value
  225. maxusrenv equ    z3env+2dh    ; Maximum user value
  226. duokfl    equ    z3env+2eh    ; Flag indicating acceptance of DU: form
  227. crttxt0    equ    z3env+33h    ; Address of number of lines of text on the
  228.                 ; ..screen of CRT0
  229.  
  230.  
  231. ; ----------  Message buffer
  232.  
  233. ecflag        equ    z3msg        ; Error return code flag
  234. ifptrfl        equ    z3msg+1        ; Pointer to current IF level
  235. ifstatfl     equ    z3msg+2        ; Flow control status byte
  236. cmdstatfl    equ    z3msg+3        ; Command status flag
  237. cmdptr        equ    z3msg+4        ; Pointer to currently running command
  238. zexinpfl    equ    z3msg+7        ; ZEX input status/control flag
  239. zexrunfl    equ    z3msg+8        ; ZEX running flag
  240. errcmd        equ    z3msg+10h    ; Error handling command line
  241. xsubflag    equ    z3msg+2ch    ; XSUB input redirection flag
  242. subflag        equ    z3msg+2dh    ; Submit running flag
  243. curusr        equ    z3msg+2eh    ; Currently logged user
  244. curdr        equ    z3msg+2fh    ; Currently logged drive
  245.  
  246.  
  247. ;-----------------------------------------------------------------------------
  248. ;
  249. ;        C O D E    M O D U L E S    S E C T I O N
  250. ;
  251. ;-----------------------------------------------------------------------------
  252.  
  253.     page
  254.  
  255. ; ZCPR33-1.Z80
  256.  
  257. ;=============================================================================
  258. ;
  259. ;    E N T R Y    P O I N T S    A N D    H E A D E R    S T R U C T U R E
  260. ;
  261. ;=============================================================================
  262.  
  263.      if    not rel        ; If generating absolute code
  264.     org    ccp
  265.      endif    ;not rel
  266.  
  267.  
  268. ; ENTRY POINTS INTO ZCPR33
  269. ;
  270. ; For compatibility with CP/M, two entry points are provided here.  In
  271. ; standard CP/M if the code is entered from the first entry point, then the
  272. ; command in the resident command buffer is executed; if entered from the
  273. ; second entry point, the command line is flushed.  With ZCPR33 and its
  274. ; multiple command line buffer, these two entry points function identically
  275. ; and go to the same address.
  276. ;
  277. ; We have kept the entry points in their standard locations but have used a
  278. ; relative jump for the second entry point and replaced the last byte with the
  279. ; version number.  In this way the version number occupies a position that
  280. ; would otherwise contain the page number at which the CPR runs.  It will
  281. ; always be possible, therefore, to distinguish ZCPR33 and later versions
  282. ; from other command processors.  The first jump is kept as an absolute jump
  283. ; so that 1) the code will be compatible with Z-COM and Z3-DOT-COM and 2) the
  284. ; execution address of a CPR module can always be determined.
  285.  
  286. entry:
  287.     jp    zcpr
  288.  
  289.     jr    zcpr
  290.  
  291. version:
  292.     defb    33h        ; Version ID squeezed in here (offset = 5)
  293.  
  294. ;-----------------------------------------------------------------------------
  295.  
  296. ; Configuration information
  297.  
  298. options:            ; (offset = 6)
  299.   optflag badduecp,rootonly,ndrenv,fcpenv,rcpenv,inclenv,aduenv,duenv
  300.   optflag highuser,drvprefix,scancur,incldir,incldu,dufirst,accptdir,accptdu
  301.   optflag no,pwcheck,pwnoecho,wdu,wpass,wprefix,fastecp,skippath
  302.  
  303. attdir    defl    [ comatt eq 80h ] or [ comatt eq 01h ] or [ not attchk ]
  304. attsys    defl    [ comatt eq 00h ] or [ comatt eq 01h ] or [ not attchk ]
  305. subquiet defl    [ subnoise eq 1 ]
  306. subecho    defl    [ subnoise gt 1 ]
  307.  
  308.   optflag shellif,attsys,attdir,attchk,subecho,subquiet,subclue,subon
  309.  
  310. ; Byte with information about the alternate colon option.  If the byte is
  311. ; zero, the option is not supported.  Otherwise the byte contains the
  312. ; prefix character that serves as an alias for a colon prefix.  Offset = 10.
  313.  
  314.      if    altcolon
  315.     defb    altchar
  316.      else
  317.     defb    0
  318.      endif    ;altcolon
  319.  
  320. ; Byte with information about the FASTECP implementation (option bit above
  321. ; indicates whether the feature is enabled at all).  If no character appears
  322. ; here (zero byte), then only a leading space can be used.  Otherwise, the
  323. ; first seven bits contain the character, and the high bit, if set, indicates
  324. ; that ONLY this character will be recognized and not a space.  Offset = 11.
  325.  
  326.      if    fastecp and altspace
  327.      if    altonly
  328.     defb    ecpchar + 80h
  329.      else    ;not altonly
  330.     defb    ecpchar
  331.      endif    ;altonly
  332.      else    ;no alternate character
  333.     defb    0
  334.      endif    ;fastecp and altspace
  335.  
  336.     defb    0,0,0,0            ; Space reserved for expansion
  337.  
  338. ;-----------------------------------------------------------------------------
  339.  
  340. ; Entry points to file name parsing code.
  341.  
  342. ; Entry point REPARSE.  A call to this point can be used to parse a command
  343. ; line tail into the default file control blocks at 5CH and 6CH.  Each time
  344. ; the parser is called it leaves the starting address of the second token in
  345. ; the PARESPTR address below so that successive calls to the routine reparse
  346. ; the command tail one token later.  A program can load its own pointer into
  347. ; PARSEPTR as well.  Offset = 16 (10h).
  348.  
  349. reparse:
  350. parseptr equ    $+1        ; Pointer for in-the-code modification
  351.     ld    hl,0
  352.     jp    parsetail
  353.  
  354. ; Entry point SCAN.  A call to this point can be used to parse a single token
  355. ; pointed to by HL into the FCB pointed to by DE.  Offset 22 (16h).
  356.  
  357. scan:
  358.     jp    scanner
  359.  
  360. ;-----------------------------------------------------------------------------
  361.  
  362. ; BUFFERS
  363. ;
  364. ; In this area various data items are kept.  First comes the list of commands
  365. ; supported by ZCPR33; then comes the name of the extended command processor
  366. ; (ECP).  By putting these items here, an 'H' command in the RCP or a utility
  367. ; like SHOW.COM can find this information and report it to the user.
  368.  
  369.  
  370. ; ----------   RESIDENT COMMAND TABLE
  371.  
  372. ; The command table entry is structured as follows:  First there is a byte
  373. ; which indicates the number of characters in each command.  Then there is a
  374. ; series of entries comprising the name of a command followed by the address
  375. ; of the entry point to the code for carrying out that command.  Finally,
  376. ; there is a null byte (00h) to mark the end of the table.  Offset = 25 (19h).
  377.  
  378.  
  379. cmdtbl:
  380.     defb    cmdsize        ; Length of command names
  381.     ctable            ; Define table via macro in Z33HDR.LIB
  382.     defb    0        ; End of table
  383.  
  384. ; ----------  NAME FOR EXTENDED COMMAND PROCESSOR
  385.  
  386. ; The name of the extended command processor is placed here after the command
  387. ; table so that utilities like SHOW or an RCP 'H' command can find it.
  388.  
  389. ecpfcb:
  390.     ecpname            ; From Z33HDR.LIB
  391.  
  392.  
  393. ; ----------   FILE TYPE FOR TRANSIENT COMMANDS (usually COM)
  394.  
  395. ; This file type also applies to the extended command processor name.
  396.  
  397. commsg:
  398.     comtyp            ; From Z33HDR.LIB
  399.  
  400.  
  401.  
  402. ; ----------   SUBMIT FILE CONTROL BLOCK
  403.  
  404.      if    subon        ; If submit facility enabled ...
  405.  
  406. subfcb:
  407.     defb    subdrv-'A'+1    ; Explicit drive for submit file
  408.     defb    '$$$     '    ; File name
  409.     subtyp            ; From Z33HDR.LIB
  410.     defb    0        ; Extent number
  411.     defb    0        ; S1 (user number 0)
  412. subfs2:
  413.     defs    1        ; S2
  414. subfrc:
  415.     defs    1        ; Record count
  416.     defs    16        ; Disk group map
  417. subfcr:
  418.     defs    1        ; Current record number
  419.  
  420.      endif    ; subon
  421.  
  422. ; End ZCPR33-1.Z80
  423.  
  424.     page
  425.  
  426. ; ZCPR33-2.Z80
  427.  
  428. ;=============================================================================
  429. ;
  430. ;    C O M M A N D    L I N E    P R O C E S S I N G    C O D E
  431. ;
  432. ;=============================================================================
  433.  
  434. ; MAIN ENTRY POINT TO CPR
  435.  
  436. ; This is the main entry point to the command processor.  On entry the C
  437. ; register must contain the value of the user/drive to be used as the current
  438. ; directory.
  439.  
  440. zcpr:
  441.     ld    sp,stack    ; Reset stack
  442.  
  443.      if    pwnoecho
  444.     ld    a,0c3h        ; Reenable BIOS conout routine
  445.     ld    (bios+0ch),a    ; ..after a warmboot
  446.      endif    ;pwnoecho
  447.  
  448.     ld    b,0fh        ; Keep nibble mask in B
  449.  
  450. ; If the HIGHUSER option is enabled, we compare the user/drive in the login
  451. ; byte in C to the values stored in the message buffer.  If, ignoring bit 4
  452. ; of the user number, they match, then we remain in the current area, which
  453. ; may be a user area above 15.
  454.  
  455.      if    highuser
  456.  
  457.     ld    a,c        ; Copy user/drive byte to A
  458.     and    b        ; Isolate drive
  459.     ld    d,a        ; ..and move to D
  460.     ld    a,c        ; Get full byte back
  461.     swap            ; Swap nibbles
  462.     and    b        ; Isolate user number
  463.     ld    e,a        ; ..and move to E
  464.     ld    hl,(curusr)    ; Get old curdr/curusr into HL
  465.     sbc    hl,de        ; Subtract new values from old (carry is clear)
  466.     ex    de,hl        ; Switch new values into HL, diff into DE
  467.     ld    a,d        ; Combine two parts of difference
  468.     or    e
  469.     and    b        ; Ignore bit for high user numbers
  470.     jr    z,zcpr1        ; Skip update if no change in DU
  471.     ld    (curusr),hl    ; Update values of current drive and user
  472. zcpr1:
  473.  
  474.      else    ;not highuser
  475.  
  476.     ld    a,c        ; Copy user/drive byte to A
  477.     and    b        ; Isolate drive
  478.     ld    h,a        ; ..and move to H
  479.     ld    a,c        ; Get full byte back
  480.     swap            ; Swap nibbles
  481.     and    b        ; Isolate user number
  482.     ld    l,a        ; ..and move to L
  483.     ld    (curusr),hl    ; ..and save them
  484.  
  485.      endif    ;highuser
  486.  
  487. ; This block of code is executed when submit processing is enabled.  We log
  488. ; into user area 0, where the submit file is kept, and we search the
  489. ; designated drive for the file.  The result is kept in SUBFLAG.  This code
  490. ; only has to be executed on reentry to the command processor at the main
  491. ; entry point.  Commands that do not reboot but simply return to the CPR will
  492. ; execute without the disk reset and file search required here.  Ron Fowler
  493. ; pointed out a shortcut based on the fact that after a disk reset, the A
  494. ; regiser contains a value of 0 if there is no file on drive A with a '$' in
  495. ; the file name and 0FFH if there is such a file.  Thus if A = 0, there can
  496. ; be no '$$$.SUB' file on drive A.  This trick is, unfortunately, not reliable
  497. ; under some versions of ZRDOS.  Therefore, an option has been included to
  498. ; use or not use this shortcut.
  499.  
  500.      if    subon        ; If submit facility enabled ..
  501.  
  502.     call    defltdma    ; Set DMA address to 80H
  503.     ld    a,0        ; Log into user area 0
  504.     call    setuser
  505.     ld    c,0dh        ; Reset disk system (returns 0FFH if a $$$.SUB
  506.     call    bdossave    ; ..file might exist in user 0)
  507.     ld    de,subfcb    ; Point to submit file FCB with explicit drive
  508.  
  509.      if    subclue
  510.     call    nz,srchfst    ; Search only if flag says it could exist
  511.      else    ;not subclue
  512.     call    srchfst        ; Search for the file unconditionally
  513.      endif    ;subclue
  514.  
  515.     ld    (subflag),a    ; Set flag for result (0 = no $$$.SUB)
  516.  
  517.      else    ;not subon
  518.  
  519.     ld    c,0dh        ; Reset disk system
  520.     call    bdossave
  521.  
  522.      endif    ; subon
  523.  
  524.     jr    nextcmd        ; Go to entry point for processing next command
  525.  
  526.  
  527. ;-----------------------------------------------------------------------------
  528.  
  529. ; NEW COMMAND LINE ENTRY POINT
  530.  
  531. ; This entry point is used when ZCPR33 finds the command line empty.  A call to
  532. ; READBUF gets the next command line from the following possible sources in
  533. ; this order:
  534. ;    1) a running ZEX script
  535. ;    2) the submit file $$$.SUB (if enabled)
  536. ;    3) the shell stack
  537. ;    4) the user
  538. ; If the line comes from the shell stack, then the shell bit in the command
  539. ; status flag is set.
  540.  
  541. restart:
  542.     ld    sp,stack    ; Reset stack
  543.     xor    a
  544.     ld    (cmdstatfl),a    ; Reset ZCPR3 command status flag
  545.     inc    a        ; Set ZEX message byte to 1 to
  546.     ld    (zexinpfl),a    ; ..indicate command prompt
  547.      if    subon
  548.     ld    (xsubflag),a    ; Ditto for XSUB flag
  549.      endif    ;subon
  550.     ld    hl,cmdlin    ; HL --> beginning of command line buffer
  551.     ld    (nxtchr),hl    ; Save as pointer to next character to process
  552.     ld    (hl),0        ; Zero out command line (in case of warm boot)
  553.     push    hl        ; Save pointer to command line
  554.     call    readbuf        ; Input command line (ZEX, submit, shell,
  555.                 ; ..or user)
  556.     pop    hl        ; Get back pointer to command line
  557.     ld    a,(hl)        ; Check for comment line
  558.     cp    comment        ; Begins with comment character?
  559.     jr    z,restart    ; If so, go back for another line
  560.                 ; Otherwise, fall through
  561.  
  562. ;-----------------------------------------------------------------------------
  563.  
  564. ; COMMAND CONTINUATION PROCESSING ENTRY POINT
  565.  
  566. ; This is the entry point for continuing the processing of an existing command
  567. ; line.  The current drive and user values as known to the CPR are combined
  568. ; and made into the user/drive byte that CP/M keeps at location 0004.  If the
  569. ; HIGHUSER option is enabled, the user number for this byte is forced to be
  570. ; in the range 0..15.  Next the command status flag is processed.  The error
  571. ; and ECP bits in the actual flag are reset, and the original flag is checked
  572. ; for an ECP error return (both ECP bit and error bit set).  In that case,
  573. ; control is transferred to the error handler.
  574.  
  575. nextcmd:
  576.     ld    hl,(curusr)    ; Get currently logged drive and user
  577.     ld    a,l        ; Work on user number
  578.      if     highuser
  579.     and    0fh        ; Keep value modulo 16
  580.      endif    ;highuser
  581.     swap            ; Get user into high nibble
  582.     or    h        ; ..and drive into low nibble
  583.     ld    (udflag),a    ; Set user/disk flag in page 0
  584.  
  585.     ld    a,2        ; Turn ZEX input redirection off
  586.     ld    (zexinpfl),a
  587.      if    subon
  588.     ld    (xsubflag),a    ; Turn off XSUB input redirection
  589.      endif    ;subon
  590.  
  591.     ld    hl,cmdstatfl    ; Point to the command status flag (CSF)
  592.     ld    a,(hl)        ; Get a copy into register A
  593.     res    1,(hl)        ; Reset the actual error bit
  594.     res    2,(hl)        ; Reset the actual ECP bit
  595.     and    110b        ; Select ECP and error bits in original flag
  596.     cp    110b        ; Test for an ECP error
  597.     jp    z,error        ; Process ECP error with error handler
  598.  
  599. nextcmd1:
  600.     ld    sp,stack    ; Reset stack
  601.     call    logcurrent    ; Return to default directory
  602.     ld    hl,(nxtchr)    ; Point to first character of next command
  603.     push    hl        ; Save pointer to next character to process
  604.  
  605. ; We have to capitalize the command line each time because an alias or other
  606. ; command line generator may have stuck some new text in.  The code is shorter
  607. ; if we simply capitalize the entire command rather than trying to capitalize
  608. ; only the one command we are about to execute.
  609.  
  610. capbuf:                ; Capitalize the command line
  611.     ld    a,(hl)        ; Get character
  612.     call    ucase        ; Convert to upper case
  613.     ld    (hl),a        ; Put it back
  614.     inc    hl        ; Point to next one
  615.     or    a        ; See if end of line (marked with null)
  616.     jr    nz,capbuf    ; If not, loop back
  617.  
  618.     pop    hl        ; Restore pointer to next character to process
  619.  
  620. nextcmd3:
  621.  
  622. ; ZCPR33 provides a convenience feature to make it easier to enter a leading
  623. ; colon to force the current directory to be scanned and to make the CPR skip
  624. ; resident commands.  If ALTCOLON is active, an alternate character can be
  625. ; entered as the first character of a command.  The default (and recommended)
  626. ; alternative character is the period (it could not have any other meaning
  627. ; here).  If FASTECP (see below) is not enabled or if ALTONLY is enabled,
  628. ; leading spaces on the command line are skipped before looking for the
  629. ; alternate character for the colon
  630.  
  631.      if    [ not fastecp ] or [ fastecp and altonly ]
  632.     call    sksp
  633.      endif    ;[ not fastecp ] or [ fastecp and altonly ]
  634.  
  635.      if    altcolon    ; If allowing alias character for leading colon
  636.                 ; Set B = ':' and C = alias character ('.')
  637.     ld    bc,':' shl 8 + altchar
  638.     ld    a,(hl)        ; Get first character in new command line
  639.     cp    c        ; If first character is ALTCHAR, treat as ':'
  640.     jr    nz,nextcmd3a    ; Branch if not '.'
  641.     ld    (hl),b        ; Else replace with colon
  642. nextcmd3a:
  643.      endif    ;altcolon
  644.  
  645.  
  646. ; ZCPR33 supports three new options that can speed up command processing. 
  647. ; FASTECP allows commands with a leading space to bypass the search for
  648. ; resident commands or transient commands (COM files) along the path and go
  649. ; directly to the extended command processor.  With SKIPPATH enabled, when
  650. ; a command is prefixed by an explicit directory specification (but not a
  651. ; lone colon), searching of the path and invocation of the ECP are disabled.
  652. ; If the command is not found in the specified directory, the error handler
  653. ; is invoked immediately.  Finally, if BADDUECP is enabled, when an attempt
  654. ; is made to log into an invalid directory, the command is sent directly to
  655. ; the ECP, which can provide special handling.  To implement these three
  656. ; features, the first actual character of the command line is saved as a
  657. ; flag in FIRSTCHAR.  My apologies for the complexity of these nested
  658. ; conditionals.
  659.  
  660.      if    fastecp or skippath or badduecp
  661.  
  662.         ; With FASTECP we store the first actual
  663.         ; ..character and then skip over spaces (unless ALTONLY is
  664.         ; ..enabled, in which case we skipped spaces above)
  665.  
  666.      if    fastecp
  667.  
  668.      if    altspace    ; If allowing alias character for leading space
  669.                 ; Set B = ' ' and C = alias character ('/')
  670.     ld    bc,' ' shl 8 + ecpchar
  671.     ld    a,(hl)        ; Get first character in new command line
  672.     cp    c        ; If first character is ECPCHAR treat as ' '
  673.     jr    nz,nextcmd3b    ; Branch if not '/' (alternate character)
  674.     ld    (hl),b        ; Else replace with space
  675. nextcmd3b:
  676.      endif    ;altspace
  677.  
  678.     ld    a,(hl)        ; Get first character in command line
  679.     ld    (firstchar),a    ; Save it in flag
  680.     call    sksp        ; Then skip leading spaces
  681.      endif    ;fastecp
  682.  
  683.         ; With SKIPPATH but not FASTECP we store the first
  684.         ; ..character of the command (spaces were skipped above)
  685.  
  686.      if    [ not fastecp ] and skippath
  687.     ld    (firstchar),a    ; Store first nonspace character
  688.      endif    ;[ not fastecp ] and skippath
  689.  
  690.         ; With only BADDUECP (and neither SKIPPATH nor FASTECP)
  691.         ; ..we store a null in the FIRSTCHAR flag
  692.  
  693.      if    [ not fastecp ] and [ not skippath ]
  694.     xor    a
  695.     ld    (firstchar),a
  696.      endif    ;[ not fastecp ] and [ not skippath ]
  697.  
  698.      endif    ;fastecp or skippath or badduecp
  699.  
  700. ; Resume processing of the command line
  701.  
  702.     or    a        ; Now at end of line?
  703.     jr    z,restart    ; If so, get a new command line
  704.     cp    ctrlc        ; Flush ^C to prevent error-handler
  705.     jr    z,restart    ; ..invocation on warm boots
  706.  
  707.     cp    cmdsep        ; Is it a command separator?
  708.     jr    nz,nextcmd4    ; If not, skip ahead to process the command
  709.     inc    hl        ; If it is, skip over it
  710.     jr    nextcmd3    ; ..and process next command
  711.  
  712. nextcmd4:
  713.  
  714. ; Unless we are now running the external error handler, the following code
  715. ; saves the address of the current command in Z3MSG+4 for use by programs
  716. ; to determine the command line with which they were invoked.
  717.  
  718.     ld    a,(cmdstatfl)    ; Get command status flag
  719.     bit    1,a        ; Test for error handler invocation
  720.     jr    nz,nextcmd5    ; If so, skip over next instruction
  721.     ld    (cmdptr),hl
  722.  
  723. nextcmd5:
  724.     call    parser        ; Parse entire command line, then look for
  725.                 ; ..the command
  726.  
  727.  
  728. ;=============================================================================
  729.  
  730. ;        C O M M A N D    S E A R C H    C O D E
  731.  
  732. ;=============================================================================
  733.  
  734. ; CODE FOR FINDING AND RUNNING THE COMMAND
  735.  
  736. ; Here is the code for running a command.  Commands are searched for and
  737. ; processed in the following order:
  738. ;
  739. ;    1) flow control package (FCP) commands and IF state testing
  740. ;    2) resident command package (RCP)
  741. ;    3) command processor (CPR)
  742. ;    4) transient (COM file or extended command processor)
  743. ;    5) external error handler
  744. ;    6) internal error message and processing
  745. ;
  746. ; Special notes:
  747. ;
  748. ;    a)    If the current command is a shell command, special handling of flow
  749. ;     control is required.  If SHELLIF is enabled so that flow commands are
  750. ;    allowed in shell alias scripts, then we reset the flow state to its
  751. ;    initial condition (none) with each shell invocation (and after each
  752. ;    command is run, we reset the shell bit in the code after CALLPROG).
  753. ;    In this case shells will run regardless of flow state, and residual
  754. ;    conditionals from the last running of the shell are flushed.  Each
  755. ;    shell input sequence begins afresh.  On the other hand, if SHELLIF is
  756. ;    off, flow control commands inside a shell script must be flushed so
  757. ;    that they do not interfere with user entered commands.
  758. ;    b)    Directory prefixes are ignored for flow commands, since all flow control
  759. ;    processing must pass through the FCP (the command must run even when
  760. ;    the current flow state is false).
  761. ;    c)    If the command is not found in the FCP, then the current flow state is
  762. ;    tested.  If it is false, the command is flushed and the code branches
  763. ;    back to get the next command.
  764. ;    d)    If the command had a directory prefix (a colon alone is sufficient),
  765. ;    then steps #2 and #3 are skipped over,and the command is processed
  766. ;    immediately as a transient program.
  767. ;    e)    In ZCPR33, unlike ZCPR30, RCP commands are scanned before CPR commands.
  768. ;    This has been done so that more powerful RCP commands can supercede
  769. ;    CPR commands.
  770. ;    f)    If the SKIPPATH option is enabled, when an explicit directory is
  771. ;    specified with a command (but not just a colon), searching of the path
  772. ;    is bypassed.  If the FASTECP option is enabled, commands with leading
  773. ;    spaces are sent directly to the ECP for processing.
  774. ;    g)    If no external command can be found, ZCPR33 performs extensive error
  775. ;    handling.  If the command error occurred while looking for a shell
  776. ;    program, then the shell stack is popped.  Otherwise, ZCPR33 tries to
  777. ;    invoke an external, user-specified error handling command line.  If
  778. ;    none was specified or if the error handler invoked by that command
  779. ;    line cannot be found, the internal error message (step #6) is displayed.
  780.  
  781.  
  782. ;-----------------------------------------------------------------------------
  783.  
  784. runcmd:
  785.      if    shellif        ; If shells reininitialize flow control...
  786.     ld    a,(cmdstatfl)    ; Get command status flag
  787.     bit    0,a        ; Shell bit set?
  788.     jr    z,fcpcmd    ; If not a shell, process command
  789.     xor    a        ; Otherwise, shell is running, so
  790.     ld    (ifptrfl),a    ; ..reinitialize the IF system and continue
  791.      endif    ;shellif
  792.  
  793.  
  794. ; ---------- Module <<1>>: Flow Control Processing
  795.  
  796. ; An option is supported here to allow the address of the FCP to be obtained
  797. ; from the environment descriptor.  This is logically consistent with the
  798. ; pholosopy of the Z-System and is useful when one wants to have a single block
  799. ; of FCP/RCP memory that can be allocated dynamically between FCP and RCP
  800. ; functions.
  801.  
  802. fcpcmd:
  803.  
  804.      if    fcp ne 0    ; Omit code if FCP not implemented
  805.  
  806.      if    fcpenv        ; If getting FCP address from Z3ENV
  807.  
  808.     ld    e,12h        ; Offset in Z3ENV to FCP address
  809.     call    pkgoff        ; Set HL to FCP+5
  810.     jr    z,runcmd1    ; Skip if no FCP present
  811.  
  812.      else    ; using fixed FCP address
  813.  
  814.     ld    hl,fcp+5    ; Get address from Z3BASE.LIB
  815.  
  816.      endif    ;fcpenv
  817.  
  818.  
  819. ; If flow control processing is not allowed in shell aliases (scripts running
  820. ; as shell commands), then we have to make sure that we flush any flow control
  821. ; commmands, otherwise the CPR will attempt to execute them as transients,
  822. ; with dire consequences.  In the code below we check the shell bit.  If it
  823. ; is not set, we proceed normally.  If it is set, we scan for flow commands
  824. ; and then jump past the flow testing to RUNFCP2, where the code will flush
  825. ; the command if it was a flow command and execute it unconditionally if not.
  826.  
  827.      if    not shellif
  828.     ld    a,(cmdstatfl)    ; Get command status flag
  829.     bit    0,a        ; If shell bit not set,
  830.     jr    z,runfcp1    ; ..we do normal processing
  831.     call    cmdscan        ; Otherwise, check for flow command
  832.     jr    runfcp2        ; ..and flush if so using code below
  833.      endif    ;not shellif
  834.  
  835. runfcp1:
  836.     call    cmdscan        ; Scan command table in the module
  837.     jr    z,callprog    ; Run if found (with no leading CRLF)
  838.  
  839. ; This is where we test the current IF state.  If it is false, we skip this
  840. ; command.
  841.  
  842.     call    iftest        ; Check current IF status
  843.  
  844. runfcp2:            ; If false, skip this command and go on to next
  845.      if    drvprefix    ; If DRVPREFIX we can use code below
  846.     jr    z,jpnextcmd    ; ..to save a byte
  847.      else            ; Otherwise, we have to do an
  848.     jp    z,nextcmd    ; ..absolute jump
  849.      endif    ;drvprefix
  850.  
  851.      endif    ;fcp ne 0
  852.  
  853.  
  854. runcmd1:
  855.      if    fastecp or badduecp
  856.     ld    a,(firstchar)    ; If FIRSTCHAR flag set for ECP invocation,
  857.     cp    ' '        ; ..then go straight to transient processing
  858.     jr    z,com
  859.      endif    ;fastecp or badduecp
  860.  
  861. colon    equ    $+1        ; Flag for in-the-code modification
  862.     ld    a,0        ; If command had a directory prefix (even just
  863.     or    a        ; ..a colon) then skip over resident commands
  864.     jr    nz,comdir
  865.  
  866.  
  867. ; ---------- Module <<2>>: RCP Processing
  868.  
  869. ; An option is supported here to allow the address of the RCP to be obtained
  870. ; from the environment descriptor.  This is logically consistent with the
  871. ; pholosopy of the Z-System and is useful when one wants to have a single block
  872. ; of FCP/RCP memory that can be allocated dynamically between FCP and RCP
  873. ; functions.
  874.  
  875.      if    rcp ne 0    ; Omit code if RCP not implemented
  876.  
  877. rcpcmd:
  878.  
  879.      if    rcpenv        ; If getting address of rcp from Z3ENV
  880.  
  881.     ld    e,0ch        ; Offset in Z3ENV to RCP address
  882.     call    pkgoff        ; Set HL to address of RCP+5
  883.     jr    z,cprcmd    ; Skip if no RCP
  884.  
  885.      else    ; using fixed RCP address
  886.  
  887.     ld    hl,rcp+5    ; Get address from Z3BASE.LIB
  888.  
  889.      endif    ; rcpenv
  890.  
  891.     call    cmdscan        ; Check for command in RCP
  892.     jr    z,callproglf    ; If so, run it (with leading CRLF)
  893.  
  894.      endif    ;rcp ne 0
  895.  
  896.  
  897. ; ---------- Module <<3>>: CPR-Resident Command Processing
  898.  
  899. cprcmd:
  900.  
  901.     ld    hl,cmdtbl    ; Point to CPR-resident command table
  902.     call    cmdscan        ; ..and scan for the command
  903.     jr    z,callprog    ; If found, run it (with no leading CRLF)
  904.  
  905.  
  906.  
  907. ; ---------- Module <<4>>: Transient Command Processing
  908.  
  909. comdir:                ; Test for DU: or DIR: only (directory change)
  910.  
  911.      if    drvprefix
  912.  
  913.     ld    a,(cmdfcb+1)    ; Any command name?
  914.     cp    ' '
  915.     jr    nz,com        ; If so, must be transient or error
  916.  
  917.         ; Entry point for change of directory only
  918.  
  919.      if    wdu        ; If controlled by wheel..
  920.  
  921.     ld    a,(z3whl)    ; Get wheel byte
  922.     or    a        ; If wheel on, go on ahead
  923.     jr    nz,comdir1
  924.  
  925.      if    badduecp
  926.     ld    (colon),a    ; Pretend there is no colon
  927.     ld    a,' '        ; Force invocation of ECP
  928.     ld    (firstchar),a
  929.     jr    com
  930.      else    ;not badduecp
  931.     ld    a,ecduchg
  932.     jr    z,error
  933.      endif    ;badduecp
  934.  
  935.      endif    ; wdu
  936.  
  937. comdir1:
  938.     ld    hl,(tempusr)    ; Get temporary drive and user bytes
  939.  
  940.      if    not highuser    ; If only users 0..15 can be logged
  941.     ld    a,l        ; Get user number and
  942.     cp    16        ; ..make sure not above 15
  943.     jr    nc,baddirerr    ; If out of range, invoke error handling
  944.      endif    ;not highuser
  945.  
  946.     dec    h        ; Shift drive to range 0..15
  947.     ld    (curusr),hl    ; Make the temporary DU into the current DU
  948.     call    logcurrent    ; Log into the new current directory
  949. jpnextcmd:
  950.     jp    nextcmd        ; Resume command line processing
  951.  
  952.      else    ;not drvprefix
  953.  
  954.      if    badduecp
  955.     xor    a        ; Pretend there is no colon
  956.     ld    (colon),a
  957.     ld    a,' '        ; Force invocation of ECP
  958.     ld    (firstchar),a
  959.      else    ;not badduecp
  960.     ld    a,ecduchg
  961.     jr    z,error
  962.      endif    ;badduecp
  963.  
  964.      endif    ;drvprefix
  965.  
  966.  
  967. com:                ; Process transient command
  968.  
  969.     ld    a,(cmdstatfl)    ; Check command status flag to see if
  970.     and    2        ; ..error handler is running
  971.     ld    (zexinpfl),a    ; Store result in ZEX control flag (2 will turn
  972.                 ; ..ZEX input redirection off (0 = on)
  973.      if    subon
  974.     ld    (xsubflag),a    ; Turn off XSUB input redirection also
  975.      endif    ;subon
  976.  
  977.     ld    hl,tpa        ; Set default execution/load address
  978.     ld    a,3        ; Dynamically load type-3 and above ENVs
  979.     call    mload        ; Load memory with file specified in cmd line
  980.     ld    a,(cmdstatfl)    ; Check command status flag to see if
  981.     and    100b        ; ..ECP running (and suppress leading CRLF)
  982.  
  983. ; CALLPROG is the entry point for the execution of the loaded program.  At
  984. ; alternate entry point CALLPROGLF if the zero flag is set, a CRLF is sent to
  985. ; the console before running the program.
  986.  
  987. callproglf:
  988.     call    z,crlf        ; Leading new line
  989.  
  990. callprog:
  991.         ; Copy command tail into TBUFF
  992.  
  993. tailsv    equ    $+1        ; Pointer for in-the-code modification
  994.     ld    hl,0        ; Address of first character of command tail
  995.     ld    de,tbuff    ; Point to TBUFF
  996.     push    de        ; Save pointer
  997.     ld    bc,7e00h    ; C=0 (byte counter) and B=7E (max bytes)
  998.     inc    de        ; Point to first char
  999. tail:
  1000.     ld    a,(hl)        ; Get character from tail
  1001.     call    tsteol        ; Check for EOL
  1002.     jr    z,tail1        ; Jump if we are done
  1003.     ld    (de),a        ; Put character into TBUFF
  1004.     inc    hl        ; Advance pointers
  1005.     inc    de
  1006.     inc    c        ; Increment character count
  1007.     djnz    tail        ; If room for more characters, continue
  1008.     call    print        ; Display overflow message
  1009.     db    bell        ; ..ring bell
  1010.     db    'Ovf','l'+80h    ; ..then continue anyway
  1011. tail1:
  1012.     xor    a        ; Store ending zero
  1013.     ld    (de),a
  1014.     pop    hl        ; Get back pointer to character count byte
  1015.     ld    (hl),c        ; Store the count
  1016.  
  1017. ; Run loaded transient program
  1018.  
  1019.     call    defltdma    ; Set DMA to 0080h standard value
  1020.  
  1021. ; Perform automatic installation of Z3 programs (unless type-2 environment)
  1022.  
  1023.     ld    hl,(execadr)    ; Get current execution address
  1024.     call    z3chk        ; See if file is a Z3 program
  1025.     jr    nz,noinstall    ; Branch if not
  1026.  
  1027.     cp    2        ; If type-2 (internal) environment
  1028.     jr    z,noinstall    ; ..do not perform installation
  1029.  
  1030.     inc    hl        ; Advance to place for ENV address
  1031.     ld    (hl),low z3env    ; Put in low byte of environment address
  1032.     inc    hl
  1033.     ld    (hl),high z3env    ; Put in high byte
  1034.  
  1035. noinstall:
  1036.  
  1037. ; Execution of the program occurs here by calling it as a subroutine
  1038.  
  1039.     ld    hl,z3env    ; Pass environment address to program in HL
  1040. execadr    equ    $+1        ; Pointer for in-line code modification
  1041.     call    0        ; Call transient
  1042.  
  1043. ; Return from execution
  1044.  
  1045.      if    shellif        ; If flow processing allowed in shells...
  1046.     ld    hl,cmdstatfl    ; Reset the shell bit in the command status
  1047.     res    0,(hl)        ; ..flag so multiple-command shells will work
  1048.      endif    ;shellif
  1049.  
  1050.                 ; Continue command processing
  1051.      if    drvprefix    ; If DRVPREFIX we can save a byte by
  1052.     jr    jpnextcmd    ; ..doing a two-step relative jump
  1053.      else            ; Otherwise, we just have to do
  1054.     jp    nextcmd        ; ..the absolute jump
  1055.      endif    ;drvprefix
  1056.  
  1057.  
  1058. ; ---------- Module <<5>>: External Error Handler Processing
  1059.  
  1060. baddirerr:
  1061.     ld    a,ecbaddir    ; Error code for bad directory specification
  1062.  
  1063. error:
  1064.  
  1065. ; If we are returning from an external command to process an error, we want
  1066. ; to leave the error return code as it was set by the transient program.
  1067.  
  1068.     ld    hl,cmdstatfl    ; Point to command status flag
  1069.     bit    3,(hl)        ; Check transient error flag bit
  1070.     jr    nz,error1    ; If set, leave error code as set externally
  1071.     ld    (ecflag),a    ; Otherwise, save error code from A register
  1072.  
  1073. error1:
  1074.     res    2,(hl)        ; Reset the ECP bit to prevent recursion of
  1075.                 ; ..error handler by programs that don't
  1076.                 ; ..clear the bit
  1077.     bit    0,(hl)        ; Was error in attempting to run a shell?
  1078.     jr    nz,errsh    ; If so, pop shell stack
  1079.  
  1080. ; The following code is included to avoid a catastrophic infinite loop when
  1081. ; the external error handler cannot be found.  After one unsuccessful try,
  1082. ; the internal code is invoked.
  1083.  
  1084.     bit    1,(hl)        ; Was an error handler already called?
  1085.     jr    nz,errintrnl    ; If so, use internal error handler
  1086.  
  1087. ; If the current IF state is false, we would like to ignore the error and just
  1088. ; go on with the next command.  Unfortunately, for some errors (e.g., a bad
  1089. ; command format such as a command with a wildcard character) the error handler
  1090. ; is invoked before the pointer in the multiple command line buffer is set up
  1091. ; to the next command.  In that case, we fall into an infinite loop.  We also
  1092. ; must not allow the external error handler to run, since it will not run and
  1093. ; we will again fall into an infinite loop.  The present code is not so bad, of
  1094. ; course, since even a command in a false part of a command sequence should not
  1095. ; have a true error in it.  We have already put in code to bypass password
  1096. ; checking during a false IF state, since a command with a password is not an
  1097. ; invalid command.
  1098.  
  1099.      if    fcp ne 0
  1100.     call    iftest        ; If we are in a false IF state, external
  1101.     jr    z,errintrnl    ; ..handler will not run, so use built-in
  1102.      endif    ;fcp ne 0
  1103.  
  1104.     set    1,(hl)        ; Set command status flag for error invocation
  1105.     ld    hl,errcmd    ; Point to error handler command line
  1106.     ld    a,(hl)        ; Check first byte for presence of an
  1107.     or    a        ; ..error command line
  1108.     jr    z,errintrnl    ; If no error handler, use built-in one
  1109.     ld    (nxtchr),hl    ; Else, use error command line as next command
  1110.     jp    nextcmd1    ; Run command without resetting status flag
  1111.  
  1112.  
  1113. ; ---------- Module <<6>>: Resident Error Handler Code
  1114.  
  1115. ; If the error is with the invocation of a shell command, we pop the bad shell
  1116. ; command off the stack to prevent recursion of the error.  We then use the
  1117. ; the internal error handler to echo the bad shell command.
  1118.  
  1119. errsh:
  1120.  
  1121.     ld    de,shstk    ; Point to current entry in shell stack
  1122.     ld    hl,shstk+shsize    ; Point to next entry in stack
  1123.     ld    bc,[shstks-1]*shsize    ; Bytes to move
  1124.     ldir            ; Pop the stack
  1125.     xor    a        ; Clear the last entry position
  1126.     ld    (de),a
  1127.  
  1128. errintrnl:
  1129.      if    subon
  1130.     call    subkil        ; Terminate active submit file if any
  1131.      endif    ;subon
  1132.  
  1133.     call    crlf        ; New line
  1134.     ld    hl,(cmdptr)    ; Point to beginning of bad command
  1135.     call    printhl        ; Echo it to console
  1136.     call    print        ; Print '?'
  1137.     defb    '?'+80h
  1138.     jp    restart        ; Restart CPR
  1139.  
  1140. ; End ZCPR33-2.Z80
  1141.  
  1142.     page
  1143.  
  1144. ; ZCPR33-3.Z80
  1145.  
  1146. ;=============================================================================
  1147. ;
  1148. ;       C O M M A N D    L I N E     P A R S I N G    C O D E
  1149. ;
  1150. ;=============================================================================
  1151.  
  1152. ; This code parses the command line pointed to by HL.  The command verb is
  1153. ; parsed, placing the requested program name into the command file control
  1154. ; block.  The drive and user bytes are set.  If an explicit DU or DIR was
  1155. ; given, the COLON flag is set so that the processor knows about this later
  1156. ; when the command search path is built.
  1157.  
  1158. parser:
  1159.  
  1160.     ld    de,cmdfcb    ; Point to the command FCB
  1161.     push    de
  1162.     call    initfcb        ; Initialize the FCB
  1163.     pop    de
  1164.     ld    (duerrflag),a    ; Store zero (INITFCB ends with A=0) into flag
  1165.     call    scanner        ; Parse first token on command line into FCB
  1166.     jr    nz,badcmd    ; Invoke error handler if '?' in command
  1167.  
  1168. duerrflag equ    $+1        ; Pointer for in-the-code modification
  1169.     ld    a,0        ; See if bad DU/DIR specified with command verb
  1170.     or    a
  1171.  
  1172.      if    badduecp
  1173.     jr    z,parser1    ; If DU/DIR is OK, skip ahead
  1174.     ld    a,(cmdstatfl)    ; If ECP already running
  1175.     bit    2,a        ; ..skip ahead
  1176.     jr    nz,parser1
  1177.     ld    a,(cmdfcb+1)    ; If not a directory change command
  1178.     sub    ' '        ; ..invoke error handler
  1179.     jr    nz,baddirerr
  1180.                 ; If bad directory change attempt,
  1181.     ld    (tmpcolon),a    ; ..pretend there is no colon (A=0)
  1182.     ld    a,' '        ; ..and force immediate ECP invocation
  1183.     ld    (firstchar),a    ; ..when command is processed
  1184.      else            ; If errors not processed by ECP then
  1185.     jr    nz,baddirerr    ; ..invoke error handler
  1186.      endif    ; badduecp
  1187.  
  1188. parser1:
  1189.     ld    de,cmdfcb+9    ; Make sure no explicit file type was given
  1190.     ld    a,(de)        ; Get first character of file type
  1191.     cp    ' '        ; Must be blank
  1192. badcmd:
  1193.     ld    a,ecbadcmd    ; Error code for illegal command form
  1194.     jr    nz,error    ; If not, invoke error handler
  1195.  
  1196.     push    hl        ; Save pointer to next byte of command
  1197.     ld    hl,commsg    ; Place default file type (COM) into FCB
  1198.     ld    bc,3
  1199.     ldir
  1200.     pop    hl        ; Get command line pointer back
  1201.  
  1202. ; The following block of code is arranged so that the COLON flag is set only
  1203. ; when an explicit directory specification is detected in the command verb.
  1204. ; Other parses also change the TMPCOLON flag, but only when passing here does
  1205. ; the flag get transferred to COLON.
  1206.  
  1207. tmpcolon equ    $+1        ; Pointer for in-the-code modification
  1208.     ld    a,0        ; ..by SCANNER routine
  1209.     ld    (colon),a    ; If explicit DU/DIR, set COLON flag
  1210.  
  1211. ; Find the end of this command and set up the pointer to the next command.
  1212.  
  1213.     push    hl        ; Save command line pointer
  1214.     dec    hl        ; Adjust for preincrementing below
  1215. parser2:            ; Find end of this command
  1216.     inc    hl        ; Point to next character
  1217.     ld    a,(hl)        ; ..and get it
  1218.     call    tsteol        ; Test for end of command
  1219.     jr    nz,parser2    ; Keep looping if not
  1220.  
  1221.     ld    (nxtchr),hl    ; Set pointer to next command
  1222.     pop    hl        ; Get back pointer to current command tail
  1223.  
  1224. ; This block of code parses two tokens in the command line into the two
  1225. ; default FCBs at 5Ch and 6Ch.  It also sets a pointer to the command tail
  1226. ; for later copying into the command tail buffer at 80h.  This code is used
  1227. ; first when attempting to parse a normal command line and possibly again
  1228. ; later when the entire user's command is treated as a tail to the extended
  1229. ; command processor.  The resident JUMP and SAVE commands use it also, and
  1230. ; the entry point is available at location CCP+9 for use by other programs.
  1231.  
  1232. parsetail:
  1233.     ld    (tailsv),hl    ; Save pointer to command tail
  1234.  
  1235.                 ; Process first token
  1236.  
  1237.     ld    de,tfcb        ; Point to first default FCB
  1238.     push    de        ; Save pointer while initializing
  1239.     call    initfcb        ; Initialize both default FCBs
  1240.     pop    de
  1241.     call    sksp        ; Skip over spaces in command line
  1242.     call    nz,scanner    ; If not end of line, parse the token
  1243.                 ; ..into first FCB
  1244.     ld    (parseptr),hl    ; Save pointer to second token for reparsing
  1245.  
  1246.                 ; Process second token
  1247.  
  1248.     call    sksp        ; Skip over spaces
  1249.     ret    z        ; Done if end of line or end of command
  1250.     ld    de,tfcb2    ; Point to second default FCB
  1251.                 ; ..and fall through to SCANNER routine
  1252.  
  1253. ;-----------------------------------------------------------------------------
  1254.  
  1255. ; This routine processes a command line token pointed to by HL.  It attempts
  1256. ; to interpret the token according to the form [DU:|DIR:]NAME.TYP and places
  1257. ; the corresponding values into the FCB pointed to by DE.  On exit, HL points
  1258. ; to the delimiter encountered at the end of the token.  The Z flag is set if
  1259. ; a wild card was detected in the token.
  1260.  
  1261. scanner:
  1262.     xor    a        ; Initialize various flags
  1263.     ld    (tmpcolon),a    ; Set no colon
  1264.     ld    bc,(curusr)    ; Get current drive and user into BC
  1265.     inc    b        ; Shift drive range from 0..15 to 1..16
  1266.     ld    (tempusr),bc    ; Initialize temporary DU
  1267.  
  1268.     call    scanfld8    ; Extract possible file name
  1269.     cp    ':'        ; Was terminating character a colon?
  1270.     jr    nz,scantype    ; If not, go on to extract file type
  1271.     ld    (tmpcolon),a    ; Otherwise, set colon and process DU/DIR
  1272.     inc    hl        ; Point to character after colon
  1273.  
  1274. ; Code for resolving directory specifications (macro RESOLVE is defined in
  1275. ; Z33MAC.LIB).  RESOLVE returns with a nonzero value and a NZ flag setting
  1276. ; if the DU/DIR specification cannot be resolved.  There are quite a few
  1277. ; possibilities here.
  1278.  
  1279.         ; Case where both forms are accepted
  1280.  
  1281.      if    accptdir and accptdu
  1282.      if    dufirst
  1283.     resolve    du,dir        ; Check DU: form before DIR: form
  1284.      else
  1285.     resolve    dir,du        ; Check DIR: form before DU: form
  1286.      endif    ;dufirst
  1287.      endif    ;accptdir and accptdu
  1288.  
  1289.         ; Cases of only one form accepted
  1290.  
  1291.      if    accptdu and not accptdir
  1292.     resolve    du,        ; Check only DU: form
  1293.      endif    ;accptdu and not accptdir
  1294.  
  1295.      if    accptdir and not accptdu
  1296.     resolve dir,        ; Check only DIR: form
  1297.      endif    ;accptdir and not accptdu
  1298.  
  1299.         ; Case of neither form accepted
  1300.  
  1301.      if    not accptdir and not accptdu
  1302.     push    hl        ; Save pointer to command string
  1303.     inc    de        ; Point to first character of name
  1304.     ld    a,(de)        ; Get it
  1305.     dec    de        ; Restore the pointer
  1306.     sub    ' '        ; If no name is there, A=0 and Z flag set
  1307.      endif    ;not accptdir and not accptdu
  1308.     
  1309.     push    de        ; Save pointer to FCB again
  1310.     push    af        ; Save bad directory flag
  1311.     ld    a,(tempdr)    ; Set designated drive
  1312.     ld    (de),a        ; ..into FCB
  1313.     inc    de        ; Point to file name field
  1314.     call    ifcb        ; Perform partial init (set user code)
  1315.     pop    af        ; Get bad directory flag back
  1316.     ld    (duerrflag),a    ; Save flag in parser code
  1317.     jr    z,scanner1    ; Branch if valid directory specified
  1318.     dec    de        ; Back up to record count byte
  1319.     dec    de
  1320.     ld    (de),a        ; Store error flag there (NZ if error)
  1321. scanner1:
  1322.     pop    de        ; Get FCB pointer back
  1323.     pop    hl        ; Restore pointer to command string
  1324.     call    scanfld8    ; Scan for file name
  1325.  
  1326. ; This code processes the file type specification in the token
  1327.  
  1328. scantype:
  1329.     ld    a,(hl)        ; Get ending character of file name field
  1330.     ex    de,hl        ; Switch FCB pointer into HL
  1331.     ld    bc,8        ; Offset to file type field
  1332.     add    hl,bc
  1333.     ex    de,hl        ; Switch pointers back
  1334.  
  1335.     ld    b,3        ; Maximum characters in file type
  1336.     cp    '.'        ; See if file type specified
  1337.     jr    nz,scantype2    ; If not, skip over file type parsing
  1338.  
  1339.     inc    hl        ; Point to character after '.'
  1340.     push    de        ; Save pointer to FCB file type
  1341.     call    scanfield    ; Parse file type into FCB
  1342.     pop    de
  1343.  
  1344. scantype2:
  1345.     ex    de,hl        ; Swap pointers again
  1346.     ld    bc,5        ; Offset from file type to S1 field in FCB
  1347.     add    hl,bc
  1348.     ex    de,hl        ; Swap pointers back
  1349.     ld    a,(tempusr)    ; Get specified user number
  1350.     ld    (de),a        ; ..and store in S1 byte of FCB
  1351.  
  1352. scan3:                ; Skip to space character, character after an
  1353.                 ; ..equal sign, or to end of command
  1354.     ld    a,(hl)        ; Get next character
  1355.     cp    ' '+1        ; Done if less than space
  1356.     jr    c,scan4
  1357.     call    tsteol        ; Done if end of line or end of command
  1358.     jr    z,scan4
  1359.     inc    hl        ; Skip on to next character
  1360.     cp    '='        ; If not equal sign
  1361.     jr    nz,scan3    ; ..keep scanning
  1362.  
  1363. scan4:                ; Set zero flag if '?' in filename.typ
  1364.  
  1365. qmcnt    equ    $+1        ; Pointer for in-the-code modification
  1366.     ld    a,0        ; Number of question marks
  1367.     or    a        ; Set zero flag
  1368.     ret
  1369.  
  1370. ; This routine invokes SCANFIELD for a file name field.  It initializes the
  1371. ; question mark count and preserves the FCB pointer.
  1372.  
  1373. scanfld8:
  1374.     xor    a        ; Initialize question mark count
  1375.     ld    (qmcnt),a
  1376.     push    de        ; Save pointer to FCB
  1377.     ld    b,8        ; Scan up to 8 characters
  1378.     call    scanfield
  1379.     pop    de        ; Restore pointer to FCB
  1380.     ret
  1381.  
  1382. ; This routine scans a command-line token pointed to by HL for a field whose
  1383. ; maximum length is given by the contents of the B register.  The result is
  1384. ; placed into the FCB buffer pointed to by DE.  The FCB must have had its name
  1385. ; and type fields initialized before this routine is called.  Wild cards of
  1386. ; '?' and '*' are expanded.  On exit, HL points to the terminating delimiter.
  1387.  
  1388. scanfield:
  1389.     call    sdelm        ; Done if delimiter encountered
  1390.     ret    z
  1391.     inc    de        ; Point to next byte in FCB
  1392.     cp    '*'        ; Is character a wild card?
  1393.     jr    nz,scanfld1    ; Continue if not
  1394.  
  1395.     ld    a,'?'        ; Process '*' by filling with '?'s
  1396.     ld    (de),a
  1397.     call    qcountinc    ; Increment count of question marks
  1398.     jr    scanfld2    ; Skip so HL pointer left on '*'
  1399.  
  1400. scanfld1:            ; Not wildcard character '*'
  1401.     ld    (de),a        ; Store character in FCB
  1402.     inc    hl        ; Point to next character in command line
  1403.     cp    '?'        ; Check for question mark (wild)
  1404.     call    z,qcountinc    ; Increment question mark count
  1405. scanfld2:
  1406.     djnz    scanfield    ; Decrement char count until limit reached
  1407. scanfld3:
  1408.     call    sdelm        ; Skip until delimiter
  1409.     ret    z        ; Zero flag set if delimiter found
  1410.     inc    hl        ; Pt to next char in command line
  1411.     jr    scanfld3
  1412.  
  1413.  
  1414. ; Subroutine to increment the count of question mark characters in the
  1415. ; parsed file name.
  1416.  
  1417. qcountinc:
  1418.     push    hl
  1419.     ld    hl,qmcnt    ; Point to count
  1420.     inc    (hl)        ; Increment it
  1421.     pop    hl
  1422.     ret
  1423.  
  1424. ;-----------------------------------------------------------------------------
  1425.  
  1426. ; Validate the password pointed to by HL.  Prompt user for password entry
  1427. ; and return zero if it is correct.
  1428.  
  1429.      if    pwcheck
  1430.  
  1431. passck:
  1432.     push    hl        ; Save pointer to password
  1433.     call    printC        ; Prompt user
  1434.     defb    'PW?',' '+80h
  1435.     ld    hl,pwlin    ; Set up buffer for user input
  1436.     ld    bc,90ah        ; Set 0ah (BDOS readln function) in C
  1437.     ld    (hl),b        ; ..and 9 (max character count) in B
  1438.     ex    de,hl        ; Switch buffer pointer to DE
  1439.  
  1440.      if    pwnoecho
  1441.     ld    a,0c9h        ; Disable BIOS conout routine to
  1442.     ld    (bios+0ch),a    ; ..suppress password echoing
  1443.     call    bdossave    ; Get user input
  1444.     ld    a,0c3h        ; Reenable BIOS conout routine
  1445.     ld    (bios+0ch),a
  1446.      else    ;not pwnoecho
  1447.     call    bdossave    ; Get user input
  1448.      endif    ;pwnoecho
  1449.  
  1450.     ex    de,hl        ; Restore pointer to HL
  1451.     inc    hl        ; Point to count of characters entered
  1452.     ld    a,(hl)        ; Get character count
  1453.     inc    hl        ; Point to first character
  1454.     push    hl        ; Save pointer while marking end of input
  1455.     call    addah        ; Advance HL to just past last character
  1456.     ld    (hl),' '    ; Place space there
  1457.     pop    de        ; Restore pointer to beginning of user input
  1458.     pop    hl        ; Restore pointer to password from NDR
  1459.     ld    b,8        ; Maximum characters to compare
  1460. pwck:
  1461.     ld    a,(de)        ; Get next user character
  1462.     call    ucase        ; Capitalize it
  1463.     cp    (hl)        ; Compare to NDR
  1464.     ret    nz        ; No match
  1465.     cp    ' '        ; If last user character matched space in
  1466.     ret    z        ; ..NDR, then we have a complete match
  1467.     inc    hl        ; If not done, point to next characters
  1468.     inc    de
  1469.     djnz    pwck        ; (flags not affected by DJNZ)
  1470.     xor    a        ; Set zero flag and
  1471.     ret            ; ..return Z to show success
  1472.  
  1473.      endif    ; pwcheck
  1474.  
  1475. ;-----------------------------------------------------------------------------
  1476.  
  1477. ; This code attempts to interpret the token in the FCB pointed to by register
  1478. ; pair DE as a DIR (named directory) prefix.  If it is successful, the drive
  1479. ; and user values are stored in TEMPDR and TEMPUSR, the zero flag is set, and
  1480. ; a value of zero is returned in register A.
  1481. ;
  1482. ; If the named directory is found to be password restricted, then the user is
  1483. ; asked for the password (unless the directory is the one currently logged or
  1484. ; the current IF state is false).  If an incorrect password is entered, the
  1485. ; error handler is generally invoked directly.  The exception to this is when
  1486. ; the transient program bit is set in the command status flag (this bit would
  1487. ; be set by a non-CPR program that calls REPARSE).  In this case the default
  1488. ; directory is returned, the zero flag is reset, and a nonzero value in
  1489. ; returned in register A to show a bad directory.  In addition, the code in
  1490. ; SCANNER will set record-count byte in the FCB to a nonzero value so that
  1491. ; the calling program can detect the error.  [Note: if DU processing is also
  1492. ; allowed and it follows DIR processing, DUSCAN will also be called.  Unless
  1493. ; there is a passworded directory with a DU form, this will cause no trouble.]
  1494.  
  1495.      if    accptdir
  1496.  
  1497. dirscan:
  1498.  
  1499. ; If the DU form is not allowed, we have to detect a colon-only condition here.
  1500. ; Otherwise DUSCAN will take care of it.
  1501.  
  1502.     inc    de        ; Point to first byte of directory form
  1503.  
  1504.      if    not accptdu
  1505.     ld    a,(de)        ; Get first character of directory
  1506.     sub    ' '        ; If it is a blank space
  1507.     ret    z        ; ..we have a successful directory resolution
  1508.      endif    ;not accptdu
  1509.  
  1510.     ex    de,hl        ; Switch pointer to FCB to HL
  1511.  
  1512.      if    ndrenv        ; If getting NDR address for Z3ENV
  1513.     ld    e,15h        ; Offset to NDR address
  1514.     push    hl        ; Preserve pointer to FCB
  1515.     call    pkgoff        ; Get NDR address from ENV into DE
  1516.     pop    hl
  1517.     jr    z,direrr    ; Branch if no NDR implemented
  1518.      else    ; using fixed address of NDR buffer
  1519.     ld    de,z3ndir    ; Point to first entry in NDR
  1520.      endif    ; ndrenv
  1521.  
  1522. dirscan1:
  1523.     ld    a,(de)        ; Get next character
  1524.     or    a        ; Zero if end of NDR
  1525.     jr    z,direrr
  1526.     inc    de        ; Point to name of directory
  1527.     inc    de
  1528.     push    hl        ; Save pointer to name we are looking for
  1529.     push    de        ; Save pointer to NDR entry
  1530.     ld    b,8        ; Number of characters to compare
  1531.  
  1532. dirscan2:
  1533.     ld    a,(de)
  1534.     cp    (hl)
  1535.     jr    nz,dirscan3    ; If no match, quit and go on to next DIR
  1536.     inc    hl        ; Point to next characters to compare
  1537.     inc    de
  1538.     djnz    dirscan2    ; Count down
  1539.  
  1540. dirscan3:
  1541.     pop    de        ; Restore pointers
  1542.     pop    hl
  1543.     jr    z,dirscan4    ; Branch if we have good match
  1544.  
  1545.     ex    de,hl        ; Advance to next entry in NDR
  1546.     ld    bc,16        ; 8 bytes for name + 8 bytes for password
  1547.     add    hl,bc
  1548.     ex    de,hl
  1549.     jr    dirscan1    ; Continue comparing
  1550.  
  1551. ; If ACCPTDU is enabled, we can share similar code in DUSCAN and do not need
  1552. ; the code here.
  1553.  
  1554.      if    not accptdu
  1555. direrr:                ; No match found
  1556.     dec    a
  1557.     ret
  1558.      endif    ;not accptdu
  1559.  
  1560. dirscan4:            ; Match found
  1561.     ex    de,hl        ; Switch pointer to NDR entry into HL
  1562.     push    hl        ; ..and save it for later
  1563.     dec    hl        ; Point to user corresponding to the DIR
  1564.     ld    c,(hl)        ; Get user value into C
  1565.     dec    hl        ; Point to drive
  1566.     ld    b,(hl)        ; Get it into B
  1567.  
  1568.      if    pwcheck
  1569.  
  1570.     ld    hl,(curusr)    ; Get current drive/user into HL
  1571.     inc    h        ; Shift drive to range 1..16
  1572.     xor    a        ; Clear carry flag
  1573.     sbc    hl,bc        ; Compare
  1574.     pop    hl        ; Restore pointer to NDR entry
  1575.     jr    z,setdu        ; If same, accept values without PW checking
  1576.  
  1577. ; If WPASS is set, then password checking is bypassed when the wheel byte is
  1578. ; set.
  1579.  
  1580.      if    wpass
  1581.     ld    a,(z3whl)    ; Get wheel byte
  1582.     or    a        ; If wheel byte set
  1583.     jr    nz,setdu    ; ..skip checking passwords
  1584.      endif    ;wpass
  1585.  
  1586. ; This code is a bit tricky.  We do not want to be asked for passwords for
  1587. ; named directory references in commands when the current IF state is false.
  1588. ; So, first we check to see if there is a password on the directory.  If not,
  1589. ; we proceed to set the temporary DU to the specified directory.  If there is
  1590. ; a password, we check the current IF state.  If it is false, we do not check
  1591. ; passwords and pretend there was no password.  However, we leave the current
  1592. ; directory in effect.  This will work properly in all but one rare
  1593. ; circumstance.  When the command is an 'OR' command with a reference to a
  1594. ; passworded named directory (e.g., "OR EXIST SECRET:FN.FT"), the password
  1595. ; will not be requested and the current directory will be used instead of the
  1596. ; specified one.
  1597.  
  1598.     push    bc        ; Save requested drive/user
  1599.     ld    bc,8        ; Point to password in NDR
  1600.     add    hl,bc
  1601.     ld    a,(hl)        ; Get first character of password
  1602.     cp    ' '        ; Is there a password?
  1603.  
  1604.      if    fcp eq 0    ; If FCP not implemented ...
  1605.  
  1606.     call    nz,passck    ; Perform password checking if pw present
  1607.  
  1608.      else    ;fcp ne 0    ; FCP implemented ...
  1609.  
  1610.     jr    z,dirscan5    ; If no pw, skip ahead
  1611.     call    iftest        ; Otherwise, test current IF state
  1612.     pop    bc        ; Restore BC in case we return now
  1613.     ret    z        ; If false IF in effect, fake success without
  1614.                 ; ..checking password (but TEMPDR/TEMPUSR not
  1615.                 ; ..set)
  1616.     push    bc        ; Otherwise, save BC again
  1617.     call    passck        ; Perform password checking
  1618.  
  1619.      endif    ;fcp eq 0
  1620.  
  1621. dirscan5:
  1622.     pop    bc        ; Restore requested drive/user
  1623.     jr    z,setdu        ; If not bad password, set it up
  1624.     ld    a,(cmdstatfl)    ; See if external invocation (disable
  1625.     bit    3,a        ; ..error handling if so)
  1626.     ret    nz        ; Return NZ to show bad directory
  1627.     ld    a,ecbadpass    ; Error code for bad password
  1628.     jp    error
  1629.  
  1630.      else    ;not pwcheck
  1631.  
  1632.     pop    hl        ; Clean up stack
  1633.      if    accptdu        ; If we cannot fall through, branch
  1634.     jr    setdu
  1635.      endif    ;accptdu
  1636.  
  1637.      endif    ;pwcheck
  1638.  
  1639.      if    not accptdu    ; If NOT ACCPTDU, we have to supply code here
  1640. setdu:
  1641.     ld    (tempusr),bc
  1642.     xor    a        ; Set Z to flag success
  1643.     ret
  1644.      endif    ;not accptdu
  1645.  
  1646.      endif    ;accptdir
  1647.  
  1648. ;-----------------------------------------------------------------------------
  1649.  
  1650. ; This code attempts to interpret the token in the FCB pointed to by register
  1651. ; pair DE as a DU (drive/user) prefix.  If it is successful, the drive and
  1652. ; user values are stored in TEMPDR and TEMPUSR, the zero flag is set, and a
  1653. ; value of zero is returned in register A.  Otherwise the zero flag is reset
  1654. ; and a nonzero value is returned in register A.
  1655. ;
  1656. ; The ADUENV option allows acceptance of the DU form to be controlled by the
  1657. ; DUOK flag in the environment descriptor.  An additional feature of this code
  1658. ; when the ADUENV option is enabled is that a DU value is always accepted,
  1659. ; even if DUOK is off and even if it is outside the normally allowed range,
  1660. ; if it corresponds to a named directory with no password.  The currently
  1661. ; logged directory is unconditionally acceptable (if you got there once, you
  1662. ; can stay as long as you like without further hassles).
  1663.  
  1664.      if    accptdu        ; Allow DU: form
  1665.  
  1666. direrr:                ; This code may do double duty for DIRSCAN
  1667.                 ; ..above
  1668. duerr:
  1669.     xor    a        ; Return NZ to show failure
  1670.     dec    a
  1671.     ret
  1672.  
  1673. duscan:
  1674.     ex    de,hl        ; Switch FCB pointer to HL
  1675.     inc    hl        ; Point to first byte of file name in FCB
  1676.  
  1677.     ld    bc,(curusr)    ; Preset C to current user, B to current drive
  1678.     ld    a,(hl)        ; Get possible drive specification
  1679.     sub    'A'        ; Otherwise convert to number 0..15
  1680.     jr    c,duscan1    ; If < 0, leave B as is
  1681.     cp    16
  1682.     jr    nc,duscan1    ; If > 15, leave B as is
  1683.     ld    b,a        ; Otherwise use value given
  1684.     inc    hl        ; ..and point to next character
  1685.  
  1686. duscan1:
  1687.     inc    b        ; Shift drive to range 1..16
  1688.     ld    a,(hl)        ; Get possible user specification
  1689.     cp    ' '
  1690.     jr    z,duscan2    ; If none present, leave C as is
  1691.     push    bc        ; Save DU values in BC
  1692.     call    decimal1    ; Get specified decimal user number into BC
  1693.     pop    hl        ; Restore values to HL
  1694.     jr    c,duerr        ; Return NZ if invalid decimal conversion
  1695.     ld    a,b        ; Get high byte of result
  1696.     or    a        ; Make sure it is zero
  1697.     ret    nz        ; If not, return NZ to show bad user number
  1698.     ld    b,h        ; DU value is now in BC
  1699.  
  1700. ; If the specified directory is the currently logged directory, accept it
  1701. ; even if it is out of range and/or password protected.
  1702.  
  1703. duscan2:
  1704.     ld    hl,(curusr)    ; Get current drive/user into HL
  1705.     inc    h        ; Shift drive to range 1..16
  1706.     xor    a        ; Clear carry flag
  1707.     sbc    hl,bc        ; Compare values
  1708.     jr    z,setdu
  1709.  
  1710. ; If the specified DU corresponds to a named directory with no password, or
  1711. ; if WPASS is enabled so that password checking is not performed when the
  1712. ; wheel byte is set, then accept it.
  1713.  
  1714.      if    z3ndir ne 0
  1715.  
  1716.     call    du2dir        ; See if there is a matching named directory
  1717.     jr    z,duscan3    ; If not, skip on
  1718.  
  1719.      if    pwcheck        ; If passwords are being checked...
  1720.  
  1721.      if    wpass
  1722.     ld    a,(z3whl)    ; Get wheel byte
  1723.     or    a        ; If wheel byte set, skip checking passwords
  1724.     jr    nz,setdu    ; ..and accept the DU values
  1725.      endif    ;wpass
  1726.  
  1727.     ld    de,9        ; Advance to password
  1728.     add    hl,de
  1729.     ld    a,(hl)        ; Get first character of password
  1730.     cp    ' '
  1731.     jr    z,setdu        ; If none, we have a valid DU
  1732.  
  1733.      else    ;not pwcheck
  1734.  
  1735.     jr    setdu        ; Set the DU
  1736.  
  1737.      endif    ;pwcheck
  1738.  
  1739.      endif    ;z3ndir ne 0
  1740.  
  1741. duscan3:
  1742.      if    aduenv        ; Check DUOK flag in ENV
  1743.     ld    a,(duokfl)    ; Get flag
  1744.     or    a        ; If DU not accepted
  1745.     jr    z,duerr        ; ..skip over the DU scan
  1746.      endif    ;aduenv
  1747.  
  1748.      if    duenv        ; If getting max drive and user from ENV
  1749.     ld    hl,(maxdrenv)    ; Get max drive into L and max user into H
  1750.     ld    a,l        ; Test drive value
  1751.     cp    b
  1752.     jr    c,duerr
  1753.     ld    a,h        ; Test user value
  1754.     cp    c
  1755.     jr    c,duerr
  1756.      else            ; Using fixed values of max DU
  1757.     ld    a,maxdisk
  1758.     cp    b
  1759.     jr    c,duerr
  1760.     ld    a,maxusr
  1761.     cp    c
  1762.     jr    c,duerr
  1763.      endif    ;duenv
  1764.  
  1765. setdu:
  1766.     ld    (tempusr),bc
  1767.     xor    a        ; Set Z to flag success
  1768.     ret
  1769.  
  1770.      endif    ; accptdu
  1771.  
  1772. ; End ZCPR33-3.Z80
  1773.  
  1774.     page
  1775.  
  1776. ; ZCPR33-4.Z80
  1777.  
  1778. ;=============================================================================
  1779. ;
  1780. ;    G E N E R A L    S U B R O U T I N E S    S E C T I O N
  1781. ;
  1782. ;=============================================================================
  1783.  
  1784.  
  1785. ;-----------------------------------------------------------------------------
  1786. ;
  1787. ;    CHARACTER I/O BDOS ROUTINES
  1788. ;
  1789. ;-----------------------------------------------------------------------------
  1790.  
  1791. ; Get uppercase character from console (with ^S processing).  Registers B,
  1792. ; D, H, and L are preserved.  The character is returned in A.
  1793.  
  1794. conin:
  1795.     ld    c,1        ; BDOS conin function
  1796.     call    bdossave
  1797.                 ; Fall through to UCASE
  1798.  
  1799. ;--------------------
  1800.  
  1801. ; Convert character in A to upper case.  All registers except A are preserved.
  1802.  
  1803. ucase:
  1804.     and    7fh        ; Mask out msb
  1805.     cp    61h        ; Less than lower-case 'a'?
  1806.     ret    c        ; If so, return
  1807.     cp    7bh        ; Greater than lower-case 'z'?
  1808.     ret    nc        ; If so, return
  1809.     and    5fh        ; Otherwise capitalize
  1810.     ret
  1811.  
  1812. ;----------------------------------------
  1813.  
  1814. ; Output CRLF
  1815.  
  1816. crlf:
  1817.     call    print
  1818.     db    cr
  1819.     db    lf or 80h
  1820.     ret
  1821.  
  1822. ;----------------------------------------
  1823.  
  1824. ; Output character in A to the console.  All registers are preserved.
  1825.  
  1826. conout:
  1827.     push    de
  1828.     push    bc
  1829.     ld    c,2        ; BDOS conout function
  1830. output:                ; Entry point for LCOUT below
  1831.     ld    e,a
  1832.     call    bdossave
  1833.     pop    bc
  1834.     pop    de
  1835.     ret
  1836.  
  1837. ;----------------------------------------
  1838.  
  1839. ; Print the character string immediately following the call to this routine.
  1840. ; The string terminates with a character whose high bit is set or with a null.
  1841. ; At entry point PRINTC the string is automatically preceded by a
  1842. ; carriage-return-linefeed sequence.  All registers are preserved except A.
  1843.  
  1844. printc:
  1845.     call    crlf        ; New line
  1846.  
  1847. print:
  1848.     ex    (sp),hl        ; Get pointer to string
  1849.     call    printhl        ; Print string
  1850.     ex    (sp),hl        ; Restore HL and set return address
  1851.     ret
  1852.  
  1853. ;----------------------------------------
  1854.  
  1855. ; Print the character string pointed to by HL.  Terminate on character with
  1856. ; the high bit set or on a null character.  On return HL points to the byte
  1857. ; after the last character displayed.  All other registers except A are
  1858. ; preserved.
  1859.  
  1860. printhl:
  1861.     ld    a,(hl)        ; Get a character
  1862.     inc    hl        ; Point to next byte
  1863.     or    a        ; End of string null?
  1864.     ret    z
  1865.     push    af        ; Save flags
  1866.     and    7fh        ; Mask out msb
  1867.     call    conout        ; Print character
  1868.     pop    af        ; Get flags
  1869.     ret    m        ; String terminated by msb set
  1870.     jr    printhl
  1871.  
  1872.  
  1873. ;-----------------------------------------------------------------------------
  1874. ;
  1875. ;    FILE I/O BDOS ROUTINES
  1876. ;
  1877. ;-----------------------------------------------------------------------------
  1878.  
  1879. ; Read a record from a file to be listed or typed
  1880.  
  1881.      if    lton        ; Only needed for LIST and TYPE functions
  1882.  
  1883. readf:
  1884.     ld    de,tfcb
  1885.     jr    read
  1886.  
  1887.      endif    ; lton
  1888.  
  1889. ;----------------------------------------
  1890.  
  1891. ; Read a record from the command file named in CMDFCB
  1892.  
  1893. readcmd:
  1894.     ld    de,cmdfcb
  1895.  
  1896. ; Read a record from file whose FCB is pointed to by DE
  1897.  
  1898. read:
  1899.     ld    c,14h        ; Read-sequential function
  1900.                 ; Fall through to BDOSSAVE
  1901.  
  1902. ;--------------------
  1903.  
  1904. ; Call BDOS for read and write operations.  The flags are set appropriately.
  1905. ; The BC, DE, and HL registers are preserved.
  1906.  
  1907. bdossave:
  1908.     putreg
  1909.     call    bdos
  1910.     getreg
  1911.     or    a        ; Set flags
  1912. note:                ; This return is used for NOTE command, too
  1913.     ret
  1914.  
  1915.  
  1916. ;-----------------------------------------------------------------------------
  1917. ;
  1918. ;    MISCELLANEOUS BDOS ROUTINES
  1919. ;
  1920. ;-----------------------------------------------------------------------------
  1921.  
  1922. ; Set DMA address.  At the entry point DEFLTDMA the address is set to the
  1923. ; default value of 80H.  At the entry point DMASET it is set to the value
  1924. ; passed in the DE registers.
  1925.  
  1926. defltdma:
  1927.     ld    de,tbuff
  1928. dmaset:
  1929.     ld    c,1ah
  1930.     jr    bdossave
  1931.  
  1932. ;----------------------------------------
  1933.  
  1934. ; Log in the drive value passed in the A register (A=0).
  1935.  
  1936. setdrive:
  1937.     ld    e,a
  1938.     ld    c,0eh
  1939.     jr    bdossave
  1940.  
  1941. ;----------------------------------------
  1942.  
  1943. ; Open a file.  At entry point OPENCMD the file is the one specified in
  1944. ; CMDFCB, and the current record is set to zero.  At entry point OPEN
  1945. ; the file whose FCB is pointed to by DE is used.
  1946.  
  1947. opencmd:
  1948.     xor    a        ; Set current record to 0
  1949.     ld    (cmdfcb+32),a
  1950.     ld    de,cmdfcb    ; Command file control block
  1951.                 ; Fall through to open
  1952.  
  1953. open:
  1954.     ld    c,0fh        ; BDOS open function
  1955.                 ; Fall through to BDOSTEST
  1956.  
  1957. ;--------------------
  1958.  
  1959. ; Invoke BDOS for disk functions.  This routine increments the return code in
  1960. ; register A so that the zero flag is set if there was an error.  Registers
  1961. ; BC, DE, and HL are preserved.
  1962.  
  1963. bdostest:
  1964.     call    bdossave
  1965.     inc    a        ; Set zero flag for error return
  1966.     ret
  1967.  
  1968. ;----------------------------------------
  1969.  
  1970. ; Close file whose FCB is pointed to by DE.
  1971.  
  1972.      if    saveon or subon
  1973. close:
  1974.     ld    c,10h
  1975.     jr    bdostest
  1976.      endif    ;saveon or subon
  1977.  
  1978. ;----------------------------------------
  1979.  
  1980. ; Search for first matching file.  At entry point SRCHFST1 the first default FCB
  1981. ; is used.  At entry point SRCHFST the FCB pointed to by DE is used.
  1982.  
  1983.      if    diron or eraon or renon or saveon
  1984. srchfst1:
  1985.     ld    de,tfcb        ; Use first default FCB
  1986.      endif    ;diron or eraon or renon or saveon
  1987.  
  1988. srchfst:
  1989.     ld    c,11h
  1990.     jr    bdostest
  1991.  
  1992. ;-----------------------------------------------------------------------------
  1993.  
  1994. ; Search for next matching file whose FCB is pointed to by DE.
  1995.  
  1996.      if    diron or eraon    ; Only needed by DIR and ERA functions
  1997. srchnxt:
  1998.     ld    c,12h
  1999.     jr    bdostest
  2000.      endif    ; diron or eraon
  2001.  
  2002. ;-----------------------------------------------------------------------------
  2003.  
  2004. ; Kill any submit file that is executing.
  2005.  
  2006.      if    subon
  2007.  
  2008. subkil:
  2009.     ld    hl,subflag    ; Check for submit file in execution
  2010.     ld    a,(hl)
  2011.     or    a        ; 0=no
  2012.     ret    z        ; If none executing, return now
  2013.                 ; Kill submit file
  2014.     xor    a
  2015.     ld    (hl),a        ; Zero submit flag
  2016.     call    setuser        ; Log in user 0
  2017.     ld    de,subfcb    ; Delete submit file
  2018.                 ; ..by falling through to delete routine
  2019.  
  2020.      endif    ; subon
  2021.  
  2022. ;--------------------
  2023.  
  2024. ; Delete file whose FCB is pointed to by DE.
  2025.  
  2026.      if    eraon or renon or saveon or subon
  2027. delete:
  2028.     ld    c,13h
  2029.     jr    bdossave
  2030.      endif    ;eraon or renon or saveon or subon
  2031.  
  2032. ;-----------------------------------------------------------------------------
  2033.  
  2034. ; Get and set user number.  Registers B, D, H, and L are preserved.  Register
  2035. ; E is also preserved at entry point SETUSER1.
  2036.  
  2037. getuser:
  2038.     ld    a,0ffh        ; Get current user number
  2039. setuser:
  2040.     ld    e,a        ; User number in E
  2041. setuser1:
  2042.     ld    c,20h        ; Get/Set BDOS function
  2043.     jr    bdossave
  2044.  
  2045.  
  2046. ;-----------------------------------------------------------------------------
  2047. ;
  2048. ;    GENERAL UTILITY ROUTINES
  2049. ;
  2050. ;-----------------------------------------------------------------------------
  2051.  
  2052.  
  2053. ; This subroutine checks to see if a program loaded at an address given by HL
  2054. ; has a Z3ENV header.  If the header is not present, the zero flag is reset.
  2055. ; If it is present, the zero flag is set, and on return HL points to the
  2056. ; environment-type byte and A contains that byte.
  2057.  
  2058. z3chk:
  2059.     ld    de,z3env+3    ; Point to 'Z3ENV' string in ENV
  2060.     inc    hl        ; Advance three bytes to possible program
  2061.     inc    hl        ; ..header
  2062.     inc    hl
  2063.     ld    b,5        ; Characters to compare
  2064. z3chk1:                ; Check for Z3 ID header
  2065.     ld    a,(de)        ; Get character from ENV descriptor
  2066.     cp    (hl)        ; Compare it to loaded file
  2067.     ret    nz        ; Quit now if mismatch
  2068.     inc    hl        ; If same, advance to next characters
  2069.     inc    de        ; ..and continue comparing
  2070.     djnz    z3chk1        ; (flags not affected by DJNZ)
  2071.     ld    a,(hl)        ; Return the environment type in A
  2072.     ret            ; Return Z if all 5 characters match
  2073.  
  2074. ;----------------------------------------
  2075.  
  2076. ; Subroutine to skip over spaces in the buffer pointed to by HL.  On return,
  2077. ; the zero flag is set if we encountered the end of the line or a command
  2078. ; separator character.
  2079.  
  2080. sksp:
  2081.     ld    a,(hl)        ; Get next character
  2082.     inc    hl        ; Point to the following character
  2083.     cp    ' '        ; Space?
  2084.     jr    z,sksp        ; If so, keep skipping
  2085.     dec    hl        ; Back up to non-space
  2086.                 ; ..and fall through
  2087.  
  2088. ;--------------------
  2089.  
  2090. ; Subroutine to check if character is the command separator or marks the end
  2091. ; of the line.
  2092.  
  2093. tsteol:
  2094.     or    a        ; End of command line?
  2095.     ret    z        ; Return with zero flag set
  2096.     cp    cmdsep        ; Command separator?
  2097.     ret            ; Return with flag set appropriately
  2098.  
  2099. ;----------------------------------------
  2100.  
  2101. ; Initialize complete FCB pointed to by DE
  2102.  
  2103. initfcb:
  2104.     xor    a
  2105.     ld    (de),a        ; Set default disk (dn byte is 0)
  2106.     inc    de        ; Point to file name field
  2107.     call    ifcb        ; Fill 1st part of FCB
  2108.                 ; Fall through to IFCB to run again
  2109.  
  2110. ;--------------------
  2111.  
  2112. ; Initialize part of FCB whose file name field is pointed to by DE on entry.
  2113. ; The file name and type are set to space characters; the EX, S2, RC, and the
  2114. ; following CR (current record ) or DN (disk number) fields are set to zero.
  2115. ; The S1 byte is set to the current user number.  On exit, DE points to the
  2116. ; byte at offset 17 in the FCB (two bytes past the record count byte).
  2117.  
  2118. ifcb:
  2119.     ld    b,11        ; Store 11 spaces for file name and type
  2120.     ld    a,' '
  2121.     call    fill
  2122.     xor    a
  2123.     ld    (de),a        ; Set extent byte to zero
  2124.     inc    de
  2125.     ld    a,(curusr)
  2126.     ld    (de),a        ; Set S1 byte to current user
  2127.     inc    de
  2128.     ld    b,3        ; Store 3 zeroes
  2129.     xor    a        ; Fall thru to fill
  2130.  
  2131. ;--------------------
  2132.  
  2133. ; Fill memory pointed to by DE with character in A for B bytes
  2134.  
  2135. fill:
  2136.     ld    (de),a        ; Fill with byte in A
  2137.     inc    de        ; Point to next
  2138.     djnz    fill
  2139.     ret
  2140.  
  2141. ;----------------------------------------
  2142.  
  2143. ; Subroutine to display the 'no file' error message for the built-in
  2144. ; commands DIR, ERA, LIST, TYPE, and/or REN.
  2145.  
  2146.      if    diron or eraon
  2147.  
  2148. prnnf:
  2149.     call    printc        ; No file message
  2150.     defb    'No Fil','e'+80h
  2151.     ret
  2152.      endif ; diron or eraon
  2153.  
  2154. ;----------------------------------------
  2155.  
  2156. ; Calculate address of command table in package from Z3ENV.  On entry, E
  2157. ; contains the offset to the address of the package in the environment.  On
  2158. ; exit, DE points to the beginning of the package and HL points to the fifth
  2159. ; byte (where the command table starts in the RCP and FCP modules).  The zero
  2160. ; flag is set on return if the package is not supported.
  2161.  
  2162.      if    fcpenv or rcpenv or ndrenv
  2163. pkgoff:
  2164.     ld    hl,z3env    ; Point to beginning of ENV descriptor
  2165.     ld    d,0        ; Make DE have offset
  2166.     add    hl,de        ; ..and add it
  2167.     ld    a,(hl)        ; Get low byte of package address
  2168.     inc    hl        ; Point to high byte
  2169.     ld    h,(hl)        ; ..and get it
  2170.     ld    l,a        ; Move full address into HL
  2171.     or    h        ; Set zero flag if no package
  2172.     ld    de,5        ; Offset to start of table
  2173.     ex    de,hl        ; Preserve start address of package in DE
  2174.     add    hl,de        ; Pointer to 5th byte of package in HL
  2175.     ret            ; Return with zero flag set appropriately
  2176.  
  2177.      endif    ;fcpenv or rcpenv or ndrenv
  2178.  
  2179. ;----------------------------------------
  2180.  
  2181. ; This subroutine checks to see if we are in a false IF state.  If that is
  2182. ; the case, the routine returns with the zero flag set.  If there is not active
  2183. ; IF state or if it is true, then the zero flag is reset.
  2184.  
  2185.      if    fcp ne 0    ; Omit code if FCP not implemented
  2186.  
  2187. iftest:
  2188.     ld    bc,(ifptrfl)    ; Current IF pointer into C, IF status into B
  2189.     ld    a,c        ; See if any IF in effect
  2190.     or    a
  2191.     jr    z,iftest1    ; Branch if no IF state is active
  2192.     and    b        ; Mask the current IF status
  2193.     ret
  2194. iftest1:
  2195.     dec    a        ; Reset the zero flag
  2196.     ret
  2197.  
  2198.      endif    ;fcp ne 0
  2199.  
  2200. ;----------------------------------------
  2201.  
  2202. ; Print the command prompt with the time
  2203.  
  2204.      IF TIMEON        ; If time in prompt line
  2205. CONVRT:    ld    L,'0'-1
  2206. TENS:
  2207.     inc    L        ; This routine is moved from the USER # area
  2208.     sub    10        ; and called by both TIMEON and USER #
  2209.     jr    nc,TENS
  2210.     add    a,10+'0'
  2211.     ld    H,a
  2212.     ld    a,L
  2213.     RET
  2214.      ENDIF
  2215.  
  2216. prompt:
  2217.      IF TIMEON    ; If time in prompt line
  2218.     LD    BC,0F486H    ; N*'s TSS/C time HH:MM (point to min. first)
  2219.     LD    A,(BC)        ; Load hour
  2220.     CALL    CONVRT        ; Convert BCD to ASCII
  2221.     LD    (TIME+1),HL    ; Store hours in prompt time string
  2222.     INC    BC        ; Point to min.
  2223.     LD    A,(BC)
  2224.     CALL    CONVRT
  2225.     LD    (TIME+4),HL
  2226.     CALL    PRINTC
  2227. TIME:    DEFB    '(00:00)',' '+80H  ; Time string
  2228.      ELSE
  2229.     call    crlf
  2230.      ENDIF            ; Time on
  2231.  
  2232. ; Print the command prompt with DU and/or DIR (but without any trailing
  2233. ; character).  This is also the code in which the current drive and user
  2234. ; will be stored.  The conditional assemblies are somewhat involved because
  2235. ; of the possibilities of either or both of the DU or DIR forms being omitted
  2236. ; from the prompt.
  2237.  
  2238.      if    incldu        ; If drive/user in prompt
  2239.  
  2240.     ld    BC,(curusr)    ; Get current drive/user into BC
  2241.     PUSH    BC        ; Save for later
  2242.  
  2243. ; If INCLENV is enabled, the drive and user (DU) will be included in the
  2244. ; prompt based on the state of the DUOK flag in the environment.  If INCLENV
  2245. ; is disabled, the DU form will always be included if INCLDU is on.
  2246.  
  2247.      if    inclenv
  2248.     ld    a,(duokfl)    ; If ENV disallows DU,
  2249.     or    a        ; ..then don't show it in
  2250.     jr    z,prompt2    ; ..the prompt, either
  2251.      endif    ;inclenv
  2252.  
  2253.     ld    a,B        ; Get current drive
  2254.     add    a,'A'        ; Convert to ascii A-P
  2255.     call    conout
  2256.     ld    a,C        ; Get current user
  2257.  
  2258.      if    supres        ; If suppressing user # report for user 0
  2259.     or    a
  2260.     jr    z,prompt2
  2261.      endif
  2262.  
  2263.      if    highuser AND NOT TIMEON    ; If allowing users 16..31 and NOT TIME
  2264.  
  2265. CONVRT:
  2266.     ld    L,'0'-1
  2267. prompt0:
  2268.     inc    L
  2269.     sub    10
  2270.     jr    nc,prompt0
  2271.     add    a,10+'0'
  2272.     ld    H,a
  2273.     ld    a,L
  2274.      ENDIF        ;not time and highuser
  2275.  
  2276.      IF    HIGHUSER OR TIMEON
  2277.      IF    TIMEON
  2278.     CALL    CONVRT
  2279.      ENDIF
  2280.  
  2281.     CP    '0'
  2282.     CALL    NZ,CONOUT
  2283.     LD    A,H
  2284.      ENDIF
  2285.  
  2286.      IF    NOT HIGHUSER AND NOT TIMEON
  2287.                 ;using only standard user numbers 0..15
  2288.  
  2289.     cp    10        ; User < 10?
  2290.     jr    c,UNITS
  2291.     sub    10        ; Subtract 10 from user number
  2292.     LD    H,A        ; Save low digit
  2293.     call    print        ; Display a '1' for tens digit
  2294.     defb    '1' or 80h
  2295.     LD    A,H
  2296.  
  2297. UNITS:
  2298.     add    a,'0'        ; Output 1's digit (convert to ascii)
  2299.      ENDIF
  2300.     call    conout
  2301. prompt2:
  2302.      endif    ; incldu
  2303.  
  2304.                 ; Display named directory
  2305.  
  2306.      if    incldir
  2307.  
  2308.      if    incldu
  2309.     POP    BC        ; Get drive/user
  2310.      else
  2311.     ld    bc,(curusr)    ; Get current drive and user into BC
  2312.      endif    ;incldu
  2313.  
  2314.     inc    b        ; Switch drive to range 1..16
  2315.     call    du2dir        ; See if there is a corresponding DIR form
  2316.     ret    z        ; If not, return now
  2317.  
  2318.      if    incldu        ; Separate DU and DIR with colon
  2319.  
  2320.      if    inclenv
  2321.     ld    a,(duokfl)    ; If not displaying DU, then
  2322.     or    a        ; ..don't send separator, either
  2323.     ld    a,':'        ; Make the separator
  2324.     call    nz,conout    ; ..and send if permitted
  2325.      else
  2326.     call    print        ; Put in colon separator
  2327.     defb    ':' or 80h
  2328.      endif    ;inclenv
  2329.  
  2330.      endif    ; incldu
  2331.  
  2332.     ld    b,8        ; Max of 8 chars in DIR name
  2333. prompt3:
  2334.     inc    hl        ; Point to next character in DIR name
  2335.     ld    a,(hl)        ; ..and get it
  2336.     cp    ' '        ; Done if space
  2337.     ret    z
  2338.     call    conout        ; Print character
  2339.     djnz    prompt3        ; Count down
  2340.  
  2341.      endif    ; incldir
  2342.  
  2343.     ret
  2344.  
  2345. ;-----------------------------------------------------------------------------
  2346.  
  2347. ; Subroutine to convert DU value in BC into pointer to a matching entry in
  2348. ; the NDR.  If there is no match, the routine returns with the zero flag set.
  2349. ; If a match is found, the zero flag is reset, and the code returns with HL
  2350. ; pointing to the byte before the directory name.
  2351.  
  2352.      if    z3ndir ne 0
  2353.  
  2354. du2dir:
  2355.  
  2356.      if    ndrenv        ; If getting NDR address from environment
  2357.     ld    e,15h        ; Offset to NDR in Z3ENV
  2358.     call    pkgoff        ; Get address of NDR into DE
  2359.     ex    de,hl        ; ..and switch into HL
  2360.     ret    z        ; If no NDR, return with zero flag set
  2361.     jr    du2dir2
  2362.      else
  2363.     ld    hl,z3ndir-17    ; Scan directory for match
  2364.      endif    ;ndrenv
  2365.  
  2366. du2dir1:            ; Advance to next entry in NDR
  2367.     ld    de,16+1        ; Skip user (1 byte) and name/pw (16 bytes)
  2368.     add    hl,de
  2369.  
  2370. du2dir2:
  2371.     ld    a,(hl)        ; End of NDR?
  2372.     or    a
  2373.     ret    z        ; If so, return with zero flag set
  2374.  
  2375.     inc    hl        ; Point to user number in NDR entry
  2376.     cp    b        ; Compare drive values
  2377.     jr    nz,du2dir1    ; If mismatch, back for another try
  2378.     ld    a,(hl)        ; Get user number
  2379.     sub    c        ; ..and compare
  2380.     jr    nz,du2dir1    ; If mismatch, back for another try
  2381.     dec    a        ; Force NZ to show successful match
  2382.     ret
  2383.  
  2384.      endif    ;z3ndir ne 0
  2385.  
  2386. ;-----------------------------------------------------------------------------
  2387.  
  2388. ; This routine gets the next line of input for the command buffer.  The
  2389. ; following order of priority is followed:
  2390. ;    If ZEX is active, the next line is obtained from ZEX
  2391. ;    If a submit file is running, its last record provides the input
  2392. ;    If there is a command line on the shell stack, use it
  2393. ;    Finally, if none of the above, the input is obtained from the user
  2394.  
  2395. readbuf:
  2396.  
  2397.     ld    a,(zexrunfl)    ; Get ZEX-running flag
  2398.     or    a
  2399.     jr    nz,userinput    ; If ZEX running, go directly to user input
  2400.  
  2401.      if    subon        ; If submit facility is enabled, check for it
  2402.  
  2403.     ld    a,(subflag)    ; Test for submit file running
  2404.     or    a
  2405.     jr    z,shellinput    ; If not, go on to possible shell input
  2406.  
  2407.     xor    a        ; Log into user 0
  2408.     call    setuser
  2409.     call    defltdma    ; Initialize DMA pointer
  2410.     ld    de,subfcb    ; Point to submit file FCB
  2411.     call    open        ; Try to open file
  2412.     jr    z,readbuf1    ; Branch if open failed
  2413.  
  2414.     ld    hl,subfrc    ; Point to record count in submit FCB
  2415.     ld    a,(hl)        ; Get the number of records in file
  2416.     dec    a        ; Reduce to number of last record
  2417.     ld    (subfcr),a    ; ..and put into current record field
  2418.     call    read        ; Attempt to read submit file
  2419.     jr    nz,readbuf1    ; Branch if read failed
  2420.  
  2421.     dec    (hl)        ; Reduce file record cound
  2422.     dec    hl        ; Point to S2 byte of FCB (yes, this is req'd!)
  2423.     ld    (hl),a        ; Stuff a zero in there (A=0 from call to READ)
  2424.     call    close        ; Close the submit file one record smaller
  2425.     jr    z,readbuf1    ; Branch if close failed
  2426.  
  2427. ; Now we copy the line read from the file into the multiple command line
  2428. ; buffer
  2429.  
  2430.     ld    de,chrcnt    ; Point to command length byte in command buffer
  2431.     ld    hl,tbuff    ; Point to sector read in from submit file
  2432.  
  2433.      if    buflen gt 7fh    ; If command line buffer is longer than record,
  2434.     ld    bc,80h        ; ..then copy entire record from $$$.SUB file
  2435.      else    ;buflen le 7fh    ; Otherwise copy only enough to fill
  2436.     ld    bc,buflen+1    ; ..the command line buffer
  2437.      endif    ;buflen gt 7fh
  2438.  
  2439.     ldir            ; Transfer line from submit file to buffer
  2440.  
  2441. ; We now deal with various options that control the display of commands fed
  2442. ; to the command processor from a submit file.
  2443.  
  2444.      if    subnoise gt 0    ; If subnoise = 0 we omit all this display code
  2445.  
  2446.      if    subnoise eq 1    ; If subnoise = 1 we follow the quiet flag
  2447.     ld    a,(quietfl)
  2448.     or    a
  2449.     jr    nz,readbuf0    ; If quiet, skip echoing the command
  2450.      endif    ;subnoise eq 1
  2451.  
  2452.     call    prompt        ; Print prompt
  2453.     call    print        ; Print submit prompt trailer
  2454.     defb    sprmpt or 80h
  2455.     ld    hl,cmdlin    ; Print command line
  2456.     call    printhl
  2457.  
  2458.      endif    ;subnoise gt 0
  2459.  
  2460. readbuf0:
  2461.     call    break        ; Check for abort (any char)
  2462.     ret    nz        ; If no ^C, return to caller and run
  2463.  
  2464. readbuf1:
  2465.     call    subkil        ; Kill submit file and abort
  2466.     jp    restart        ; Restart CPR
  2467.  
  2468.      endif    ; subon
  2469.  
  2470. shellinput:
  2471.     ld    hl,shstk    ; Point to shell stack
  2472.     ld    a,(hl)        ; Check first byte
  2473.     cp    ' '+1        ; See if any entry
  2474.     jr    c,userinput    ; Get user input if none
  2475.  
  2476.     ld    de,cmdlin    ; Point to first character of command line
  2477.     ld    bc,shsize    ; Copy shell line into command line buffer
  2478.     ldir            ; Do copy
  2479.     ex    de,hl        ; HL points to end of line
  2480.     ld    a,1        ; Set command status flag to show
  2481.     ld    (cmdstatfl),a    ; ..that a shell has been invoked
  2482.     jr    readbuf3    ; Store ending zero and exit
  2483.  
  2484. userinput:
  2485.     call    prompt        ; Print prompt
  2486.     call    print        ; Print prompt trailer
  2487.     defb    cprmpt or 80h
  2488.     ld    c,0ah        ; Read command line from user
  2489.     ld    de,bufsiz    ; Point to buffer size byte of command line
  2490.     call    bdos
  2491.  
  2492.                 ; Store null at end of line
  2493.  
  2494.     ld    hl,chrcnt    ; Point to character count
  2495.     ld    a,(hl)        ; ..and get its value
  2496.     inc    hl        ; Point to first character of command line
  2497.     call    addah        ; Make pointer to byte past end of command line
  2498. readbuf3:
  2499.     ld    (hl),0        ; Store ending zero
  2500.     ret
  2501.  
  2502. ;-----------------------------------------------------------------------------
  2503.  
  2504. ; Check for any character from the user console.  Return with the character
  2505. ; in A.  If the character is a control-C, then the zero flag will be set.
  2506.  
  2507.      if    subon or diron or eraon or lton
  2508.  
  2509. break:
  2510.     ld    c,0bh        ; BDOS console status function
  2511.     call    bdossave    ; Call BDOS and set flags
  2512.     call    nz,conin    ; Get input character if there is one
  2513.     cp    'C'-'@'        ; Check for abort
  2514.     ret
  2515.  
  2516.      endif    ; subon or diron or eraon or lton
  2517.  
  2518. ;-----------------------------------------------------------------------------
  2519.  
  2520. ; Add A to HL (HL=HL+A)
  2521.  
  2522. addah:
  2523.     add    a,l
  2524.     ld    l,a
  2525.     ret    nc
  2526.     inc    h
  2527.     ret
  2528.  
  2529. ;-----------------------------------------------------------------------------
  2530.  
  2531. ; The routine NUMBER evaluates a string in the first FCB as either a decimal
  2532. ; or, if terminated with the NUMBASE hexadecimal marker, a HEX number.  If the
  2533. ; conversion is successful, the value is returned as a 16-bit quantity in BC.
  2534. ; If an invalid character is encountered in the string, the routine returns
  2535. ; with the carry flag set and HL pointing to the offending character.
  2536.  
  2537.      if    saveon
  2538.  
  2539. number:
  2540.     ld    hl,tfcb+8    ; Set pointer to end of number string
  2541.     ld    bc,8        ; Number of characters to scan
  2542.     ld    a,numbase    ; Scan for HEX identifier
  2543.     cpdr            ; Do the search
  2544.     jr    nz,decimal    ; Branch if HEX identifier not found
  2545.  
  2546.     inc    hl        ; Point to HEX marker
  2547.     ld    (hl),' '    ; Replace HEX marker with valid terminator
  2548.                 ; ..and fall through to HEXNUM
  2549.  
  2550.      endif    ;saveon
  2551.  
  2552. ;----------------------------------------
  2553.  
  2554. ; At this entry point the character string in the first default FCB is
  2555. ; converted as a hexadecimal number (there must NOT be a HEX marker).
  2556.  
  2557. hexnum:
  2558.     ld    hl,tfcb+1    ; Point to string in first FCB
  2559.  
  2560. ; At this entry point the character string pointed to by HL is converted
  2561. ; as a hexadecimal number (there must be NO HEX marker at the end).
  2562.  
  2563. hexnum1:
  2564.     ld    de,16        ; HEX radix base
  2565.     jr    radbin        ; Invoke the generalized conversion routine
  2566.  
  2567. ;----------------------------------------
  2568.  
  2569. ; This entry point performs decimal conversion of the string in the first
  2570. ; default FCB.
  2571.  
  2572. decimal:
  2573.     ld    hl,tfcb+1    ; Set pointer to number string
  2574.  
  2575. ; This entry point performs decimal conversion of the string pointed to
  2576. ; by HL.
  2577.  
  2578. decimal1:
  2579.     ld    de,10        ; Decimal radix base
  2580.                 ; Fall through to generalized
  2581.                 ; ..radix conversion routine
  2582.  
  2583. ; This routine converts the string pointed to by HL using the radix passed in
  2584. ; DE.  If the conversion is successful, the value is returned in BC.  HL points
  2585. ; to the character that terminated the number, and A contains that character.
  2586. ; If an invalid character is encountered, the routine returns with the carry
  2587. ; flag set, and HL points to the offending character.
  2588.  
  2589. radbin:
  2590.     ld    bc,0        ; Initialize result
  2591. radbin1:
  2592.     or    a        ; Make sure carry is reset
  2593.     call    sdelm        ; Test for delimiter (returns Z if delimiter)
  2594.     ret    z        ; Return if delimiter encountered
  2595.  
  2596.     sub    '0'        ; See if less than '0'
  2597.     ret    c        ; Return with carry set if so
  2598.     cp    10        ; See if in range '0'..'9'
  2599.     jr    c,radbin2    ; Branch if it is valid
  2600.     cp    'A'-'0'        ; Bad character if < 'A'
  2601.     ret    c        ; ..so we return with carry set
  2602.     sub    7        ; Convert to range 10..15
  2603. radbin2:
  2604.     cp    e        ; Compare to radix in E
  2605.     ccf            ; Carry should be set; this will clear it
  2606.     ret    c        ; If carry now set, we have an error
  2607.  
  2608.     inc    hl        ; Point to next character
  2609.     push    bc        ; Push the result we are forming onto the stack
  2610.     ex    (sp),hl        ; Now HL=result, (sp)=source pointer
  2611.     call    mpy16        ; HLBC = previous$result * radix
  2612.     ld    h,0        ; Discard high 16 bits and
  2613.     ld    l,a        ; ..move current digit into HL
  2614.     add    hl,bc        ; Form new result
  2615.     ld    c,l        ; Move it into BC
  2616.     ld    b,h
  2617.     pop    hl        ; Get string pointer back
  2618.     jr    radbin1        ; Loop until delimiter
  2619.  
  2620. ;-----------------------------------------------------------------------------
  2621.  
  2622. ; This routine multiplies the 16-bit values in DE and HL and returns the
  2623. ; 32-bit result in HLBC (HL has high 16 bits; BC has low 16 bits).  Register
  2624. ; pair AF is preserved.
  2625.  
  2626. mpy16:
  2627.     ex    af,af'        ; Save AF
  2628.     ld    a,h        ; Transfer factor in HL to A and C
  2629.     ld    c,l
  2630.     ld    hl,0        ; Initialize product
  2631.     ld    b,16        ; Set bit counter
  2632.     rra            ; Shift AC right so first multiplier bit
  2633.     rr    c        ; ..is in carry flag
  2634. mp161:
  2635.     jr    nc,mp162     ; If carry not set, skip the addition
  2636.     add    hl,de        ; Add multiplicand
  2637. mp162:
  2638.     rr    h        ; Rotate HL right, low bit into carry
  2639.     rr    l
  2640.     rra            ; Continue rotating through AC, with
  2641.     rr    c        ; ..next multiplier bit moving into carry
  2642.     djnz    mp161        ; Loop through 16 bits
  2643.  
  2644.     ld    b,a        ; Move A to B so result is in HLBC
  2645.     ex    af,af'        ; Restore original AF registers
  2646.     ret
  2647.  
  2648. ;-----------------------------------------------------------------------------
  2649.  
  2650. ; This routine checks for a delimiter character pointed to by HL.  It returns
  2651. ; with the character in A and the zero flag set if it is a delimiter.  All
  2652. ; registers are preserved except A.
  2653.  
  2654. sdelm:
  2655.     ld    a,(hl)        ; Get the character
  2656.     exx            ; Use alternate register set (shorter code)
  2657.     ld    hl,deldat    ; Point to delimiter list
  2658.     ld    bc,delend-deldat; Length of delimiter list
  2659.     cpir            ; Scan for match
  2660.     exx            ; Restore registers
  2661.     ret            ; Returns Z if delimiter
  2662.  
  2663. deldat:                ; List of delimiter characters
  2664.     db    ' '
  2665.     db    '='
  2666.     db    '_'
  2667.     db    '.'
  2668.     db    ':'
  2669.     db    ';'
  2670.     db    '<'
  2671.     db    '>'
  2672.     db    ','
  2673.     db    0
  2674.      if    cmdsep ne ';'
  2675.     db    cmdsep
  2676.      endif    ;cmdsep ne ';'
  2677. delend:
  2678.  
  2679. ;-----------------------------------------------------------------------------
  2680.  
  2681. ; Log into DU contained in FCB pointed to by DE.  Registers DE are preserved;
  2682. ; all others are changed.  Explicit values for the temporary drive and user
  2683. ; are extracted from the FCB.  If the record-count byte has an FF in it, that
  2684. ; is a signal that the directory specification was invalid.  We then invoke
  2685. ; the error handler.
  2686.  
  2687.      if    diron or eraon or lton or renon or saveon
  2688.  
  2689. fcblog:
  2690.     push    de        ; Save pointer to FCB
  2691.     ex    de,hl
  2692.     ld    a,(hl)        ; Get drive
  2693.     ld    bc,13        ; Offset to S1 field
  2694.     add    hl,bc
  2695.     ld    c,(hl)        ; Get user into C
  2696.     or    a        ; See if drive value was 0
  2697.     jr    nz,fcblog1    ; If not, branch ahead
  2698.     ld    a,(curdr)    ; Otherwise substitute current drive
  2699.     inc    a        ; ..shifted to range 1..16
  2700. fcblog1:
  2701.     ld    b,a        ; Get drive into B
  2702.     ld    (tempusr),bc    ; Set up temporary DU values
  2703.     call    logtemp        ; ..and log into it
  2704.     pop    de        ; Restore pointer to FCB
  2705.  
  2706. ; Now check to make sure that the directory specification was valid.
  2707.  
  2708.     inc    hl        ; Advance pointer to record-count byte
  2709.     inc    hl
  2710.     ld    a,(hl)        ; See if it is nonzero
  2711.     or    a
  2712.     jp    nz,baddirerr    ; If so, invoke error handler
  2713.  
  2714.     ret            ; Otherwise return
  2715.  
  2716.      endif    ;diron or eraon or lton or renon or saveon
  2717.  
  2718. ;-----------------------------------------------------------------------------
  2719.  
  2720. ; Log into the temporary directory.  Registers B, H, and L are preserved.
  2721.  
  2722. logtemp:
  2723.     ld    de,(tempusr)    ; Set D = tempdr, E = tempusr
  2724.     call    setuser1    ; Register D is preserved during this call
  2725.     ld    a,d        ; Move drive into A
  2726.     dec    a        ; Adjust for drive range 0..15
  2727.     jp    setdrive    ; Log in new drive and return
  2728.  
  2729. ;-----------------------------------------------------------------------------
  2730.  
  2731. ; This routine scans the command table pointed to by HL for the command name
  2732. ; stored in the command FCB.  If the command is not found, the routine returns
  2733. ; with the zero flag reset.  If the command is found, the address vector is
  2734. ; stored in EXECADR and the zero flag is set.
  2735.  
  2736. cmdscan:
  2737.     ld    b,(hl)        ; Get length of each command
  2738.     inc    hl        ; Point to first command name
  2739.  
  2740. scannext:
  2741.     ld    a,(hl)        ; Check for end of table
  2742.     or    a
  2743.     jr    z,scanend    ; Branch if end
  2744.  
  2745.     ld    de,cmdfcb+1    ; Point to name of requested command
  2746.     push    bc        ; Save size of commands in table
  2747.  
  2748.      if    wheel
  2749.                 ; Ignore commands with high bit set in first
  2750.                 ; ..char of command name if wheel is false
  2751.     ld    a,(z3whl)    ; Get the wheel byte
  2752.     or    a
  2753.     ld    c,0ffh        ; Make a mask that passes all characters
  2754.     jr    z,scancmp    ; Use this mask if wheel not set
  2755.  
  2756.      endif    ; wheel
  2757.  
  2758.     ld    c,7fh        ; Use mask to block high bit if wheel set
  2759.                 ; ..or not in use
  2760.  
  2761. scancmp:
  2762.     ld    a,(de)        ; Compare against table entry
  2763.  
  2764.     xor    (hl)
  2765.     and    c        ; Mask high bit of comparison
  2766.     jr    nz,scanskip    ; No match, so skip rest of command name
  2767.  
  2768.     inc    de        ; Advance to next characters to compare
  2769.     inc    hl
  2770.     res    7,c        ; Mask out high bit on characters after first
  2771.     djnz    scancmp        ; Count down
  2772.  
  2773.     ld    a,(de)        ; See if next character in input command
  2774.     cp    ' '        ; ..is a space
  2775.     jr    nz,scanbad    ; If not, user command is longer than commands
  2776.                 ; ..in the command table
  2777.  
  2778.                 ; Matching command found
  2779.  
  2780.     pop    bc        ; Clear stack
  2781.     ld    a,(hl)        ; Get address from table into HL
  2782.     inc    hl
  2783.     ld    h,(hl)
  2784.     ld    l,a
  2785.     ld    (execadr),hl    ; Set execution address
  2786.     xor    a        ; Set zero flag to show that command found
  2787.     ret
  2788.  
  2789. scanskip:
  2790.     inc    hl        ; Skip to next command table entry
  2791.     djnz    scanskip
  2792.  
  2793. scanbad:
  2794.     pop    bc        ; Get back size of each command
  2795.     inc    hl        ; Skip over address vector
  2796.     inc    hl
  2797.     jr    scannext    ; Try scanning next entry in table
  2798.  
  2799. scanend:
  2800.     xor    a        ; Reset zero flag to show
  2801.     dec    a        ; ..that command was not found
  2802.     ret
  2803.  
  2804. ; End ZCPR33-4.Z80
  2805.  
  2806.     page
  2807.  
  2808. ; ZCPR33-5.Z80
  2809.  
  2810. ;=============================================================================
  2811. ;
  2812. ;        R E S I D E N T    C O M M A N D    C O D E
  2813. ;
  2814. ;=============================================================================
  2815.  
  2816. ; Command:    DIR
  2817. ; Function:    To display a directory of the files on disk
  2818. ; Forms:
  2819. ;    DIR <afn>    Displays the DIR-attribute files
  2820. ;    DIR        Same as DIR *.*
  2821. ;    DIR <afn> S    Displays the SYS-attribute files
  2822. ;    DIR /S        Same as DIR *.* S
  2823. ;    DIR <afn> A    Display both DIR and SYS files
  2824. ;    DIR /A        Same as DIR *.* A
  2825.  
  2826.      if    diron
  2827.  
  2828. dir:
  2829.     ld    de,tfcb        ; Point to target FCB
  2830.     push    de        ; ..and save the pointer for later
  2831.     inc    de        ; Point to file name
  2832.     ld    a,(de)        ; Get first character
  2833.  
  2834.      if    slashfl        ; If allowing "DIR /S" and "DIR /A" formats
  2835.     cp    '/'        ; If name does not start with '/'
  2836.     jr    nz,dir1        ; ..branch and process normally
  2837.     inc    de        ; Point to second character
  2838.     ld    a,(de)        ; Get option character after slash
  2839.     ld    (tfcb2+1),a    ; ..and put it into second FCB
  2840.     dec    de        ; Back to first character
  2841.     ld    a,' '        ; Simulate empty FCB
  2842.      endif    ;slashfl
  2843.  
  2844. dir1:
  2845.     cp    ' '        ; If space, make all wild
  2846.     jr    nz,dir2
  2847.     ld    b,11
  2848.     ld    a,'?'
  2849.     call    fill
  2850.  
  2851. dir2:
  2852.     pop    de        ; Restore pointer to FCB
  2853.     call    fcblog        ; Log in the specified directory
  2854.  
  2855.      if    whldir
  2856.     ld    a,(z3whl)    ; Check wheel status
  2857.     or    a        ; If not set, then ignore options
  2858.     jr    z,dir2a
  2859.      endif    ;whldir
  2860.  
  2861.     ld    a,(tfcb2+1)    ; Check for any option letter
  2862.     ld    b,1        ; Flag for both DIR and SYS files
  2863.     cp    allchar        ; See if all (SYS and DIR) option letter
  2864.     jr    z,dirpr        ; Branch if so
  2865.     dec    b        ; B = 0 for SYS files only
  2866.     cp    syschar        ; See if SYS-only option letter
  2867.     jr    z,dirpr        ; Branch if so
  2868. dir2a:
  2869.     ld    b,80h        ; Flag for DIR-only selection
  2870.                 ; Drop into DIRPR to print directory
  2871.  
  2872.      endif    ; diron
  2873.  
  2874. ;--------------------
  2875.  
  2876. ; Directory display routine
  2877.  
  2878. ; On entry, if attribute checking is required, the B register is
  2879. ; set as follows:
  2880. ;    00H for SYS files only
  2881. ;    80H for DIR files only
  2882. ;    01H for both
  2883.  
  2884.      if    diron or eraon
  2885.  
  2886. dirpr:
  2887.      if    diron        ; Attribute checking needed only for DIR
  2888.     ld    a,b        ; Get flag
  2889.     ld    (systst),a    ; Set system test flag
  2890.      endif
  2891.  
  2892.     ld    e,0        ; Set column counter to zero
  2893.     push    de        ; Save column counter (E)
  2894.     call    srchfst1    ; Search for specified file (first occurrence)
  2895.     jr    nz,dir3
  2896.     call    prnnf        ; Print no-file message
  2897.     pop    de        ; Restore DE
  2898.     xor    a        ; Set Z to show no files found
  2899.     ret
  2900.  
  2901. ; Entry selection loop.  On entering this code, A contains the offset in the
  2902. ; directory block as returned by the search-first or search-next call.
  2903.  
  2904. dir3:
  2905.      if    diron        ; Attribute checking needed only for DIR cmd
  2906.  
  2907.     call    getsbit        ; Get and test for type of files
  2908.     jr    z,dir6
  2909.  
  2910.      else    ;not diron
  2911.  
  2912.     dec    a        ; Adjust returned value from 1..4 to 0..3
  2913.     rrca            ; Multiply by 32 to convert number to
  2914.     rrca            ; ..offset into TBUFF
  2915.     rrca
  2916.     ld    c,a        ; C = offset to entry in TBUFF
  2917.  
  2918.      endif    ;diron
  2919.  
  2920.     pop    de        ; Restore count of
  2921.     ld    a,e        ; ..entries displayed
  2922.     inc    e        ; Increment entry counter
  2923.     push    de        ; Save it
  2924.     and    03h        ; Output CRLF if 4 entries printed in line
  2925.     jr    nz,dir4
  2926.     call    crlf        ; New line
  2927.     jr    dir5
  2928. dir4:
  2929.     call    print
  2930.  
  2931.      if    wide
  2932.  
  2933.     defb    '  '        ; 2 spaces
  2934.     defb    fence        ; Then fence char
  2935.     defb    ' ',' '+80h    ; Then 2 more spaces
  2936.  
  2937.      else    ;not wide
  2938.  
  2939.     defb    ' '        ; Space
  2940.     defb    fence        ; Then fence char
  2941.     defb    ' '+80h        ; Then space
  2942.  
  2943.      endif    ; wide
  2944.  
  2945. dir5:
  2946.     ld    a,1
  2947.     call    dirptr        ; HL now points to 1st byte of file name
  2948.     call    prfn        ; Print file name
  2949. dir6:
  2950.     call    break        ; Check for abort
  2951.     jr    z,dir7
  2952.     call    srchnxt        ; Search for next file
  2953.     jr    nz,dir3        ; Continue if file found
  2954.  
  2955. dir7:
  2956.     pop    de        ; Restore stack
  2957.     dec    a        ; Set NZ flag
  2958.     ret
  2959.  
  2960.      endif    ; diron or eraon
  2961.  
  2962. ;-----------------------------------------------------------------------------
  2963.  
  2964.      if    diron or attchk or eraon
  2965.  
  2966. ; This routine returns a pointer in HL to the directory entry in TBUFF that
  2967. ; corresponds to the offset specified in registers C (file offset) and C
  2968. ; (byte offset within entry).
  2969.  
  2970. dirptr:
  2971.     ld    hl,tbuff
  2972.     add    a,c        ; Add the two offset contributions
  2973.     call    addah        ; Set pointer to desired byte
  2974.     ld    a,(hl)        ; Get the desired byte
  2975.     ret
  2976.  
  2977.      endif    ; diron or attchk or eraon
  2978.  
  2979. ;-----------------------------------------------------------------------------
  2980.  
  2981. ; Test File in FCB for existence, ask user to delete if so, and abort if he
  2982. ;  choses not to
  2983.  
  2984.      if    saveon or renon
  2985.  
  2986. extest:
  2987.     ld    de,tfcb        ; Point to FCB
  2988.     push    de        ; ..and save it for later
  2989.     call    fcblog        ; Log into specified directory
  2990.     call    srchfst1    ; Look for specified file
  2991.     pop    de        ; Restore pointer
  2992.     ret    z        ; OK if not found, so return
  2993.     call    printc
  2994.      if    bellfl
  2995.     defb    bell
  2996.      endif    ;bellfl
  2997.     defb    'Erase',' '+80h
  2998.     ld    hl,tfcb+1    ; Point to file name field
  2999.     call    prfn        ; Print it
  3000.     call    print        ; Add question mark
  3001.     defb    '?' or 80h
  3002.     call    conin        ; Get user response
  3003.     cp    'Y'        ; Test for permission to erase file
  3004.     jp    nz,restart    ; If not, flush the entire command line
  3005.     jp    delete        ; Delete the file
  3006.  
  3007.      endif    ; saveon or renon
  3008.  
  3009. ;-----------------------------------------------------------------------------
  3010.  
  3011. ; Print file name pointed to by HL
  3012.  
  3013.      if    diron or renon or saveon
  3014.  
  3015. prfn:
  3016.     ld    b,8        ; Display 8 characters in name
  3017.     call    prfn1
  3018.     call    print        ; Put in dot
  3019.     defb    '.' or 80h
  3020.     ld    b,3        ; Display 3 characters in type
  3021. prfn1:
  3022.     ld    a,(hl)        ; Get character
  3023.     inc    hl        ; Point to next
  3024.     call    conout        ; Print character
  3025.     djnz    prfn1        ; Loop through them all
  3026.     ret
  3027.  
  3028.      endif    ;diron or renon or saveon
  3029.  
  3030. ;-----------------------------------------------------------------------------
  3031.  
  3032. ; This routine returns NZ if the file has the required attributes and Z if
  3033. ; it does not.  It works by performing the 'exclusive or' of the mask passed
  3034. ; in register A and the filename attribute obtained by masking out all but
  3035. ; the highest bit of the character.  For the 'both' case, setting any bit
  3036. ; in the mask other than bit 7 will guarantee a nonzero result.
  3037. ;
  3038. ;    File name: : X 0 0 0  0 0 0 0    (After 80H mask, X=1 if SYS, 0 if DIR)
  3039. ;
  3040. ;    SYS-ONLY   : 0 0 0 0  0 0 0 0    (XOR gives 00H if X=0 and 80H if X=1)
  3041. ;    DIR-ONLY   : 1 0 0 0  0 0 0 0    (XOR gives 80H if X=0 and 00H if X=1)
  3042. ;    BOTH       : 0 0 0 0  0 0 0 1    (XOR gives 01H if X=0 and 81H if X=1)
  3043.  
  3044.      if    diron or attchk
  3045.  
  3046. getsbit:
  3047.     dec    a        ; Adjust to returned value from 1..4 to 0..3
  3048.     rrca            ; Multiply by 32 to convert number to
  3049.     rrca            ; ..offset into TBUFF
  3050.     rrca
  3051.     ld    c,a        ; Save offset in TBUFF in C
  3052.     ld    a,10        ; Add 10 to point to SYS attribute bit
  3053.     call    dirptr        ; A = SYS byte
  3054.     and    80h        ; Look only at attribute bit
  3055. systst    equ    $+1        ; In-the-code variable
  3056.     xor    0        ; If SYSTST=0, SYS only; if SYSTST=80H, DIR
  3057.                 ; ..only; if SYSTST=1, both SYS and DIR
  3058.     ret            ; NZ if OK, Z if not OK
  3059.  
  3060.      endif    ;diron or attchk
  3061.  
  3062. ;-----------------------------------------------------------------------------
  3063.  
  3064. ; Command:    REN
  3065. ; Function:    To change the name of an existing file
  3066. ; Forms:    REN <New UFN>=<Old UFN>
  3067. ; Notes:    If either file spec is ambiguous, or if the source file does
  3068. ;        not exist, the error handler will be entered.  If a file with
  3069. ;        the new name already exists, the user is prompted for deletion
  3070. ;        and ZEX is turned off during the prompt.
  3071.  
  3072.      if    renon
  3073.  
  3074. ren:
  3075.     ld    hl,tfcb        ; Check for ambiguity in first file name
  3076.     call    ambchk
  3077.     call    fcblog        ; Login to fcb
  3078.     ld    hl,tfcb2    ; Check for ambiguity in second file name
  3079.     call    ambchk
  3080.     xor    a        ; Use current drive for 2nd file
  3081.     ld    (de),a
  3082.     call    srchfst        ; Check for old file's existence
  3083.     jr    nz,ren0a    ; Branch if file exists
  3084. jpnofile:
  3085.     ld    a,ecnofile    ; Set error code for file not found
  3086.     jp    error        ; ..and invoke error handler
  3087. ren0a:
  3088.     call    extest        ; Test for file existence and return if not
  3089.     ld    b,12        ; Exchange new and old file names
  3090.     push    de        ; Save pointer to FCB
  3091.     ld    hl,tfcb2    ; Point to FCB for old file name
  3092. ren0:
  3093.     ld    a,(de)        ; Get character of old name
  3094.     ld    c,a        ; ..into C register
  3095.     ld    a,(hl)        ; Get character of new name
  3096.     ld    (de),a        ; ..into place in old name
  3097.     ld    (hl),c        ; Put character of old name into new name
  3098.     inc    hl        ; Advance pointers
  3099.     inc    de
  3100.     djnz    ren0
  3101.  
  3102. ; Perform rename function
  3103.  
  3104.     pop    de        ; Restore pointer to FCB
  3105.     ld    c,17h        ; BDOS rename function
  3106.     jp    bdostest
  3107.  
  3108.      endif    ;renon
  3109.  
  3110. ;-----------------------------------------------------------------------------
  3111.  
  3112. ; Command:    ERA
  3113. ; Function:    Erase files
  3114. ; Forms:
  3115. ;    ERA <afn>    Erase specified files and dislay their names
  3116. ;    ERA <afn> I    Display names of files to be erased and prompt for
  3117. ;            inspection before erase is performed. (Character 'I'
  3118. ;            is defined by INSPCH in Z33HDR.LIB; if it is ' ', then
  3119. ;            any character triggers inspection.)
  3120.  
  3121.      if    eraon
  3122.  
  3123. era:
  3124.      if    inspfl and eraok; 'I' flag and verification enabled?
  3125.     ld    a,(tfcb2+1)    ; Get flag, if any, entered by user
  3126.     ld    (eraflg),a    ; Save it in code below
  3127.      endif    ;erav and eraok
  3128.  
  3129.     ld    de,tfcb        ; Point to target FCB
  3130.     call    fcblog        ; ..and log into the specified directory
  3131.  
  3132.      if    diron or attchk    ; Attribute checking only in these cases
  3133.     ld    b,1        ; Display all matching files
  3134.      endif    ;diron or attchk
  3135.  
  3136.     call    dirpr        ; Print directory of erased files
  3137.     ret    z        ; Abort if no files
  3138.  
  3139.      if    eraok        ; Print prompt
  3140.  
  3141.      if    inspfl        ; Test verify flag
  3142.  
  3143. eraflg    equ    $+1        ; Address of flag (in-the-code modification)
  3144.     ld    a,0
  3145.     cp    inspch        ; Is it an inspect option?
  3146.  
  3147.      if    inspch ne ' '    ; If an explicit inspect character is specified
  3148.     jr    nz,era2        ; ..skip prompt if it is not that character
  3149.      else            ; If INSPCH is the space character
  3150.     jr    z,era2        ; ..then skip prompt only if FCB has a space
  3151.      endif    ;inspch ne ' '
  3152.  
  3153.      endif    ;inspfl
  3154.  
  3155.     call    printc
  3156.     defb    'OK to Erase','?'+80h
  3157.     call    conin        ; Get reply
  3158.     cp    'Y'        ; Yes?
  3159.     ret    nz        ; Abort if not
  3160.  
  3161.      endif    ; eraok
  3162.  
  3163. era2:
  3164.     ld    de,tfcb
  3165.     jp    delete        ; Delete files and return
  3166.  
  3167.      endif            ; Eraon
  3168.  
  3169. ;-----------------------------------------------------------------------------
  3170.  
  3171. ; Command:    LIST
  3172. ; Function:    Print out specified file on the LST: device
  3173. ; Forms:    LIST <ufn>    Print file (No Paging)
  3174. ; Notes:    The flags which apply to TYPE do not take effect with LIST
  3175.  
  3176.      if    lton
  3177.  
  3178. list:
  3179.     ld    a,0ffh        ; Turn on printer flag
  3180.     jr    type0
  3181.  
  3182. ;-----------------------------------------------------------------------------
  3183.  
  3184. ; Command:    TYPE
  3185. ; Function:    Print out specified file on the CON: Device
  3186. ; Forms:    TYPE <ufn>    Print file with default paging option
  3187. ;        TYPE <ufn> P    Print file with paging option reversed
  3188.  
  3189. type:
  3190.     xor    a        ; Turn off printer flag
  3191.  
  3192. ; Common entry point for LIST and TYPE functions
  3193.  
  3194. type0:
  3195.     ld    (prflg),a    ; Set printer/console flag
  3196.     ld    a,(tfcb2+1)    ; Check for user page toggle ('P') option
  3197.     ld    (pgflg),a    ; Save it as a flag in code below
  3198.     ld    hl,tfcb        ; Point to target file FCB
  3199.     call    ambchk        ; Check for ambiguous file spec (vectors to
  3200.                 ; ..error handler if so)
  3201.     call    fcblog        ; Log into specified directory
  3202.     call    open        ; Open the file
  3203.  
  3204.      if    renon        ; If REN on, share code
  3205.     jr    z,jpnofile
  3206.      else    ;not renon    ; Otherwise repeat code here
  3207.     ld    a,ecnofile
  3208.     jp    z,error
  3209.      endif    ;renon
  3210.  
  3211.     call    crlf        ; New line
  3212.     ld    a,(crttxt0)    ; Set line count using value from the
  3213.                 ; ..environment for CRT0
  3214.     inc    a        ; One extra the first time through
  3215.     ld    (pagcnt),a
  3216.     ld    bc,080h        ; Set character position and tab count
  3217.                 ; (B = 0 = tab, C = 080h = char position)
  3218.  
  3219. ; Main loop for loading next block
  3220.  
  3221. type2:
  3222.     ld    a,c        ; Get character count
  3223.     cp    80h        ; If not end of disk record
  3224.     jr    c,type3        ; ..then skip
  3225.  
  3226.     call    readf        ; Read next record of file
  3227.     ret    nz        ; Quit if end of file
  3228.  
  3229.     ld    c,0        ; Reset character count
  3230.     ld    hl,tbuff    ; Point to first character
  3231.  
  3232. ; Main loop for printing characters in TBUFF
  3233.  
  3234. type3:
  3235.     ld    a,(hl)        ; Get next character
  3236.     and    7fh        ; Mask out MSB
  3237.     cp    1ah        ; Check for end of file (^z)
  3238.     ret    z        ; Quit if so
  3239.  
  3240. ; Output character to CON: or LST: device with tabulation
  3241.  
  3242.     cp    cr        ; If carriage return,
  3243.     jr    z,type4        ; ..branch to reset tab count
  3244.     cp    lf        ; If line feed, then output
  3245.     jr    z,type4a    ; ..with no change in tab count
  3246.     cp    tab        ; If tab
  3247.     jr    z,type5        ; ..expand to spaces
  3248.  
  3249. ; Output character and increment character count
  3250.  
  3251.     call    lcout        ; Output character
  3252.     inc    b        ; Increment tab count
  3253.     jr    type6
  3254.  
  3255. ; Output CR and reset tab count
  3256.  
  3257. type4:
  3258.     ld    b,0        ; Reset tab counter
  3259.  
  3260. ; Output LF and leave tab count as is
  3261.  
  3262. type4a:
  3263.     call    lcout        ; Output <cr> or <lf>
  3264.     jr    type6
  3265.  
  3266. ; Process tab character
  3267.  
  3268. type5:
  3269.     ld    a,' '        ; Space
  3270.     call    lcout
  3271.     inc    b        ; Increment tab count
  3272.     ld    a,b
  3273.     and    7
  3274.     jr    nz,type5    ; Loop until column = n * 8 + 7
  3275.  
  3276. ; Continue processing
  3277.  
  3278. type6:
  3279.     inc    c        ; Increment character count
  3280.     inc    hl        ; Point to next character
  3281.     push    bc
  3282.     call    break        ; Check for user abort
  3283.     pop    bc
  3284.     ret    z        ; Quit if so
  3285.     jr    type2        ; Else back for more
  3286.  
  3287. ;--------------------
  3288.  
  3289. ; Output character in A to console or list device depending on a flag.
  3290. ; Registers are preserved.  This code is used only by the LIST and TYPE
  3291. ; commands.
  3292.  
  3293. lcout:
  3294.     push    af        ; Save character
  3295. prflg    equ    $+1        ; Pointer for in-the-code modification
  3296.     ld    a,0        ; ..to determine destination (CON or LST)
  3297.     or    a        ; Z=type, NZ=list
  3298.     jr    z,lc1
  3299.  
  3300.                 ; Output to list device
  3301.  
  3302.     pop    af        ; Get character back
  3303.     push    de
  3304.     push    bc
  3305.     ld    c,5        ; LISTOUT function
  3306.     jp    output
  3307.  
  3308.                 ; Output to console with paging
  3309.  
  3310. lc1:
  3311.     pop    af        ; Get character back
  3312.     push    af        ; Save it again for page check
  3313.     call    conout        ; Output to console
  3314.     pop    af        ; Get character back again
  3315.     cp    lf        ; Check for new line (paging)
  3316.     ret    nz        ; If not new line, we are done
  3317.  
  3318.                 ; Paging routines
  3319.  
  3320. pager:
  3321.     push    hl
  3322.     ld    hl,pagcnt    ; Decrement lines remaining on screen
  3323.     dec    (hl)
  3324.     jr    nz,pager1    ; Jump if not end of page
  3325.  
  3326.                 ; New page
  3327.     ld    a,(crttxt0)    ; Get full page count from environment
  3328.     ld    (hl),a        ; Reset count to a full page
  3329. pgflg    equ    $+1        ; Pointer to in-the-code buffer pgflg
  3330.     ld    a,0
  3331.     cp    pagech        ; Page default override option wanted?
  3332.  
  3333.      if    pagech ne ' '    ; If using explicit character for page toggle
  3334.  
  3335.      if    pagefl        ; If paging is default
  3336.     jr    z,pager1    ; ..PAGECH means no paging
  3337.      else            ; If paging not default
  3338.     jr    nz,pager1    ; ..PAGECH means please paginate
  3339.      endif    ;pagefl
  3340.  
  3341.      else            ; Any character toggles paging
  3342.  
  3343.      if    pagefl        ; If paging is default
  3344.     jr    nz,pager1    ; ..any character means no paging
  3345.      else            ; If paging not default
  3346.     jr    z,pager1    ; ..any character means please paginate
  3347.      endif    ;pagefl
  3348.  
  3349.      endif    ;pagech ne ' '
  3350.  
  3351.                 ; End of page
  3352.     push    bc
  3353.     call    bios+9        ; Wait for user input (BIOS console input)
  3354.     pop    bc
  3355.     cp    'C'-'@'        ; Did user enter control-c?
  3356.     jp    z,nextcmd    ; If so, terminate this command
  3357.  
  3358. pager1:
  3359.     pop    hl        ; Restore HL
  3360.     ret
  3361.  
  3362.      endif    ; lton
  3363.  
  3364. ;-----------------------------------------------------------------------------
  3365.  
  3366. ; Command: SAVE
  3367. ; Function:  To save the contents of the TPA onto disk as a file
  3368. ; Forms:
  3369. ;    SAVE <Number of Pages> <ufn>
  3370. ;        Save specified number of pages (starting at 100H) from TPA
  3371. ;        into specified file
  3372. ;
  3373. ;    SAVE <Number of Sectors> <ufn> <S>
  3374. ;        Like SAVE above, but numeric argument specifies
  3375. ;        number of sectors rather than pages
  3376.  
  3377.      if    saveon
  3378.  
  3379. ; Entry point for SAVE command
  3380.  
  3381. save:
  3382.     call    number        ; Extract number from command line
  3383.     jr    c,badnumber    ; Invoke error handler if bad number
  3384.     push    bc        ; Save the number
  3385.     call    reparse        ; Reparse tail after number of sectors/pages
  3386.     pop    hl        ; Get sector/page count back into HL
  3387.     ld    a,(tfcb2+1)    ; Check sector flag in second FCB
  3388.     cp    sectch
  3389.  
  3390.      if    sectch ne ' '    ; If using a specific character, then jump
  3391.     jr    z,save0        ; ..if it is that character
  3392.      else            ; If allowing any character (SECTCH=' ')
  3393.     jr    nz,save0    ; ..jump if it is anything other than space
  3394.      endif    ;sectch ne ' '
  3395.  
  3396.     add    hl,hl        ; Double page count to get sector count
  3397. save0:
  3398.     ld    a,1        ; Maximum allowed value in H
  3399.     cp    h        ; Make sure sector count < 512 (64K)
  3400.     jr    c,badnumber    ; If >511, invoke error handler
  3401.  
  3402.     push    hl        ; Save sector count
  3403.     ld    hl,tfcb
  3404.     call    ambchk        ; Check for ambiguous file spec (vectors to
  3405.                 ; ..error handler if so)
  3406.  
  3407.     call    extest        ; Test for existence of file and abort if so
  3408.     ld    c,16h        ; BDOS make file function
  3409.     call    bdostest
  3410.     jr    z,save3        ; Branch if error in creating file
  3411.  
  3412.     pop    bc        ; Get sector count into BC
  3413.     ld    hl,tpa-80h    ; Set pointer to one record before TPA
  3414.  
  3415. save1:
  3416.     ld    a,b        ; Check for BC = 0
  3417.     or    c
  3418.     dec    bc        ; Count down on sectors (flags unchanged,
  3419.                 ; ..B=0FFH if all records written successfully)
  3420.     jr    z,save2        ; If BC=0, save is done so branch
  3421.  
  3422.     push    bc        ; Save sector count
  3423.     ld    de,80h        ; Advance address by one record
  3424.     add    hl,de
  3425.     push    hl        ; Save address on stack
  3426.     ex    de,hl        ; Put address into DE for BDOS call
  3427.     call    dmaset        ; Set DMA address for write
  3428.     ld    de,tfcb        ; Write sector
  3429.     ld    c,15h        ; BDOS write sector function
  3430.     call    bdossave
  3431.     pop    hl        ; Get address back into HL
  3432.     pop    bc        ; Get sector count back into BC
  3433.     jr    z,save1        ; If write successful, go back for more
  3434.  
  3435.     ld    b,0        ; B=0 if write failed
  3436.  
  3437. save2:
  3438.     call    close        ; Close file even if last write failed
  3439.     and    b        ; Combine close return code with
  3440.                 ; ..write success flag
  3441.     ret    nz        ; Return if all ok
  3442.  
  3443. save3:                ; Disk must be full
  3444.     ld    a,ecdiskfull    ; Disk full error code
  3445.     jr    jperror
  3446.  
  3447.      endif    ; saveon
  3448.  
  3449. ;-----------------------------------------------------------------------------
  3450.  
  3451.      if    lton or    saveon or renon or geton
  3452.  
  3453. ; Check file control block pointed to by HL for any wildcard characters ('?').
  3454. ; Return to calling program if none found.  Otherwise branch to error handler.
  3455. ; The routine also treats an empty file name as ambiguous.
  3456.  
  3457. ambchk:
  3458.     push    hl        ; Save pointer to FCB
  3459.     inc    hl        ; Point to first character in file name
  3460.     ld    a,(hl)        ; See if first character is a space
  3461.     cp    ' '
  3462.     jr    z,ambchk1    ; If so, branch to error return
  3463.  
  3464.     ld    a,'?'        ; Set up for scan for question mark
  3465.     ld    bc,11        ; Scan 11 characters
  3466.     cpir
  3467.     pop    de        ; Restore pointer to FCB in DE
  3468.     ret    nz        ; Return if no '?' found
  3469. ambchk1:
  3470.     ld    a,ecambig    ; Error code for ambiguous file name
  3471.     jr    jperror
  3472.  
  3473.      endif    ;lton or renon or saveon or geton
  3474.  
  3475.      if    lton or renon or saveon or geton or jumpon
  3476.  
  3477. badnumber:
  3478.     ld    a,ecbadnum    ; Error code for bad number value
  3479. jperror:            ; Local entry point for relative jump
  3480.     jp    error        ; ..to go to error handler
  3481.  
  3482.      endif    ;lton or renon or saveon or geton or jumpon
  3483.  
  3484. ;-----------------------------------------------------------------------------
  3485.  
  3486. ; Command:    JUMP
  3487. ; Function:    To execute a program already loaded into some specified memory
  3488. ;        address
  3489. ; Forms:    JUMP <adr> <tail>
  3490. ;        The address is in hex; the tail will be parsed as usual
  3491.  
  3492.      if    jumpon
  3493.  
  3494. jump:
  3495.     call    hexnum        ; Get load address into BC
  3496.     jr    c,badnumber    ; If bad number, invoke error handling
  3497.     push    bc        ; ..and save it
  3498.     call    reparse        ; Reparse tail after address value
  3499.     pop    hl        ; Restore execution address to HL
  3500.     jr    getproglf    ; Perform call via code below
  3501.  
  3502.      endif    ;jumpon
  3503.  
  3504. ;-----------------------------------------------------------------------------
  3505.  
  3506. ; Command:    GO
  3507. ; Function:    To Call the program in the TPA without loading
  3508. ;        loading from disk. Same as JUMP 100H, but much
  3509. ;        more convenient, especially when used with
  3510. ;        parameters for programs like STAT. Also can be
  3511. ;        allowed on remote-access systems with no problems.
  3512. ;
  3513. ;Form:        GO <tail>
  3514.  
  3515.      if    goon
  3516.  
  3517. go:
  3518.     ld    hl,tpa        ; Set up TPA as the execution address
  3519.  
  3520.      endif    ; goon
  3521.  
  3522.      if    jumpon or goon    ; Common code
  3523.  
  3524. getproglf:
  3525.     ld    (execadr),hl
  3526.     xor    a        ; Set zero flag to enable leading CRLF
  3527.     jp    callproglf    ; Perform call (with leading CRLF)
  3528.  
  3529.      endif    ;jumpon or goon
  3530.  
  3531. ;-----------------------------------------------------------------------------
  3532.  
  3533. ; Command:    GET
  3534. ; Function:    To load the specified file from disk to the specified address
  3535. ; Forms:    GET <adr> <ufn> 
  3536. ;        Loads the specified file to the specified hexadecimal address
  3537. ;        Note that the normal file search path is used to find the file.
  3538. ;        If SCANCUR is off, the file may not be found in the current
  3539. ;        directory unless a colon is included in the file spec.
  3540.  
  3541.      if    geton
  3542.  
  3543. get:
  3544.  
  3545. ; TMPCOLON was set when the file name was parsed.  We use that as the colon
  3546. ; flag so that the file will be loaded from a directory just as if it had
  3547. ; been entered as the command name.
  3548.  
  3549.      if    drvprefix and [not scancur]
  3550.     ld    a,(tmpcolon)    ; Allow GET to load from specified
  3551.     ld    (colon),a    ; directory
  3552.      endif    ;drvprefix and [not scancur]
  3553.  
  3554.     ld    hl,tfcb2    ; Copy TFCB2 to CMDFCB for load
  3555.     push    hl
  3556.     ld    de,cmdfcb
  3557.     ld    bc,14
  3558.     ldir
  3559.     pop    hl
  3560.     call    ambchk        ; Make sure file is not ambiguous (vectors
  3561.                 ; ..to error handler if so)
  3562.  
  3563. ; If GET fails to find the specified file along the search path, we do not
  3564. ; want the ECP to be engaged.  To prevent that, we fool the command processor
  3565. ; by telling it that the ECP is already engaged.
  3566.  
  3567.     ld    hl,cmdstatfl    ; Point to command status flag
  3568.     set    2,(hl)        ; Turn on ECP flag to prevent use of ECP
  3569.     call    hexnum        ; Get load address into BC
  3570.     jr    c,badnumber    ; If invalid number, invoke error handler
  3571.  
  3572.      if    not fullget
  3573.     ld    a,b        ; If trying to load into base page
  3574.     or    a        ; ..treat as error
  3575.     jr    z,badnumber
  3576.      endif    ;not fullget
  3577.  
  3578.     ld    h,b        ; Move address into HL
  3579.     ld    l,c
  3580.     ld    a,0ffh        ; Disable dynamic loading
  3581.                 ; Fall through to mload
  3582.  
  3583.      endif    ; geton
  3584.  
  3585. ; End ZCPR33-5.Z80
  3586.  
  3587.     page
  3588.  
  3589. ; ZCPR33-6.Z80
  3590.  
  3591. ;=============================================================================
  3592. ;
  3593. ;   P A T H    S E A R C H    A N D    F I L E    L O A D I N G    C O D E
  3594. ;
  3595. ;=============================================================================
  3596.  
  3597. ; This block of code loads a file into memory.  The normal address at which
  3598. ; loading is to begin is passed to the routine in the HL register.  The name
  3599. ; of the file to load is passed in the command file control block.
  3600. ;
  3601. ; This code supports an advanced option that loads files to a dynamic address
  3602. ; specified in the header to the file using a new type-3 environment.  In a
  3603. ; type-3 environment, the execution/load address is stored in the word
  3604. ; following the environment descriptor address.  A value is passed to MLOAD in
  3605. ; the A register that controls this dynamic loading mechanism.  The value
  3606. ; specifies the lowest environment type value for which dynamic loading will
  3607. ; be performed.  This value will be 3 when MLOAD is called for normal COM file
  3608. ; execution and will be 0FFH when chained to from the GET command.  In the
  3609. ; latter case, the user-specified load address must be used.
  3610. ;
  3611. ; MLOAD guards against loading a file over the operating system.  It computes
  3612. ; the lower of the following two addresses: 1) the CPR entry point; 2) the
  3613. ; bottom of protected memory as indicated by the DOS entry address stored at
  3614. ; address 0006H.  If the load would exceed this limit, error handling is
  3615. ; engaged (except for the GET command when FULLGET is enabled).
  3616.  
  3617. mload:
  3618.     ld    (envtype),a    ; Set up in-the-code modification below
  3619.     ld    (execadr),hl    ; Set up execution/load address
  3620.     call    defltdma    ; Set DMA address to 80H for file searches
  3621.  
  3622.  
  3623. ; This code sets the attributes of COM files which are acceptable.  If both
  3624. ; SYS and DIR type files are acceptable, there is no need to include this code,
  3625. ; and ATTCHK can be set to false.
  3626.  
  3627.      if    attchk        ; Only if attribute checking enabled
  3628.     ld    a,comatt    ; Attributes specified in Z33HDR.LIB
  3629.     ld    (systst),a    ; Set flag
  3630.      endif    ;attchk
  3631.  
  3632. ;-----------------------------------------------------------------------------
  3633.  
  3634. ; PATH BUILDING CODE
  3635.  
  3636. ; In ZCPR33 the minpath feature, optional in ZCPR30, is always used.  To
  3637. ; minimize the size of the CPR code, however, there is an option to place the
  3638. ; minpath in an external buffer (outside the CPR).  If the path is short
  3639. ; enough, the minpath can be placed at the bottom of the system stack.
  3640.  
  3641.     ld    de,path        ; Point to first element in user's symbolic path
  3642.     ld    hl,mpath    ; Point to minpath buffer
  3643.     xor    a
  3644.     ld    (hl),a        ; Initialize to empty minpath
  3645.  
  3646.  
  3647. ; If DRVPREFIX is enabled, the CPR will recognize an explicit directory
  3648. ; reference in a command.  The first element of the path will then be this
  3649. ; explicit directory.  If no explicit directory was given in the command,
  3650. ; then no entry is made into the search path.  If the WPREFIX option is
  3651. ; on, explicit directory prefixes will be recognized only when the wheel
  3652. ; byte is on.
  3653.  
  3654.      if    drvprefix    ; Pay attention to du:com prefix?
  3655.  
  3656.     ld    a,(colon)    ; See if colon was present in command
  3657.     or    a
  3658.     jr    z,makepath2    ; If not, skip ahead
  3659.  
  3660.      if    wprefix
  3661.     ld    a,(z3whl)    ; See if wheel byte is on
  3662.     or    a
  3663.     jr    z,makepath2    ; If not, skip ahead
  3664.      endif    ;wprefix
  3665.  
  3666.     ld    a,(cmdfcb)    ; Get drive from command FCB
  3667.     ld    (hl),a        ; Put drive into minpath
  3668.     inc    hl        ; Advance pointer
  3669.     ld    a,(cmdfcb+13)    ; Get user number from command FCB
  3670.     ld    (hl),a        ; Put it into minpath
  3671.     inc    hl        ; Advance pointer to next path element
  3672.     xor    a        ; A=0
  3673.     ld    (hl),a        ; Store ending 0 in mpath
  3674. makepath2:
  3675.      endif    ; drvprefix
  3676.  
  3677.  
  3678. ; If SCANCUR is enabled in Z33HDR.LIB, then we always include the current
  3679. ; directory automatically, even without a '$$' element in the user's path.
  3680. ; If WPREFIX is enabled, however, we do not want to allow the current
  3681. ; directory to be included, but we must make sure that it is included in
  3682. ; the building of the root path, in case the user's symbolic path is empty.
  3683.  
  3684.      if    scancur        ; Scan current directory at all times?
  3685.  
  3686.     ld    bc,(curusr)    ; C = current user, B = current drive
  3687.     inc    b        ; Set drive to range 1..16
  3688.  
  3689.      if    wprefix
  3690.  
  3691.     ld    a,(z3whl)    ; See if wheel byte is on
  3692.     or    a
  3693.     jr    nz,addpath    ; If it is, add element to path; if not,
  3694.                 ; ..fall through to MAKEPATH3
  3695.      else    ;not wprefix
  3696.  
  3697.     jr    addpath        ; Begin loop of placing entries into mpath
  3698.  
  3699.      endif    ;wprefix
  3700.  
  3701.      else    ;not scancur
  3702.  
  3703. ; If SCANCUR is off and ROOTONLY is in effect, we have to make sure that some
  3704. ; directory values are put into the root path in the case where the user's
  3705. ; path is completely empty.  To do so, we preset BC for directory A0.
  3706.  
  3707.      if    rootonly
  3708.     ld    bc,0100h    ; Setup for drive A (B=1), user 0 (C=0)
  3709.      endif    ;rootonly
  3710.  
  3711.      endif    ;scancur
  3712.  
  3713.  
  3714. ; Convert symbolic entries in user's path into absolute DU values in minpath.
  3715. ; Entries are read one-by-one from the symbolic path.  If the 'current' drive
  3716. ; or user indicator is present (default symbol is '$'), then the current
  3717. ; drive or user value is fetched.  Otherwise the explicit binary value from the
  3718. ; path is used.  After each absolute DU value is formed, the minpath as it
  3719. ; exists so far is scanned to see if this DU value is already there.  If it is
  3720. ; not, then the DU value is appended to the path.  Otherwise it is ignored.
  3721.  
  3722. makepath3:
  3723.     ld    a,(de)        ; Get next symbolic path entry
  3724.     or    a        ; If 0, we are at end of path
  3725.     jr    z,makepath6
  3726.  
  3727.     ld    bc,(curusr)    ; C = current user, B = current drive
  3728.     inc    b        ; Set drive to range 1..16
  3729.     cp    curind        ; Check for current drive symbol (default '$')
  3730.     jr    z,makepath4    ; If so, leave current drive in B
  3731.     ld    b,a        ; Else move specified drive into B
  3732. makepath4:
  3733.     inc    de        ; Point to user value in symbolic path
  3734.     ld    a,(de)        ; Get user
  3735.     inc    de        ; Point to next element in symbolic path
  3736.     cp    curind        ; Check for current user symbol (default '$')
  3737.     jr    z,makepath5    ; If so, leave current drive in C
  3738.     ld    c,a        ; Else move specified user into C
  3739. makepath5:
  3740.  
  3741. ; At this point in the code we have a potential path element in BC.  We first
  3742. ; have to scan the minpath we have so far to see if that element is already
  3743. ; there.  In that case we ignore it; otherwise we add it to the end of the path.
  3744.  
  3745. addpath:
  3746.             ; Skip path if directory given explicitly
  3747.  
  3748.      if    skippath
  3749.  
  3750.      if    wprefix
  3751.     ld    a,(z3whl)    ; See if wheel byte is on
  3752.     or    a
  3753.     call    nz,skipchk    ; If not, fall through
  3754.      else    ;not wprefix
  3755.     call    skipchk        ; See if path should be skipped
  3756.      endif    ;wprefix
  3757.  
  3758.     jr    nz,makepath3    ; If so, branch out of ADDPATH
  3759.  
  3760.      endif    ;skippath
  3761.  
  3762.     ld    hl,mpath    ; Point to beginning of minpath
  3763.  
  3764. addpath1:            ; Point of reentry
  3765.     ld    a,(hl)        ; Get drive value
  3766.     or    a        ; Check for end of minpath
  3767.     jr    z,addpath2    ; If end, jump and add BC to minpath
  3768.  
  3769.     inc    hl        ; Increment pointer to user
  3770.     cp    b        ; Check for drive match
  3771.     ld    a,(hl)        ; Get user from minpath
  3772.     inc    hl        ; Point to next minpath entry
  3773.     jr    nz,addpath1    ; If drive was different, loop back again
  3774.     cp    c        ; Check for user match
  3775.     jr    nz,addpath1    ; If user is different, loop back again
  3776.     jr    makepath3    ; Branch if we have a duplicate
  3777.  
  3778. ; We have a new DU; add it to minpath
  3779.  
  3780. addpath2:
  3781.     ld    (hl),b        ; Store drive
  3782.     inc    hl
  3783.     ld    (hl),c        ; Store user
  3784.     inc    hl
  3785.     ld    (hl),0        ; Store ending 0
  3786.     jr    makepath3    ; Continue scanning user's path
  3787.  
  3788. ; If the ECP facility is set up to use the root directory, then create a
  3789. ; root path.  BC presently contains the proper DU.
  3790.  
  3791. makepath6:
  3792.  
  3793.      if    rootonly
  3794.     ld    hl,rootpth    ; Point to special path to contain root
  3795.     ld    (hl),b        ; Store disk
  3796.     inc    hl
  3797.     ld    (hl),c        ; Store user
  3798.      endif    ;rootonly
  3799.  
  3800. ;-----------------------------------------------------------------------------
  3801.  
  3802. ; This is the code for loading the specified file by searching the minpath.
  3803.  
  3804.     xor    a        ; Always use current disk specification in the
  3805.     ld    (cmdfcb),a    ; ..command FCB
  3806.  
  3807. mload1:
  3808.  
  3809.     ld    hl,mpath    ; Point to beginning of minpath
  3810.  
  3811. mload2:
  3812.  
  3813. ; Either the FASTECP or BADDUECP option may have set FIRSTCHAR to a space
  3814. ; character as a signal to go directly to extended command processing.  If
  3815. ; neither option is enabled but SKIPPATH is, then the FIRSTCHAR data is
  3816. ; stored in the routine below where path skipping is implemented.
  3817.  
  3818.      if    fastecp or badduecp
  3819.  
  3820.     ld    a,(cmdstatfl)    ; If ECP is running
  3821.     bit    2,a        ; ..we branch to look for ECP along path
  3822.     jr    nz,mload2a
  3823. firstchar equ    $+1        ; Pointer for in-the-code modification
  3824.     ld    a,0
  3825.     cp    ' '        ; Was command invoked with leading space?
  3826.     jr    z,ecprun    ; If so, go directly to ECP code
  3827.  
  3828.      endif    ;fastecp or badduecp
  3829.  
  3830. mload2a:
  3831.     ld    a,(hl)        ; Get drive from path
  3832.     or    a        ; If end of path, command not found
  3833.     jr    nz,mload3    ; If not end of path, skip over ECP code
  3834.  
  3835. ;-----------------------------------------------------------------------------
  3836.  
  3837. ; EXTENDED COMMAND PROCESSING
  3838.  
  3839. ; At this point we have exhausted the search path.  We now engage the
  3840. ; extended command processor.
  3841.  
  3842. ecprun:
  3843.      if    skippath
  3844.     call    skipchk        ; See if path should be skipped
  3845.     jr    nz,jnzerror    ; If so, invoke error handler
  3846.      endif    ;skippath
  3847.  
  3848.     ld    hl,cmdstatfl    ; Point to command status flag
  3849.     ld    a,(hl)        ; ..and get value
  3850.     and    110b        ; Isolate ECP and error handler bits
  3851. jnzerror:            ; If either is set,
  3852.     ld    a,ecnocmd    ; Error code for command not found
  3853.     jp    nz,error    ; ..process as an error
  3854.  
  3855.     set    2,(hl)        ; Set ECP bit
  3856.  
  3857.     ld    hl,ecpfcb    ; Copy name of ECP to command FCB
  3858.     ld    de,cmdfcb
  3859.     ld    bc,12        ; Only 12 bytes required
  3860.     ldir
  3861.  
  3862.     ld    hl,(cmdptr)    ; Get pointer to current command line
  3863.     call    parsetail    ; Parse entire command as the command tail
  3864.  
  3865.      if    rootonly    ; Look for ECP in root directory only
  3866.     ld    hl,rootpth    ; Point to path containing root directory only
  3867.     jr    mload2        ; Search for command
  3868.      else    ; not rootonly
  3869.     jr    mload1        ; Search the entire minpath for the ECP
  3870.      endif    ; rootonly
  3871.  
  3872. ;-----------------------------------------------------------------------------
  3873.  
  3874. mload3:
  3875.     ld    b,a        ; Drive into B
  3876.     inc    hl        ; Point to user number
  3877.     ld    c,(hl)        ; User into C
  3878.     ld    (tempusr),bc    ; Save the values
  3879.     inc    hl        ; Point to next entry in path
  3880.     call    logtemp        ; Log in path-specified user/drive
  3881.  
  3882.      if    attchk        ; If allowing execution only of COM files with
  3883.                 ; ..specific attributes
  3884.  
  3885.     ld    de,cmdfcb    ; Point to command FCB
  3886.     call    srchfst        ; Look for directory entry for file
  3887.     jr    z,mload2a    ; Continue path search if file not found
  3888.     push    hl        ; Save path pointer
  3889.     call    getsbit        ; Check system attribute bit
  3890.     pop    hl        ; Restore path pointer
  3891.     jr    z,mload2a    ; Continue if attributes do not match
  3892.     call    opencmd        ; Open file for input
  3893.     jr    z,mload2a    ; If open failed, back to next path element
  3894.  
  3895.      else    ;not attchk
  3896.  
  3897.     call    opencmd        ; Open file for input
  3898.     jr    z,mload2a    ; If open failed, back to next path element
  3899.  
  3900.      endif    ; attchk
  3901.  
  3902.     call    readcmd        ; Read first record into default DMA address
  3903.     jr    nz,mload5    ; Branch if zero-length file
  3904.     xor    a        ; Set file current record back to zero
  3905.     ld    (cmdfcb+20h),a
  3906.     ld    hl,80h        ; Pointer to start of code
  3907.     call    z3chk
  3908.     jr    nz,mload3a    ; If not Z3 file, branch
  3909.  
  3910. ; The following test is modified by earlier code.  For normal COM file loading,
  3911. ; a 3 is inserted for the minimum environment type for dynamic load address
  3912. ; determination.  For the GET command, where the user-specified address should
  3913. ; be used, a value of 0FFH is put in here so the carry flag will always be set.
  3914.  
  3915. envtype    equ    $+1        ; Pointer for in-the-code modification
  3916.     cp    3        ; See if no higher than a type-3 environment
  3917.     jr    c,mload3a    ; If higher than type 3, branch
  3918.  
  3919.     inc    hl        ; Advance to load address word
  3920.     inc    hl
  3921.     inc    hl
  3922.     ld    a,(hl)        ; Get load address into HL
  3923.     inc    hl
  3924.     ld    h,(hl)
  3925.     ld    l,a
  3926.     ld    (execadr),hl    ; Set new execution/load address
  3927.  
  3928. mload3a:
  3929.     ld    hl,(execadr)    ; Get initial loading address
  3930.  
  3931. ; Load the file, making sure neither CPR nor protected memory is overwritten
  3932.  
  3933. mload4:
  3934.      if    fullget
  3935.     ld    a,(envtype)    ; If ENVTYPE is FF (from GET command)
  3936.     inc    a        ; ..then skip memory limit checking
  3937.     jr    z,mload4b
  3938.      endif    ;fullget
  3939.  
  3940.      if    rel
  3941.     ld    bc,entry    ; We have to use a relocatable form to get
  3942.     dec    b        ; ..highest page below the CPR
  3943.      else    ;not rel
  3944.     ld    b,high entry - 1 ; We can use shorter code for absolute form
  3945.      endif    ;rel
  3946.  
  3947.     ld    a,(0007h)    ; Get highest page below
  3948.     dec    a        ; ..protected memory
  3949.     cp    b        ; If A is lower value,
  3950.     jr    c,mload4a    ; ..branch
  3951.     ld    a,b        ; Otherwise use lower value in B
  3952. mload4a:
  3953.     cp    h        ; Are we going to overwrite protected memory?
  3954.     ld    a,ectpafull    ; Get ready with TPA overflow error code
  3955.     jp    c,error        ; Error if about to overwrite protected memory
  3956. mload4b:
  3957.     push    hl        ; Save this load address
  3958.     ex    de,hl        ; Set DMA address
  3959.     call    dmaset
  3960.     call    readcmd
  3961.     pop    hl        ; Get last load address back
  3962.     jr    nz,mload5    ; Read error or eof?
  3963.     ld    de,128        ; Increment load address by 128
  3964.     add    hl,de
  3965.     jr    mload4        ; Continue loading
  3966.  
  3967. ; In case a program would like to find out in what directory the command
  3968. ; processor found the program, temporary DU is stored in bytes 13 (user) and
  3969. ; 14 (drive) in the command FCB.
  3970.  
  3971. mload5:
  3972.  
  3973. tempusr    equ    $+1        ; Pointers for in-the-code modification
  3974. tempdr    equ    $+2
  3975.     ld    hl,0
  3976.     ld    (cmdfcb+13),hl
  3977.  
  3978. logcurrent:            ; Return to original logged directory
  3979.     ld    hl,(curusr)    ; Set L = current user, H = current drive
  3980.     ld    a,h
  3981.     call    setdrive    ; Login current drive
  3982.     ld    a,l
  3983.     jp    setuser        ; Log in new user and return from MLOAD
  3984.  
  3985. ;----------------------------------------
  3986.  
  3987. ; This routine checks to see if building the path or running the ECP should
  3988. ; be skipped.  If there is a colon in the command (an explicit directory
  3989. ; given) but it was not a lone colon (indicating desire to skip resident
  3990. ; commands), then the routine returns with the zero flag reset.
  3991.  
  3992.      if    skippath
  3993.  
  3994. skipchk:
  3995.     ld    a,(colon)    ; Was there a colon in the command?
  3996.     or    a
  3997.     ret    z        ; Return with zero flag set if not
  3998.  
  3999.      if    fastecp or badduecp
  4000.     ld    a,(firstchar)    ; See if the first character was the colon
  4001.      else
  4002. firstchar equ    $+1        ; Put data here if other two options are
  4003.     ld    a,0        ; ..false (in-the-code modification)
  4004.      endif    ;fastecp or badduecp
  4005.  
  4006.     cp    ':'
  4007.     ret            ; Return: Z if lone colon, NZ otherwise
  4008.  
  4009.      endif    ;skippath
  4010.  
  4011.  
  4012. ; End ZCPR33-6.Z80
  4013.  
  4014.     page
  4015.  
  4016. ;-----------------------------------------------------------------------------
  4017. ;
  4018. ;        D A T A    A R E A    D E F I N I T I O N S
  4019. ;
  4020. ;-----------------------------------------------------------------------------
  4021.  
  4022. ; ----------   Page line count buffer
  4023.  
  4024.      if    lton        ; Needed only if TYPE command included
  4025.  
  4026. pagcnt:
  4027.     defs    1        ; Lines left on page (filled in by code)
  4028.  
  4029.      endif    ;lton
  4030.  
  4031.  
  4032. ; ---------- Minpath/Rootpth buffers
  4033.  
  4034.      if    extmpath
  4035.  
  4036. mpath    equ    extmpathadr    ; Assign external minpath address
  4037.  
  4038.      else
  4039.  
  4040. mpath:
  4041.      if    drvprefix
  4042.     defs    2        ; Two bytes for specified DU
  4043.      endif
  4044.  
  4045.      if    scancur
  4046.     defs    2        ; Two bytes for current DU
  4047.      endif
  4048.  
  4049.     defs    2 * expaths    ; Space for path from path buffer
  4050.  
  4051.     defs    1        ; One byte for ending null
  4052.  
  4053.      endif    ; not extmpath
  4054.  
  4055.  
  4056.      if    rootonly
  4057. rootpth:
  4058.     defs    2        ; Special path for root dir only
  4059.     defb    0        ; End of path
  4060.      endif    ; rootonly
  4061.  
  4062. ;-----------------------------------------------------------------------------
  4063.  
  4064. ; The following will cause an error message to appear if
  4065. ; the size of ZCPR33 is over 2K bytes.
  4066.  
  4067.      if    [ $ - entry ] gt 800h
  4068.     *** ZCPR33 IS LARGER THAN 2K BYTES ***
  4069.      endif
  4070.  
  4071.      endif    ;errflag
  4072.  
  4073.     end    ; ZCPR33
  4074.