-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcpm.asm
2536 lines (2534 loc) · 73.8 KB
/
cpm.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
;**************************************************************
;*
;* C P / M version 2 . 2
;*
;* Reconstructed from memory image on February 27, 1981
;*
;* by Clark A. Calkins
;*
;**************************************************************
;
; Set memory limit here. This is the amount of contigeous
; ram starting from 0000. CP/M will reside at the end of this space.
;
IOBYTE EQU 3 ;i/o definition byte.
TDRIVE EQU 4 ;current drive name and user number.
ENTRY EQU 5 ;entry point for the cp/m bdos.
TFCB EQU 5CH ;default file control block.
TBUFF EQU 80H ;i/o buffer and command line storage.
TBASE EQU 100H ;transiant program storage area.
;
; Set control character equates.
;
CNTRLC EQU 3 ;control-c
CNTRLE EQU 05H ;control-e
BS EQU 08H ;backspace
TAB EQU 09H ;tab
LF EQU 0AH ;line feed
FF EQU 0CH ;form feed
CR EQU 0DH ;carriage return
CNTRLP EQU 10H ;control-p
CNTRLR EQU 12H ;control-r
CNTRLS EQU 13H ;control-s
CNTRLU EQU 15H ;control-u
CNTRLX EQU 18H ;control-x
CNTRLZ EQU 1AH ;control-z (end-of-file mark)
DEL EQU 7FH ;rubout
;
; Set origin for CP/M
;
;
CBASE:
display "CCP: ", $
incbin "zcpr.bin"
;**************************************************************
;*
;* B D O S E N T R Y
;*
;**************************************************************
;
FBASE: JP FBASE1
;
; Bdos error table.
;
BADSCTR:DEFW ERROR1 ;bad sector on read or write.
BADSLCT:DEFW ERROR2 ;bad disk select.
RODISK: DEFW ERROR3 ;disk is read only.
ROFILE: DEFW ERROR4 ;file is read only.
;
; Entry into bdos. (DE) or (E) are the parameters passed. The
; function number desired is in register (C).
;
FBASE1: EX DE,HL ;save the (DE) parameters.
LD (PARAMS),HL
EX DE,HL
LD A,E ;and save register (E) in particular.
LD (EPARAM),A
LD HL,0
LD (STATUS),HL ;clear return status.
ADD HL,SP
LD (USRSTACK),HL ;save users stack pointer.
LD SP,STKAREA ;and set our own.
XOR A ;clear auto select storage space.
LD (AUTOFLAG),A
LD (AUTO),A
LD HL,GOBACK ;set return address.
PUSH HL
LD A,C ;get function number.
CP NFUNCTS ;valid function number?
RET NC
LD C,E ;keep single register function here.
LD HL,FUNCTNS ;now look thru the function table.
LD E,A
LD D,0 ;(DE)=function number.
ADD HL,DE
ADD HL,DE ;(HL)=(start of table)+2*(function number).
LD E,(HL)
INC HL
LD D,(HL) ;now (DE)=address for this function.
LD HL,(PARAMS) ;retrieve parameters.
EX DE,HL ;now (DE) has the original parameters.
JP (HL) ;execute desired function.
;
; BDOS function jump table.
;
NFUNCTS EQU 41 ;number of functions in followin table.
;
FUNCTNS:DEFW WBOOT,GETCON,OUTCON,GETRDR,PUNCH,LIST,DIRCIO,GETIOB
DEFW SETIOB,PRTSTR,RDBUFF,GETCSTS,GETVER,RSTDSK,SETDSK,OPENFIL
DEFW CLOSEFIL,GETFST,GETNXT,DELFILE,READSEQ,WRTSEQ,FCREATE
DEFW RENFILE,GETLOG,GETCRNT,PUTDMA,GETALOC,WRTPRTD,GETROV,SETATTR
DEFW GETPARM,GETUSER,RDRANDOM,WTRANDOM,FILESIZE,SETRAN,LOGOFF,RTN
DEFW RTN,WTSPECL
;
; Bdos error message section.
;
ERROR1: LD HL,BADSEC ;bad sector message.
CALL PRTERR ;print it and get a 1 char responce.
CP CNTRLC ;re-boot request (control-c)?
JP Z,0 ;yes.
RET ;no, return to retry i/o function.
;
ERROR2: LD HL,BADSEL ;bad drive selected.
JP ERROR5
;
ERROR3: LD HL,DISKRO ;disk is read only.
JP ERROR5
;
ERROR4: LD HL,FILERO ;file is read only.
;
ERROR5: CALL PRTERR
JP 0 ;always reboot on these errors.
;
BDOSERR:DEFB 'Bdos Err On '
BDOSDRV:DEFB ' : $'
BADSEC: DEFB 'Bad Sector$'
BADSEL: DEFB 'Select$'
FILERO: DEFB 'File '
DISKRO: DEFB 'R/O$'
;
; Print bdos error message.
;
PRTERR: PUSH HL ;save second message pointer.
CALL OUTCRLF ;send (cr)(lf).
LD A,(ACTIVE) ;get active drive.
ADD A,'A' ;make ascii.
LD (BDOSDRV),A ;and put in message.
LD BC,BDOSERR ;and print it.
CALL PRTMESG
POP BC ;print second message line now.
CALL PRTMESG
;
; Get an input character. We will check our 1 character
; buffer first. This may be set by the console status routine.
;
GETCHAR:LD HL,CHARBUF ;check character buffer.
LD A,(HL) ;anything present already?
LD (HL),0 ;...either case clear it.
OR A
RET NZ ;yes, use it.
JP CONIN ;nope, go get a character responce.
;
; Input and echo a character.
;
GETECHO:CALL GETCHAR ;input a character.
CALL CHKCHAR ;carriage control?
RET C ;no, a regular control char so don't echo.
PUSH AF ;ok, save character now.
LD C,A
CALL OUTCON ;and echo it.
POP AF ;get character and return.
RET
;
; Check character in (A). Set the zero flag on a carriage
; control character and the carry flag on any other control
; character.
;
CHKCHAR:CP CR ;check for carriage return, line feed, backspace,
RET Z ;or a tab.
CP LF
RET Z
CP TAB
RET Z
CP BS
RET Z
CP ' ' ;other control char? Set carry flag.
RET
;
; Check the console during output. Halt on a control-s, then
; reboot on a control-c. If anything else is ready, clear the
; zero flag and return (the calling routine may want to do
; something).
;
CKCONSOL: LD A,(CHARBUF) ;check buffer.
OR A ;if anything, just return without checking.
JP NZ,CKCON2
CALL CONST ;nothing in buffer. Check console.
AND 01H ;look at bit 0.
RET Z ;return if nothing.
CALL CONIN ;ok, get it.
CP CNTRLS ;if not control-s, return with zero cleared.
JP NZ,CKCON1
CALL CONIN ;halt processing until another char
CP CNTRLC ;is typed. Control-c?
JP Z,0 ;yes, reboot now.
XOR A ;no, just pretend nothing was ever ready.
RET
CKCON1: LD (CHARBUF),A ;save character in buffer for later processing.
CKCON2: LD A,1 ;set (A) to non zero to mean something is ready.
RET
;
; Output (C) to the screen. If the printer flip-flop flag
; is set, we will send character to printer also. The console
; will be checked in the process.
;
OUTCHAR:LD A,(OUTFLAG) ;check output flag.
OR A ;anything and we won't generate output.
JP NZ,OUTCHR1
PUSH BC
CALL CKCONSOL ;check console (we don't care whats there).
POP BC
PUSH BC
CALL CONOUT ;output (C) to the screen.
POP BC
PUSH BC
LD A,(PRTFLAG) ;check printer flip-flop flag.
OR A
CALL NZ,LIST ;print it also if non-zero.
POP BC
OUTCHR1:LD A,C ;update cursors position.
LD HL,CURPOS
CP DEL ;rubouts don't do anything here.
RET Z
INC (HL) ;bump line pointer.
CP ' ' ;and return if a normal character.
RET NC
DEC (HL) ;restore and check for the start of the line.
LD A,(HL)
OR A
RET Z ;ingnore control characters at the start of the line.
LD A,C
CP BS ;is it a backspace?
JP NZ,OUTCHR2
DEC (HL) ;yes, backup pointer.
RET
OUTCHR2:CP LF ;is it a line feed?
RET NZ ;ignore anything else.
LD (HL),0 ;reset pointer to start of line.
RET
;
; Output (A) to the screen. If it is a control character
; (other than carriage control), use ^x format.
;
SHOWIT: LD A,C
CALL CHKCHAR ;check character.
JP NC,OUTCON ;not a control, use normal output.
PUSH AF
LD C,'^' ;for a control character, preceed it with '^'.
CALL OUTCHAR
POP AF
OR '@' ;and then use the letter equivelant.
LD C,A
;
; Function to output (C) to the console device and expand tabs
; if necessary.
;
OUTCON: LD A,C
CP TAB ;is it a tab?
JP NZ,OUTCHAR ;use regular output.
OUTCON1:LD C,' ' ;yes it is, use spaces instead.
CALL OUTCHAR
LD A,(CURPOS) ;go until the cursor is at a multiple of 8
AND 07H ;position.
JP NZ,OUTCON1
RET
;
; Echo a backspace character. Erase the prevoius character
; on the screen.
;
BACKUP: CALL BACKUP1 ;backup the screen 1 place.
LD C,' ' ;then blank that character.
CALL CONOUT
BACKUP1:LD C,BS ;then back space once more.
JP CONOUT
;
; Signal a deleted line. Print a '#' at the end and start
; over.
;
NEWLINE:LD C,'#'
CALL OUTCHAR ;print this.
CALL OUTCRLF ;start new line.
NEWLN1: LD A,(CURPOS) ;move the cursor to the starting position.
LD HL,STARTING
CP (HL)
RET NC ;there yet?
LD C,' '
CALL OUTCHAR ;nope, keep going.
JP NEWLN1
;
; Output a (cr) (lf) to the console device (screen).
;
OUTCRLF:LD C,CR
CALL OUTCHAR
LD C,LF
JP OUTCHAR
;
; Print message pointed to by (BC). It will end with a '$'.
;
PRTMESG:LD A,(BC) ;check for terminating character.
CP '$'
RET Z
INC BC
PUSH BC ;otherwise, bump pointer and print it.
LD C,A
CALL OUTCON
POP BC
JP PRTMESG
;
; Function to execute a buffered read.
;
RDBUFF: LD A,(CURPOS) ;use present location as starting one.
LD (STARTING),A
LD HL,(PARAMS) ;get the maximum buffer space.
LD C,(HL)
INC HL ;point to first available space.
PUSH HL ;and save.
LD B,0 ;keep a character count.
RDBUF1: PUSH BC
PUSH HL
RDBUF2: CALL GETCHAR ;get the next input character.
AND 7FH ;strip bit 7.
POP HL ;reset registers.
POP BC
CP CR ;en of the line?
JP Z,RDBUF17
CP LF
JP Z,RDBUF17
CP BS ;how about a backspace?
JP NZ,RDBUF3
LD A,B ;yes, but ignore at the beginning of the line.
OR A
JP Z,RDBUF1
DEC B ;ok, update counter.
LD A,(CURPOS) ;if we backspace to the start of the line,
LD (OUTFLAG),A ;treat as a cancel (control-x).
JP RDBUF10
RDBUF3: CP DEL ;user typed a rubout?
JP NZ,RDBUF4
LD A,B ;ignore at the start of the line.
OR A
JP Z,RDBUF1
LD A,BS ;ok, echo the prevoius character.
DEC B ;and reset pointers (counters).
LD A,(CURPOS) ;if we backspace to the start of the line,
LD (OUTFLAG),A ;treat as a cancel (control-x).
JP RDBUF10
RDBUF4: CP CNTRLE ;physical end of line?
JP NZ,RDBUF5
PUSH BC ;yes, do it.
PUSH HL
CALL OUTCRLF
XOR A ;and update starting position.
LD (STARTING),A
JP RDBUF2
RDBUF5: CP CNTRLP ;control-p?
JP NZ,RDBUF6
PUSH HL ;yes, flip the print flag filp-flop byte.
LD HL,PRTFLAG
LD A,1 ;PRTFLAG=1-PRTFLAG
SUB (HL)
LD (HL),A
POP HL
JP RDBUF1
RDBUF6: CP CNTRLX ;control-x (cancel)?
JP NZ,RDBUF8
POP HL
RDBUF7: LD A,(STARTING) ;yes, backup the cursor to here.
LD HL,CURPOS
CP (HL)
JP NC,RDBUFF ;done yet?
DEC (HL) ;no, decrement pointer and output back up one space.
CALL BACKUP
JP RDBUF7
RDBUF8: CP CNTRLU ;cntrol-u (cancel line)?
JP NZ,RDBUF9
CALL NEWLINE ;start a new line.
POP HL
JP RDBUFF
RDBUF9: CP CNTRLR ;control-r?
JP NZ,RDBUF14
RDBUF10:PUSH BC ;yes, start a new line and retype the old one.
CALL NEWLINE
POP BC
POP HL
PUSH HL
PUSH BC
RDBUF11:LD A,B ;done whole line yet?
OR A
JP Z,RDBUF12
INC HL ;nope, get next character.
LD C,(HL)
DEC B ;count it.
PUSH BC
PUSH HL
CALL SHOWIT ;and display it.
POP HL
POP BC
JP RDBUF11
RDBUF12:PUSH HL ;done with line. If we were displaying
LD A,(OUTFLAG) ;then update cursor position.
OR A
JP Z,RDBUF2
LD HL,CURPOS ;because this line is shorter, we must
SUB (HL) ;back up the cursor (not the screen however)
LD (OUTFLAG),A ;some number of positions.
RDBUF13:CALL BACKUP ;note that as long as (OUTFLAG) is non
LD HL,OUTFLAG ;zero, the screen will not be changed.
DEC (HL)
JP NZ,RDBUF13
JP RDBUF2 ;now just get the next character.
;
; Just a normal character, put this in our buffer and echo.
;
RDBUF14:INC HL
LD (HL),A ;store character.
INC B ;and count it.
RDBUF15:PUSH BC
PUSH HL
LD C,A ;echo it now.
CALL SHOWIT
POP HL
POP BC
LD A,(HL) ;was it an abort request?
CP CNTRLC ;control-c abort?
LD A,B
JP NZ,RDBUF16
CP 1 ;only if at start of line.
JP Z,0
RDBUF16:CP C ;nope, have we filled the buffer?
JP C,RDBUF1
RDBUF17:POP HL ;yes end the line and return.
LD (HL),B
LD C,CR
JP OUTCHAR ;output (cr) and return.
;
; Function to get a character from the console device.
;
GETCON: CALL GETECHO ;get and echo.
JP SETSTAT ;save status and return.
;
; Function to get a character from the tape reader device.
;
GETRDR: CALL READER ;get a character from reader, set status and return.
JP SETSTAT
;
; Function to perform direct console i/o. If (C) contains (FF)
; then this is an input request. If (C) contains (FE) then
; this is a status request. Otherwise we are to output (C).
;
DIRCIO: LD A,C ;test for (FF).
INC A
JP Z,DIRC1
INC A ;test for (FE).
JP Z,CONST
JP CONOUT ;just output (C).
DIRC1: CALL CONST ;this is an input request.
OR A
JP Z,GOBACK1 ;not ready? Just return (directly).
CALL CONIN ;yes, get character.
JP SETSTAT ;set status and return.
;
; Function to return the i/o byte.
;
GETIOB: LD A,(IOBYTE)
JP SETSTAT
;
; Function to set the i/o byte.
;
SETIOB: LD HL,IOBYTE
LD (HL),C
RET
;
; Function to print the character string pointed to by (DE)
; on the console device. The string ends with a '$'.
;
PRTSTR: EX DE,HL
LD C,L
LD B,H ;now (BC) points to it.
JP PRTMESG
;
; Function to interigate the console device.
;
GETCSTS:CALL CKCONSOL
;
; Get here to set the status and return to the cleanup
; section. Then back to the user.
;
SETSTAT:LD (STATUS),A
RTN: RET
;
; Set the status to 1 (read or write error code).
;
IOERR1: LD A,1
JP SETSTAT
;
OUTFLAG:DEFB 0 ;output flag (non zero means no output).
STARTING: DEFB 2 ;starting position for cursor.
CURPOS: DEFB 0 ;cursor position (0=start of line).
PRTFLAG:DEFB 0 ;printer flag (control-p toggle). List if non zero.
CHARBUF:DEFB 0 ;single input character buffer.
;
; Stack area for BDOS calls.
;
USRSTACK: DEFW 0 ;save users stack pointer here.
;
DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
STKAREA EQU $ ;end of stack area.
;
USERNO: DEFB 0 ;current user number.
ACTIVE: DEFB 0 ;currently active drive.
PARAMS: DEFW 0 ;save (DE) parameters here on entry.
STATUS: DEFW 0 ;status returned from bdos function.
;
; Select error occured, jump to error routine.
;
SLCTERR:LD HL,BADSLCT
;
; Jump to (HL) indirectly.
;
JUMPHL: LD E,(HL)
INC HL
LD D,(HL) ;now (DE) contain the desired address.
EX DE,HL
JP (HL)
;
; Block move. (DE) to (HL), (C) bytes total.
;
DE2HL: INC C ;is count down to zero?
DE2HL1: DEC C
RET Z ;yes, we are done.
LD A,(DE) ;no, move one more byte.
LD (HL),A
INC DE
INC HL
JP DE2HL1 ;and repeat.
;
; Select the desired drive.
;
SELECT: LD A,(ACTIVE) ;get active disk.
LD C,A
CALL SELDSK ;select it.
LD A,H ;valid drive?
OR L ;valid drive?
RET Z ;return if not.
;
; Here, the BIOS returned the address of the parameter block
; in (HL). We will extract the necessary pointers and save them.
;
LD E,(HL) ;yes, get address of translation table into (DE).
INC HL
LD D,(HL)
INC HL
LD (SCRATCH1),HL ;save pointers to scratch areas.
INC HL
INC HL
LD (SCRATCH2),HL ;ditto.
INC HL
INC HL
LD (SCRATCH3),HL ;ditto.
INC HL
INC HL
EX DE,HL ;now save the translation table address.
LD (XLATE),HL
LD HL,DIRBUF ;put the next 8 bytes here.
LD C,8 ;they consist of the directory buffer
CALL DE2HL ;pointer, parameter block pointer,
LD HL,(DISKPB) ;check and allocation vectors.
EX DE,HL
LD HL,SECTORS ;move parameter block into our ram.
LD C,15 ;it is 15 bytes long.
CALL DE2HL
LD HL,(DSKSIZE) ;check disk size.
LD A,H ;more than 256 blocks on this?
LD HL,BIGDISK
LD (HL),0FFH ;set to samll.
OR A
JP Z,SELECT1
LD (HL),0 ;wrong, set to large.
SELECT1:LD A,0FFH ;clear the zero flag.
OR A
RET
;
; Routine to home the disk track head and clear pointers.
;
HOMEDRV:CALL HOME ;home the head.
XOR A
LD HL,(SCRATCH2) ;set our track pointer also.
LD (HL),A
INC HL
LD (HL),A
LD HL,(SCRATCH3) ;and our sector pointer.
LD (HL),A
INC HL
LD (HL),A
RET
;
; Do the actual disk read and check the error return status.
;
DOREAD: CALL READ
JP IORET
;
; Do the actual disk write and handle any bios error.
;
DOWRITE:CALL WRITE
IORET: OR A
RET Z ;return unless an error occured.
LD HL,BADSCTR ;bad read/write on this sector.
JP JUMPHL
;
; Routine to select the track and sector that the desired
; block number falls in.
;
TRKSEC: LD HL,(FILEPOS) ;get position of last accessed file
LD C,2 ;in directory and compute sector #.
CALL SHIFTR ;sector #=file-position/4.
LD (BLKNMBR),HL ;save this as the block number of interest.
LD (CKSUMTBL),HL ;what's it doing here too?
;
; if the sector number has already been set (BLKNMBR), enter
; at this point.
;
TRKSEC1:LD HL,BLKNMBR
LD C,(HL) ;move sector number into (BC).
INC HL
LD B,(HL)
LD HL,(SCRATCH3) ;get current sector number and
LD E,(HL) ;move this into (DE).
INC HL
LD D,(HL)
LD HL,(SCRATCH2) ;get current track number.
LD A,(HL) ;and this into (HL).
INC HL
LD H,(HL)
LD L,A
TRKSEC2:LD A,C ;is desired sector before current one?
SUB E
LD A,B
SBC A,D
JP NC,TRKSEC3
PUSH HL ;yes, decrement sectors by one track.
LD HL,(SECTORS) ;get sectors per track.
LD A,E
SUB L
LD E,A
LD A,D
SBC A,H
LD D,A ;now we have backed up one full track.
POP HL
DEC HL ;adjust track counter.
JP TRKSEC2
TRKSEC3:PUSH HL ;desired sector is after current one.
LD HL,(SECTORS) ;get sectors per track.
ADD HL,DE ;bump sector pointer to next track.
JP C,TRKSEC4
LD A,C ;is desired sector now before current one?
SUB L
LD A,B
SBC A,H
JP C,TRKSEC4
EX DE,HL ;not yes, increment track counter
POP HL ;and continue until it is.
INC HL
JP TRKSEC3
;
; here we have determined the track number that contains the
; desired sector.
;
TRKSEC4:POP HL ;get track number (HL).
PUSH BC
PUSH DE
PUSH HL
EX DE,HL
LD HL,(OFFSET) ;adjust for first track offset.
ADD HL,DE
LD B,H
LD C,L
CALL SETTRK ;select this track.
POP DE ;reset current track pointer.
LD HL,(SCRATCH2)
LD (HL),E
INC HL
LD (HL),D
POP DE
LD HL,(SCRATCH3) ;reset the first sector on this track.
LD (HL),E
INC HL
LD (HL),D
POP BC
LD A,C ;now subtract the desired one.
SUB E ;to make it relative (1-# sectors/track).
LD C,A
LD A,B
SBC A,D
LD B,A
LD HL,(XLATE) ;translate this sector according to this table.
EX DE,HL
CALL SECTRN ;let the bios translate it.
LD C,L
LD B,H
JP SETSEC ;and select it.
;
; Compute block number from record number (SAVNREC) and
; extent number (SAVEXT).
;
GETBLOCK: LD HL,BLKSHFT ;get logical to physical conversion.
LD C,(HL) ;note that this is base 2 log of ratio.
LD A,(SAVNREC) ;get record number.
GETBLK1:OR A ;compute (A)=(A)/2^BLKSHFT.
RRA
DEC C
JP NZ,GETBLK1
LD B,A ;save result in (B).
LD A,8
SUB (HL)
LD C,A ;compute (C)=8-BLKSHFT.
LD A,(SAVEXT)
GETBLK2:DEC C ;compute (A)=SAVEXT*2^(8-BLKSHFT).
JP Z,GETBLK3
OR A
RLA
JP GETBLK2
GETBLK3:ADD A,B
RET
;
; Routine to extract the (BC) block byte from the fcb pointed
; to by (PARAMS). If this is a big-disk, then these are 16 bit
; block numbers, else they are 8 bit numbers.
; Number is returned in (HL).
;
EXTBLK: LD HL,(PARAMS) ;get fcb address.
LD DE,16 ;block numbers start 16 bytes into fcb.
ADD HL,DE
ADD HL,BC
LD A,(BIGDISK) ;are we using a big-disk?
OR A
JP Z,EXTBLK1
LD L,(HL) ;no, extract an 8 bit number from the fcb.
LD H,0
RET
EXTBLK1:ADD HL,BC ;yes, extract a 16 bit number.
LD E,(HL)
INC HL
LD D,(HL)
EX DE,HL ;return in (HL).
RET
;
; Compute block number.
;
COMBLK: CALL GETBLOCK
LD C,A
LD B,0
CALL EXTBLK
LD (BLKNMBR),HL
RET
;
; Check for a zero block number (unused).
;
CHKBLK: LD HL,(BLKNMBR)
LD A,L ;is it zero?
OR H
RET
;
; Adjust physical block (BLKNMBR) and convert to logical
; sector (LOGSECT). This is the starting sector of this block.
; The actual sector of interest is then added to this and the
; resulting sector number is stored back in (BLKNMBR). This
; will still have to be adjusted for the track number.
;
LOGICAL:LD A,(BLKSHFT) ;get log2(physical/logical sectors).
LD HL,(BLKNMBR) ;get physical sector desired.
LOGICL1:ADD HL,HL ;compute logical sector number.
DEC A ;note logical sectors are 128 bytes long.
JP NZ,LOGICL1
LD (LOGSECT),HL ;save logical sector.
LD A,(BLKMASK) ;get block mask.
LD C,A
LD A,(SAVNREC) ;get next sector to access.
AND C ;extract the relative position within physical block.
OR L ;and add it too logical sector.
LD L,A
LD (BLKNMBR),HL ;and store.
RET
;
; Set (HL) to point to extent byte in fcb.
;
SETEXT: LD HL,(PARAMS)
LD DE,12 ;it is the twelth byte.
ADD HL,DE
RET
;
; Set (HL) to point to record count byte in fcb and (DE) to
; next record number byte.
;
SETHLDE:LD HL,(PARAMS)
LD DE,15 ;record count byte (#15).
ADD HL,DE
EX DE,HL
LD HL,17 ;next record number (#32).
ADD HL,DE
RET
;
; Save current file data from fcb.
;
STRDATA:CALL SETHLDE
LD A,(HL) ;get and store record count byte.
LD (SAVNREC),A
EX DE,HL
LD A,(HL) ;get and store next record number byte.
LD (SAVNXT),A
CALL SETEXT ;point to extent byte.
LD A,(EXTMASK) ;get extent mask.
AND (HL)
LD (SAVEXT),A ;and save extent here.
RET
;
; Set the next record to access. If (MODE) is set to 2, then
; the last record byte (SAVNREC) has the correct number to access.
; For sequential access, (MODE) will be equal to 1.
;
SETNREC:CALL SETHLDE
LD A,(MODE) ;get sequential flag (=1).
CP 2 ;a 2 indicates that no adder is needed.
JP NZ,STNREC1
XOR A ;clear adder (random access?).
STNREC1:LD C,A
LD A,(SAVNREC) ;get last record number.
ADD A,C ;increment record count.
LD (HL),A ;and set fcb's next record byte.
EX DE,HL
LD A,(SAVNXT) ;get next record byte from storage.
LD (HL),A ;and put this into fcb as number of records used.
RET
;
; Shift (HL) right (C) bits.
;
SHIFTR: INC C
SHIFTR1:DEC C
RET Z
LD A,H
OR A
RRA
LD H,A
LD A,L
RRA
LD L,A
JP SHIFTR1
;
; Compute the check-sum for the directory buffer. Return
; integer sum in (A).
;
CHECKSUM: LD C,128 ;length of buffer.
LD HL,(DIRBUF) ;get its location.
XOR A ;clear summation byte.
CHKSUM1:ADD A,(HL) ;and compute sum ignoring carries.
INC HL
DEC C
JP NZ,CHKSUM1
RET
;
; Shift (HL) left (C) bits.
;
SHIFTL: INC C
SHIFTL1:DEC C
RET Z
ADD HL,HL ;shift left 1 bit.
JP SHIFTL1
;
; Routine to set a bit in a 16 bit value contained in (BC).
; The bit set depends on the current drive selection.
;
SETBIT: PUSH BC ;save 16 bit word.
LD A,(ACTIVE) ;get active drive.
LD C,A
LD HL,1
CALL SHIFTL ;shift bit 0 into place.
POP BC ;now 'or' this with the original word.
LD A,C
OR L
LD L,A ;low byte done, do high byte.
LD A,B
OR H
LD H,A
RET
;
; Extract the write protect status bit for the current drive.
; The result is returned in (A), bit 0.
;
GETWPRT:LD HL,(WRTPRT) ;get status bytes.
LD A,(ACTIVE) ;which drive is current?
LD C,A
CALL SHIFTR ;shift status such that bit 0 is the
LD A,L ;one of interest for this drive.
AND 01H ;and isolate it.
RET
;
; Function to write protect the current disk.
;
WRTPRTD:LD HL,WRTPRT ;point to status word.
LD C,(HL) ;set (BC) equal to the status.
INC HL
LD B,(HL)
CALL SETBIT ;and set this bit according to current drive.
LD (WRTPRT),HL ;then save.
LD HL,(DIRSIZE) ;now save directory size limit.
INC HL ;remember the last one.
EX DE,HL
LD HL,(SCRATCH1) ;and store it here.
LD (HL),E ;put low byte.
INC HL
LD (HL),D ;then high byte.
RET
;
; Check for a read only file.
;
CHKROFL:CALL FCB2HL ;set (HL) to file entry in directory buffer.
CKROF1: LD DE,9 ;look at bit 7 of the ninth byte.
ADD HL,DE
LD A,(HL)
RLA
RET NC ;return if ok.
LD HL,ROFILE ;else, print error message and terminate.
JP JUMPHL
;
; Check the write protect status of the active disk.
;
CHKWPRT:CALL GETWPRT
RET Z ;return if ok.
LD HL,RODISK ;else print message and terminate.
JP JUMPHL
;
; Routine to set (HL) pointing to the proper entry in the
; directory buffer.
;
FCB2HL: LD HL,(DIRBUF) ;get address of buffer.
LD A,(FCBPOS) ;relative position of file.
;
; Routine to add (A) to (HL).
;
ADDA2HL:ADD A,L
LD L,A
RET NC
INC H ;take care of any carry.
RET
;
; Routine to get the 's2' byte from the fcb supplied in
; the initial parameter specification.
;
GETS2: LD HL,(PARAMS) ;get address of fcb.
LD DE,14 ;relative position of 's2'.
ADD HL,DE
LD A,(HL) ;extract this byte.
RET
;
; Clear the 's2' byte in the fcb.
;
CLEARS2:CALL GETS2 ;this sets (HL) pointing to it.
LD (HL),0 ;now clear it.
RET
;
; Set bit 7 in the 's2' byte of the fcb.
;
SETS2B7:CALL GETS2 ;get the byte.
OR 80H ;and set bit 7.
LD (HL),A ;then store.
RET
;
; Compare (FILEPOS) with (SCRATCH1) and set flags based on
; the difference. This checks to see if there are more file
; names in the directory. We are at (FILEPOS) and there are
; (SCRATCH1) of them to check.
;
MOREFLS:LD HL,(FILEPOS) ;we are here.
EX DE,HL
LD HL,(SCRATCH1) ;and don't go past here.
LD A,E ;compute difference but don't keep.
SUB (HL)
INC HL
LD A,D
SBC A,(HL) ;set carry if no more names.
RET
;
; Call this routine to prevent (SCRATCH1) from being greater
; than (FILEPOS).
;
CHKNMBR:CALL MOREFLS ;SCRATCH1 too big?
RET C
INC DE ;yes, reset it to (FILEPOS).
LD (HL),D
DEC HL
LD (HL),E
RET
;
; Compute (HL)=(DE)-(HL)
;
SUBHL: LD A,E ;compute difference.
SUB L
LD L,A ;store low byte.
LD A,D
SBC A,H