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