forked from z80playground/cpm-fat
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmload.asm
1493 lines (1493 loc) · 36.2 KB
/
mload.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
; title 'MLOAD MULTI-FILE HEX LOAD UTILITY'
;
; *********************************
; * MLOAD.ASM *
; * Multi-file Hex Load Utility *
; * for CP/M *
; *********************************
;
;
; Replacement for the cp/m "LOAD" program: this program
; fixes many of the problems associated with the "CP/M"
; load program, and adds many new features.
;
; ----------------
;
; Rev 2.5
; 03/10/88
; Property of NightOwl Software, Inc. Fort Atkinson, WI 53538
; Written by Ron Fowler, Nightowl Software, Inc.
;
; ----------------
; Notice: this program is NOT public domain; copyright is retained by
; NightOwl Software, Inc. of Fort Atkinson, WI ... All Rights Reserved.
;
; License is granted for free use and re-distribution this program, as
; long as such use and re-distribution is done without profit.
;
; ----------------
;
; modification history:
;
; 2.5 (WOD) This version corrects a bug that overlayed the first six
; bytes of the CCP. The error did not show up unless a
; jump to the CCP was done without a warm boot since MLOAD
; used. This source file has been modified here with
; concurrence of the author of MLOAD, Ron Fowler.
;
; 2.4 (RGF) We apologize for this relatively insubstantial update,
; but someone has caused what we consider to be a problem,
; by making changes to the program, and re-releasing under
; the same version number. The changes in this case were
; conversion of the opcode fields (but not the comments,
; can you believe that??) of every line to upper case! That
; totally invalidated the CRC of the source file, since there
; are now two different MLOAD 2.3's running around.
;
; We DO NOT want these stupid mixed upper/lower case changes.
; Someone somewhere has decided that this is the way assembly
; language source should be, and we most VEHEMENTLY disagree.
; It's a pain in the neck to make changes to and we don't
; care to run our programs through conversion programs every
; time we make changes.
;
; So ... leave the case of this file AS IS. Any changes made
; to this program and not co-ordinated through us may very
; well endanger availability of source code when we make
; future updates. 'nuff said --NightOwl Software
;
; 2.3 (RGF) Trivial cosmetic changes
; 2.2 (RGF) Modified copyright notice to show new owner of the
; program.
; 2.1 (RGF) Fixed problem on disk-full when writing output file
; (mload previously didn't error out on a full disk)
; 2.0 (RGF) Added the ability to pre-load a non-hex file, allowing
; mload to be used to load hex file patches (obviating any
; need to use DDT). The normal mload syntax is preserved.
; the first (and only the first) filespec (after the "=",
; if used) may be non-hex; the filetype must be specified.
; Examples:
;
; 1) mload ws.com,wspatch
; 2) mload MEXTEST=MEX112.COM,MXO-US13
; 3) mload ws.ovr,ovrpatch
;
; The first example loads WS.COM, overlays it with
; wspatch.hex, and writes the output to WS.COM. The
; second example loads MEX112.COM, overlays it with
; MXO-US13.HEX, and writes the output file to MEXTEST.COM.
; (note that the second example is the recommended technique,
; since it preserves the original file). The third example
; loads WS.OVR and patches it with the file "OVRPATCH.HEX".
;
; Also added this rev: ZCPR2-style du specs are now fully
; supported, for both input and output files. Thus, the
; following command lines are permissable:
;
; b3>mload a4:myfile.com=0:bigfil,b6:patch1,c9:patch2
; a6>mload b5:=c3:mdm717.com,mdmpatch
;
; After loading, an additional information line is now printed
; in the statistics report, which displays the true size of the
; saved image (the previous report was technically correct, but
; could result in confusion for certain kinds of files with
; imbedded "DS" and "ORG" statements in the original source code).
;
; 1.0 - 1.4 (RGF) change log removed to conserve space
;
; originally written by ron fowler, fort atkinson, wisconsin
;
;
;
; For assembly with asm.com or mac (delete above title line if
; assembling with asm.com).
;
; This program is a replacement for the cp/m "LOAD" program.
; Why replace "LOAD"? well... LOAD.COM has a few deficiencies.
; For example, if your hex file's origin is above 100h, LOAD.COM
; prepends blank space to the output file to insure it will work
; as a CP/M transient. It cares not if the file is not intended
; as a CP/M transient. it also doesn't like hex records with mixed
; load addresses (for example, one that loads below a previous record --
; which is a perfectly legitimate happenstance). Also, LOAD.COM
; can load only one program at a time, and has no provision for
; a load bias in the command specification. Finally, there is no
; provision for user specification of the output file name.
;
;
; Hence, this program....
;
;------------------------------------------------------------
;
; Syntax is as follows:
;
; mload [<outnam=] <filename>[,<filename>...] [bias]
;
; where <outnam> is the (optional!;) output file name (only the drive
; spec and primary filename may be specified; the output filetype is
; derived exclusively from the 3-byte string at 103h within MLOAD),
; <filename> specifies files to load and <bias> is the offset within
; the saved image to apply when loading the file.
;
; MLOAD with no arguments prints a small help message -- this message
; is also printed whenever a command line syntax error occurs.
;
; Filenames may contain drive/user specs, and must not contain wildcards.
; Input filenames must be separated by commas, and a space is required
; between the last filename and the optional bias.
;
; A load information summary is printed at the successful conclusion
; of the load. Any errors in loading will generally include the name
; of the file in question.
;
; If no output filename is specified, it will be derived from the first
; input filename, with filetype of 'COM', if not otherwise specified
; (this default filetype may be patched directly into mload via DDT
; (or with MLOAD itself, using a patch file) -- its location is at 103H
; in MLOAD.COM). Note that a command line of the form "C:=<FILENAME>"
; will place the output file on the "C" drive with the same primary
; filename as the input file.
;
; In its simplest form, MLOAD's syntax is identical to LOAD.COM; thus
; there should be no problem in learning to use the new program. The
; only significant difference here is that, under LOAD.COM, all files
; are output starting at 100h, even if they originate elsewhere. MLOAD
; outputs starting at the hex file origin (actually, the first hex rec-
; ord specifies the output load address). The bias option may be used
; to override this.
;
; An example should clarify this. Suppose you have a file that loads
; at 1000h. LOAD.COM would save an output file that begins at 100h and
; loads past 1000h (to wherever the program ends). MLOAD will save an
; output file starting from 1000h only. If, for some reason you need the
; file to start at 100h in spite of its 1000h origin (i can think of sev-
; eral circumstances where this would be necessary), you'd have to specify
; a bias to mload. thus, using this example, "MLOAD MYFILE 0F00" would do.
;
; Note that this program re-initializes itself each time it is run.
; Thus, if your system supports a direct branch to the tpa (via a zero-length
; .COM file, or the ZCPR "GO" command), you may safely re-execute MLOAD.
;
; Please report any bugs, bug fixes, or enhancements to
;
; "FORT FONE FILE FOLDER" rcpm/cbbs
; Fort Atkinson, Wisconsin
; (414) 563-9932 (no ring back)
;
; --Ron Fowler
; 03/08/84 updated 1/31/85
;
;------------------------------------------------------------
;
; CP/M equates
;
warmbt equ 0 ;warm boot
system equ 5 ;system entry (also top of mem pntr)
dfcb equ 5ch ;default file control block
ft equ 9 ;fcb offset to filetype
tbuf equ 80h ;default buffer
tpa equ 100h ;transient program area
eof equ 1ah ;cp/m end-of-file mark
fcbsiz equ 33 ;size of file control block
;
; CP/M system calls
;
pcharf equ 2 ;print char
seldf equ 14 ;select disk drive
openf equ 15 ;open file
closef equ 16 ;close file
fsrchf equ 17 ;search for first
fsrchn equ 18 ;search for next
erasef equ 19 ;delete file
readf equ 20 ;read record
writef equ 21 ;write record
creatf equ 22 ;create file
getdrf equ 25 ;return dflt drive #
sdmaf equ 26 ;set dma address
gsuser equ 32 ;get/set user #
rrand equ 33 ;read random
wrand equ 34 ;write random
filszf equ 35 ;compute file size
srand equ 36 ;set random
;
; ASCII character constants
;
cr equ 13
lf equ 10
bel equ 7
tab equ 9
;
; without further ado...
;
org tpa
;
jmp begin ;jump over default output filetype
;
; the default output filetype is located at 103h for easy patching
;
outtyp: db 'COM'
;
begin: lxi h,0 ;save system stackpointer
dad sp
shld spsave
lxi sp,stack ;load local stack
call ilprnt ;sign on
db 'MLOAD ver. 2.5 Copyright (C) 1983, 1984, 1985'
db cr,lf
db 'by NightOwl Software, Inc.'
db cr,lf,0
call setup ;initialize
main: call nxtfil ;parse and read next input file
jc done ;no more...
call lodfil ;yep, load it
call closfl ;close it (in case MP/M or TurboDOS)
jmp main ;maybe more
done: call wrtfil ;write the output file
;
; exit to cp/m
;
exit: lxi d,tbuf ;restore dma address
mvi c,sdmaf
call bdos
lda system+2 ;get top of memory pointer
sui 9 ;allow for ccp+slop
lxi h,hiload+1 ;highest load address
sub m ;above ccp?
jc warmbt ;then warm-boot
lhld spsave ;nope, ccp still in memory
sphl ;restore its stack
ret ;return to ccp
;
; load program initialization
;
setup: lxi h,varset ;initialize variables
lxi d,vars
mvi b,varlen ;by moving in default values
call move
lhld cmdptr ;get first free mem pointer
xchg ;in de
lxi h,tbuf ;point to command tail bufr
mov a,m ;get its length
inx h
ora a ;does it have any length?
jz help ;nope, go give usage help
mov b,a ;yep, get length to b
call move ;move cmd tail to buffer
xchg ;end of dest to hl
mvi m,0 ;stuff a terminator
inx h ;point to first free memory
shld filbuf ;set up file buffer
xchg ;file bufr adrs to de
lhld system+1 ;get top of memory pointer
xra a ;round system to page boundary
sub e
mov c,a ;with result in bc
mov a,h
sui 9 ;allow for ccp
sbb d
mov b,a
xchg ;buffer pointer to hl
nitmem: mvi m,0 ;clear buffer
inx h
dcx b
mov a,b
ora c
jnz nitmem
;
; look for a bias specification in command line
;
lhld cmdptr ;point to command buffer-1
dcx h
call scanbk ;scan past blanks
ora a ;no non-blank chars?
jz help ;then go print help text
fndspc: inx h ;point to next
mov a,m ;fetch it
ora a ;test it
rz ;line ended, return
cpi ' ' ;nope, test for blank
jnz fndspc ;not blank, continue
call scanbk ;skip blanks
ora a ;end-of-line?
rz ;return if so
;
; hl points to bias in command line
;
lxi d,0 ;init bias
call hexdig ;insure a hex digit
jc synerr ;bad...
hexlp: mov a,m ;no. get next char
inx h ;skip over it
call hexdig ;test for hex digit
jnc digok ;jump if good hex digit
ora a ;must end on null terminator
jnz synerr
xchg ;good end, get bias to hl
shld bias ;stuff it
ret ;done
digok: xchg ;bias to hl
dad h ;skift left 4 to make room
dad h ; for new hex digit
dad h
dad h
xchg ;back to de
add e ;add in new digit
mov e,a
jnc hexlp ;jump if no 8-bit ovfl
inr d ;carry
jmp hexlp
;
; parse next input name, and open resultant file
;
nxtfil: lhld cmdptr ;get command line pointer
next2: lxi d,dfcb ;destination fcb
call fparse ;parse a filename
cpi '=' ;stopped on output specifier?
jnz noteq
lda outnam+2 ;insure no name yet specified
cpi ' '
jnz synerr ;syntax error if already named
lda outflg ;already been here?
ora a
jnz synerr ;can't be here twice
inr a ;flag that we've been here
sta outflg
inr b ;insure no ambiguous output name
dcr b
jnz afnerr
inx h ;skip over '='
push h ;save cmd line pointer
lxi h,dfcb-1 ;move the name to output name hold
lxi d,outnam
mvi b,13 ;drive spec too
call move
pop h ;restore command line pointer
jmp next2 ;go parse another
noteq: cpi ',' ;stopped on comma?
jz gcomma ;jump if so
mvi m,0 ;nope, insure end of input
jmp nxt2 ;don't advance over fake end
gcomma: inx h ;skip over comma
nxt2: shld cmdptr ;save new command line pntr
mov a,b ;get ambig char count
ora a ;test it
jnz afnerr ;allow no ambig characters
sta bufptr ;force a disk read
lxi d,dfcb+1 ;look at parsed filename
ldax d
cpi ' ' ;blank? (input ended?)
stc ;get carry ready in case so
rz ;return cy if input gone
dcx d ;nope, point de to start of fcb
open2: push d ;try to open the file
mvi c,openf
call bdos
pop d
inr a ;return=0ffh?
jnz openok ;jump if not
;
; file not found: if filetype blank, set to 'hex' and try again
;
lxi h,dfcb+ft ;point to file type
mov a,m ;anything there?
cpi ' '
jnz fnferr ;yes, so file not found
mvi m,'H' ;nope, fill in 'hex'
inx h
mvi m,'E'
inx h
mvi m,'X'
jmp open2 ;go try again
;
; here after a good file open
;
openok: call hexchk ;is this a hex file?
rz ;if so, all done
lxi h,comflg ;no, get pointer to flag
mov a,m ;loading first file?
ora a
rnz ;if not, ignore type, consider hex
inr m ;else, set the flag
ret
;
; load current file
;
lodfil: lxi h,comflg ;loading a com file?
mov a,m ;get flag
ani 1
jnz lodcom ;jump if so
lhld bias ;else get bias on top of stack
push h
;
; load a hex record
;
loadlp: call gnb ;get next file byte
sbi ':' ;look for start-record mark
jnz loadlp ;scan until found
sta cksum ;got it, init checksum to zero
mov d,a ;upper byte of rec cnt=0
pop b ;retrieve bias adrs
push b ;save it again
call ghbcks ;get hex byte w/checksum
mov e,a ;de now has record length
ora a ;test it
jnz notend ;jump if len<>0 (not eof rec)
pop h ;all done
ret
notend: call ghbcks ;hi byte of rec ld adrs
mov h,a ;accumulate in hl
call ghbcks ;get lo byte
mov l,a ;put lo in l
lda lodflg ;test load flag
ora a
cz lodnit ;not first record, initialize
push h ;save load address
dad d ;add in record length
dcx h ;make highest, not next
lda hipc ;a new high?
sub l
lda hipc+1
sbb h
jnc notgt ;jump if not
shld hipc ;yep, update hipc
push d ;save reclen
xchg ;load adrs to de
lhld offset ;get offset to form true memory adrs
dad d ;add in offset
dad b ;and bias
shld hiload ;mark highest true memory load adrs
lda system+2 ;validate against top-mem pointer
cmp h
jc memful ;jump if out of memory
pop d ;restore reclen
notgt: pop h ;restore load address
dad b ;add bias to load adrs
push d ;save record length
push h
lhld bytcnt ;add record length to byte count
dad d
shld bytcnt
pop h
xchg
lhld offset ;calculate true memory adrs
dad d ;hl=true loading adrs
pop d ;restore record length
call ghbcks ;skip unused byte of intel format
;
; move the record into memory
;
reclp: call ghbcks ;get hex byte
mov m,a ;store it in buffer
inx h ;point to next
dcr e ;count down
jnz reclp ;until record all read
call ghbcks ;get checksum byte
jnz cserr ;final add cksum should sum 0
jmp loadlp ;good load, go do nxt record
;
; get next hex byte from input, and
; accumulate a checksum
;
ghbcks: push b ;save em all
push h
push d
call hexin ;get hex byte
mov b,a ;save in b
lxi h,cksum ;add to checksum
mov a,m
add b
mov m,a
mov a,b ;get byte back
pop d ;restore checksum
pop h ;restore other regs
pop b
ret
;
; routine to get next byte from input...forms
; byte from two ascii hex characters
;
hexin: call gnb ;get next input file byte
call hexval ;convert to binary w/validation
rlc ;move into ms nybble
rlc
rlc
rlc
ani 0f0h ;kill possible garbage
push psw ;save it
call gnb ;get next byte
call hexval ;convert it, w/validation
pop b ;get back first
ora b ;or in second
ret ;good byte in a
;
; gnb - utility subroutine to get next
; byte from disk file
gnb: push h ;save all regs
push d
push b
lda bufptr ;get input bufr pointer
ani 7fh ;wound back to 0?
jz diskrd ;go read sector if so
gnb1: mvi d,0 ;else form 16 bit offset
mov e,a
lxi h,tbuf ;from tbuf
dad d ;add in offset
mov a,m ;get next byte
cpi eof ;end of file?
jz eoferr ;error if so
lxi h,bufptr ;else bump buf ptr
inr m
ora a ;return carry clear
pop b ;restore and return
pop d
pop h
ret
;
; read next sector from disk
;
diskrd: mvi c,readf ;bdos "READ SEC" function
lxi d,dfcb
call bdos ;read sector
ora a
jnz eoferr ;error if phys end of file
sta bufptr ;store 0 as new buf ptr
jmp gnb1 ;go re-join gnb code
;
; load a com file
;
lodcom: inr m ;bump the comfile flag
lxi h,tpa ;set origin
call lodnit ;and initialize
xchg ;load address in de
lhld bias ;add in bias
dad d
xchg
lhld offset ;and offset
dad d
xchg ;de has absolute mem adrs of load
;
comlp: lxi h,128 ;calculate next dma
dad d
lda system+2 ;check for space
cmp h
jc memful ;jump if none
push h ;else save next dma
push d ;and this dma
mvi c,sdmaf ;set this dma
call bdos
lxi d,dfcb ;read next record
mvi c,readf
call bdos
pop h ;recall this dma
pop d ;de=next dma
ora a ;end of read?
jnz lodend ;jump if so
lhld comsiz ;no, advance com byte count
lxi b,128
dad b
shld comsiz
jmp comlp ;continue
;
lodend: dcx h ;one less byte is highest
shld hiload ;set a new high
lhld comsiz ;hi pc=bytecount+100h
lxi d,tpa
dad d
xchg ;to de
lhld bias ;add in bias
dad d
shld hipc
lxi d,tbuf ;reset dma for hex files
mvi c,sdmaf
call bdos
ret
;
; write output file
;
wrtfil: lxi d,dfcb ;point to fcb
push d ;save 2 copies of pointer
push d
call nitfcb ;initialize output fcb
lxi h,outnam ;move output name in
dcx d ;point to user # (prior to fcb)
mvi b,10 ;move user, drive, primary name
call move
mov a,m ;output type blank?
cpi ' '
jnz wrtnb ;jump if not
lxi h,outtyp ;yes, move dflt output filetype in
wrtnb: mvi b,3
call move
pop d ;restore fcb pointer
mvi c,erasef ;erase any existing file
call bdos
pop d ;restore fcb pointer
mvi c,creatf ;create a new file
call bdos
inr a ;good create?
jz dirful ;goto directory full error if not
lhld hiload ;yep, get top of bufr pntr
xchg ;in de
lhld filbuf ;get start of bufr adrs
mov a,e ;calculate output file size
sub l
mov c,a ;with result in bc
mov a,d
sbb h
mov b,a
mov a,b ;test length
ora c
jz loderr ;nothing to write???
lxi d,dfcb ;get fcb pointer
wrlp: push b ;save count
push d ;and fcb pointer
xchg ;get memory pointer to de
lxi h,128 ;add in sector length for next pass
dad d
xthl ;save next dma
push h ;above fcb
mvi c,sdmaf ;set transfer address
call bdos
pop d ;fetch fcb pointer
push d ;save it again
mvi c,writef ;write a sector
call bdos
ora a ;test result
jnz dskful ;disk full error...
lhld reccnt ;no,increment count of records
inx h
shld reccnt
pop d ;restore fcb pointer
pop h ;and memory write pointer
pop b ;and count
mov a,c ;subtract 128 (sec size) from count
sui 128
mov c,a
jnc wrlp ;jump if some left
mov a,b ;hi-order borrow
sui 1 ;do it (can't "DCR B", doesn't affect cy)
mov b,a ;restore
jnc wrlp ;jump if more left
call closfl ;close output file
;
; report statistics to console
;
call ilprnt
db 'Loaded ',0
lhld bytcnt ;print # bytes
call decout
call ilprnt
db ' bytes (',0
call hexout
call ilprnt
db 'H)',0
call ilprnt
db ' to file %',0
lda comflg ;did we load a comfile too?
ora a
jz notcom ;jump if not
call ilprnt
db cr,lf,'Over a ',0
lhld comsiz
call decout
call ilprnt
db ' byte binary file',0
notcom: call ilprnt
db cr,lf,'Start address: ',0
lhld lodadr ;print loading address
call hexout
call ilprnt
db 'H Ending address: ',0
lhld hipc ;print ending load address
call hexout
call ilprnt
db 'H Bias: ',0
lhld bias
call hexout
call ilprnt
db 'H',cr,lf,0
call ilprnt
db 'Saved image size: ',0
lhld reccnt ;get count of image records
push h ;save it
mvi b,7 ;convert to bytes
xlp: dad h
dcr b
jnz xlp
call decout ;print it
call ilprnt
db ' bytes (',0
call hexout ;now in hex
call ilprnt
db 'H, - ',0
pop h ;recall record count
call decout ;print it
call ilprnt
db ' records)',cr,lf,0
lhld lodadr ;fetch loading address
mov a,l ;test if =tpa
ora a
jnz nottpa ;tpa always on page boundary
mov a,h ;lo ok, test hi
cpi (tpa shr 8) and 0ffh
rz ;return if tpa
nottpa: call ilprnt ;not, so print warning msg
db cr,lf,bel
db '++ Warning: program origin NOT at 100H ++'
db cr,lf,0
ret ;done
;
; ***********************
; * utility subroutines *
; ***********************
;
;
; routine to close any open file
;
closfl: lxi d,dfcb
mvi c,closef
call bdos
inr a ;test close result
jz clserr ;jump if error
ret
;
; print message in-line with code
;
ilprnt: xthl ;message pntr to hl
call prathl ;print it
xthl ;restore and return
ret
;
; print msg pointed to by hl until null. expand
; '%' char to current filename.
;
prathl: mov a,m ;fetch char
inx h ;point to next
ora a ;terminator?
rz ;then done
cpi '%' ;want filename?
jz prtfn ;go do it if so
call type ;nope, just print char
jmp prathl ;continue
;
prtfn: push h ;save pointer
push b
lda dfcb ;fetch dr field of dfcb
ora a ;default drive?
jnz prndf ;jump if not
call getdsk ;get logged-in drive #
inr a ;make it one-relative (as in fcb)
prndf: adi 'A'-1 ;make drive name printable
call type ;print it
lda dfcb-1 ;get user #
cpi 0ffh ;null?
cz getusr ;iff so, get current user
mov l,a ;to hl
mvi h,0
call decout ;print it
mvi a,':' ;drive names followed by colon
call type
lxi h,dfcb+1 ;setup for name
mvi b,8 ;print up to 8
call prtnam
mvi a,'.' ;print dot
call type
mvi b,3 ;print filetype field
call prtnam
pop b
pop h ;restore and continue
jmp prathl
;
; print file name .HL max length in b. don't print spaces
;
prtnam: mov a,m ;fetch a char
cpi ' ' ;blank?
jz pwind ;go wind if so
inx h ;nope, move to next
call type ;print it
dcr b ;count down
jnz prtnam ;continue
ret
pwind: inx h ;skip remainder of blank name
dcr b
jnz pwind
ret
;
; print HL in decimal on console
;
decout: push h ;save everybody
push d
push b
lxi b,-10 ;conversion radix
lxi d,-1
declp: dad b
inx d
jc declp
lxi b,10
dad b
xchg
mov a,h
ora l
cnz decout ;this is recursive
mov a,e
adi '0'
call type
pop b
pop d
pop h
ret
;
; newline on console
;
crlf: mvi a,cr
call type
mvi a,lf
jmp type
;
; print hl on console in hex
;
hexout: mov a,h ;get hi
call hexbyt ;print it
mov a,l ;get lo, fall into hexbyt
;
; type accumulator on console in hex
;
hexbyt: push psw ;save byte
rar ;get ms nybble..
rar ;..into lo 4 bits
rar
rar
call nybble
pop psw ;get back byte
nybble: ani 0fh ;mask ms nybble
adi 90h ;add offset
daa ;decimal adjust a-reg
aci 40h ;add offset
daa ;fall into type
;
; type char in a on console
;
type: push h ;save all
push d
push b
mov e,a ;cp/m outputs from e
mvi c,pcharf
call bdos
pop b
pop d
pop h
ret
;
; move: from @hl to @de, count in b
;
move: inr b ;up one
movlp: dcr b ;count down
rz ;return if done
mov a,m ;not done, continue
stax d
inx h ;pointers=pointers+1
inx d
jmp movlp
;
; scan to first non-blank char in string @hl
;
scanbk: inx h ;next
mov a,m ;fetch it
cpi ' '
jz scanbk
ret
;
; get hex digit and validate
;
hexval: call hexdig ;get hex digit
jc formerr ;jump if bad
ret
;
; get hex digit, return cy=1 if bad digit
;
hexdig: cpi '0' ;lo boundary test
rc ;bad already?
cpi '9'+1 ;no, test hi
jc hexcvt ;jump if numeric
cpi 'A' ;test alpha
rc ;bad?
cpi 'F'+1 ;no, upper alpha bound
cmc ;pervert carry
rc ;bad?
sui 7 ;no, adjust to 0-f
hexcvt: ani 0fh ;make it binary
ret
;
; ******************
; * error handlers *
; ******************
;
synerr: call crlf
call ilprnt
db ' Command line syntax error',cr,lf,cr,lf,0
jmp help ;give help msg too
;
afnerr: call errxit ;exit with message
db 'Ambiguous file name: % not allowed.',0
;
fnferr: call errxit
db 'File % not found.',0
;
dskful: call errxit
db 'Disk full.',0
;
dirful: call errxit
db 'Directory full.',0
;
eoferr: call errxit
db 'Premature end-of-file in %',0
;
cserr: call errxit
db 'Checksum error in %',0
;
clserr: call errxit
db 'Can''t close %',0
;
memful: call errxit
db 'Memory full while loading %',0
;
formerr:call errxit
db 'Format error in file %',0
;
loderr: call errxit
db 'Writing %, nothing loaded',0
;
help: call errxit ;print help text
db 'MLOAD syntax:',cr,lf,cr,lf
db 'MLOAD [<OUTFIL>=] <FILE1>[,<FILE2>...] [<BIAS>]',cr,lf
db tab,' (brackets denote optional items)',cr,lf,cr,lf
db tab,'<OUTFIL> is the optional output filename',cr,lf
db tab,'<FILEn> are input file(s)',cr,lf
db tab,'<BIAS> is a hex load offset within the output file'
db cr,lf,cr,lf
db tab,'<FILE1> may be an optional non-HEX file to be patched',cr,lf
db tab,'by subsequently named HEX files (specifying',cr,lf
db tab,'The filetype enables this function).'
db cr,lf,cr,lf
db 'Note that ZCPR2-style drive/user notation may be used in all'
db cr,lf
db 'file specifications (e.g., "B3:MYFILE.COM, "A14:MDM7.HEX").'
db cr,lf,0
;
; general error handler
;
errxit: call crlf ;new line
pop h ;fetch error msg pointer
call prathl ;print it
call crlf
jmp exit ;done
;
; initialize load parameters
;
lodnit: mvi a,1 ;first record, set load flag
sta lodflg
shld lodadr ;save load address
shld hipc ;and hi load
push d ;save record length
xchg ;de=load address
lhld filbuf ;get address of file buffer
mov a,l ;subtract load adrs from file buffer
sub e
mov l,a
mov a,h
sbb d
mov h,a
shld offset ;save as load offset