home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / cpm80 / mload.asm < prev    next >
Assembly Source File  |  2020-01-01  |  37KB  |  1,494 lines

  1.     title    'MLOAD MULTI-FILE HEX LOAD UTILITY'
  2. ;
  3. ;        *********************************
  4. ;        *       MLOAD.ASM        *
  5. ;        *   Multi-file Hex Load Utility *
  6. ;        *       for CP/M        *
  7. ;        *********************************
  8. ;
  9. ;
  10. ;    Replacement for the cp/m "LOAD" program: this program
  11. ;    fixes many of the problems associated with the "CP/M"
  12. ;    load program, and adds many new features.
  13. ;
  14. ; ----------------
  15. ;
  16. ;    Rev 2.5
  17. ;    03/10/88
  18. ;    Property of NightOwl Software, Inc. Fort Atkinson, WI 53538
  19. ;    Written by Ron Fowler, Nightowl Software, Inc.
  20. ;
  21. ; ----------------
  22. ; Notice: this program is NOT public domain; copyright is retained by
  23. ; NightOwl Software, Inc. of Fort Atkinson, WI ... All Rights Reserved.
  24. ;
  25. ; License is granted for free use and re-distribution this program, as
  26. ; long as such use and re-distribution is done without profit.
  27. ;
  28. ; ----------------
  29. ;
  30. ; modification history:
  31. ;
  32. ; 2.5    (WOD)    This version corrects a bug that overlayed the first six
  33. ;        bytes of the CCP.  The error did not show up unless a
  34. ;        jump to the CCP was done without a warm boot since MLOAD
  35. ;        used.  This source file has been modified here with
  36. ;        concurrence of the author of MLOAD, Ron Fowler.
  37. ;
  38. ; 2.4    (RGF)    We apologize for this relatively insubstantial update,
  39. ;        but someone has caused what we consider to be a problem,
  40. ;        by making changes to the program, and re-releasing under
  41. ;        the same version number.  The changes in this case were
  42. ;        conversion of the opcode fields (but not the comments,
  43. ;        can you believe that??) of every line to upper case! That
  44. ;        totally invalidated the CRC of the source file, since there
  45. ;        are now two different MLOAD 2.3's running around.
  46. ;
  47. ;        We DO NOT want these stupid mixed upper/lower case changes.
  48. ;        Someone somewhere has decided that this is the way assembly
  49. ;        language source should be, and we most VEHEMENTLY disagree.
  50. ;        It's a pain in the neck to make changes to and we don't
  51. ;        care to run our programs through conversion programs every
  52. ;        time we make changes.
  53. ;
  54. ;        So ... leave the case of this file AS IS.  Any changes made
  55. ;        to this program and not co-ordinated through us may very
  56. ;        well endanger availability of source code when we make
  57. ;        future updates.  'nuff said        --NightOwl Software
  58. ;
  59. ; 2.3    (RGF)    Trivial cosmetic changes
  60. ; 2.2    (RGF)    Modified copyright notice to show new owner of the
  61. ;        program.
  62. ; 2.1    (RGF)    Fixed problem on disk-full when writing output file
  63. ;        (mload previously didn't error out on a full disk)
  64. ; 2.0    (RGF)    Added the ability to pre-load a non-hex file, allowing
  65. ;        mload to be used to load hex file patches (obviating any
  66. ;        need to use DDT).  The normal mload syntax is preserved.
  67. ;        the first (and only the first) filespec (after the "=",
  68. ;        if used) may be non-hex; the filetype must be specified.
  69. ;        Examples:
  70. ;
  71. ;            1)    mload ws.com,wspatch
  72. ;            2)    mload MEXTEST=MEX112.COM,MXO-US13
  73. ;            3)    mload ws.ovr,ovrpatch
  74. ;
  75. ;        The first example loads WS.COM, overlays it with
  76. ;        wspatch.hex, and writes the output to WS.COM.  The
  77. ;        second example loads MEX112.COM, overlays it with
  78. ;        MXO-US13.HEX, and writes the output file to MEXTEST.COM.
  79. ;        (note that the second example is the recommended technique,
  80. ;        since it preserves the original file). The third example
  81. ;        loads WS.OVR and patches it with the file "OVRPATCH.HEX".
  82. ;
  83. ;        Also added this rev: ZCPR2-style du specs are now fully
  84. ;        supported, for both input and output files.  Thus, the
  85. ;        following command lines are permissable:
  86. ;
  87. ;            b3>mload a4:myfile.com=0:bigfil,b6:patch1,c9:patch2
  88. ;            a6>mload b5:=c3:mdm717.com,mdmpatch
  89. ;
  90. ;        After loading, an additional information line is now printed
  91. ;        in the statistics report, which displays the true size of the
  92. ;        saved image (the previous report was technically correct, but
  93. ;        could result in confusion for certain kinds of files with
  94. ;        imbedded "DS" and "ORG" statements in the original source code).
  95. ;
  96. ; 1.0 - 1.4    (RGF) change log removed to conserve space
  97. ;
  98. ;    originally written by ron fowler, fort atkinson, wisconsin
  99. ;
  100. ;
  101. ;
  102. ; For assembly with asm.com or mac (delete above title line if
  103. ; assembling with asm.com).
  104. ;
  105. ; This program is a replacement for the cp/m "LOAD" program.
  106. ; Why replace "LOAD"?  well... LOAD.COM has a few deficiencies.
  107. ; For example, if your hex file's origin is above 100h, LOAD.COM
  108. ; prepends blank space to the output file to insure it will work
  109. ; as a CP/M transient.    It cares not if the file is not intended
  110. ; as a CP/M transient.    it also doesn't like hex records with mixed
  111. ; load addresses  (for example, one that loads below a previous record --
  112. ; which is a perfectly legitimate happenstance).  Also, LOAD.COM
  113. ; can load only one program at a time, and has no provision for
  114. ; a load bias in the command specification. Finally, there is no
  115. ; provision for user specification of the output file name.
  116. ;
  117. ;
  118. ; Hence, this program....
  119. ;
  120. ;------------------------------------------------------------
  121. ;
  122. ; Syntax is as follows:
  123. ;
  124. ;    mload [<outnam=] <filename>[,<filename>...] [bias]
  125. ;
  126. ; where <outnam> is the (optional!;) output file name (only the drive
  127. ; spec and primary filename may be specified; the output filetype is
  128. ; derived exclusively from the 3-byte string at 103h within MLOAD),
  129. ; <filename> specifies files to load and <bias> is the offset within
  130. ; the saved image to apply when loading the file.
  131. ;
  132. ; MLOAD with no arguments prints a small help message -- this message
  133. ; is also printed whenever a command line syntax error occurs.
  134. ;
  135. ; Filenames may contain drive/user specs, and must not contain wildcards.
  136. ; Input filenames must be separated by commas, and a space is required
  137. ; between the last filename and the optional bias.
  138. ;
  139. ; A load information summary is printed at the successful conclusion
  140. ; of the load.    Any errors in loading will generally include the name
  141. ; of the file in question.
  142. ;
  143. ; If no output filename is specified, it will be derived from the first
  144. ; input filename, with filetype of 'COM', if not otherwise specified
  145. ; (this default filetype may be patched directly into mload via DDT
  146. ; (or with MLOAD itself, using a patch file) -- its location is at 103H
  147. ; in MLOAD.COM). Note that a command line of the form "C:=<FILENAME>"
  148. ; will place the output file on the "C" drive with the same primary
  149. ; filename as the input file.
  150. ;
  151. ; In its simplest form, MLOAD's syntax is identical to LOAD.COM; thus
  152. ; there should be no problem in learning to use the new program.  The
  153. ; only significant difference here is that, under LOAD.COM, all files
  154. ; are output starting at 100h, even if they originate elsewhere.  MLOAD
  155. ; outputs starting at the hex file origin (actually, the first hex rec-
  156. ; ord specifies the output load address).  The bias option may be used
  157. ; to override this.
  158. ;
  159. ; An example should clarify this.  Suppose you have a file that loads
  160. ; at 1000h.  LOAD.COM would save an output file that begins at 100h and
  161. ; loads past 1000h (to wherever the program ends).  MLOAD will save an
  162. ; output file starting from 1000h only.  If, for some reason you need the
  163. ; file to start at 100h in spite of its 1000h origin (i can think of sev-
  164. ; eral circumstances where this would be necessary), you'd have to specify
  165. ; a bias to mload.  thus, using this example, "MLOAD MYFILE 0F00" would do.
  166. ;
  167. ; Note that this program re-initializes itself each time it is run.
  168. ; Thus, if your system supports a direct branch to the tpa (via a zero-length
  169. ; .COM file, or the ZCPR "GO" command), you may safely re-execute MLOAD.
  170. ;
  171. ; Please report any bugs, bug fixes, or enhancements to
  172. ;
  173. ;        "FORT FONE FILE FOLDER" rcpm/cbbs
  174. ;        Fort Atkinson, Wisconsin
  175. ;        (414) 563-9932 (no ring back)
  176. ;
  177. ;                --Ron Fowler
  178. ;                  03/08/84   updated 1/31/85
  179. ;
  180. ;------------------------------------------------------------
  181. ;
  182. ; CP/M equates
  183. ;
  184. warmbt    equ    0        ;warm boot
  185. system    equ    5        ;system entry (also top of mem pntr)
  186. dfcb    equ    5ch        ;default file control block
  187. ft    equ    9        ;fcb offset to filetype
  188. tbuf    equ    80h        ;default buffer
  189. tpa    equ    100h        ;transient program area
  190. eof    equ    1ah        ;cp/m end-of-file mark
  191. fcbsiz    equ    33        ;size of file control block
  192. ;
  193. ; CP/M system calls
  194. ;
  195. pcharf    equ    2        ;print char
  196. seldf    equ    14        ;select disk drive
  197. openf    equ    15        ;open file
  198. closef    equ    16        ;close file
  199. fsrchf    equ    17        ;search for first
  200. fsrchn    equ    18        ;search for next
  201. erasef    equ    19        ;delete file
  202. readf    equ    20        ;read record
  203. writef    equ    21        ;write record
  204. creatf    equ    22        ;create file
  205. getdrf    equ    25        ;return dflt drive #
  206. sdmaf    equ    26        ;set dma address
  207. gsuser    equ    32        ;get/set user #
  208. rrand    equ    33        ;read random
  209. wrand    equ    34        ;write random
  210. filszf    equ    35        ;compute file size
  211. srand    equ    36        ;set random
  212. ;
  213. ; ASCII character constants
  214. ;
  215. cr    equ    13
  216. lf    equ    10
  217. bel    equ    7
  218. tab    equ    9
  219. ;
  220. ; without further ado...
  221. ;
  222.     org    tpa
  223. ;
  224.     jmp    begin        ;jump over default output filetype
  225. ;
  226. ; the default output filetype is located at 103h for easy patching
  227. ;
  228. outtyp:    db    'COM'
  229. ;
  230. begin:    lxi    h,0        ;save system stackpointer
  231.     dad    sp
  232.     shld    spsave
  233.     lxi    sp,stack    ;load local stack
  234.     call    ilprnt        ;sign on
  235.     db    'MLOAD ver. 2.5   Copyright (C) 1983, 1984, 1985'
  236.     db    cr,lf
  237.     db    'by NightOwl Software, Inc.'
  238.     db    cr,lf,0
  239.     call    setup        ;initialize
  240. main:    call    nxtfil        ;parse and read next input file
  241.     jc    done        ;no more...
  242.     call    lodfil        ;yep, load it
  243.     call    closfl        ;close it (in case MP/M or TurboDOS)
  244.     jmp    main        ;maybe more
  245. done:    call    wrtfil        ;write the output file
  246. ;
  247. ; exit to cp/m
  248. ;
  249. exit:    lxi    d,tbuf        ;restore dma address
  250.     mvi    c,sdmaf
  251.     call    bdos
  252.     lda    system+2    ;get top of memory pointer
  253.     sui    9        ;allow for ccp+slop
  254.     lxi    h,hiload+1    ;highest load address
  255.     sub    m        ;above ccp?
  256.     jc    warmbt        ;then warm-boot
  257.     lhld    spsave        ;nope, ccp still in memory
  258.     sphl            ;restore its stack
  259.     ret            ;return to ccp
  260. ;
  261. ; load program initialization
  262. ;
  263. setup:    lxi    h,varset    ;initialize variables
  264.     lxi    d,vars
  265.     mvi    b,varlen    ;by moving in default values
  266.     call    move
  267.     lhld    cmdptr        ;get first free mem pointer
  268.     xchg            ;in de
  269.     lxi    h,tbuf        ;point to command tail bufr
  270.     mov    a,m        ;get its length
  271.     inx    h
  272.     ora    a        ;does it have any length?
  273.     jz    help        ;nope, go give usage help
  274.     mov    b,a        ;yep, get length to b
  275.     call    move        ;move cmd tail to buffer
  276.     xchg            ;end of dest to hl
  277.     mvi    m,0        ;stuff a terminator
  278.     inx    h        ;point to first free memory
  279.     shld    filbuf        ;set up file buffer
  280.     xchg            ;file bufr adrs to de
  281.     lhld    system+1    ;get top of memory pointer
  282.     xra    a        ;round system to page boundary
  283.     sub    e
  284.     mov    c,a        ;with result in bc
  285.     mov    a,h
  286.     sui    9        ;allow for ccp
  287.     sbb    d
  288.     mov    b,a
  289.     xchg            ;buffer pointer to hl
  290. nitmem:    mvi    m,0        ;clear buffer
  291.     inx    h
  292.     dcx    b
  293.     mov    a,b
  294.     ora    c
  295.     jnz    nitmem
  296. ;
  297. ; look for a bias specification in command line
  298. ;
  299.     lhld    cmdptr        ;point to command buffer-1
  300.     dcx    h
  301.     call    scanbk        ;scan past blanks
  302.     ora    a        ;no non-blank chars?
  303.     jz    help        ;then go print help text
  304. fndspc:    inx    h        ;point to next
  305.     mov    a,m        ;fetch it
  306.     ora    a        ;test it
  307.     rz            ;line ended, return
  308.     cpi    ' '        ;nope, test for blank
  309.     jnz    fndspc        ;not blank, continue
  310.     call    scanbk        ;skip blanks
  311.     ora    a        ;end-of-line?
  312.     rz            ;return if so
  313. ;
  314. ; hl points to bias in command line
  315. ;
  316.     lxi    d,0        ;init bias
  317.     call    hexdig        ;insure a hex digit
  318.     jc    synerr        ;bad...
  319. hexlp:    mov    a,m        ;no.  get next char
  320.     inx    h        ;skip over it
  321.     call    hexdig        ;test for hex digit
  322.     jnc    digok        ;jump if good hex digit
  323.     ora    a        ;must end on null terminator
  324.     jnz    synerr
  325.     xchg            ;good end, get bias to hl
  326.     shld    bias        ;stuff it
  327.     ret            ;done
  328. digok:    xchg            ;bias to hl
  329.     dad    h        ;skift left 4 to make room
  330.     dad    h        ;   for new hex digit
  331.     dad    h
  332.     dad    h
  333.     xchg            ;back to de
  334.     add    e        ;add in new digit
  335.     mov    e,a
  336.     jnc    hexlp        ;jump if no 8-bit ovfl
  337.     inr    d        ;carry
  338.     jmp    hexlp
  339. ;
  340. ; parse next input name, and open resultant file
  341. ;
  342. nxtfil:    lhld    cmdptr        ;get command line pointer
  343. next2:    lxi    d,dfcb        ;destination fcb
  344.     call    fparse        ;parse a filename
  345.     cpi    '='        ;stopped on output specifier?
  346.     jnz    noteq
  347.     lda    outnam+2    ;insure no name yet specified
  348.     cpi    ' '
  349.     jnz    synerr        ;syntax error if already named
  350.     lda    outflg        ;already been here?
  351.     ora    a
  352.     jnz    synerr        ;can't be here twice
  353.     inr    a        ;flag that we've been here
  354.     sta    outflg
  355.     inr    b        ;insure no ambiguous output name
  356.     dcr    b
  357.     jnz    afnerr
  358.     inx    h        ;skip over '='
  359.     push    h        ;save cmd line pointer
  360.     lxi    h,dfcb-1    ;move the name to output name hold
  361.     lxi    d,outnam
  362.     mvi    b,13        ;drive spec too
  363.     call    move
  364.     pop    h        ;restore command line pointer
  365.     jmp    next2        ;go parse another
  366. noteq:    cpi    ','        ;stopped on comma?
  367.     jz    gcomma        ;jump if so
  368.     mvi    m,0        ;nope, insure end of input
  369.     jmp    nxt2        ;don't advance over fake end
  370. gcomma:    inx    h        ;skip over comma
  371. nxt2:    shld    cmdptr        ;save new command line pntr
  372.     mov    a,b        ;get ambig char count
  373.     ora    a        ;test it
  374.     jnz    afnerr        ;allow no ambig characters
  375.     sta    bufptr        ;force a disk read
  376.     lxi    d,dfcb+1    ;look at parsed filename
  377.     ldax    d
  378.     cpi    ' '        ;blank? (input ended?)
  379.     stc            ;get carry ready in case so
  380.     rz            ;return cy if input gone
  381.     dcx    d        ;nope, point de to start of fcb
  382. open2:    push    d        ;try to open the file
  383.     mvi    c,openf
  384.     call    bdos
  385.     pop    d
  386.     inr    a        ;return=0ffh?
  387.     jnz    openok        ;jump if not
  388. ;
  389. ; file not found: if filetype blank, set to 'hex' and try again
  390. ;
  391.     lxi    h,dfcb+ft    ;point to file type
  392.     mov    a,m        ;anything there?
  393.     cpi    ' '
  394.     jnz    fnferr        ;yes, so file not found
  395.     mvi    m,'H'        ;nope, fill in 'hex'
  396.     inx    h
  397.     mvi    m,'E'
  398.     inx    h
  399.     mvi    m,'X'
  400.     jmp    open2        ;go try again
  401. ;
  402. ; here after a good file open
  403. ;
  404. openok:    call    hexchk        ;is this a hex file?
  405.     rz            ;if so, all done
  406.     lxi    h,comflg    ;no, get pointer to flag
  407.     mov    a,m        ;loading first file?
  408.     ora    a
  409.     rnz            ;if not, ignore type, consider hex
  410.     inr    m        ;else, set the flag
  411.     ret
  412. ;
  413. ; load current file
  414. ;
  415. lodfil:    lxi    h,comflg    ;loading a com file?
  416.     mov    a,m        ;get flag
  417.     ani    1
  418.     jnz    lodcom        ;jump if so
  419.     lhld    bias        ;else get bias on top of stack
  420.     push    h
  421. ;
  422. ; load a hex record
  423. ;
  424. loadlp:    call    gnb        ;get next file byte
  425.     sbi    ':'        ;look for start-record mark
  426.     jnz    loadlp        ;scan until found
  427.     sta    cksum        ;got it, init checksum to zero
  428.     mov    d,a        ;upper byte of rec cnt=0
  429.     pop    b        ;retrieve bias adrs
  430.     push    b        ;save it again
  431.     call    ghbcks        ;get hex byte w/checksum
  432.     mov    e,a        ;de now has record length
  433.     ora    a        ;test it
  434.     jnz    notend        ;jump if len<>0 (not eof rec)
  435.     pop    h        ;all done
  436.     ret
  437. notend:    call    ghbcks        ;hi byte of rec ld adrs
  438.     mov    h,a        ;accumulate in hl
  439.     call    ghbcks        ;get lo byte
  440.     mov    l,a        ;put lo in l
  441.     lda    lodflg        ;test load flag
  442.     ora    a
  443.     cz    lodnit        ;not first record, initialize
  444.     push    h        ;save load address
  445.     dad    d        ;add in record length
  446.     dcx    h        ;make highest, not next
  447.     lda    hipc        ;a new high?
  448.     sub    l
  449.     lda    hipc+1
  450.     sbb    h
  451.     jnc    notgt        ;jump if not
  452.     shld    hipc        ;yep, update hipc
  453.     push    d        ;save reclen
  454.     xchg            ;load adrs to de
  455.     lhld    offset        ;get offset to form true memory adrs
  456.     dad    d        ;add in offset
  457.     dad    b        ;and bias
  458.     shld    hiload        ;mark highest true memory load adrs
  459.     lda    system+2    ;validate against top-mem pointer
  460.     cmp    h
  461.     jc    memful        ;jump if out of memory
  462.     pop    d        ;restore reclen
  463. notgt:    pop    h        ;restore load address
  464.     dad    b        ;add bias to load adrs
  465.     push    d        ;save record length
  466.     push    h
  467.     lhld    bytcnt        ;add record length to byte count
  468.     dad    d
  469.     shld    bytcnt
  470.     pop    h
  471.     xchg
  472.     lhld    offset        ;calculate true memory adrs
  473.     dad    d        ;hl=true loading adrs
  474.     pop    d        ;restore record length
  475.     call    ghbcks        ;skip unused byte of intel format
  476. ;
  477. ; move the record into memory
  478. ;
  479. reclp:    call    ghbcks        ;get hex byte
  480.     mov    m,a        ;store it in buffer
  481.     inx    h        ;point to next
  482.     dcr    e        ;count down
  483.     jnz    reclp        ;until record all read
  484.     call    ghbcks        ;get checksum byte
  485.     jnz    cserr        ;final add cksum should sum 0
  486.     jmp    loadlp        ;good load, go do nxt record
  487. ;
  488. ; get next hex byte from input, and
  489. ; accumulate a checksum
  490. ;
  491. ghbcks:    push    b        ;save em all
  492.     push    h
  493.     push    d
  494.     call    hexin        ;get hex byte
  495.     mov    b,a        ;save in b
  496.     lxi    h,cksum        ;add to checksum
  497.     mov    a,m
  498.     add    b
  499.     mov    m,a
  500.     mov    a,b        ;get byte back
  501.     pop    d        ;restore checksum
  502.     pop    h        ;restore other regs
  503.     pop    b
  504.     ret
  505. ;
  506. ; routine to get next byte from input...forms
  507. ; byte from two ascii hex characters
  508. ;
  509. hexin:    call    gnb        ;get next input file byte
  510.     call    hexval        ;convert to binary w/validation
  511.     rlc            ;move into ms nybble
  512.     rlc
  513.     rlc
  514.     rlc
  515.     ani    0f0h        ;kill possible garbage
  516.     push    psw        ;save it
  517.     call    gnb        ;get next byte
  518.     call    hexval        ;convert it, w/validation
  519.     pop    b        ;get back first
  520.     ora    b        ;or in second
  521.     ret            ;good byte in a
  522. ;
  523. ; gnb - utility subroutine to get next
  524. ;    byte from disk file
  525. gnb:    push    h        ;save all regs
  526.     push    d
  527.     push    b
  528.     lda    bufptr        ;get input bufr pointer
  529.     ani    7fh        ;wound back to 0?
  530.     jz    diskrd        ;go read sector if so
  531. gnb1:    mvi    d,0        ;else form 16 bit offset
  532.     mov    e,a
  533.     lxi    h,tbuf        ;from tbuf
  534.     dad    d        ;add in offset
  535.     mov    a,m        ;get next byte
  536.     cpi    eof        ;end of file?
  537.     jz    eoferr        ;error if so
  538.     lxi    h,bufptr    ;else bump buf ptr
  539.     inr    m
  540.     ora    a        ;return carry clear
  541.     pop    b        ;restore and return
  542.     pop    d
  543.     pop    h
  544.     ret
  545. ;
  546. ; read next sector from disk
  547. ;
  548. diskrd:    mvi    c,readf        ;bdos "READ SEC" function
  549.     lxi    d,dfcb
  550.     call    bdos        ;read sector
  551.     ora    a
  552.     jnz    eoferr        ;error if phys end of file
  553.     sta    bufptr        ;store 0 as new buf ptr
  554.     jmp    gnb1        ;go re-join gnb code
  555. ;
  556. ; load a com file
  557. ;
  558. lodcom:    inr    m        ;bump the comfile flag
  559.     lxi    h,tpa        ;set origin
  560.     call    lodnit        ;and initialize
  561.     xchg            ;load address in de
  562.     lhld    bias        ;add in bias
  563.     dad    d
  564.     xchg
  565.     lhld    offset        ;and offset
  566.     dad    d
  567.     xchg            ;de has absolute mem adrs of load
  568. ;
  569. comlp:    lxi    h,128        ;calculate next dma
  570.     dad    d
  571.     lda    system+2    ;check for space
  572.     cmp    h
  573.     jc    memful        ;jump if none
  574.     push    h        ;else save next dma
  575.     push    d        ;and this dma
  576.     mvi    c,sdmaf        ;set this dma
  577.     call    bdos
  578.     lxi    d,dfcb        ;read next record
  579.     mvi    c,readf
  580.     call    bdos
  581.     pop    h        ;recall this dma
  582.     pop    d        ;de=next dma
  583.     ora    a        ;end of read?
  584.     jnz    lodend        ;jump if so
  585.     lhld    comsiz        ;no, advance com byte count
  586.     lxi    b,128
  587.     dad    b
  588.     shld    comsiz
  589.     jmp    comlp        ;continue
  590. ;
  591. lodend:    dcx    h        ;one less byte is highest
  592.     shld    hiload        ;set a new high
  593.     lhld    comsiz        ;hi pc=bytecount+100h
  594.     lxi    d,tpa
  595.     dad    d
  596.     xchg            ;to de
  597.     lhld    bias        ;add in bias
  598.     dad    d
  599.     shld    hipc
  600.     lxi    d,tbuf        ;reset dma for hex files
  601.     mvi    c,sdmaf
  602.     call    bdos
  603.     ret
  604. ;
  605. ; write output file
  606. ;
  607. wrtfil:    lxi    d,dfcb        ;point to fcb
  608.     push    d        ;save 2 copies of pointer
  609.     push    d
  610.     call    nitfcb        ;initialize output fcb
  611.     lxi    h,outnam    ;move output name in
  612.     dcx    d        ;point to user # (prior to fcb)
  613.     mvi    b,10        ;move user, drive, primary name
  614.     call    move
  615.     mov    a,m        ;output type blank?
  616.     cpi    ' '
  617.     jnz    wrtnb        ;jump if not
  618.     lxi    h,outtyp    ;yes, move dflt output filetype in
  619. wrtnb:    mvi    b,3
  620.     call    move
  621.     pop    d        ;restore fcb pointer
  622.     mvi    c,erasef    ;erase any existing file
  623.     call    bdos
  624.     pop    d        ;restore fcb pointer
  625.     mvi    c,creatf    ;create a new file
  626.     call    bdos
  627.     inr    a        ;good create?
  628.     jz    dirful        ;goto directory full error if not
  629.     lhld    hiload        ;yep, get top of bufr pntr
  630.     xchg            ;in de
  631.     lhld    filbuf        ;get start of bufr adrs
  632.     mov    a,e        ;calculate output file size
  633.     sub    l
  634.     mov    c,a        ;with result in bc
  635.     mov    a,d
  636.     sbb    h
  637.     mov    b,a
  638.     mov    a,b        ;test length
  639.     ora    c
  640.     jz    loderr        ;nothing to write???
  641.     lxi    d,dfcb        ;get fcb pointer
  642. wrlp:    push    b        ;save count
  643.     push    d        ;and fcb pointer
  644.     xchg            ;get memory pointer to de
  645.     lxi    h,128        ;add in sector length for next pass
  646.     dad    d
  647.     xthl            ;save next dma
  648.     push    h        ;above fcb
  649.     mvi    c,sdmaf        ;set transfer address
  650.     call    bdos
  651.     pop    d        ;fetch fcb pointer
  652.     push    d        ;save it again
  653.     mvi    c,writef    ;write a sector
  654.     call    bdos
  655.     ora    a        ;test result
  656.     jnz    dskful        ;disk full error...
  657.     lhld    reccnt        ;no,increment count of records
  658.     inx    h
  659.     shld    reccnt
  660.     pop    d        ;restore fcb pointer
  661.     pop    h        ;and memory write pointer
  662.     pop    b        ;and count
  663.     mov    a,c        ;subtract 128 (sec size) from count
  664.     sui    128
  665.     mov    c,a
  666.     jnc    wrlp        ;jump if some left
  667.     mov    a,b        ;hi-order borrow
  668.     sui    1        ;do it (can't "DCR B", doesn't affect cy)
  669.     mov    b,a        ;restore
  670.     jnc    wrlp        ;jump if more left
  671.     call    closfl        ;close output file
  672. ;
  673. ; report statistics to console
  674. ;
  675.     call    ilprnt
  676.     db    'Loaded ',0
  677.     lhld    bytcnt        ;print # bytes
  678.     call    decout
  679.     call    ilprnt
  680.     db    ' bytes (',0
  681.     call    hexout
  682.     call    ilprnt
  683.     db    'H)',0
  684.     call    ilprnt
  685.     db    ' to file %',0
  686.     lda    comflg        ;did we load a comfile too?
  687.     ora    a
  688.     jz    notcom        ;jump if not
  689.     call    ilprnt
  690.     db    cr,lf,'Over a ',0
  691.     lhld    comsiz
  692.     call    decout
  693.     call    ilprnt
  694.     db    ' byte binary file',0
  695. notcom:    call    ilprnt
  696.     db    cr,lf,'Start address: ',0
  697.     lhld    lodadr        ;print loading address
  698.     call    hexout
  699.     call    ilprnt
  700.     db    'H  Ending address: ',0
  701.     lhld    hipc        ;print ending load address
  702.     call    hexout
  703.     call    ilprnt
  704.     db    'H  Bias: ',0
  705.     lhld    bias
  706.     call    hexout
  707.     call    ilprnt
  708.     db    'H',cr,lf,0
  709.     call    ilprnt
  710.     db    'Saved image size: ',0
  711.     lhld    reccnt        ;get count of image records
  712.     push    h        ;save it
  713.     mvi    b,7        ;convert to bytes
  714. xlp:    dad    h
  715.     dcr    b
  716.     jnz    xlp
  717.     call    decout        ;print it
  718.     call    ilprnt
  719.     db    ' bytes (',0
  720.     call    hexout        ;now in hex
  721.     call    ilprnt
  722.     db    'H, - ',0
  723.     pop    h        ;recall record count
  724.     call    decout        ;print it
  725.     call    ilprnt
  726.     db    ' records)',cr,lf,0
  727.     lhld    lodadr        ;fetch loading address
  728.     mov    a,l        ;test if =tpa
  729.     ora    a
  730.     jnz    nottpa        ;tpa always on page boundary
  731.     mov    a,h        ;lo ok, test hi
  732.     cpi    (tpa shr 8) and    0ffh
  733.     rz            ;return if tpa
  734. nottpa:    call    ilprnt        ;not, so print warning msg
  735.     db    cr,lf,bel
  736.     db    '++ Warning: program origin NOT at 100H ++'
  737.     db    cr,lf,0
  738.     ret            ;done
  739. ;
  740. ;    ***********************
  741. ;    * utility subroutines *
  742. ;    ***********************
  743. ;
  744. ;
  745. ; routine to close any open file
  746. ;
  747. closfl:    lxi    d,dfcb
  748.     mvi    c,closef
  749.     call    bdos
  750.     inr    a        ;test close result
  751.     jz    clserr        ;jump if error
  752.     ret
  753. ;
  754. ; print message in-line with code
  755. ;
  756. ilprnt:    xthl            ;message pntr to hl
  757.     call    prathl        ;print it
  758.     xthl            ;restore and return
  759.     ret
  760. ;
  761. ; print msg pointed to by hl until null.  expand
  762. ; '%' char to current filename.
  763. ;
  764. prathl:    mov    a,m        ;fetch char
  765.     inx    h        ;point to next
  766.     ora    a        ;terminator?
  767.     rz            ;then done
  768.     cpi    '%'        ;want filename?
  769.     jz    prtfn        ;go do it if so
  770.     call    type        ;nope, just print char
  771.     jmp    prathl        ;continue
  772. ;
  773. prtfn:    push    h        ;save pointer
  774.     push    b
  775.     lda    dfcb        ;fetch dr field of dfcb
  776.     ora    a        ;default drive?
  777.     jnz    prndf        ;jump if not
  778.     call    getdsk        ;get logged-in drive #
  779.     inr    a        ;make it one-relative (as in fcb)
  780. prndf:    adi    'A'-1        ;make drive name printable
  781.     call    type        ;print it
  782.     lda    dfcb-1        ;get user #
  783.     cpi    0ffh        ;null?
  784.     cz    getusr        ;iff so, get current user
  785.     mov    l,a        ;to hl
  786.     mvi    h,0
  787.     call    decout        ;print it
  788.     mvi    a,':'        ;drive names followed by colon
  789.     call    type
  790.     lxi    h,dfcb+1    ;setup for name
  791.     mvi    b,8        ;print up to 8
  792.     call    prtnam
  793.     mvi    a,'.'        ;print dot
  794.     call    type
  795.     mvi    b,3        ;print filetype field
  796.     call    prtnam
  797.     pop    b
  798.     pop    h        ;restore and continue
  799.     jmp    prathl
  800. ;
  801. ; print file name .HL max length in b.    don't print spaces
  802. ;
  803. prtnam:    mov    a,m        ;fetch a char
  804.     cpi    ' '        ;blank?
  805.     jz    pwind        ;go wind if so
  806.     inx    h        ;nope, move to next
  807.     call    type        ;print it
  808.     dcr    b        ;count down
  809.     jnz    prtnam        ;continue
  810.     ret
  811. pwind:    inx    h        ;skip remainder of blank name
  812.     dcr    b
  813.     jnz    pwind
  814.     ret
  815. ;
  816. ; print HL in decimal on console
  817. ;
  818. decout:    push    h        ;save everybody
  819.     push    d
  820.     push    b
  821.     lxi    b,-10        ;conversion radix
  822.     lxi    d,-1
  823. declp:    dad    b
  824.     inx    d
  825.     jc    declp
  826.     lxi    b,10
  827.     dad    b
  828.     xchg
  829.     mov    a,h
  830.     ora    l
  831.     cnz    decout        ;this is recursive
  832.     mov    a,e
  833.     adi    '0'
  834.     call    type
  835.     pop    b
  836.     pop    d
  837.     pop    h
  838.     ret
  839. ;
  840. ; newline on console
  841. ;
  842. crlf:    mvi    a,cr
  843.     call    type
  844.     mvi    a,lf
  845.     jmp    type
  846. ;
  847. ; print hl on console in hex
  848. ;
  849. hexout:    mov    a,h        ;get hi
  850.     call    hexbyt        ;print it
  851.     mov    a,l        ;get lo, fall into hexbyt
  852. ;
  853. ; type accumulator on console in hex
  854. ;
  855. hexbyt:    push    psw        ;save byte
  856.     rar            ;get ms nybble..
  857.     rar            ;..into lo 4 bits
  858.     rar
  859.     rar
  860.     call    nybble
  861.     pop    psw        ;get back byte
  862. nybble:    ani    0fh        ;mask ms nybble
  863.     adi    90h        ;add offset
  864.     daa            ;decimal adjust a-reg
  865.     aci    40h        ;add offset
  866.     daa            ;fall into type
  867. ;
  868. ; type char in a on console
  869. ;
  870. type:    push    h        ;save all
  871.     push    d
  872.     push    b
  873.     mov    e,a        ;cp/m outputs from e
  874.     mvi    c,pcharf
  875.     call    bdos
  876.     pop    b
  877.     pop    d
  878.     pop    h
  879.     ret
  880. ;
  881. ; move: from @hl to @de, count in b
  882. ;
  883. move:    inr    b        ;up one
  884. movlp:    dcr    b        ;count down
  885.     rz            ;return if done
  886.     mov    a,m        ;not done, continue
  887.     stax    d
  888.     inx    h        ;pointers=pointers+1
  889.     inx    d
  890.     jmp    movlp
  891. ;
  892. ; scan to first non-blank char in string @hl
  893. ;
  894. scanbk:    inx    h        ;next
  895.     mov    a,m        ;fetch it
  896.     cpi    ' '
  897.     jz    scanbk
  898.     ret
  899. ;
  900. ; get hex digit and validate
  901. ;
  902. hexval:    call    hexdig        ;get hex digit
  903.     jc    formerr        ;jump if bad
  904.     ret
  905. ;
  906. ; get hex digit, return cy=1 if bad digit
  907. ;
  908. hexdig:    cpi    '0'        ;lo boundary test
  909.     rc            ;bad already?
  910.     cpi    '9'+1        ;no, test hi
  911.     jc    hexcvt        ;jump if numeric
  912.     cpi    'A'        ;test alpha
  913.     rc            ;bad?
  914.     cpi    'F'+1        ;no, upper alpha bound
  915.     cmc            ;pervert carry
  916.     rc            ;bad?
  917.     sui    7        ;no, adjust to 0-f
  918. hexcvt:    ani    0fh        ;make it binary
  919.     ret
  920. ;
  921. ;    ******************
  922. ;    * error handlers *
  923. ;    ******************
  924. ;
  925. synerr:    call    crlf
  926.     call    ilprnt
  927.     db    '      Command line syntax error',cr,lf,cr,lf,0
  928.     jmp    help        ;give help msg too
  929. ;
  930. afnerr:    call    errxit        ;exit with message
  931.     db    'Ambiguous file name: % not allowed.',0
  932. ;
  933. fnferr:    call    errxit
  934.     db    'File % not found.',0
  935. ;
  936. dskful:    call    errxit
  937.     db    'Disk full.',0
  938. ;
  939. dirful:    call    errxit
  940.     db    'Directory full.',0
  941. ;
  942. eoferr:    call    errxit
  943.     db    'Premature end-of-file in %',0
  944. ;
  945. cserr:    call    errxit
  946.     db    'Checksum error in %',0
  947. ;
  948. clserr:    call    errxit
  949.     db    'Can''t close %',0
  950. ;
  951. memful:    call    errxit
  952.     db    'Memory full while loading %',0
  953. ;
  954. formerr:call    errxit
  955.     db    'Format error in file %',0
  956. ;
  957. loderr:    call    errxit
  958.     db    'Writing %, nothing loaded',0
  959. ;
  960. help:    call    errxit        ;print help text
  961.     db    'MLOAD syntax:',cr,lf,cr,lf
  962.     db    'MLOAD [<OUTFIL>=] <FILE1>[,<FILE2>...] [<BIAS>]',cr,lf
  963.     db    tab,'    (brackets denote optional items)',cr,lf,cr,lf
  964.     db    tab,'<OUTFIL> is the optional output filename',cr,lf
  965.     db    tab,'<FILEn>  are input file(s)',cr,lf
  966.     db    tab,'<BIAS>   is a hex load offset within the output file'
  967.     db    cr,lf,cr,lf
  968.     db    tab,'<FILE1> may be an optional non-HEX file to be patched',cr,lf
  969.     db    tab,'by subsequently named HEX files (specifying',cr,lf
  970.     db    tab,'The filetype enables this function).'
  971.     db    cr,lf,cr,lf
  972.     db    'Note that ZCPR2-style drive/user notation may be used in all'
  973.     db    cr,lf
  974.     db    'file specifications (e.g., "B3:MYFILE.COM, "A14:MDM7.HEX").'
  975.     db    cr,lf,0
  976. ;
  977. ; general error handler
  978. ;
  979. errxit:    call    crlf        ;new line
  980.     pop    h        ;fetch error msg pointer
  981.     call    prathl        ;print it
  982.     call    crlf
  983.     jmp    exit        ;done
  984. ;
  985. ; initialize load parameters
  986. ;
  987. lodnit:    mvi    a,1        ;first record, set load flag
  988.     sta    lodflg
  989.     shld    lodadr        ;save load address
  990.     shld    hipc        ;and hi load
  991.     push    d        ;save record length
  992.     xchg            ;de=load address
  993.     lhld    filbuf        ;get address of file buffer
  994.     mov    a,l        ;subtract load adrs from file buffer
  995.     sub    e
  996.     mov    l,a
  997.     mov    a,h
  998.     sbb    d
  999.     mov    h,a
  1000.     shld    offset        ;save as load offset
  1001.     push    d        ;save load address on stack
  1002.     push    b        ;save bias
  1003.     lxi    d,outnam+2    ;check output filename
  1004.     ldax    d        ;(first char)
  1005.     cpi    ' '
  1006.     jnz    namskp        ;jump if so
  1007.     lxi    h,dfcb+1    ;get first name pointer
  1008.     mvi    b,8        ;(don't include drive spec)
  1009.     call    move
  1010. ;
  1011. ; check for outflg=1 (presence of an "=").  note that the
  1012. ; filename may well be blank, and yet outflg <>0, for example
  1013. ; in the case of "A:=<FILENAME>" or "C4:=<FILENAME>". in
  1014. ; this case, we want to remember the drive/user specified, but
  1015. ; use the first input file to form the output name. otherwise,
  1016. ; we use the current drive/user.
  1017. ;
  1018.     lda    outflg        ;was there an "="?
  1019.     ora    a
  1020.     jnz    namskp        ;jump if so
  1021.     lxi    h,outnam    ;get destination pointer
  1022.     call    getusr        ;get current user #
  1023.     mov    m,a
  1024.     inx    h        ;point to drive
  1025.     call    getdsk        ;get it
  1026.     inr    a        ;fcb's drive is 1-relative
  1027.     mov    m,a
  1028. namskp:    mvi    a,1        ;insure "=" cannot occur anymore
  1029.     sta    outflg
  1030.     pop    b        ;restore bias
  1031.     pop    h        ;load address to hl
  1032.     pop    d        ;restore record length
  1033.     ret
  1034. ;
  1035. ;    *********************************
  1036. ;    * file name parsing subroutines *
  1037. ;    *********************************
  1038. ;
  1039. ; credit where credit's due:
  1040. ; --------------------------
  1041. ; these routines were lifted from bob van valzah's
  1042. ; "FAST" program.
  1043. ;
  1044. ;
  1045. ;
  1046. ;    *********************************
  1047. ;    * file name parsing subroutines *
  1048. ;    *********************************
  1049. ;
  1050. ;
  1051. ; getfn gets a file name from text pointed to by reg hl into
  1052. ; an fcb pointed to by reg de.    leading delimeters are
  1053. ; ignored. allows drive spec of the form <du:> (drive/user).
  1054. ; this routine formats all 33 bytes of the fcb (but not ran rec).
  1055. ;
  1056. ; entry de    first byte of fcb
  1057. ; exit b=# of '?' in name
  1058. ; fcb-1= user # parsed (if specified) or 255
  1059. ;
  1060. ;
  1061. fparse:    call    nitfcb        ;init 1st half of fcb
  1062.     call    gstart        ;scan to first character of name
  1063.     call    getdrv        ;get drive/user spec. if present
  1064.     mov    a,b        ;get user # or 255
  1065.     cpi    0ffh        ;255?
  1066.     jz    fpars1        ;jump if so
  1067.     dcx    d        ;back up to byte preceeding fcb
  1068.     dcx    d
  1069.     stax    d        ;stuff user #
  1070.     inx    d        ;onward
  1071.     inx    d
  1072. fpars1:    call    getps        ;get primary and secondary name
  1073.     ret
  1074. ;
  1075. ; nitfcb fills the fcb with dflt info - 0 in drive field
  1076. ; all-blank in name field, and 0 in ex,s1,s2,rc, disk
  1077. ; allocation map, and random record # fields
  1078. ;
  1079. nitfcb:    push    h
  1080.     push    d
  1081.     call    getusr        ;init user field
  1082.     pop    d
  1083.     pop    h
  1084.     push    d        ;save fcb loc
  1085.     dcx    d
  1086.     stax    d        ;init user # to currnt user #
  1087.     inx    d
  1088.     xchg            ;move it to hl
  1089.     mvi    m,0        ;drive=default
  1090.     inx    h        ;bump to name field
  1091.     mvi    b,11        ;zap all of name fld
  1092. nitlp:    mvi    m,' '
  1093.     inx    h
  1094.     dcr    b
  1095.     jnz    nitlp
  1096.     mvi    b,33-11        ;zero others, up to nr field
  1097. zlp:    mvi    m,0
  1098.     inx    h
  1099.     dcr    b
  1100.     jnz    zlp
  1101.     xchg            ;restore hl
  1102.     pop    d        ;restore fcb pointer
  1103.     ret
  1104. ;
  1105. ; gstart advances the text pointer (reg hl) to the first
  1106. ; non delimiter character (i.e. ignores blanks).  returns a
  1107. ; flag if end of line (00h or ';') is found while scaning.
  1108. ; exit    hl    pointing to first non delimiter
  1109. ;    a    clobbered
  1110. ;    zero    set if end of line was found
  1111. ;
  1112. gstart:    call    getch        ;see if pointing to delim?
  1113.     rnz            ;nope - return
  1114.     ora    a        ;physical end?
  1115.     rz            ;yes - return w/flag
  1116.     inx    h        ;nope - move over it
  1117.     jmp    gstart        ;and try next char
  1118. ;
  1119. ; getdrv checks for the presence of a du: spec at the text
  1120. ; pointer, and if present formats drive into fcb and returns
  1121. ; user # in b.
  1122. ;
  1123. ; entry hl    text pointer
  1124. ;    de    pointer to first byte of fcb
  1125. ; exit    hl    possibly updated text pointer
  1126. ;    de    pointer to second (primary name) byte of fcb
  1127. ;    b    user # if specified or 0ffh
  1128. ;
  1129. getdrv:    mvi    b,0ffh        ;default no user #
  1130.     push    h        ;save text pointer
  1131. dscan:    call    getch        ;get next char
  1132.     inx    h        ;skip pointer over it
  1133.     jnz    dscan        ;scan until delimiter
  1134.     cpi    ':'        ;delimiter a colon?
  1135.     inx    d        ;skip dr field in fcb in case not
  1136.     pop    h        ;and restore text pointer
  1137.     rnz            ;return if no du: spec
  1138.     mov    a,m        ;got one, get first char
  1139.     call    cvtuc        ;may be drive name, cvt to upper case
  1140.     cpi    'A'        ;alpha?
  1141.     jc    isnum        ;jump to get user # if not
  1142.     sui    'A'-1        ;yes, convert from ascii to #
  1143.     dcx    d        ;back up fcb pointer to dr field
  1144.     stax    d        ;store drive # into fcb
  1145.     inx    d        ;pass pointer over drv
  1146.     inx    h        ;skip drive spec in text
  1147. isnum:    mov    a,m        ;fetch next
  1148.     inx    h
  1149.     cpi    ':'        ;du delimiter?
  1150.     rz            ;done then
  1151.     dcx    h        ;nope, back up text pointer
  1152.     mvi    b,0        ;got a digit, init user value
  1153. uloop:    mov    a,b        ;get accumulated user #
  1154.     add    a        ;* 10 for new digit
  1155.     add    a
  1156.     add    b
  1157.     add    a
  1158.     mov    b,a        ;back to b
  1159.     mov    a,m        ;get text char
  1160.     sui    '0'        ;make binary
  1161.     add    b        ;add to user #
  1162.     mov    b,a        ;updated user #
  1163.     inx    h        ;skip over it
  1164.     mov    a,m        ;get next
  1165.     cpi    ':'        ;end of spec?
  1166.     jnz    uloop        ;jump if not
  1167.     inx    h        ;yep, return txt pointer past du:
  1168.     ret
  1169. ;
  1170. ; getps gets the primary and secondary names into the fcb.
  1171. ; entry hl    text pointer
  1172. ; exit    hl    character following secondary name (if present)
  1173. ;
  1174. getps:    mvi    c,8        ;max length of primary name
  1175.     mvi    b,0        ;init count of '?'
  1176.     call    getnam        ;pack primary name into fcb
  1177.     mov    a,m        ;see if terminated by a period
  1178.     cpi    '.'
  1179.     rnz            ;nope - secondary name not given
  1180.                 ;return default (blanks)
  1181.     inx    h        ;yup - move text pointer over period
  1182. ftpoint:mov    a,c        ;yup - update fcb pointer to secondary
  1183.     ora    a
  1184.     jz    getft
  1185.     inx    d
  1186.     dcr    c
  1187.     jmp    ftpoint
  1188. getft:    mvi    c,3        ;max length of secondary name
  1189.     call    getnam        ;pack secondary name into fcb
  1190.     ret
  1191. ;
  1192. ; getnam copies a name from the text pointer into the fcb for
  1193. ; a given maximum length or until a delimiter is found, which
  1194. ; ever occurs first.  if more than the maximum number of
  1195. ; characters is present, character are ignored until a
  1196. ; a delimiter is found.
  1197. ; entry hl    first character of name to be scanned
  1198. ;    de    pointer into fcb name field
  1199. ;    c    maximum length
  1200. ; exit    hl    pointing to terminating delimiter
  1201. ;    de    next empty byte in fcb name field
  1202. ;    c    max length - number of characters transfered
  1203. ;
  1204. getnam:    call    getch        ;are we pointing to a delimiter yet?
  1205.     rz            ;if so, name is transfered
  1206.     inx    h        ;if not, move over character
  1207.     cpi    '*'        ;ambigious file reference?
  1208.     jz    ambig        ;if so, fill the rest of field with '?'
  1209.     cpi    '?'        ;afn reference?
  1210.     jnz    notqm        ;skip if not
  1211.     inr    b        ;else bump afn count
  1212. notqm:    call    cvtuc        ;if not, convert to upper case
  1213.     stax    d        ;and copy into name field
  1214.     inx    d        ;increment name field pointer
  1215.     dcr    c        ;if name field full?
  1216.     jnz    getnam        ;nope - keep filling
  1217.     jmp    getdel        ;yup - ignore until delimiter
  1218. ambig:    mvi    a,'?'        ;fill character for wild card match
  1219. fillq:    stax    d        ;fill until field is full
  1220.     inx    d
  1221.     inr    b        ;increment count of '?'
  1222.     dcr    c
  1223.     jnz    fillq        ;fall thru to ingore rest of name
  1224. getdel:    call    getch        ;pointing to a delimiter?
  1225.     rz            ;yup - all done
  1226.     inx    h        ;nope - ignore antoher one
  1227.     jmp    getdel
  1228. ;
  1229. ; getch gets the character pointed to by the text pointer
  1230. ; and sets the zero flag if it is a delimiter.
  1231. ; entry hl    text pointer
  1232. ; exit    hl    preserved
  1233. ;    a    character at text pointer
  1234. ;    z    set if a delimiter
  1235. ;
  1236. getch:    mov    a,m        ;get the character, test for delim
  1237. ;
  1238. ; global entry: test char in a for filename delimiter
  1239. ;
  1240. fndelm:    cpi    '/'
  1241.     rz
  1242.     cpi    '.'
  1243.     rz
  1244.     cpi    ','
  1245.     rz
  1246.     cpi    ' '
  1247.     rz
  1248.     cpi    ':'
  1249.     rz
  1250.     cpi    '='
  1251.     rz
  1252.     ora    a        ;set zero flag on end of text
  1253.     ret
  1254. ;
  1255. ; bdos entry: preserves bc, de.  if system call is a file
  1256. ;          function, this routine logs into the drive/
  1257. ;          user area specified, then logs back after
  1258. ;          the call.
  1259. ;
  1260. bdos:    call    filfck        ;check for a file function
  1261.     jnz    bdos1        ;jump if not a file function
  1262.     call    getdu        ;get drive/user
  1263.     shld    savedu
  1264.     ldax    d        ;get fcb's drive
  1265.     sta    fcbdrv        ;save it
  1266.     dcr    a        ;make 0-relative
  1267.     jm    bdos0        ;if not default drive, jump
  1268.     mov    h,a        ;copy to h
  1269. bdos0:    xra    a        ;set fcb to default
  1270.     stax    d
  1271.     dcx    d        ;get fcb's user #
  1272.     ldax    d
  1273.     mov    l,a
  1274.     inx    d        ;restore de
  1275.     call    setdu        ;set fcb's user
  1276. ;
  1277. ; note that unspecified user # (value=0ffh) becomes
  1278. ; a getusr call, preventing ambiguity.
  1279. ;
  1280.     call    bdos1        ;do user's system call
  1281.     push    psw        ;save result
  1282.     push    h
  1283.     lda    fcbdrv        ;restore fcb's drive
  1284.     stax    d
  1285.     lhld    savedu        ;restore prior drive/user
  1286.     call    setdu
  1287.     pop    h        ;restore bdos result registers
  1288.     pop    psw
  1289.     ret
  1290. ;
  1291. ; local variables for bdos replacement routine
  1292. ;
  1293. savedu:    dw    0        ;saved drive,user
  1294. fcbdrv:    db    0        ;fcb's drive
  1295. dmadr:    dw    80h        ;current dma adrs
  1296. ;
  1297. bdos1:    push    d
  1298.     push    b
  1299.     mov    a,c        ;doing setdma?
  1300.     cpi    sdmaf
  1301.     jnz    bdos1a        ;jump if not
  1302.     xchg            ;yep, keep a record of dma addresses
  1303.     shld    dmadr
  1304.     xchg
  1305. bdos1a:    call    system
  1306.     pop    b
  1307.     pop    d
  1308.     ret
  1309. ;
  1310. ; get drive, user: h=drv, l=user
  1311. ;
  1312. getdu:    push    b        ;don't modify bc
  1313.     push    d
  1314.     mvi    c,gsuser    ;get user #
  1315.     mvi    e,0ffh
  1316.     call    bdos1
  1317.     push    psw        ;save it
  1318.     mvi    c,getdrf    ;get drive
  1319.     call    bdos1
  1320.     mov    h,a        ;drive returned in h
  1321.     pop    psw
  1322.     mov    l,a        ;user in l
  1323.     pop    d
  1324.     pop    b        ;restore caller's bc
  1325.     ret
  1326. ;
  1327. ; set drive, user: h=drv, l=user
  1328. ;
  1329. setdu:    push    b        ;don't modify bc
  1330.     push    d
  1331.     push    h        ;save info
  1332.     mov    e,h        ;drive to e
  1333.     mvi    c,seldf        ;set it
  1334.     call    bdos1
  1335.     pop    h        ;recall info
  1336.     push    h
  1337.     mov    e,l        ;user # to e
  1338.     mvi    c,gsuser
  1339.     call    bdos1        ;set it
  1340.     pop    h
  1341.     pop    d
  1342.     pop    b
  1343.     ret
  1344. ;
  1345. ; check for file-function: open, close, read random, write
  1346. ;    random, read sequential, write sequential.
  1347. ;
  1348. filfck:    mov    a,c        ;get function #
  1349.     cpi    openf
  1350.     rz
  1351.     rc            ;ignore lower function #'s
  1352.     cpi    closef        ;(they're not file-related)
  1353.     rz
  1354.     cpi    readf
  1355.     rz
  1356.     cpi    writef
  1357.     rz
  1358.     cpi    rrand
  1359.     rz
  1360.     cpi    wrand
  1361.     rz
  1362.     cpi    fsrchf
  1363.     rz
  1364.     cpi    fsrchn
  1365.     rz
  1366.     cpi    erasef
  1367.     rz
  1368.     cpi    creatf
  1369.     rz
  1370.     cpi    filszf
  1371.     rz
  1372.     cpi    srand
  1373.     ret
  1374. ;
  1375. ; convert char to upper case
  1376. ;
  1377. cvtuc:    cpi    'a'        ;check lo bound
  1378.     rc
  1379.     cpi    'z'+1        ;check hi
  1380.     rnc
  1381.     sui    20h        ;convert
  1382.     ret
  1383. ;
  1384. ; check for hex filetype in fcb name
  1385. ;
  1386. hexchk:    push    h
  1387.     push    d
  1388.     push    b
  1389.     mvi    b,3        ;type is 3 chars
  1390.     lxi    d,dfcb+9    ;point de to type field
  1391.     lxi    h,hextyp    ;point hl to "COM"
  1392. hexlop:    ldax    d
  1393.     ani    7fh        ;ignore attributes
  1394.     cmp    m
  1395.     inx    h
  1396.     inx    d
  1397.     jnz    hexit        ;jump if not com
  1398.     dcr    b
  1399.     jnz    hexlop
  1400. hexit:    pop    b        ;z reg has result
  1401.     pop    d
  1402.     pop    h
  1403.     ret
  1404. ;
  1405. hextyp:    db    'HEX'
  1406. ;
  1407. ; routine to return user # without disturbing registers
  1408. ;
  1409. getusr:    push    h
  1410.     push    d
  1411.     push    b
  1412.     mvi    c,gsuser
  1413.     mvi    e,0ffh
  1414.     call    bdos
  1415.     pop    b
  1416.     pop    d
  1417.     pop    h
  1418.     ret
  1419. ;
  1420. ; routine to return drive # without disturbing registers
  1421. ;
  1422. getdsk:    push    h
  1423.     push    d
  1424.     push    b
  1425.     mvi    c,getdrf
  1426.     call    bdos
  1427.     pop    b
  1428.     pop    d
  1429.     pop    h
  1430.     ret
  1431. ;
  1432. ; these are the initial values of the variables, and
  1433. ; are moved into the variables area by the setup routine.
  1434. ; if you add variables, be sure to add their intial value
  1435. ; into this table in the order corresponding to their
  1436. ; occurance in the variables section.
  1437. ;
  1438. varset:    dw    0            ;bias
  1439.     dw    0            ;hiload
  1440.     dw    0            ;hipc
  1441.     db    0            ;cksum
  1442.     dw    cmdbuf            ;cmdptr
  1443.     db    0            ;bufptr
  1444.     db    0            ;lodflg
  1445.     dw    cmdbuf            ;filbuf
  1446.     dw    0            ;offset
  1447.     dw    0            ;lodadr
  1448.     db    0,0,'           '    ;outnam
  1449.     dw    0            ;reccnt
  1450.     dw    0            ;bytcnt
  1451.     db    0            ;comflg
  1452.     dw    0            ;comsiz
  1453.     db    0            ;outflg
  1454. ;
  1455. varlen    equ    $-varset    ;define length of init table
  1456. ;
  1457. ; working variables
  1458. ;
  1459. vars    equ    $        ;define variables area start
  1460. ;
  1461. bias:    ds    2        ;load offset
  1462. hiload:    ds    2        ;highest true load address
  1463. hipc:    ds    2        ;highest pc
  1464. cksum:    ds    1        ;record checksum
  1465. cmdptr:    ds    2        ;command line pointer
  1466. bufptr:    ds    1        ;input buffer pointer
  1467. lodflg:    ds    1        ;something-loaded flag
  1468. filbuf:    ds    2        ;file buffer location
  1469. offset:    ds    2        ;load offset into buffer
  1470. lodadr:    ds    2        ;load address
  1471. outnam:    ds    13        ;output drive+name
  1472. reccnt:    ds    2        ;output file record count
  1473. bytcnt:    ds    2        ;output file bytes loaded  count
  1474. comflg:    ds    1        ;flags com file encountered
  1475. comsiz:    ds    2        ;size of a loaded com file
  1476. outflg:    ds    1        ;flags an "=" present in cmd line
  1477. ;
  1478. ; end of working variables
  1479. ;
  1480. ;
  1481. ;
  1482. ; stack stuff
  1483. ;
  1484. spsave:    ds    2        ;system stack pntr save
  1485. ;
  1486. ;
  1487.     ds    100        ;50-level stack
  1488. ;
  1489. stack    equ    $
  1490. cmdbuf    equ    $        ;command buffer location
  1491. ;
  1492. ;
  1493.     end
  1494.