aboutsummaryrefslogtreecommitdiff
path: root/tools/mload.asm
blob: 668796a589625e0826f7767b9b43a19277900873 (plain)
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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
	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
	push	d		;save load address on stack
	push	b		;save bias
	lxi	d,outnam+2	;check output filename
	ldax	d		;(first char)
	cpi	' '
	jnz	namskp		;jump if so
	lxi	h,dfcb+1	;get first name pointer
	mvi	b,8		;(don't include drive spec)
	call	move
;
; check for outflg=1 (presence of an "=").  note that the
; filename may well be blank, and yet outflg <>0, for example
; in the case of "A:=<FILENAME>" or "C4:=<FILENAME>". in
; this case, we want to remember the drive/user specified, but
; use the first input file to form the output name. otherwise,
; we use the current drive/user.
;
	lda	outflg		;was there an "="?
	ora	a
	jnz	namskp		;jump if so
	lxi	h,outnam	;get destination pointer
	call	getusr		;get current user #
	mov	m,a
	inx	h		;point to drive
	call	getdsk		;get it
	inr	a		;fcb's drive is 1-relative
	mov	m,a
namskp:	mvi	a,1		;insure "=" cannot occur anymore
	sta	outflg
	pop	b		;restore bias
	pop	h		;load address to hl
	pop	d		;restore record length
	ret
;
;	*********************************
;	* file name parsing subroutines *
;	*********************************
;
; credit where credit's due:
; --------------------------
; these routines were lifted from bob van valzah's
; "FAST" program.
;
;
;
;	*********************************
;	* file name parsing subroutines *
;	*********************************
;
;
; getfn gets a file name from text pointed to by reg hl into
; an fcb pointed to by reg de.	leading delimeters are
; ignored. allows drive spec of the form <du:> (drive/user).
; this routine formats all 33 bytes of the fcb (but not ran rec).
;
; entry de	first byte of fcb
; exit b=# of '?' in name
; fcb-1= user # parsed (if specified) or 255
;
;
fparse:	call	nitfcb		;init 1st half of fcb
	call	gstart		;scan to first character of name
	call	getdrv		;get drive/user spec. if present
	mov	a,b		;get user # or 255
	cpi	0ffh		;255?
	jz	fpars1		;jump if so
	dcx	d		;back up to byte preceeding fcb
	dcx	d
	stax	d		;stuff user #
	inx	d		;onward
	inx	d
fpars1:	call	getps		;get primary and secondary name
	ret
;
; nitfcb fills the fcb with dflt info - 0 in drive field
; all-blank in name field, and 0 in ex,s1,s2,rc, disk
; allocation map, and random record # fields
;
nitfcb:	push	h
	push	d
	call	getusr		;init user field
	pop	d
	pop	h
	push	d		;save fcb loc
	dcx	d
	stax	d		;init user # to currnt user #
	inx	d
	xchg			;move it to hl
	mvi	m,0		;drive=default
	inx	h		;bump to name field
	mvi	b,11		;zap all of name fld
nitlp:	mvi	m,' '
	inx	h
	dcr	b
	jnz	nitlp
	mvi	b,33-11		;zero others, up to nr field
zlp:	mvi	m,0
	inx	h
	dcr	b
	jnz	zlp
	xchg			;restore hl
	pop	d		;restore fcb pointer
	ret
;
; gstart advances the text pointer (reg hl) to the first
; non delimiter character (i.e. ignores blanks).  returns a
; flag if end of line (00h or ';') is found while scaning.
; exit	hl	pointing to first non delimiter
;	a	clobbered
;	zero	set if end of line was found
;
gstart:	call	getch		;see if pointing to delim?
	rnz			;nope - return
	ora	a		;physical end?
	rz			;yes - return w/flag
	inx	h		;nope - move over it
	jmp	gstart		;and try next char
;
; getdrv checks for the presence of a du: spec at the text
; pointer, and if present formats drive into fcb and returns
; user # in b.
;
; entry hl	text pointer
;	de	pointer to first byte of fcb
; exit	hl	possibly updated text pointer
;	de	pointer to second (primary name) byte of fcb
;	b	user # if specified or 0ffh
;
getdrv:	mvi	b,0ffh		;default no user #
	push	h		;save text pointer
dscan:	call	getch		;get next char
	inx	h		;skip pointer over it
	jnz	dscan		;scan until delimiter
	cpi	':'		;delimiter a colon?
	inx	d		;skip dr field in fcb in case not
	pop	h		;and restore text pointer
	rnz			;return if no du: spec
	mov	a,m		;got one, get first char
	call	cvtuc		;may be drive name, cvt to upper case
	cpi	'A'		;alpha?
	jc	isnum		;jump to get user # if not
	sui	'A'-1		;yes, convert from ascii to #
	dcx	d		;back up fcb pointer to dr field
	stax	d		;store drive # into fcb
	inx	d		;pass pointer over drv
	inx	h		;skip drive spec in text
isnum:	mov	a,m		;fetch next
	inx	h
	cpi	':'		;du delimiter?
	rz			;done then
	dcx	h		;nope, back up text pointer
	mvi	b,0		;got a digit, init user value
uloop:	mov	a,b		;get accumulated user #
	add	a		;* 10 for new digit
	add	a
	add	b
	add	a
	mov	b,a		;back to b
	mov	a,m		;get text char
	sui	'0'		;make binary
	add	b		;add to user #
	mov	b,a		;updated user #
	inx	h		;skip over it
	mov	a,m		;get next
	cpi	':'		;end of spec?
	jnz	uloop		;jump if not
	inx	h		;yep, return txt pointer past du:
	ret
;
; getps gets the primary and secondary names into the fcb.
; entry hl	text pointer
; exit	hl	character following secondary name (if present)
;
getps:	mvi	c,8		;max length of primary name
	mvi	b,0		;init count of '?'
	call	getnam		;pack primary name into fcb
	mov	a,m		;see if terminated by a period
	cpi	'.'
	rnz			;nope - secondary name not given
				;return default (blanks)
	inx	h		;yup - move text pointer over period
ftpoint:mov	a,c		;yup - update fcb pointer to secondary
	ora	a
	jz	getft
	inx	d
	dcr	c
	jmp	ftpoint
getft:	mvi	c,3		;max length of secondary name
	call	getnam		;pack secondary name into fcb
	ret
;
; getnam copies a name from the text pointer into the fcb for
; a given maximum length or until a delimiter is found, which
; ever occurs first.  if more than the maximum number of
; characters is present, character are ignored until a
; a delimiter is found.
; entry hl	first character of name to be scanned
;	de	pointer into fcb name field
;	c	maximum length
; exit	hl	pointing to terminating delimiter
;	de	next empty byte in fcb name field
;	c	max length - number of characters transfered
;
getnam:	call	getch		;are we pointing to a delimiter yet?
	rz			;if so, name is transfered
	inx	h		;if not, move over character
	cpi	'*'		;ambigious file reference?
	jz	ambig		;if so, fill the rest of field with '?'
	cpi	'?'		;afn reference?
	jnz	notqm		;skip if not
	inr	b		;else bump afn count
notqm:	call	cvtuc		;if not, convert to upper case
	stax	d		;and copy into name field
	inx	d		;increment name field pointer
	dcr	c		;if name field full?
	jnz	getnam		;nope - keep filling
	jmp	getdel		;yup - ignore until delimiter
ambig:	mvi	a,'?'		;fill character for wild card match
fillq:	stax	d		;fill until field is full
	inx	d
	inr	b		;increment count of '?'
	dcr	c
	jnz	fillq		;fall thru to ingore rest of name
getdel:	call	getch		;pointing to a delimiter?
	rz			;yup - all done
	inx	h		;nope - ignore antoher one
	jmp	getdel
;
; getch gets the character pointed to by the text pointer
; and sets the zero flag if it is a delimiter.
; entry hl	text pointer
; exit	hl	preserved
;	a	character at text pointer
;	z	set if a delimiter
;
getch:	mov	a,m		;get the character, test for delim
;
; global entry: test char in a for filename delimiter
;
fndelm:	cpi	'/'
	rz
	cpi	'.'
	rz
	cpi	','
	rz
	cpi	' '
	rz
	cpi	':'
	rz
	cpi	'='
	rz
	ora	a		;set zero flag on end of text
	ret
;
; bdos entry: preserves bc, de.  if system call is a file
;	      function, this routine logs into the drive/
;	      user area specified, then logs back after
;	      the call.
;
bdos:	call	filfck		;check for a file function
	jnz	bdos1		;jump if not a file function
	call	getdu		;get drive/user
	shld	savedu
	ldax	d		;get fcb's drive
	sta	fcbdrv		;save it
	dcr	a		;make 0-relative
	jm	bdos0		;if not default drive, jump
	mov	h,a		;copy to h
bdos0:	xra	a		;set fcb to default
	stax	d
	dcx	d		;get fcb's user #
	ldax	d
	mov	l,a
	inx	d		;restore de
	call	setdu		;set fcb's user
;
; note that unspecified user # (value=0ffh) becomes
; a getusr call, preventing ambiguity.
;
	call	bdos1		;do user's system call
	push	psw		;save result
	push	h
	lda	fcbdrv		;restore fcb's drive
	stax	d
	lhld	savedu		;restore prior drive/user
	call	setdu
	pop	h		;restore bdos result registers
	pop	psw
	ret
;
; local variables for bdos replacement routine
;
savedu:	dw	0		;saved drive,user
fcbdrv:	db	0		;fcb's drive
dmadr:	dw	80h		;current dma adrs
;
bdos1:	push	d
	push	b
	mov	a,c		;doing setdma?
	cpi	sdmaf
	jnz	bdos1a		;jump if not
	xchg			;yep, keep a record of dma addresses
	shld	dmadr
	xchg
bdos1a:	call	system
	pop	b
	pop	d
	ret
;
; get drive, user: h=drv, l=user
;
getdu:	push	b		;don't modify bc
	push	d
	mvi	c,gsuser	;get user #
	mvi	e,0ffh
	call	bdos1
	push	psw		;save it
	mvi	c,getdrf	;get drive
	call	bdos1
	mov	h,a		;drive returned in h
	pop	psw
	mov	l,a		;user in l
	pop	d
	pop	b		;restore caller's bc
	ret
;
; set drive, user: h=drv, l=user
;
setdu:	push	b		;don't modify bc
	push	d
	push	h		;save info
	mov	e,h		;drive to e
	mvi	c,seldf		;set it
	call	bdos1
	pop	h		;recall info
	push	h
	mov	e,l		;user # to e
	mvi	c,gsuser
	call	bdos1		;set it
	pop	h
	pop	d
	pop	b
	ret
;
; check for file-function: open, close, read random, write
;	random, read sequential, write sequential.
;
filfck:	mov	a,c		;get function #
	cpi	openf
	rz
	rc			;ignore lower function #'s
	cpi	closef		;(they're not file-related)
	rz
	cpi	readf
	rz
	cpi	writef
	rz
	cpi	rrand
	rz
	cpi	wrand
	rz
	cpi	fsrchf
	rz
	cpi	fsrchn
	rz
	cpi	erasef
	rz
	cpi	creatf
	rz
	cpi	filszf
	rz
	cpi	srand
	ret
;
; convert char to upper case
;
cvtuc:	cpi	'a'		;check lo bound
	rc
	cpi	'z'+1		;check hi
	rnc
	sui	20h		;convert
	ret
;
; check for hex filetype in fcb name
;
hexchk:	push	h
	push	d
	push	b
	mvi	b,3		;type is 3 chars
	lxi	d,dfcb+9	;point de to type field
	lxi	h,hextyp	;point hl to "COM"
hexlop:	ldax	d
	ani	7fh		;ignore attributes
	cmp	m
	inx	h
	inx	d
	jnz	hexit		;jump if not com
	dcr	b
	jnz	hexlop
hexit:	pop	b		;z reg has result
	pop	d
	pop	h
	ret
;
hextyp:	db	'HEX'
;
; routine to return user # without disturbing registers
;
getusr:	push	h
	push	d
	push	b
	mvi	c,gsuser
	mvi	e,0ffh
	call	bdos
	pop	b
	pop	d
	pop	h
	ret
;
; routine to return drive # without disturbing registers
;
getdsk:	push	h
	push	d
	push	b
	mvi	c,getdrf
	call	bdos
	pop	b
	pop	d
	pop	h
	ret
;
; these are the initial values of the variables, and
; are moved into the variables area by the setup routine.
; if you add variables, be sure to add their intial value
; into this table in the order corresponding to their
; occurance in the variables section.
;
varset:	dw	0			;bias
	dw	0			;hiload
	dw	0			;hipc
	db	0			;cksum
	dw	cmdbuf			;cmdptr
	db	0			;bufptr
	db	0			;lodflg
	dw	cmdbuf			;filbuf
	dw	0			;offset
	dw	0			;lodadr
	db	0,0,'           '	;outnam
	dw	0			;reccnt
	dw	0			;bytcnt
	db	0			;comflg
	dw	0			;comsiz
	db	0			;outflg
;
varlen	equ	$-varset	;define length of init table
;
; working variables
;
vars	equ	$		;define variables area start
;
bias:	ds	2		;load offset
hiload:	ds	2		;highest true load address
hipc:	ds	2		;highest pc
cksum:	ds	1		;record checksum
cmdptr:	ds	2		;command line pointer
bufptr:	ds	1		;input buffer pointer
lodflg:	ds	1		;something-loaded flag
filbuf:	ds	2		;file buffer location
offset:	ds	2		;load offset into buffer
lodadr:	ds	2		;load address
outnam:	ds	13		;output drive+name
reccnt:	ds	2		;output file record count
bytcnt:	ds	2		;output file bytes loaded  count
comflg:	ds	1		;flags com file encountered
comsiz:	ds	2		;size of a loaded com file
outflg:	ds	1		;flags an "=" present in cmd line
;
; end of working variables
;
;
;
; stack stuff
;
spsave:	ds	2		;system stack pntr save
;
;
	ds	100		;50-level stack
;
stack	equ	$
cmdbuf	equ	$		;command buffer location
;
;
	end