]> cloudbase.mooo.com Git - kermit-80.git/blame - mload.asm
Bugfix in outmdm (output buffer flush)
[kermit-80.git] / mload.asm
CommitLineData
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
184warmbt equ 0 ;warm boot\r
185system equ 5 ;system entry (also top of mem pntr)\r
186dfcb equ 5ch ;default file control block\r
187ft equ 9 ;fcb offset to filetype\r
188tbuf equ 80h ;default buffer\r
189tpa equ 100h ;transient program area\r
190eof equ 1ah ;cp/m end-of-file mark\r
191fcbsiz equ 33 ;size of file control block\r
192;\r
193; CP/M system calls\r
194;\r
195pcharf equ 2 ;print char\r
196seldf equ 14 ;select disk drive\r
197openf equ 15 ;open file\r
198closef equ 16 ;close file\r
199fsrchf equ 17 ;search for first\r
200fsrchn equ 18 ;search for next\r
201erasef equ 19 ;delete file\r
202readf equ 20 ;read record\r
203writef equ 21 ;write record\r
204creatf equ 22 ;create file\r
205getdrf equ 25 ;return dflt drive #\r
206sdmaf equ 26 ;set dma address\r
207gsuser equ 32 ;get/set user #\r
208rrand equ 33 ;read random\r
209wrand equ 34 ;write random\r
210filszf equ 35 ;compute file size\r
211srand equ 36 ;set random\r
212;\r
213; ASCII character constants\r
214;\r
215cr equ 13\r
216lf equ 10\r
217bel equ 7\r
218tab 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
228outtyp: db 'COM'\r
229;\r
230begin: 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
240main: 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
245done: call wrtfil ;write the output file\r
246;\r
247; exit to cp/m\r
248;\r
249exit: 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
263setup: 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
290nitmem: 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
304fndspc: 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
319hexlp: 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
328digok: 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
342nxtfil: lhld cmdptr ;get command line pointer\r
343next2: 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
366noteq: 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
370gcomma: inx h ;skip over comma\r
371nxt2: 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
382open2: 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
404openok: 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
415lodfil: 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
424loadlp: 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
437notend: 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
463notgt: 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
479reclp: 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
491ghbcks: 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
509hexin: 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
525gnb: 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
531gnb1: 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
548diskrd: 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
558lodcom: 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
569comlp: 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
591lodend: 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
607wrtfil: 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
619wrtnb: 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
642wrlp: 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
695notcom: 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
714xlp: 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
734nottpa: 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
747closfl: 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
756ilprnt: 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
764prathl: 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
773prtfn: 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
780prndf: 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
803prtnam: 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
811pwind: 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
818decout: 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
823declp: 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
842crlf: 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
849hexout: 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
855hexbyt: 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
862nybble: 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
870type: 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
883move: inr b ;up one\r
884movlp: 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
894scanbk: 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
902hexval: 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
908hexdig: 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
918hexcvt: ani 0fh ;make it binary\r
919 ret\r
920;\r
921; ******************\r
922; * error handlers *\r
923; ******************\r
924;\r
925synerr: 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
930afnerr: call errxit ;exit with message\r
931 db 'Ambiguous file name: % not allowed.',0\r
932;\r
933fnferr: call errxit\r
934 db 'File % not found.',0\r
935;\r
936dskful: call errxit\r
937 db 'Disk full.',0\r
938;\r
939dirful: call errxit\r
940 db 'Directory full.',0\r
941;\r
942eoferr: call errxit\r
943 db 'Premature end-of-file in %',0\r
944;\r
945cserr: call errxit\r
946 db 'Checksum error in %',0\r
947;\r
948clserr: call errxit\r
949 db 'Can''t close %',0\r
950;\r
951memful: call errxit\r
952 db 'Memory full while loading %',0\r
953;\r
954formerr:call errxit\r
955 db 'Format error in file %',0\r
956;\r
957loderr: call errxit\r
958 db 'Writing %, nothing loaded',0\r
959;\r
960help: 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
979errxit: 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
987lodnit: 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
1028namskp: 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
1061fparse: 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
1072fpars1: 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
1079nitfcb: 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
1092nitlp: 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
1097zlp: 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
1112gstart: 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
1129getdrv: mvi b,0ffh ;default no user #\r
1130 push h ;save text pointer\r
1131dscan: 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
1147isnum: 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
1153uloop: 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
1174getps: 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
1182ftpoint: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
1188getft: 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
1204getnam: 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
1212notqm: 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
1218ambig: mvi a,'?' ;fill character for wild card match\r
1219fillq: 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
1224getdel: 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
1236getch: 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
1240fndelm: 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
1260bdos: 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
1269bdos0: 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
1293savedu: dw 0 ;saved drive,user\r
1294fcbdrv: db 0 ;fcb's drive\r
1295dmadr: dw 80h ;current dma adrs\r
1296;\r
1297bdos1: 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
1305bdos1a: 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
1312getdu: 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
1329setdu: 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
1348filfck: 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
1377cvtuc: 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
1386hexchk: 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
1392hexlop: 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
1400hexit: pop b ;z reg has result\r
1401 pop d\r
1402 pop h\r
1403 ret\r
1404;\r
1405hextyp: db 'HEX'\r
1406;\r
1407; routine to return user # without disturbing registers\r
1408;\r
1409getusr: 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
1422getdsk: 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
1438varset: 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
1455varlen equ $-varset ;define length of init table\r
1456;\r
1457; working variables\r
1458;\r
1459vars equ $ ;define variables area start\r
1460;\r
1461bias: ds 2 ;load offset\r
1462hiload: ds 2 ;highest true load address\r
1463hipc: ds 2 ;highest pc\r
1464cksum: ds 1 ;record checksum\r
1465cmdptr: ds 2 ;command line pointer\r
1466bufptr: ds 1 ;input buffer pointer\r
1467lodflg: ds 1 ;something-loaded flag\r
1468filbuf: ds 2 ;file buffer location\r
1469offset: ds 2 ;load offset into buffer\r
1470lodadr: ds 2 ;load address\r
1471outnam: ds 13 ;output drive+name\r
1472reccnt: ds 2 ;output file record count\r
1473bytcnt: ds 2 ;output file bytes loaded count\r
1474comflg: ds 1 ;flags com file encountered\r
1475comsiz: ds 2 ;size of a loaded com file\r
1476outflg: 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
1484spsave: ds 2 ;system stack pntr save\r
1485;\r
1486;\r
1487 ds 100 ;50-level stack\r
1488;\r
1489stack equ $\r
1490cmdbuf equ $ ;command buffer location\r
1491;\r
1492;\r
1493 end\r