home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / BEEHIVE / ZSUS / ZSUS002.LBR / CONCAT07.LBR / CONCAT07.MZC / CONCAT07.MAC
Text File  |  1989-12-16  |  22KB  |  887 lines

  1. ; CONCAT.MAC -- file concatenation utility
  2. ;
  3. Vers    equ    07        ; version number
  4. SubVers    equ    'a'        ; modification level
  5. ;
  6. ; Concatenates two or more source files into a destination file.
  7. ; For ZCPR3 only.
  8. ;
  9. ; USAGE:
  10. ;
  11. ;    CONCAT {dir:}outfile = {dir:}infile {{dir:}infile {...}} {/options}
  12. ;
  13. ; Any file without a DU or DIR specification is assumed to be in
  14. ; the current drive/user.
  15. ;
  16. ; OPTIONS:
  17. ;
  18. ;    A    Append mode.  Appends sources files to end of target
  19. ;        file, which must exist.
  20. ;
  21. ;    O    object file mode; ignore ^Z.  The default is text mode,
  22. ;        checking for a ^Z (CP/M end-of-file character).
  23. ;
  24. ;    Q    toggle quiet mode.  A configuration byte at 111h decides
  25. ;        whether CONCAT will print the names of the output and
  26. ;        source files (a non-zero value defaults to quiet mode).
  27. ;        This option puts CONCAT in the opposite mode.  Setting
  28. ;        the ZCPR3 quiet flag also puts CONCAT in quiet mode, but
  29. ;        this option will toggle the meaning of the quiet flag.
  30. ;
  31. ; CONCAT requires an output file and at least one input file.  Actually,
  32. ; it can be used as a simple file-copy utility, but that's not its
  33. ; purpose.  The same filename may be given repeatedly in the input file
  34. ; list.  The equal sign separates the output file from the input files.
  35. ; Commas and/or spaces separate multiple input files.  While the equal
  36. ; sign is required, it may be separated from the filenames by one or
  37. ; more spaces.  A filename cannot begin with a slash unless it is
  38. ; preceded by a DU or DIR specification.
  39. ;
  40. ; If an error occurs, such as an input file not found, the incomplete
  41. ; output file is erased (and the ZCPR3 error flag is set).  If another
  42. ; file has the same name as the output file, it will be renamed to
  43. ; filetype BAK.
  44. ;
  45. ; Append mode (option A) by-passes several of the program's safety
  46. ; features, so use it with caution.  For instance, no temporary file or
  47. ; BAK file is created.  The target (output) file must already exist.  On
  48. ; error, the output file is closed after appending any text that was
  49. ; read before the error occurred.  The output file is not erased.
  50. ;
  51. ; An invalid option will be ignored.  If the quiet flag is set, only
  52. ; error messages will be displayed (unless the Q option is used).
  53. ;
  54. ; On error the ZCPR3 error flag is set as follows:
  55. ;     8    ambiguous or missing output or source filename
  56. ;    10    source or target file not found or not given
  57. ;    11    disk or directory full (write error)
  58. ;     4    all other errors
  59. ;
  60. ; Version 0.7 -- December 17, 1989 -- Gene Pizzetta
  61. ;    Added A (append) option, which by-passes several of CONCAT's
  62. ;    safety features.  A few more optimizations.
  63. ;
  64. ; Version 0.6 -- December 3, 1989 -- Gene Pizzetta
  65. ;    Minor changes in setting error flag.  Corrected serious error
  66. ;    in detecting top of memory, found by Howard Goldstein, who also
  67. ;    suggested several changes to make the code more efficient.
  68. ;    Thanks, Howard.
  69. ;
  70. ; Version 0.5 -- November 25, 1989 -- Gene Pizzetta
  71. ;    Added Q (quiet) option and error flag support.  Fixed parser
  72. ;    bug that allowed a null output filename.  Now closes output
  73. ;    file on error before erasing it.  Tightened code.
  74. ;
  75. ; Version 0.4 -- November 12, 1989 -- Gene Pizzetta
  76. ;    Added large output buffer.  Corrected error in parsing filespec
  77. ;    in current user.  Relaxed command line syntax: commas no longer
  78. ;    required.  Now obeys quiet flag.
  79. ;
  80. ; Version 0.3 -- September 19, 1989 -- Gene Pizzetta
  81. ;    First preliminary release.
  82. ;
  83. ; To report bugs or make suggestions:
  84. ;                Gene Pizzetta
  85. ;                481 Revere Street
  86. ;                Revere, MA 02151
  87. ;
  88. ;                Newton Centre Z-Node:  (617) 965-7259
  89. ;                Lilliput Z-Node:  (312) 649-1730
  90. ;                Voice:  (617) 284-0891
  91. ;
  92. ; Re-assembly requires MAC or SLRMAC and SYSLIB, Version 4.  With MAC,
  93. ; Z80.LIB will also be needed.  
  94. ;
  95. Bdos    equ    05h
  96. CpmDma    equ    80h
  97. TPA    equ    100h
  98. ;
  99. ; Bdos functions . . .
  100. ;
  101. FRead    equ    20
  102. FWrite    equ    21
  103. CurDsk    equ    25
  104. SetDma    equ    26
  105. CurUsr    equ    32
  106. ;
  107. LF    equ    0Ah
  108. CR    equ    0Dh
  109. CpmEOF    equ    1Ah
  110. ;
  111. ; The following output buffer size is in sectors (records) of 128 bytes.
  112. ; It may may be set to any number of sectors up to a maximum of 256 (32K).
  113. ;
  114. BufSiz    equ    256        ; output buffer in sectors
  115. ;
  116.     MACLIB    Z80
  117. ;
  118. ; Following routines are from VLIB, Z3LIB, and SYSLIB, Version 4
  119. ;
  120.     ext    f$exist,f$open,f$mopen,f$rename,f$delete,f$close
  121.     ext    logud,epstr,pfn2,crlf,cout,pafdc,initfcb,codend
  122.     ext    z3vinit,zfname,stndout,stndend,getquiet,gzmtop
  123.     ext    puter2,f$appl
  124. ;
  125.  
  126.     jmp    Start
  127. ;
  128.     db    'Z3ENV'
  129.     db    1
  130. Z3EAdr:    dw    0FE00h        ; address of environment descriptor
  131. ;
  132.     db    'QUIET>'
  133. QtFlag:    db    0        ; 0 defaults to noisy mode
  134.                 ; ..non-zero defaults to quiet mode
  135. ;
  136. ; Messages . . .
  137. ;
  138. MsgUse:    db    'CONCAT    Version '
  139.     db    Vers/10+'0','.',Vers mod 10+'0',SubVers,CR,LF
  140.     db    'Usage:',CR,LF
  141.     db    '   CONCAT {dir:}outfile = {dir:}infile {{dir:}infile {...}}'
  142.     db    ' {/options}',CR,LF
  143.     db    'Concatenates infiles to outfile.',CR,LF
  144.     db    'Options:',CR,LF
  145.     db    '   A   append to existing file',CR,LF
  146.     db    '   O   object files, ignore ^Z',CR,LF
  147.     db    '   Q   toggle quiet mode ',0
  148. MsgUon:    db    'on',0
  149. MsgUof:    db    'off',0
  150. MsgWrt:    db    'Writing to ',0
  151. MsgApd:    db    'Appending to ',0
  152. MsgRd:    db    '  Reading from ',0
  153. MsgNIG:    db    'No source file given.',0
  154. MsgNIF:    db    'File not found.',0
  155. MsgTNF:    db    'Target file not found.',0
  156. MsgFul:    db    'Output file is full.',0
  157. MsgNOG:    db    'No output file given.',0
  158. MsgREr:    db    'Read error.',0
  159. MsgWEr:    db    'Disk full!',0
  160. MsgCEr:    db    'Error closing file.',0
  161. MsgAmb:    db    'No ambiguous filenames.',0
  162. MsgDlm:    db    'Illegal command line.',0
  163. MsgDne:    db    'Done!',0
  164. ;
  165. Start:    lhld    Z3EAdr        ; set up environment
  166.     call    z3vinit
  167.     sspd    OldStk        ; save old stack pointer
  168.     call    gzmtop        ; get top of memory
  169.     sphl            ; ..and setup new stack
  170. ;
  171.     call    codend        ; set buffer addresses
  172.     shld    IWork
  173.     lxi    d,128
  174.     dad    d
  175.     shld    OWork
  176.     call    getquiet    ; is ZCPR quiet flag set?
  177.     jrnz    SetZQt        ; (yes)
  178.     lda    QtFlag        ; no, get quiet config byte
  179. SetZQt:    sta    OpQFlg        ; ..and store in Q option flag
  180.     sub    a
  181.     sta    OpAFlg        ; initialize A option flag
  182.     sta    OpOFlg        ; initialize O option flag
  183.     call    GetDfD        ; get and store default disk and user
  184.     lda    CpmDma        ; see if there's a tail
  185.     ora    a
  186.     jz    OptH        ; (no)
  187. ;
  188. GtTail:    mov    c,a        ; move command tail to storage
  189.     inr    c
  190.     mvi    b,0
  191.     lxi    h,CpmDma+1
  192.     lxi    d,CTail
  193.     ldir
  194. ;
  195.     call    GetOpt        ; get options, if any
  196.     lxi    h,CTail        ; get output filename from tail
  197.     call    EatSpc        ; eat any spaces
  198.     ora    a        ; is it NUL?
  199.     jrz    NoOFl        ; (yes, no filename)
  200.     cpi    '/'        ; is it a slash?
  201.     jrz    NoOFl        ; (yes, no filename)
  202.     cpi    '='        ; is it an equal sign?
  203.     jrnz    GtOFl        ; (no)
  204. NoOFl:    mvi    a,8        ; set error flag
  205.     lxi    h,MsgNOG
  206.     jmp    ErExit
  207. ;
  208. GtOFl:    lxi    d,OutFcb    ; ..and put it in FCB
  209.     call    zfname
  210.     jrz    CkDelm        ; (okay, so far)
  211.     mvi    a,8        ; set error flag
  212.     lxi    h,MsgAmb    ; it's ambiguous
  213.     jmp    ErExit
  214. ;
  215. CkDelm:    lda    FcbOFn        ; make sure there's a filename
  216.     cpi    '!'
  217.     jrc    NoOFl        ; (there's not)
  218.     call    EatSpc
  219.     mov    a,m
  220.     cpi    '='        ; equal sign after filename?
  221.     jrz    GtOUsr        ; (yes)
  222.     mvi    a,4        ; set error flag
  223.     lxi    h,MsgDlm
  224.     jmp    ErExit
  225. ;
  226. GtOUsr:    inx    h        ; get past delimiter
  227.     shld    TailPt        ; save command tail pointer
  228.     lda    OpAFlg        ; check for append mode
  229.     ora    a
  230.     jrnz    SkpTmp        ; (yes, no temporary file)
  231.     lxi    h,FcbOFn
  232.     lxi    d,OutFil
  233.     lxi    b,11
  234.     ldir
  235.     lxi    h,TmpTyp
  236.     lxi    d,FcbOFt
  237.     lxi    b,3
  238.     ldir
  239. SkpTmp:    lda    OutFcb        ; get drive (A=1)
  240.     ora    a        ; is there one?
  241.     jrnz    GtOUs1        ; (yes)
  242.     lda    DftDsk        ; no, get default
  243. GtOUs1:    dcr    a        ; make A=0
  244.     sta    OutDrv        ; ..and store it
  245.     lda    OutFcb+13    ; get user
  246.     sta    OutUsr        ; ..and store it
  247.     call    OpnOut        ; open output file
  248. ;
  249.     lhld    TailPt
  250.     call    EatSpc
  251.     ora    a
  252.     jrz    NoIFl
  253.     cpi    '/'
  254.     jrnz    GtIFl
  255. NoIFl:    mvi    a,10        ; set error flag
  256.     lxi    h,MsgNIG
  257.     jmp    ErExit
  258. ;
  259. GtIFl:    call    SetIFl        ; set up input FCB
  260.     call    OpnInp        ; open input file
  261. ;    call    OpnOut        ; open output file
  262. ;
  263.     lda    OpQFlg        ; quiet option?
  264.     ora    a
  265.     jrnz    NoPrt1        ; (yes, don't print anything)
  266.     lda    OpAFlg        ; append mode?
  267.     ora    a
  268.     jrz    WrtMsg        ; (no)
  269.     lxi    d,FcbOFn    ; print name of append file
  270.     lxi    h,MsgApd
  271.     jr    WrtMs1
  272. WrtMsg:    lxi    d,OutFil    ; print name of output file
  273.     lxi    h,MsgWrt
  274. WrtMs1:    call    epstr
  275.     lda    OutDrv
  276.     mov    b,a
  277.     lda    OutUsr
  278.     mov    c,a
  279.     call    PrtFn
  280.     lxi    h,MsgRd        ; ..and the file we're reading
  281.     call    epstr
  282.     lda    InDrv
  283.     mov    b,a
  284.     lda    InUsr
  285.     mov    c,a
  286.     lxi    d,FcbIFn
  287.     call    PrtFn
  288. ;
  289. NoPrt1:    call    RdLoop        ; read and write files
  290. ;
  291. CkMore:    call    ClsIFl        ; close input file
  292.     lda    DftUsr        ; set default user for parser
  293.     mov    e,a
  294.     mvi    c,CurUsr
  295.     call    Bdos
  296.     lhld    TailPt        ; any more input files?
  297.     call    EatSpc
  298.     cpi    ','
  299.     jrnz    CkMor2
  300.     inx    h
  301.     call    EatSpc
  302. CkMor2:    ora    a
  303.     jrz    NoMore
  304.     cpi    '/'
  305.     jrz    NoMore
  306.     call    SetIFl        ; set up input FCB
  307.     call    OpnInp        ; open input file
  308.     lda    OpQFlg        ; quiet option?
  309.     ora    a
  310.     jrnz    NoPrt2        ; (yes, don't print anything)
  311.     lxi    h,MsgRd        ; print filename we're reading
  312.     call    epstr
  313.     lda    InDrv
  314.     mov    b,a
  315.     lda    InUsr
  316.     mov    c,a
  317.     lxi    d,FcbIFn
  318.     call    PrtFn
  319. NoPrt2:    call    RdLoop        ; read and write files
  320.     jr    CkMore
  321. ;
  322. Abort:    push    psw        ; save error code
  323.     push    h        ; save message
  324.     call    ClsOut        ; close output file
  325.     lda    OpAFlg        ; append mode?
  326.     ora    a
  327.     jrnz    Abort1        ; (yes, skip erase)
  328.     call    f$delete    ; erase output file
  329. Abort1:    pop    h        ; get back message
  330.     pop    psw
  331.     jmp    ErExit
  332. ;
  333. NoMore:    call    ClsOFl        ; close output file
  334.     lda    OpAFlg        ; append mode?
  335.     ora    a
  336.     jrnz    Finish        ; (yes, skip renaming)
  337.     lxi    h,OutTyp    ; move real filetype into FCB
  338.     lxi    d,FcbOFt
  339.     lxi    b,3
  340.     ldir
  341.     lxi    d,OutFcb
  342.     call    initfcb
  343.     call    f$exist        ; see if XXX already exists
  344.     jrz    SkpBak        ; (it doesn't)
  345.     lxi    h,BakTyp
  346.     lxi    d,FcbOFt
  347.     lxi    b,3
  348.     ldir
  349.     lxi    d,OutFcb    ; point to existing BAK name
  350.     call    f$delete    ; erase any existing file
  351.     lxi    h,FcbOFn
  352.     lxi    d,FcbOFn+12
  353.     lxi    b,11
  354.     ldir
  355.     lxi    h,OutFil
  356.     lxi    d,FcbOFn
  357.     lxi    b,11
  358.     ldir
  359.     lxi    d,OutFcb    ; point to old XXX name
  360.     lxi    h,OutFcb+12    ; point to new BAK name
  361.     call    f$rename    ; ..and rename file
  362. SkpBak:    lxi    h,TmpTyp    ; rename temporary $$$ file
  363.     lxi    d,FcbOFt    ; ..to output filename
  364.     lxi    b,3
  365.     ldir
  366.     lxi    h,OutFil
  367.     lxi    d,FcbOFn+12
  368.     lxi    b,11
  369.     ldir
  370.     lxi    h,OutFcb+12    ; point to new XXX name
  371.     lxi    d,OutFcb    ; point to old $$$ name
  372.     call    f$rename
  373. ;
  374. Finish:    lda    OpQFlg        ; quiet option?
  375.     ora    a
  376.     mvi    a,0        ; reset error flag
  377.     jrnz    Exit        ; (yes)
  378.     lxi    h,MsgDne
  379. ErExit:    call    epstr        ; print message
  380. Exit:    call    puter2        ; set error code
  381.     lspd    OldStk        ; restore old stack pointer
  382.     ret            ; ..and return to CCP
  383. ;
  384. ; Subroutines . . .
  385. ;
  386. ; SetIFl -- set up input file control block
  387. ;
  388. SetIFl:    lxi    d,InpFcb    ; point to input FCB
  389.     call    zfname        ; ..and parse filespec
  390.     jrz    GtIUsr        ; (still okay)
  391.     mvi    a,8        ; set error flag
  392.     lxi    h,MsgAmb    ; it's ambiguous
  393.     jmp    Abort
  394. ;
  395. GtIUsr:    shld    TailPt        ; save command tail pointer
  396.     lda    InpFcb        ; get driv (A=1)
  397.     ora    a        ; is there one?
  398.     jrnz    GtIUs1        ; (yes)
  399.     lda    DftDsk        ; no, get default
  400. GtIUs1:    dcr    a        ; make A=0
  401.     sta    InDrv        ; ..and store it
  402.     lda    InpFcb+13    ; get user
  403.     sta    InUsr        ; ..and store it
  404.     ret
  405. ;
  406. ; OpnInp -- open input file
  407. ;
  408. OpnInp:    mvi    a,128        ; initialize pointer
  409.     sta    GetPtr
  410.     sub    a        ; initialize end-of-file flag
  411.     sta    GetFlg
  412.     lxi    d,InpFcb
  413.     call    initfcb
  414.     call    InDU        ; set drive/user for input
  415.     call    f$open
  416.     rz            ; (all okay)
  417.     lda    OpAFlg        ; append mode?
  418.     ora    a
  419.     cnz    ClsOFl        ; (yes, write buffer)
  420.     mvi    a,10        ; set error flag
  421.     lxi    h,MsgNIF    ; open error
  422.     jmp    Abort
  423. ;
  424. ; OpnOut -- open output file
  425. ;
  426. OpnOut:    sub    a        ; initialize counters
  427.     sta    PutCtr
  428.     sta    PutSec
  429.     lhld    OWork        ; initialize buffer pointer
  430.     shld    PutPtr
  431.     lxi    d,OutFcb
  432.     call    initfcb        ; initialize FCB
  433.     call    OutDU
  434.     lda    OpAFlg        ; append mode?
  435.     ora    a
  436.     jrnz    OpnApd        ; (yes)
  437.     call    f$delete    ; erase any existing file
  438.     call    f$mopen
  439.     rz            ; (okay)
  440.     mvi    a,11        ; set error flag
  441.     lxi    h,MsgWEr    ; open error
  442.     jmp    ErExit
  443. ;
  444. OpnApd:    push    d        ; save FCB pointer
  445.     lded    OWork        ; put DMA address in DE
  446.     mvi    c,SetDMA    ; ..and set DMA address
  447.     call    Bdos
  448.     pop    d        ; recover FCB address
  449.     call    f$appl        ; read append file
  450.     ora    a        ; any errors?
  451.     jrnz    ApdErr        ; (yes)
  452.     lda    OpOFlg        ; object file mode?
  453.     ora    a
  454.     jrnz    ApdObj        ; (yes)
  455.     lhld    PutPtr        ; point to output buffer
  456.     lda    PutCtr        ; get counter to B
  457.     mov    b,a
  458. ApdLp:    mov    a,m        ; get character
  459.     cpi    CpmEof        ; end of file?
  460.     jrz    ApdEnd        ; (end)
  461.     inx    h        ; increment pointer
  462.     inr    b        ; increment counter
  463.     mov    a,b
  464.     cpi    128        ; end of sector?
  465.     jrz    ApdObj        ; (yes, no ^Z)
  466.     jr    ApdLp        ; continue
  467. ;
  468. ApdEnd:    mov    a,b        ; store current counter position
  469.     sta    PutCtr
  470.     ret
  471. ;
  472. ApdObj:    mvi    a,128        ; set counter for full sector
  473.     sta    PutCtr
  474.     ret
  475. ;
  476. ApdErr:    cpi    3        ; file empty?
  477.     rz            ; (yes)
  478.     cpi    2        ; file full?
  479.     jrnz    ApdEr1        ; (no)
  480.     mvi    a,4
  481.     lxi    h,MsgFul
  482.     jmp    ErExit
  483. ;
  484. ApdEr1:    mvi    a,10        ; must be file not found
  485.     lxi    h,MsgTNF
  486.     jmp    ErExit
  487. ;
  488. ; RdLoop -- reads and writes until end of file
  489. ;
  490. RdLoop:    call    FGetC        ; get a character
  491.     jc    RdErr        ; (read error)
  492.     rz            ; (end of file)
  493.     cpi    CpmEof        ; end of file?
  494.     cz    ChkEof        ; (yes, check mode)
  495.     rz            ; (yes)
  496.     call    FPutC        ; write character
  497.     jc    WrtErr        ; (write error)
  498.     jr    RdLoop
  499. ;
  500. RdErr:    mvi    a,4        ; set error flag
  501.     lxi    h,MsgREr    ; we have an input read error
  502.     jmp    Abort
  503. ;
  504. WrtErr:    mvi    a,11        ; set error flag
  505.     lxi    h,MsgWEr    ; we have an output write error
  506.     jmp    Abort
  507. ;
  508. ; ChkEof -- checks for Option O and, if so, ignores end-of-file character
  509. ;
  510. ChkEof:    mov    b,a        ; save character in B
  511.     lda    OpOFlg        ; get object flag
  512.     ora    a
  513.     mov    a,b        ; get character back in A
  514.     ret
  515. ;
  516. ; ClsIFl -- close input file
  517. ;
  518. ClsIFl:    call    InDU        ; close input file
  519.     lxi    d,InpFcb
  520.     call    f$close
  521.     rz            ; (okay)
  522.     mvi    a,4        ; set error flag
  523.     lxi    h,MsgCEr
  524.     jmp    Abort
  525. ;
  526. ; ClsOFl -- closes output file
  527. ;
  528. ClsOFl:    call    OutDU
  529.     lda    OpOFlg        ; check option O flag
  530.     ora    a
  531.     jrnz    WrLst        ; (object file transfer, skip EOF)
  532.     mvi    a,CpmEof    ; put end-of-file character
  533.     call    FPutC
  534.     lda    PutCtr        ; check pointer
  535.     ora    a
  536.     jz    WrLst        ; (sector finished)
  537.     mov    b,a
  538.     mvi    a,128        ; fill rest of sector with ^Z
  539.     sub    b
  540.     mov    b,a
  541. FillZ:    mvi    a,CpmEof
  542.     push    b
  543.     call    FPutC
  544.     pop    b
  545.     djnz    FillZ
  546.     jr    WrLst
  547. ;
  548. WrLst:    lded    OWork        ; get beginning buffer address to DE
  549.     lxi    h,PutPtr    ; HL -> buffer pointer
  550.     mov    a,e        ; is pointer at zero?
  551.     cmp    m
  552.     jrnz    WrLst2        ; (no)
  553.     mov    a,d
  554.     inx    h
  555.     cmp    m
  556.     jrnz    WrLst2        ; (no)
  557.     lda    PutCtr        ; is counter at zero?
  558.     ora    a
  559.     jrnz    WrLst2        ; (no)
  560.     jr    ClsOut        ; nothing to write, so close it
  561. ;
  562. WrLst2:    call    FWrtF        ; write what's left
  563.     ora    a        ; check for error
  564.     jnz    WrtErr        ; (yes, abort)
  565. ;
  566. ClsOut:    call    OutDU        ; close output file
  567.     lxi    d,OutFcb
  568.     call    f$close
  569.     rz            ; (okay)
  570.     mvi    a,4        ; set error flag
  571.     lxi    h,MsgCEr
  572.     jmp    ErExit
  573. ;
  574. ; GetDfD -- gets default user and stores it; gets default disk (A=0)
  575. ; and stores it (A=1)
  576. ;
  577. GetDfD:    mvi    e,0FFh        ; get current user
  578.     mvi    c,CurUsr
  579.     call    Bdos
  580.     sta    DftUsr
  581.     mvi    c,CurDsk    ; get default disk
  582.     call    Bdos
  583.     inr    a        ; make it fcb compatible
  584.     sta    DftDsk
  585.     ret
  586. ;
  587. ; PrtFn -- Prints drive/user and filename on console
  588. ;
  589. PrtFn:    call    stndout
  590.     mov    a,b        ; get drive
  591.     adi    'A'        ; make it printable
  592.     call    cout        ; ..and print it
  593.     mov    a,c        ; get user
  594.     call    pafdc        ; ..and print it
  595.     mvi    a,':'
  596.     call    cout
  597.     call    pfn2        ; print filename
  598.     call    stndend
  599.     call    crlf
  600.     ret
  601. ;
  602. ; EatSpc -- gobbles up spaces
  603. ;
  604. EatSpc:    mov    a,m
  605.     cpi    ' '        ; is it a space?
  606.     inx    h
  607.     jrz    EatSpc        ; (yes)
  608.     dcx    h
  609.     ret
  610. ;
  611. ; OutDU -- sets default drive and user for output file
  612. ;
  613. OutDU:    lda    OutUsr
  614.     mov    c,a
  615.     lda    OutDrv
  616.     mov    b,a
  617.     call    logud
  618.     ret
  619. ;
  620. ; InDU -- sets default drive and user for input file
  621. ;
  622. InDU:    lda    InUsr
  623.     mov    c,a
  624.     lda    InDrv
  625.     mov    b,a
  626.     call    logud
  627.     ret
  628. ;
  629. ; FGetC -- returns character from file.  Assumes file has been
  630. ; successfully opened.  Returns character or ^Z (end-of-file) in
  631. ; A.  Zero set (Z) on end of file.  Carry set (C) if error.
  632. ;
  633. FGetC:    lda    GetFlg        ; check end-of-file flag
  634.     ora    a
  635.     jrnz    GetEof        ; (yes)
  636.     lda    GetPtr        ; get pointer
  637.     cpi    128        ; done with buffer?
  638.     jrc    GetChr        ; (no, get a character)
  639.     lded    IWork
  640.     mvi    c,SetDMA    ; set DMA address
  641.     call    Bdos
  642.     call    InDU        ; set DU
  643.     lxi    d,InpFcb
  644.     mvi    c,FRead        ; read more file
  645.     call    Bdos
  646.     cpi    1        ; return code?
  647.     jrz    GetEof        ; (end of file)
  648.     jrnc    GetErr        ; (a problem)
  649.     sta    GetPtr        ; put 0 in pointer
  650. ;
  651. GetChr:    lhld    IWork        ; point to DMA buffer
  652.     mov    e,a        ; put pointer in DE
  653.     mvi    d,0
  654.     dad    d        ; add it to HL
  655.     mov    a,m        ; get next character
  656.     lxi    h,GetPtr    ; increment pointer
  657.     inr    m
  658.     stc
  659.     cmc            ; clear carry
  660.     ret
  661. ;
  662. GetEof:    mvi    a,CpmEof
  663.     sta    GetFlg        ; set end-of-file flag
  664.     stc
  665.     cmc            ; clear carry
  666.     ret
  667. ;
  668. GetErr:    mvi    a,CpmEof
  669.     sta    GetFlg
  670.     stc            ; set carry
  671.     ret
  672. ;
  673. ; FPutC -- Writes character to file.  Assumes file has been successfully
  674. ; opened.  Expects character in A.  Returns carry set (C) on error.
  675. ;
  676. FPutC:    mov    c,a        ; save character in C
  677.     lda    PutCtr        ; get counter
  678.     cpi    128        ; buffer full?
  679.     jrc    PutChr        ; (no, so do it)
  680.     push    b        ; the character is threatened from all sides
  681.     lda    PutSec        ; get sector count
  682.     cpi    BufSiz-1    ; end of buffer?
  683.     jrz    FPutC2        ; (yes, we need to write it)
  684.     inr    a        ; increment sector count
  685.     sta    PutSec        ; ..and store it
  686.     lhld    PutPtr        ; get current sector pointer
  687.     lxi    d,128        ; ..add 128 to it
  688.     dad    d
  689.     shld    PutPtr        ; ..and store it
  690.     xra    a        ; make A = 0
  691.     jr    FPutC3
  692. ;
  693. FPutC2:    call    FWrtF        ; write buffer to disk
  694.     push    psw
  695.     lhld    OWork        ; reset buffer pointer
  696.     shld    PutPtr
  697.     xra    a        ; reset sector counter
  698.     sta    PutSec
  699.     pop    psw
  700. ;
  701. FPutC3:    pop    b        ; get back output character
  702.     ora    a        ; return code?
  703.     jrnz    PutErr        ; (problem)
  704.     sta    PutCtr        ; reset counter to 0
  705. ;
  706. PutChr:    lhld    PutPtr        ; point to current DMA buffer
  707.     mov    e,a        ; move counter to DE
  708.     mvi    d,0
  709.     dad    d        ; ..and add it to HL
  710.     mov    m,c        ; write character
  711.     lda    PutCtr
  712.     inr    a        ; increment counter
  713.     sta    PutCtr
  714.     sub    a        ; clear carry
  715.     ret
  716. ;
  717. PutErr:    stc            ; set carry
  718.     ret
  719. ;
  720. ; FWrtF -- write output buffer to disk
  721. ;
  722. FWrtF:    lda    PutSec        ; get buffer sector count
  723.     mov    b,a        ; put it in B
  724.     lded    OWork        ; point to beginning of buffer
  725. FWrtF2:    push    b        ; save sector count
  726.     push    d        ; save DMA address
  727.     call    WrtSec        ; write the sector
  728.     pop    h        ; DMA address recovered in HL
  729.     pop    b        ; get back sector count
  730.     ora    a        ; write error?
  731.     rnz            ; (yes)
  732.     cmp    b        ; end of buffer?
  733.     rz            ; (yes)
  734.     dcr    b        ; decrement sector count
  735.     lxi    d,128        ; increment DMA address
  736.     dad    d
  737.     xchg
  738.     jr    FWrtF2
  739. ;
  740. WrtSec:    mvi    c,SetDma    ; set DMA address
  741.     call    Bdos
  742.     call    OutDu        ; set drive and user
  743.     lxi    d,OutFcb    ; ..and write sector
  744.     mvi    c,FWrite
  745.     call    Bdos
  746.     ret
  747. ;
  748. ; GetOpt -- checks command tail for user supplied options and sets
  749. ; appropriate option flags.  Invalid options are ignored.
  750. ;
  751. GetOpt:    lxi    h,CTail        ; point to command tail
  752.     lda    CpmDma        ; anything there?
  753.     ora    a
  754.     rz            ; (no)
  755.     mov    b,a        ; yes, put number of chars in B
  756. ScnDLp:    mov    a,m        ; get character
  757.     cpi    '/'        ; delimiter?
  758.     jz    ScnOpt        ; (yes)
  759.     mov    d,a        ; save character
  760. ScnDL2:    inx    h        ; no, keep looking
  761.     djnz    ScnDLp
  762.     ret            ; (none found, return)
  763. ;
  764. ScnOpt:    push    psw        ; save current character
  765.     mov    a,d        ; get back previous character
  766.     pop    d        ; put current character in D
  767.     cpi    ' '        ; was previous char a space?
  768.     jnz    ScnDL2        ; (no)
  769.     jmp    ScnOp2
  770. ;
  771. ScnOLp:    call    ScnTbl
  772.     xchg            ; point back to options
  773. ScnOp2:    inx    h
  774.     djnz    ScnOLp        ; loop through options
  775.     ret
  776. ;
  777. ScnTbl:    mov    c,m        ; put option in C
  778.     lxi    d,OptTbl    ; point DE to option table
  779. ScnTLp:    ldax    d        ; get table option
  780.     ora    a        ; end of table?
  781.     jz    NoMat        ; (yes, no match)
  782.     inx    d        ; no, keep looking
  783.     cmp    c        ; match?
  784.     jz    TMatch        ; (yes)
  785.     inx    d        ; move pointer to next entry
  786.     inx    d
  787.     jmp    ScnTLp        ; ..and keep looking
  788. ;
  789. NoMat:    xchg
  790.     ret
  791. ;
  792. TMatch:    push    h        ; save option pointer
  793.     ldax    d        ; put address from table into HL
  794.     mov    l,a
  795.     inx    d
  796.     ldax    d
  797.     mov    h,a
  798.     pop    d        ; recover option pointer in DE
  799.     mvi    a,1        ; set option flag by jumping to
  800.     pchl            ; ..table routine and returning
  801. ;
  802. ; OptTbl -- Option Jump Table
  803. ;
  804. OptTbl:    db    '/'        ; / = usage message
  805.     dw    OptH
  806.     db    'A'        ; A = append mode
  807.     dw    OptA
  808.     db    'O'        ; O = object file transfer
  809.     dw    OptO
  810.     db    'Q'        ; Q = toggle configured quiet flag
  811.     dw    OptQ
  812.     db    0        ; end of option jump table
  813. ;
  814. ; Option setting routines
  815. ;
  816. OptH:    lxi    h,MsgUse    ; print usage message
  817.     call    epstr
  818.     lda    OpQFlg        ; check quiet mode
  819.     ora    a
  820.     mvi    a,0        ; reset error flag
  821.     jrz    OptH2        ; (off)
  822.     lxi    h,MsgUof
  823.     jmp    ErExit
  824. OptH2:    lxi    h,MsgUon
  825.     jmp    ErExit
  826. ;
  827. OptA:    sta    OpAFlg
  828.     ret
  829. ;
  830. OptO:    sta    OpOFlg
  831.     ret
  832. ;
  833. OptQ:    lda    OpQFlg        ; get Q flag
  834.     ora    a
  835.     jrz    OptQ2        ; (not set)
  836.     sub    a
  837.     sta    OpQFlg        ; zero it
  838.     ret
  839. OptQ2:    mvi    a,1
  840.     sta    OpQFlg        ; set it
  841.     ret
  842. ;
  843. ; Data storage . . .
  844. ;
  845. OutFil:    db    '        '    ; save original output filename here
  846. OutTyp:    db    '   '
  847. BakTyp:    db    'BAK'        ; for BAK file
  848. TmpTyp:    db    '$$$'        ; for temporary filename
  849. ;
  850. InpFcb:    db    0        ; input file fcb
  851. FcbIFn:    db    '        '
  852. FcbIFt:    db    '   '
  853.     ds    24
  854. ;
  855. OutFcb:    db    0        ; output file fcb
  856. FcbOFn:    db    '        '
  857. FcbOFt:    db    '   '
  858.     ds    24
  859. ;
  860.     DSEG
  861. ;
  862. ; Uninitialized storage . . .
  863. ;
  864. OpAFlg:    ds    1
  865. OpOFlg:    ds    1
  866. OpQFlg:    ds    1
  867. ;
  868. GetFlg:    ds    1        ; FGetC end-of-file flag
  869. GetPtr:    ds    1        ; FGetC pointer
  870. PutCtr:    ds    1        ; FPutC counter
  871. PutPtr:    ds    2        ; FPutC pointer
  872. PutSec:    ds    1        ; FPutC sector counter
  873. IWork:    ds    2        ; input buffer address
  874. OWork:    ds    2        ; output buffer address
  875. DftUsr:    ds    1        ; default user area
  876. DftDsk:    ds    1        ; default drive
  877. InDrv:    ds    1        ; input file drive
  878. InUsr:    ds    1        ; input file user
  879. OutDrv:    ds    1        ; output file drive
  880. OutUsr:    ds    1        ; output file user
  881. OldStk:    ds    2        ; old stack pointer
  882. TailPt:    ds    2        ; command tail index pointer
  883. CTail:    ds    128        ; command tail storage
  884. ;
  885.  
  886.     end
  887.