1 title 'MLOAD MULTI-FILE HEX LOAD UTILITY'
3 ; *********************************
5 ; * Multi-file Hex Load Utility *
7 ; *********************************
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.
18 ; Property of NightOwl Software, Inc. Fort Atkinson, WI 53538
19 ; Written by Ron Fowler, Nightowl Software, Inc.
22 ; Notice: this program is NOT public domain; copyright is retained by
23 ; NightOwl Software, Inc. of Fort Atkinson, WI ... All Rights Reserved.
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.
30 ; modification history:
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.
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.
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.
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
59 ; 2.3 (RGF) Trivial cosmetic changes
60 ; 2.2 (RGF) Modified copyright notice to show new owner of the
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.
71 ; 1) mload ws.com,wspatch
72 ; 2) mload MEXTEST=MEX112.COM,MXO-US13
73 ; 3) mload ws.ovr,ovrpatch
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".
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:
87 ; b3>mload a4:myfile.com=0:bigfil,b6:patch1,c9:patch2
88 ; a6>mload b5:=c3:mdm717.com,mdmpatch
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).
96 ; 1.0 - 1.4 (RGF) change log removed to conserve space
98 ; originally written by ron fowler, fort atkinson, wisconsin
102 ; For assembly with asm.com or mac (delete above title line if
103 ; assembling with asm.com).
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.
118 ; Hence, this program....
120 ;------------------------------------------------------------
122 ; Syntax is as follows:
124 ; mload [<outnam=] <filename>[,<filename>...] [bias]
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.
132 ; MLOAD with no arguments prints a small help message -- this message
133 ; is also printed whenever a command line syntax error occurs.
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.
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.
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.
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
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.
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.
171 ; Please report any bugs, bug fixes, or enhancements to
173 ; "FORT FONE FILE FOLDER" rcpm/cbbs
174 ; Fort Atkinson, Wisconsin
175 ; (414) 563-9932 (no ring back)
178 ; 03/08/84 updated 1/31/85
180 ;------------------------------------------------------------
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
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
213 ; ASCII character constants
220 ; without further ado...
224 jmp begin ;jump over default output filetype
226 ; the default output filetype is located at 103h for easy patching
230 begin: lxi h,0 ;save system stackpointer
233 lxi sp,stack ;load local stack
235 db 'MLOAD ver. 2.5 Copyright (C) 1983, 1984, 1985'
237 db 'by NightOwl Software, Inc.'
239 call setup ;initialize
240 main: call nxtfil ;parse and read next input file
242 call lodfil ;yep, load it
243 call closfl ;close it (in case MP/M or TurboDOS)
245 done: call wrtfil ;write the output file
249 exit: lxi d,tbuf ;restore dma address
252 lda system+2 ;get top of memory pointer
253 sui 9 ;allow for ccp+slop
254 lxi h,hiload+1 ;highest load address
256 jc warmbt ;then warm-boot
257 lhld spsave ;nope, ccp still in memory
258 sphl ;restore its stack
261 ; load program initialization
263 setup: lxi h,varset ;initialize variables
265 mvi b,varlen ;by moving in default values
267 lhld cmdptr ;get first free mem pointer
269 lxi h,tbuf ;point to command tail bufr
270 mov a,m ;get its length
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
284 mov c,a ;with result in bc
289 xchg ;buffer pointer to hl
290 nitmem: mvi m,0 ;clear buffer
297 ; look for a bias specification in command line
299 lhld cmdptr ;point to command buffer-1
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
307 rz ;line ended, return
308 cpi ' ' ;nope, test for blank
309 jnz fndspc ;not blank, continue
310 call scanbk ;skip blanks
314 ; hl points to bias in command line
317 call hexdig ;insure a hex digit
319 hexlp: mov a,m ;no. get next char
321 call hexdig ;test for hex digit
322 jnc digok ;jump if good hex digit
323 ora a ;must end on null terminator
325 xchg ;good end, get bias to hl
328 digok: xchg ;bias to hl
329 dad h ;skift left 4 to make room
330 dad h ; for new hex digit
334 add e ;add in new digit
336 jnc hexlp ;jump if no 8-bit ovfl
340 ; parse next input name, and open resultant file
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?
347 lda outnam+2 ;insure no name yet specified
349 jnz synerr ;syntax error if already named
350 lda outflg ;already been here?
352 jnz synerr ;can't be here twice
353 inr a ;flag that we've been here
355 inr b ;insure no ambiguous output name
359 push h ;save cmd line pointer
360 lxi h,dfcb-1 ;move the name to output name hold
362 mvi b,13 ;drive spec too
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
374 jnz afnerr ;allow no ambig characters
375 sta bufptr ;force a disk read
376 lxi d,dfcb+1 ;look at parsed filename
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
387 jnz openok ;jump if not
389 ; file not found: if filetype blank, set to 'hex' and try again
391 lxi h,dfcb+ft ;point to file type
392 mov a,m ;anything there?
394 jnz fnferr ;yes, so file not found
395 mvi m,'H' ;nope, fill in 'hex'
400 jmp open2 ;go try again
402 ; here after a good file open
404 openok: call hexchk ;is this a hex file?
406 lxi h,comflg ;no, get pointer to flag
407 mov a,m ;loading first file?
409 rnz ;if not, ignore type, consider hex
410 inr m ;else, set the flag
415 lodfil: lxi h,comflg ;loading a com file?
418 jnz lodcom ;jump if so
419 lhld bias ;else get bias on top of stack
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
434 jnz notend ;jump if len<>0 (not eof rec)
437 notend: call ghbcks ;hi byte of rec ld adrs
438 mov h,a ;accumulate in hl
439 call ghbcks ;get lo byte
441 lda lodflg ;test load flag
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?
451 jnc notgt ;jump if not
452 shld hipc ;yep, update hipc
454 xchg ;load adrs to de
455 lhld offset ;get offset to form true memory adrs
458 shld hiload ;mark highest true memory load adrs
459 lda system+2 ;validate against top-mem pointer
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
467 lhld bytcnt ;add record length to byte count
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
477 ; move the record into memory
479 reclp: call ghbcks ;get hex byte
480 mov m,a ;store it in buffer
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
488 ; get next hex byte from input, and
489 ; accumulate a checksum
491 ghbcks: push b ;save em all
494 call hexin ;get hex byte
496 lxi h,cksum ;add to checksum
500 mov a,b ;get byte back
501 pop d ;restore checksum
502 pop h ;restore other regs
506 ; routine to get next byte from input...forms
507 ; byte from two ascii hex characters
509 hexin: call gnb ;get next input file byte
510 call hexval ;convert to binary w/validation
511 rlc ;move into ms nybble
515 ani 0f0h ;kill possible garbage
517 call gnb ;get next byte
518 call hexval ;convert it, w/validation
519 pop b ;get back first
523 ; gnb - utility subroutine to get next
524 ; byte from disk file
525 gnb: push h ;save all regs
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
533 lxi h,tbuf ;from tbuf
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
540 ora a ;return carry clear
541 pop b ;restore and return
546 ; read next sector from disk
548 diskrd: mvi c,readf ;bdos "READ SEC" function
550 call bdos ;read sector
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
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
565 lhld offset ;and offset
567 xchg ;de has absolute mem adrs of load
569 comlp: lxi h,128 ;calculate next dma
571 lda system+2 ;check for space
573 jc memful ;jump if none
574 push h ;else save next dma
576 mvi c,sdmaf ;set this dma
578 lxi d,dfcb ;read next record
581 pop h ;recall this dma
584 jnz lodend ;jump if so
585 lhld comsiz ;no, advance com byte count
591 lodend: dcx h ;one less byte is highest
592 shld hiload ;set a new high
593 lhld comsiz ;hi pc=bytecount+100h
597 lhld bias ;add in bias
600 lxi d,tbuf ;reset dma for hex files
607 wrtfil: lxi d,dfcb ;point to fcb
608 push d ;save 2 copies of pointer
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
615 mov a,m ;output type blank?
617 jnz wrtnb ;jump if not
618 lxi h,outtyp ;yes, move dflt output filetype in
621 pop d ;restore fcb pointer
622 mvi c,erasef ;erase any existing file
624 pop d ;restore fcb pointer
625 mvi c,creatf ;create a new file
628 jz dirful ;goto directory full error if not
629 lhld hiload ;yep, get top of bufr pntr
631 lhld filbuf ;get start of bufr adrs
632 mov a,e ;calculate output file size
634 mov c,a ;with result in bc
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
649 mvi c,sdmaf ;set transfer address
651 pop d ;fetch fcb pointer
652 push d ;save it again
653 mvi c,writef ;write a sector
656 jnz dskful ;disk full error...
657 lhld reccnt ;no,increment count of records
660 pop d ;restore fcb pointer
661 pop h ;and memory write pointer
663 mov a,c ;subtract 128 (sec size) from count
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)
670 jnc wrlp ;jump if more left
671 call closfl ;close output file
673 ; report statistics to console
677 lhld bytcnt ;print # bytes
686 lda comflg ;did we load a comfile too?
688 jz notcom ;jump if not
694 db ' byte binary file',0
696 db cr,lf,'Start address: ',0
697 lhld lodadr ;print loading address
700 db 'H Ending address: ',0
701 lhld hipc ;print ending load address
710 db 'Saved image size: ',0
711 lhld reccnt ;get count of image records
713 mvi b,7 ;convert to bytes
717 call decout ;print it
720 call hexout ;now in hex
723 pop h ;recall record count
724 call decout ;print it
726 db ' records)',cr,lf,0
727 lhld lodadr ;fetch loading address
728 mov a,l ;test if =tpa
730 jnz nottpa ;tpa always on page boundary
731 mov a,h ;lo ok, test hi
732 cpi (tpa shr 8) and 0ffh
734 nottpa: call ilprnt ;not, so print warning msg
736 db '++ Warning: program origin NOT at 100H ++'
740 ; ***********************
741 ; * utility subroutines *
742 ; ***********************
745 ; routine to close any open file
750 inr a ;test close result
751 jz clserr ;jump if error
754 ; print message in-line with code
756 ilprnt: xthl ;message pntr to hl
757 call prathl ;print it
758 xthl ;restore and return
761 ; print msg pointed to by hl until null. expand
762 ; '%' char to current filename.
764 prathl: mov a,m ;fetch char
768 cpi '%' ;want filename?
769 jz prtfn ;go do it if so
770 call type ;nope, just print char
773 prtfn: push h ;save pointer
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
782 lda dfcb-1 ;get user #
784 cz getusr ;iff so, get current user
787 call decout ;print it
788 mvi a,':' ;drive names followed by colon
790 lxi h,dfcb+1 ;setup for name
791 mvi b,8 ;print up to 8
795 mvi b,3 ;print filetype field
798 pop h ;restore and continue
801 ; print file name .HL max length in b. don't print spaces
803 prtnam: mov a,m ;fetch a char
805 jz pwind ;go wind if so
806 inx h ;nope, move to next
811 pwind: inx h ;skip remainder of blank name
816 ; print HL in decimal on console
818 decout: push h ;save everybody
821 lxi b,-10 ;conversion radix
831 cnz decout ;this is recursive
847 ; print hl on console in hex
849 hexout: mov a,h ;get hi
850 call hexbyt ;print it
851 mov a,l ;get lo, fall into hexbyt
853 ; type accumulator on console in hex
855 hexbyt: push psw ;save byte
857 rar ;..into lo 4 bits
861 pop psw ;get back byte
862 nybble: ani 0fh ;mask ms nybble
864 daa ;decimal adjust a-reg
868 ; type char in a on console
870 type: push h ;save all
873 mov e,a ;cp/m outputs from e
881 ; move: from @hl to @de, count in b
884 movlp: dcr b ;count down
886 mov a,m ;not done, continue
888 inx h ;pointers=pointers+1
892 ; scan to first non-blank char in string @hl
900 ; get hex digit and validate
902 hexval: call hexdig ;get hex digit
903 jc formerr ;jump if bad
906 ; get hex digit, return cy=1 if bad digit
908 hexdig: cpi '0' ;lo boundary test
910 cpi '9'+1 ;no, test hi
911 jc hexcvt ;jump if numeric
914 cpi 'F'+1 ;no, upper alpha bound
917 sui 7 ;no, adjust to 0-f
918 hexcvt: ani 0fh ;make it binary
927 db ' Command line syntax error',cr,lf,cr,lf,0
928 jmp help ;give help msg too
930 afnerr: call errxit ;exit with message
931 db 'Ambiguous file name: % not allowed.',0
934 db 'File % not found.',0
940 db 'Directory full.',0
943 db 'Premature end-of-file in %',0
946 db 'Checksum error in %',0
949 db 'Can''t close %',0
952 db 'Memory full while loading %',0
955 db 'Format error in file %',0
958 db 'Writing %, nothing loaded',0
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'
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).'
972 db 'Note that ZCPR2-style drive/user notation may be used in all'
974 db 'file specifications (e.g., "B3:MYFILE.COM, "A14:MDM7.HEX").'
977 ; general error handler
979 errxit: call crlf ;new line
980 pop h ;fetch error msg pointer
981 call prathl ;print it
985 ; initialize load parameters
987 lodnit: mvi a,1 ;first record, set load flag
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
1000 shld offset ;save as load offset
1001 push d ;save load address on stack
1003 lxi d,outnam+2 ;check output filename
1004 ldax d ;(first char)
1006 jnz namskp ;jump if so
1007 lxi h,dfcb+1 ;get first name pointer
1008 mvi b,8 ;(don't include drive spec)
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.
1018 lda outflg ;was there an "="?
1020 jnz namskp ;jump if so
1021 lxi h,outnam ;get destination pointer
1022 call getusr ;get current user #
1024 inx h ;point to drive
1026 inr a ;fcb's drive is 1-relative
1028 namskp: mvi a,1 ;insure "=" cannot occur anymore
1031 pop h ;load address to hl
1032 pop d ;restore record length
1035 ; *********************************
1036 ; * file name parsing subroutines *
1037 ; *********************************
1039 ; credit where credit's due:
1040 ; --------------------------
1041 ; these routines were lifted from bob van valzah's
1046 ; *********************************
1047 ; * file name parsing subroutines *
1048 ; *********************************
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).
1056 ; entry de first byte of fcb
1057 ; exit b=# of '?' in name
1058 ; fcb-1= user # parsed (if specified) or 255
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
1066 jz fpars1 ;jump if so
1067 dcx d ;back up to byte preceeding fcb
1069 stax d ;stuff user #
1072 fpars1: call getps ;get primary and secondary name
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
1081 call getusr ;init user field
1084 push d ;save fcb loc
1086 stax d ;init user # to currnt user #
1089 mvi m,0 ;drive=default
1090 inx h ;bump to name field
1091 mvi b,11 ;zap all of name fld
1096 mvi b,33-11 ;zero others, up to nr field
1102 pop d ;restore fcb pointer
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
1110 ; zero set if end of line was found
1112 gstart: call getch ;see if pointing to delim?
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
1119 ; getdrv checks for the presence of a du: spec at the text
1120 ; pointer, and if present formats drive into fcb and returns
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
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
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
1149 cpi ':' ;du delimiter?
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
1159 mov a,m ;get text char
1160 sui '0' ;make binary
1161 add b ;add to user #
1162 mov b,a ;updated user #
1165 cpi ':' ;end of spec?
1166 jnz uloop ;jump if not
1167 inx h ;yep, return txt pointer past du:
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)
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
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
1188 getft: mvi c,3 ;max length of secondary name
1189 call getnam ;pack secondary name into fcb
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
1200 ; exit hl pointing to terminating delimiter
1201 ; de next empty byte in fcb name field
1202 ; c max length - number of characters transfered
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
1221 inr b ;increment count of '?'
1223 jnz fillq ;fall thru to ingore rest of name
1224 getdel: call getch ;pointing to a delimiter?
1226 inx h ;nope - ignore antoher one
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
1233 ; a character at text pointer
1234 ; z set if a delimiter
1236 getch: mov a,m ;get the character, test for delim
1238 ; global entry: test char in a for filename delimiter
1252 ora a ;set zero flag on end of text
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
1260 bdos: call filfck ;check for a file function
1261 jnz bdos1 ;jump if not a file function
1262 call getdu ;get drive/user
1264 ldax d ;get fcb's drive
1266 dcr a ;make 0-relative
1267 jm bdos0 ;if not default drive, jump
1269 bdos0: xra a ;set fcb to default
1271 dcx d ;get fcb's user #
1275 call setdu ;set fcb's user
1277 ; note that unspecified user # (value=0ffh) becomes
1278 ; a getusr call, preventing ambiguity.
1280 call bdos1 ;do user's system call
1281 push psw ;save result
1283 lda fcbdrv ;restore fcb's drive
1285 lhld savedu ;restore prior drive/user
1287 pop h ;restore bdos result registers
1291 ; local variables for bdos replacement routine
1293 savedu: dw 0 ;saved drive,user
1294 fcbdrv: db 0 ;fcb's drive
1295 dmadr: dw 80h ;current dma adrs
1299 mov a,c ;doing setdma?
1301 jnz bdos1a ;jump if not
1302 xchg ;yep, keep a record of dma addresses
1310 ; get drive, user: h=drv, l=user
1312 getdu: push b ;don't modify bc
1314 mvi c,gsuser ;get user #
1318 mvi c,getdrf ;get drive
1320 mov h,a ;drive returned in h
1324 pop b ;restore caller's bc
1327 ; set drive, user: h=drv, l=user
1329 setdu: push b ;don't modify bc
1337 mov e,l ;user # to e
1345 ; check for file-function: open, close, read random, write
1346 ; random, read sequential, write sequential.
1348 filfck: mov a,c ;get function #
1351 rc ;ignore lower function #'s
1352 cpi closef ;(they're not file-related)
1375 ; convert char to upper case
1377 cvtuc: cpi 'a' ;check lo bound
1384 ; check for hex filetype in fcb name
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"
1393 ani 7fh ;ignore attributes
1397 jnz hexit ;jump if not com
1400 hexit: pop b ;z reg has result
1407 ; routine to return user # without disturbing registers
1420 ; routine to return drive # without disturbing registers
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.
1455 varlen equ $-varset ;define length of init table
1459 vars equ $ ;define variables area start
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
1478 ; end of working variables
1484 spsave: ds 2 ;system stack pntr save
1487 ds 100 ;50-level stack
1490 cmdbuf equ $ ;command buffer location