-
Notifications
You must be signed in to change notification settings - Fork 2
/
CFdriver.asm
2542 lines (2165 loc) · 57.5 KB
/
CFdriver.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
;=========================================================================================================
;This is the assembly file that contains the compact flash card driver as well as the Fat32 file explorer
;
;~Welcome to hell~
;=========================================================================================================
;
; http://averstak.tripod.com/fatdox/dir.htm
; https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#EBPB32_OFS_37h
; https://www.cs.fsu.edu/~cop4610t/lectures/project3/Week11/Slides_week11.pdf
; https://www.cse.scu.edu/~tschwarz/COEN252_09/Lectures/FAT.html
;
; $96DE: high amount of detected bytes per sector
; $96DD: low amount of detected bytes per sector (it's a 16 bit value)
; $96DC: number of logical sectors per cluster
; $96DB: number of reserved logical sectors (high byte)
; $96DA: number of reserved logical sectors (low byte)
; $96D9: number of file allocation tables
;
; $96D8: number of logical sectors (high byte)
; $96D7: number of logical sectors
; $96D6: number of logical sectors
; $96D5: number of logical sectors (lowest byte)
;
; $96D4: logical sectors per FAT (high byte)
; $96D3: logical sectors per FAT
; $96D2: logical sectors per FAT
; $96D1: logical sectors per FAT (lowest byte)
;
; $96D0: cluster # of root directory start (high byte)
; $96CF: cluster # of root directory start
; $96CE: cluster # of root directory start
; $96CD: cluster # of root directory start (lowest byte)
;
; $96CC: logical number of FS information sector (high byte)
; $96CB: logical number of FS information sector (low byte)
;
; $96CA: lba address of partition 1 (high byte)
; $96C9: lba address of partition 1
; $96C8: lba address of partition 1
; $96C7: lba address of partition 1 (low byte)
;in my current test partition, the lba address of the data sector is 000025e0
; address partition 1 + num reserved sectors + (num fats * logical sectors per fat) = data region start
; (0800) (20) (2 * 0EE0)
;
; address partition 1 + num reserved sectors = fat1 start
; address partition 1 + num reserved sectors + logical sectors per fat = fat2 start
;$96BF-$96BC - a temporary storage location for storing a single 32 bit number (little endian). normally used for saving when you've just calculated it with the 32 bit math suite and need it for another 32 bit calculation but have to do another 32 bit calculation first
;======================================
;pretty important
;=====================================
; $96EF: (LBA27-LBA24) drive head value of next drive operation
; $96EE: (LBA23-LBA16) high cylinder value of next drive operation
; $96ED: (LBA15-LBA08) low cylinder value of next driver operation
; $96EC: (LBA07 - LBA00) sector number of next drive operation
;
;
;===========================================================
;the way the file structure is [going to be] kept track of |
;===========================================================
;
;all values including the variables below are stored in little endian
;it needs to make cluster numbers rather than lba addresses to make it easier to obtain the block size
;
;$2000-$2003: input parameter (cluster #) of gotoClusterSector subroutine
;$2004: cluster offset of last gotoClusterSector calculation
; $2006: sector countdown for print files subroutine
;
;$9684-$968F: file or folder name to search for in file/folder search function
;$9690-$969B: file or folder name in the current file search comparison query
;$969C: file attribute byte of current file or folder
;$969D: search result - if anything other than 0, there was an error of some kind including file not found
;$2000-$2003: cluster# to get the sizeof in the get cluster size function
;
;$9683: size in clusters high byte
;$9682: size in clusters low byte ;the size of whatever the last calculation of getClusterSize determined
;$9681: filesystem depth high byte
;$9680: how many chains deep in the filesystem (0 if at root directory)
;
;$967F: LBA27-24 of root directory
;$967E: LBA23-16 of root directory
;$967D: LBA15-08 of root directory
;$967C: LBA07-00 of root directory
;
;$967B: 32bit cluster number of root directory and therefore the upper-most file level
;$967A: 32bit cluster number of root directory and therefore the upper-most file level
;$9679: 32bit cluster number of root directory and therefore the upper-most file level
;$9678: 32bit cluster number of root directory and therefore the upper-most file level
;
;$9677: 32bit cluster number of next directory down
;$9676: 32bit cluster number of next directory down
;$9675: 32bit cluster number of next directory down
;$9674: 32bit cluster number of root directory down
;
; and so on and so fourth for:
;($967B-(current depth*4)): 32bit cluster number of current working directory
;($967A-(current depth*4)): 32bit cluster number of current working directory
;($9679-(current depth*4)): 32bit cluster number of current working directory
;($9678-(current depth*4)): 32bit cluster number of current working directory
;
;
;
;;$2FFE - used for draw rectangle filled function
;
;
;======================================
; file layout structure explained
;======================================
; $00-$1F: vfat data. Actual file starts at $20. Files created on systems before long filenames were invented don't have this
; $20-$27: 8 character file name. If less than 8 characters, pad with spaces at end
; $28-$2A: 3 character file extension padded by null characters
; $2B: file attributes. bit 0 = read only. bit 1 = hidden. bit 2 = file belongs to system. bit 3 = vfat label. bit 4 = subdirectory. bit 5 = archive (has to do with backup software). bit 6 = device. bit 7 = reserved
; $2C: CP/M crap. Just set to zero or ignore
; $2D: creation time in 10 ms intervals. valid numbers are 0 to 199.
; $2E-$2F: file creation time. bits 15-11 = hours(0-23). bits 10-5 = minutes (0-59). bits 4-0 = seconds/2 (0-29)
; $30-$31: file creation date. bits 15-9 = years since 1980 (0-127). bits 8-5 = month (0-12). bits 0-4 - day (1-31)
; $32-$33: last access date using same format as above
; $34-$35. high 2 bytes of first cluster of file
; $36-$37: last modified time
; $38-39: last modified date
; $3A-3B: low two bytes of first cluster of file
; $3C-3F: file size in bytes. little endian
driveInit:
;let's try doing a reset
ld b, $A0
ld c, $0E
ld a, %00001110
out (c), a
call smallDelay
ld a, %00001010
out (c), a
;command register drive 0
ld b, $A0
ld c, $31 ;we want to write to the feature register
ld a, $01
out (c), a ;enable 8-bit mode
call WaitCFReady
ld c, $37 ;change to command register
ld a, $EF
out (c), a ;set features command
;do this
call CFsetDefaultDriveOperationValues
ret
;set default values in a location in ram
;this way it's easy and cheap for programs to use the drive
CFsetDefaultDriveOperationValues:
;set sector save location low byte
ld hl, $96E9
ld a, $10
ld (hl), a
;set sector save location high byte
ld hl, $96EA
ld a, $21
ld (hl), a
;set sector count variable to 1 (the default value)
ld hl, $96EB
ld a, 1
ld (hl), a
;set sector number variable to 1
ld hl, $96EC
ld a, 0 ;in lba mode, this should be zero instead of 1 if you want to read the first sector
ld (hl), a
;set low cylinder variable
ld hl, $96ED
ld a, 0
ld (hl), a
;set high cylinder variable
ld hl, $96EE
ld a, 0
ld (hl), a
;set drive head variable
ld hl, $96EF
ld a, $E0
ld (hl), a
ret
getCFStatus:
ld b, $A0
ld c, $37
in a, (c)
ret
;modifies hl and bc
;increases whatever number is in $9682-$9683 by 1
incrementClusterVariableByOne:
;$9683: size in clusters high byte
;$9682: size in clusters low byte ;the size of whatever the last calculation of getClusterSize determined
;reset the cluster size to zero because this function increments it to keep track of size
ld hl, $9682
ld c, (hl)
inc hl
ld b, (hl)
inc bc
ld (hl), b
dec hl
ld (hl), c
ret
;goes to whatever cluster is in $2000-$2003 little endian, copies it to ram at $2110 and then stores the (offset / 4) of where in the 512 byte $2110 block the cluster starts into address $96BF
gotoClusterSector:
;load lda address of partition start sector into math parameter 1
call partitionStartToMathParam1
;load the number of reserved sectors into math parameter 2
ld hl, $96DA
ld de, $96A4
call addressToOtherAddress
inc hl
inc de
call addressToOtherAddress
;number of reserve sectors is a 16 bit value so load zeros into the 2 upper bits of math parameter 2
;inc hl
ex de, hl
ld a, 0
ld (hl), a
inc hl
ld (hl), a
;now add partition start + number of reserved sectors
call add32BitNumber
;call print32bitresultanswer
;call VdpInsertEnter
;the lba address of the first fat is now in the math result variable space
;save for later
ld hl, $96AB
ld de, $96BF
call addressToOtherAddress
dec hl
dec de
call addressToOtherAddress
dec hl
dec de
call addressToOtherAddress
dec hl
dec de
call addressToOtherAddress
;now that we know the address of the first sector and it's saved in ram for later use, calculate which fat sector the requested cluster number is going to be in
;divide sector size by 4. This basically means this will only work on 1023 byte or less sectors due to the limitation where the divisor can't be greater than 255
;this is almost pointless except that some cf cards use 576 byte sectors for in like 0.1% of cases, it will come in handy
ld hl, $96DD
ld de, $96A0
call addressToOtherAddress
inc hl
inc de
call addressToOtherAddress
inc hl
ld a, 0
ld (hl), a
inc hl
ld (hl), a
;divide by 4 - put an 4 into $96A4
;inc hl
ld hl, $96A4
ld a, $04
ld (hl), a
;divide the number of bytes per sector by 8.
call stupidDivisionPre
call DEHL_Div_C
call stupidDivisionPost
;call VdpInsertEnter
;call VdpInsertEnter
;call print1st32bitNum
;call VdpInsertEnter
;call print2nd32bitNum
;call VdpInsertEnter
;call print32bitresultanswer
;call VdpInsertEnter
;call VdpInsertEnter
;move the result of the last division calculation into math parameter 2
ld hl, $96A8
ld de, $96A4
;the division function only can output an 8 digit number below but ill copy the whole number in case I fix that later
call addressToOtherAddress
inc hl
inc de
call addressToOtherAddress
inc hl
inc de
call addressToOtherAddress
inc hl
inc de
call addressToOtherAddress
;cluster number to math parameter 1
ld hl, $2000
ld de, $96A0
call addressToOtherAddress
inc hl
inc de
call addressToOtherAddress
inc hl
inc de
call addressToOtherAddress
inc hl
inc de
call addressToOtherAddress
call stupidDivisionPre
call DEHL_Div_C
call stupidDivisionPost
;call print32bitresultanswer
;call VdpInsertEnter
;result ($96A8-$96AB) = the sector number that has the cluster we want
;remainder ($96AC) = the offset in the sector where the cluster entry itself is at
;put the result in math parameter 1 in preparation to add it to the lba address
call putResultInParameter1
;copy the lba address of the fat that we saved earlier into math parameter 2
ld hl, $96BF
ld de, $96A7
call addressToOtherAddress
dec hl
dec de
call addressToOtherAddress
dec hl
dec de
call addressToOtherAddress
dec hl
dec de
call addressToOtherAddress
;copy the remainder into $2004 to save it for later. If i leave it at $96AC it might get overwritten if the 32 bit add gets the carry flag and has to put a 1 at that location
ld hl, $96AC
ld de, $2004
call addressToOtherAddress
;rememeber to add parameter 1 (sector offset) and parameter 2 (lba of fat #1 start) together
call add32BitNumber
;call print32bitresultanswer
;call VdpInsertEnter
;now all that is left to do is to copy and convert the lba address we've just finished calculating into $96EC-$96EF, read the 4 bytes at the offset stored in $96BF and determine if the cluster is a single cluster, a multi cluster or a fragmented multi cluster
;really should make this into a subroutine but anyway this copies the thing in the math result address, converts the high byte to set the required upper bits and then copies it to the cf read address
ld hl, $96AB
ld a, (hl)
or %11100000
ld hl, $96EF
ld (hl),a
ld hl, $96AA
ld de, $96EE
call addressToOtherAddress
dec hl
dec de
call addressToOtherAddress
dec hl
dec de
call addressToOtherAddress
;read the cf sector at hte (hopefully) correct lba address that just got finished being calculated
call readCFSector
ret
;gets the cluster size of whatever cluster number is stored in $2000-$2003 little endian
;a valid fat 32 has to be mounted and all the relevant variables have to be updated correctly in order for this to work
getClusterSize:
;$9683: size in clusters high byte
;$9682: size in clusters low byte ;the size of whatever the last calculation of getClusterSize determined
;reset the cluster size to zero because this function increments it to keep track of size
ld hl, $9682
ld a, 0
ld (hl), a
inc hl
ld (hl), a
;go to the sector that the desired cluster number is in, load it into ram starting at $2110 and find the offset (in 4 byte blocks) of the location of the desired cluster in the loaded fat table sector
call gotoClusterSector
;there are 4 cases:
; 1. the cluster in question is a single 4 byte entry
; 2. the cluster is a valid non-fragmented multi cluster long entry
; 3. the cluster is a valid fragmented multi cluster long entry
; 4. non of the above, in which case return an error
;cluster low byte location = (4*cluster number)
;for example cluster 02's low byte would be at $2117 (assuming the sector got loaded into ram starting $2110)
;also, this is cluster offset (which will be at $2004 at this point), not absolute cluster number
;first, let's check if it's case 1
;if the block is either "0FFFFFFF" or "0FFFFFF8", then it's a single cluster
ld hl, $2004
ld e, (hl)
ld d, 0
ld a, 4
;4 * cluster number
call DE_Times_A
;hl now contains (4*cluster number) which is the offset number of the low byte entry of the desired cluster block
;add $2110 to hl to obtain the absolute memory address in ram of the lowest byte entry of the desired cluster block
ld bc, $2110
add hl, bc
;load it into the a register to start the comparisons
ld a, (hl)
cp $F8
jr z, getClusterSizeIsCase1
cp $FF
jr z, getClusterSizeIsCase1
jr getClusterSizeNotCase1
getClusterSizeIsCase1:
inc hl
ld a, (hl)
cp $FF
jr nz, getClusterSizeNotCase1
inc hl
ld a, (hl)
cp $FF
jr nz, getClusterSizeNotCase1
inc hl
ld a, (hl)
cp $0F
jr nz, getClusterSizeNotCase1
call incrementClusterVariableByOne
call VdpInsertEnter
ld hl, clustersinglefat
call VPrintString
call VdpInsertEnter
;$96DE: high amount of detected bytes per sector
;$96DD: low amount of detected bytes per sector (it's a 16 bit value)
;$96DC: number of logical sectors per cluster
;print the number of bytes in size that the cluster is - we need to use 16 bit multiplication for this
ld hl, $96DE
ld d, (hl)
ld hl, $96DD
ld e, (hl)
ld hl, $96DC
ld a, (hl)
call DE_Times_A
ex de, hl
call print16BitDecimal
ld a, " "
call VdpPrintChar
ld hl, genericBytes
call VPrintString
call VdpInsertEnter
jp getClusterSizeExitCaseComparisons
getClusterSizeNotCase1:
push hl
call incrementClusterVariableByOne
call VdpInsertEnter
ld hl, clusterNOsinglefat
call VPrintString
pop hl
;The algorithm is the same for fragmented files as it is for non-fragmented files. Doing it this way is slower and requires more drive reads (impacting reliability) but it's so much easier to debug and troubleshoot
;I have to do this in a non-recursive way or else it will cause a stack overflow on large files
;decrement hl back to the way it was before doing all those comparisons in case 1
;dec hl
;dec hl
;dec hl
;dec hl
;first, make sure that the entry isn't "F7" indicating an invalid block
ld a, (hl)
cp $F7
jp z, getClusterSizeInvalidBlock
jr getClusterSizeNotCase1LoopFirstIteration
;if it isn't an invalid block or bad sector, load the value of the cluster block into $2000-$2003
getClusterSizeNotCase1Loop:
;uncomment out all this stuff if you start having problems for some debugging messages (current hl address with press any key to continue prompt)
;push hl
;ld a, h
;call aToScreenHex
;pop hl
;push hl
;ld a, l
;call aToScreenHex
;call VdpInsertEnter
;call waitChar
;pop hl
getClusterSizeNotCase1LoopFirstIteration:
;if current block is not an end block, loop and do it again, remembering to update the cluster variable each time it happens
;ld a, (hl)
;and %11111000 ;because technically according to spec, anything from F8-FF is valid here. Should probably put this test case comparison in my single cluster block compare also.
;cp $F8
;jr nz, getCLusterLoop2Continue
inc hl
ld a, (hl)
cp $FF
dec hl
jr nz, getCLusterLoop2Continue
inc hl
inc hl
ld a, (hl)
cp $FF
dec hl
dec hl
jr nz, getCLusterLoop2Continue
inc hl
inc hl
inc hl
ld a, (hl)
cp $0F
dec hl
dec hl
dec hl
jr nz, getCLusterLoop2Continue
jr getClusterLoop2GTFO
getCLusterLoop2Continue:
ld de, $2000
call addressToOtherAddress
inc hl
inc de
call addressToOtherAddress
inc hl
inc de
call addressToOtherAddress
inc hl
inc de
call addressToOtherAddress
call gotoClusterSector
;load absolute address of new block into hl
ld hl, $2004
ld e, (hl)
ld d, 0
ld a, 4
;4 * cluster number
call DE_Times_A
;hl now contains (4*cluster number) which is the offset number of the low byte entry of the desired cluster block
;add $2110 to hl to obtain the absolute memory address in ram of the lowest byte entry of the desired cluster block
ld bc, $2110
add hl, bc
;increment counter by 1
push hl
push bc
call incrementClusterVariableByOne
pop bc
pop hl
jr getClusterSizeNotCase1Loop
;if it got to this point, that means it's at the last block
;print the number of used bytes and exit the loop
getClusterLoop2GTFO:
;$96DE: high amount of detected bytes per sector
;$96DD: low amount of detected bytes per sector (it's a 16 bit value)
;$96DC: number of logical sectors per cluster
;print the number of bytes in size that the cluster is - we need to use 16 bit multiplication for this
ld hl, $96DE
ld d, (hl)
ld hl, $96DD
ld e, (hl)
ld hl, $96DC
ld a, (hl)
call DE_Times_A
ex de, hl
;load number of bytes per cluster into math parameter 1
ld hl, $96A0
ld (hl), e
inc hl
ld (hl), d
inc hl
ld a, 0
ld (hl), a
inc hl
ld (hl), a
;load number of clusters into math parameter 2
ld hl, $9682
ld de, $96A4
call addressToOtherAddress
inc hl
inc de
call addressToOtherAddress
inc hl
inc de
call addressToOtherAddress
inc hl
ld a, 0
ld (hl), a
inc hl
ld (hl), a
call ramTomul32Do
call mul32
call VdpInsertEnter
;call print32bitresultanswer
ld hl, $96A8
ld e, (hl)
inc hl
ld d, (hl)
call print16BitDecimal
ld hl, genericBytes
call VPrintString
jr getClusterSizeExitCaseComparisons
getClusterSizeInvalidBlock:
call VdpInsertEnter
ld hl, clusterIsInvalidError
call VPrintString
getClusterSizeExitCaseComparisons:
ret
;searches for a file or folder to determine if it exists in the current working directory
searchFileInWorkingDirectory:
ret
;this currently just reads the very first sector of cf card 0
readCFSector:
ld hl, $96E6
ld a, 0
ld (hl), a
readCFSectorLoop:
call smallDelay
call smallDelay
call smallDelay
call smallDelay
call smallDelay
call WaitCFReady
call WaitCFReadyForCommand
;set sector number register to 0
ld b, $A0
ld c, $33
ld hl, $96EC
ld a, (hl)
out (c), a
;ld hl, step1
;call printStatusTest
call WaitCFReady
call WaitCFReadyForCommand
;set high and low cylinder registers to zero
ld c, $34
ld hl, $96ED
ld a, (hl)
out (c), a
call WaitCFReady
call WaitCFReadyForCommand
ld c, $35
ld hl, $96EE
ld a, (hl)
out (c), a
;ld hl, step2
;call printStatusTest
;set up drive head register values
call WaitCFReady
call WaitCFReadyForCommand
ld c, $36
ld hl, $96EF
ld a, (hl)
out (c), a
;ld hl, step3
;call printStatusTest
;set sector count register to 1 (because I only want to copy the first sector right now)
call WaitCFReady
call WaitCFReadyForCommand
ld c, $32
ld hl, $96EB
ld a, (hl)
out (c), a
;ld hl, step4
;call printStatusTest
call WaitCFReady
call WaitCFReadyForCommand
;set 20h in command register to indicate I want to read
ld c, $37
ld a, $20
out (c), a
;ld hl, step5
;call printStatusTest
;call WaitCFReady
;store the values of whatever this yields into ram starting at address $3000
ld hl, $96EA
ld d, (hl)
ld hl, $96E9
ld e, (hl)
ex de, hl
ld de, $0000
;clear the contents of the byte read counter
push hl
ld a, 0
ld hl, $96E7
ld (hl), a
inc hl
ld (hl), a
pop hl
;now run the part that actually reads the data
call smallDelay
call readDataRegisterUntilFinished
;check if the number of re-read attempts is a really high number or not
ld hl, $96E6
ld a, (hl)
inc a
ld (hl), a
cp $0F ;maximum amount of retry attempts
jr z, ReadYeildError
;now check if the number of bytes read is correct. If not, do it again
ld hl, $96E8
ld a, (hl)
cp $02 ;if number of bytes read = $0200, then it was a 100% perfect read. It never goes over 512 bytes in 5 volt mode
jp nz, readCFSectorLoop
jr ExitNoYeildError
ReadYeildError:
ld hl, CFyeilderror
call VPrintString
call VdpInsertEnter
ExitNoYeildError:
;ld hl, CFnumretries
;call VPrintString
;ld hl, $96E6
;ld a, (hl)
;call aToScreenHex
;call VdpInsertEnter
;ld d, $FF
;call WaitCFReady
;call WaitCFTransferReady
;call readDataRegister256Times
;call readDataRegister256Times
;ld d, $FD
;call readDataRegister256Times
;call readDataRegister256Times
;done
ret
readDataRegisterUntilFinished:
ld b, $A0
readDataRegisterUntilFinishedLoop:
call WaitCFReady
;push de
;call smallDelay
;pop de
call getCFStatus
push af
and %00000001
cp $01 ;if $01, that means there was an error.
pop af
jr z, readDataRegisterUntilFinishedExitError ;exit loop but print error message to indicate that the loop ended due to an error flag
and %01011000
cp $50 ;if $50, that means there's no more data to be read so the loop can be ended
jr z, readDataRegisterUntilFinishedGTFO
cp $58 ;if $58, that means there's data to be read
jr nz, readDataRegisterUntilFinishedLoop
;push de
;give the capacitors a few hundred extra nanoseoncds to charge up for the next read
;doesn't make too much of a difference but the results are ~slightly~ better when I do this
;call smallDelay
;pop de
call WaitCFTransferReady
;write the data to whatever memory address is in the hl register
ld c, $30
in a, (c)
ld (hl), a
inc hl
inc de
jr readDataRegisterUntilFinishedLoop
readDataRegisterUntilFinishedExitError:
ld hl, CFerror
call VPrintString
readDataRegisterUntilFinishedGTFO:
;put the number of transferred bytes into ram somewhere to assist in troubleshooting
;this is also required for the counter in the loop that calls this function to determine if the cf card performed a valid read or not
;the visual print subroutines can be commented out but not the stuff before it
ld hl, $96E7
ld a, e
ld (hl), a
inc hl
ld a, d
ld (hl), a
;call smallDelay
;call print16BitDecimal
;ld hl, CFbytescopied
;call VPrintString
;call VdpInsertEnter
ret
;whatever's in the de register gets printed to screen as a 5 digit decimal number
;pro-tip: use 16bitDecimalToHl instead. It can be easily used for either g4 or text mode
print16BitDecimal:
call b16bitDecimalToHl
call VPrintString
;ld hl, CFbytescopied
;call VPrintString
ret
;pretty sure this is the one that doesn't work
;prints the low 2 digits of an 8 bit decimal ignoring the highest digit
print2DigitDecimal:
call b2DigitDecimalToHl
call VPrintString
ret
;c needs to contain the value of whatever you want to print as decimal
print8bitDecimal:
call b8bitDecimalToHl
call VPrintString
ret
;prints the low 2 digits of an 8 bit decimal ignoring the highest digit
;c needs to contain the number (in hex) that you want to convert
b2DigitDecimalToHl:
ld d, 10
call CDivD
add 48
ld hl, $96C5
ld (hl), a
call CDivD
add 48
ld hl, $96C4
ld (hl), a
ld hl, $96C6
ld a, 0
ld (hl), a
ld hl, $96C4
ret
;whatever's in the de register gets put into ram as decimal and then a pointer of the ntca gets put into hl
;usage example:
; call 16bitDecimalToHl
; call VPrintString
b16bitDecimalToHl:
;ld hl, $96E7
;ld a, (hl)
;ld e, a
;inc hl
;ld a, (hl)
;ld d, a
ld hl, $96E5
ld a, 0
ld (hl), a
ex de, hl
ld c, 10
call HL_Div_C
ex de, hl
add 48
ld hl, $96E4
ld (hl),a
ex de, hl
ld c, 10
call HL_Div_C
ex de, hl
add 48
ld hl, $96E3
ld (hl),a
ex de, hl
ld c, 10
call HL_Div_C
ex de, hl
add 48
ld hl, $96E2
ld (hl),a
ex de, hl
ld c, 10
call HL_Div_C
ex de, hl
add 48
ld hl, $96E1
ld (hl),a
ex de, hl
ld c, 10
call HL_Div_C
ex de, hl
add 48
ld hl, $96E0
ld (hl),a
ld hl, $96E0
ret
;c needs to contain the value (in hex) of whatever you want to print as decimal
b8bitDecimalToHl:
ld d, 10
call CDivD
add 48
ld hl, $96C6
ld (hl), a
call CDivD
add 48
ld hl, $96C5
ld (hl), a
call CDivD
add 48
ld hl, $96C4
ld (hl), a
ld hl, $96C7
ld a, 0
ld (hl), a
;note to self: pretty sure this part is a bug and should be $96C4 instead. I gotta test it first a little though
ld hl, $96C4
ret
;readDataRegister256Times:
;ld b, $A0
;ld c, $30
;ld d, $FF
;readDataRegister256TimesLoop:
;wait for status register to be "58h" which indicates it's ready for transfer
;call WaitCFReady
;call WaitCFTransferReady
;ld c, $30
;in a, (c)
;ld (hl), a
;inc hl
;dec d
;ld a, d
;cp 0
;jr nz, readDataRegister256TimesLoop
;ret
;generic wait command
;waits until the "not ready" bit is zero, indicating that the other bits in the status register are valid and that writing stuff to registers will work
WaitCFReady: