1 ; Various functions for the Interaction with the FAT16 Filesystem
3 ; Copyright (C) 2010 Frank Zoll
4 ; Copyright (C) 2010 Sprite_tm
5 ; Copyright (C) 2010,2013 Leo C.
7 ; This file is part of avrcpm.
9 ; avrcpm is free software: you can redistribute it and/or modify it
10 ; under the terms of the GNU General Public License as published by
11 ; the Free Software Foundation, either version 3 of the License, or
12 ; (at your option) any later version.
14 ; avrcpm is distributed in the hope that it will be useful,
15 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ; GNU General Public License for more details.
19 ; You should have received a copy of the GNU General Public License
20 ; along with avrcpm. If not, see <http://www.gnu.org/licenses/>.
25 ; ============================================================================
28 ; Size of a Sector is fixed to 512 Bytes by Base - MMC Driver implementation
29 ; The Functions below therefore assume a fixed Size of 512 Bytes per Sector.
30 ; ============================================================================
35 ; ############################################################################
36 ; Defines for FAT16 Structures
37 ; ############################################################################
39 /*These are the Offsets to the Variables within the Bootsector of a FAT16
42 ;#define FAT16_BSO_SECSIZE 0x0b ; Offset to Sectorsize Word
43 #define FAT16_BSO_CLUSTSZ 0x0d ; Offset to Clustersize Byte
44 #define FAT16_BSO_RESSECT 0x0e ; Offset to Number of Reserved Sectors
45 #define FAT16_BSO_VOLPTR 0x1c ; Offset to First VolumeSector
46 #define FAT16_BSO_SECPERFAT 0x16 ; Offset to Number of Sectors per Fat
47 #define FAT16_BSO_NUMFATCP 0x10 ; Offset to Ammount of FAT Copys
48 #define FAT16_BSO_NUMDIRENT 0x11 ; Offset to Max. Root Dir. entries
50 #define FAT16_FIRST_IMAGENAME 'A' /* First letter of filename to search */
51 #define FAT16_LAST_IMAGENAME 'Z' /* Last letter of filename to */
52 #define FAT16_IMAGENAME_PREFIX "CPMDSK_"
53 #define FAT16_IMAGENAME_SUFFIX "IMG"
55 ; ############################################################################
56 ; Start of Data Segment
57 ; ############################################################################
62 fat_partfound: .byte 1 ; (partition: 0= no found 0xff=found )
63 fat_parttbl: .byte 4 ; first fat16 partition entry
64 ; only startsector is needed
65 fat_clustersize: .byte 1 ; sectors per cluster
66 fat_ptr2fat: .byte 4 ; pointer to the first fat sector
67 fat_ptr2dat: .byte 4 ; pointer to the first data sector
69 /*These variables define a cache that holds the last Cluster and Sector
70 thats been searched vor.
72 fat_last_dsk: .byte 1 ; number of disk with entry in cache
73 fat_log_clust: .byte 2 ; last searched logical cluster
74 fat_clust_offset: .byte 1 ; offset within the cluster
75 fat_clust_ptr: .byte 4 ; sector of last real cluster
77 .equ o_fat_partfound = 0
78 .equ o_fat_parttbl = 1
79 .equ o_fat_clustersize = 5
80 .equ o_fat_ptr2fat = 6
81 .equ o_fat_ptr2dat = 10
82 .equ o_fat_last_dsk = 14
83 .equ o_fat_log_clust = 15
84 .equ o_fat_clust_offset= 17
85 .equ o_fat_clust_ptr = 18
88 ; ############################################################################
89 ; Start of Code Segment
90 ; ############################################################################
94 fat_cfname: .db FAT16_IMAGENAME_PREFIX, 0
95 fat_cfext: .db FAT16_IMAGENAME_SUFFIX, 0
98 ; ============================================================================
99 ; Function: Initialize internal FAT-Partition Variables
100 ; ============================================================================
102 ; ----------------------------------------------------------------------------
104 ; Variables : [out] fat_parttabl
105 ; ----------------------------------------------------------------------------
107 ; This Routine initializes the internal Variables, that point to the
108 ; first Found FAT16 Partition.
109 ; ============================================================================
110 fat_init_partitiontable:
113 std y+o_fat_partfound, _0
114 std y+o_fat_parttbl+0, _0
115 std y+o_fat_parttbl+1, _0
116 std y+o_fat_parttbl+2, _0
117 std y+o_fat_parttbl+3, _0
121 ; ============================================================================
122 ; Function: Resets the Cache
123 ; ============================================================================
125 ; ----------------------------------------------------------------------------
127 ; Variables : [out] fat_log_clust
129 ; ----------------------------------------------------------------------------
131 ; This Routine resets the internal Cache- Variables. After reset, the
132 ; next read or write Command will initialize a scan of the FAT of
133 ; the FAT16-Partition for the given sector.
134 ; ============================================================================
137 std y+o_fat_log_clust ,_255
138 std y+o_fat_log_clust+1,_255
139 std y+o_fat_last_dsk ,_255
142 ; ============================================================================
143 ; Function: Saves FAT16 Partitiondata for later Scanning
144 ; ============================================================================
146 ; ----------------------------------------------------------------------------
147 ; Registers : [in] z Pointer to the Partitondata
148 ; Variables : [out] fat_partfound Boolean for "Partition found"
149 ; [out] fat_parttbl Pointer to Partitiontable
150 ; ----------------------------------------------------------------------------
152 ; This funktion sets the internal Variables to the Start and Size
153 ; of a given FAT16 Paritition. This Information will be used for a
154 ; later scanning of the Partition. See Function "fat_scan_partition"
155 ; for more information.
156 ; ============================================================================
161 printstring "fat16 part found"
167 ; set fat16 partition found flag
169 std y+o_fat_partfound,_255
171 ; save data from first fat16 partition
173 ldd temp,z+PART_START
174 std y+o_fat_parttbl,temp
175 ldd temp,z+PART_START+1
176 std y+o_fat_parttbl+1,temp
177 ldd temp,z+PART_START+2
178 std y+o_fat_parttbl+2,temp
179 ldd temp,z+PART_START+3
180 std y+o_fat_parttbl+3,temp
184 ; ============================================================================
185 ; Read and Scann a FAT16 Partition for Imagedatefiles
186 ; ============================================================================
189 ; ----------------------------------------------------------------------------
190 ; This Routine reads the Bootblock and scanns it for a Diskimage
191 ; ============================================================================
197 printstring "fat16 scanning"
201 ; Check if a FAT16 Partition was realy found
208 printstring "free entries in ptable ?"
212 ; Check for free entries in partition table
218 printstring "read fat bootblock."
222 ; Scan partition start
229 ; Load first sector from Partition
232 breq fat_bootblock_check
234 ; Read error: Block not found
242 printstring "fat16 bootblock check"
249 ; get sectors per cluster from bootblock
250 ldd temp,z+FAT16_BSO_CLUSTSZ
251 std y+o_fat_clustersize,temp
254 printstring "Sectors per Cluster "
259 ; get max num of entries in root direktory from bootblock
260 ldd temp ,z+FAT16_BSO_NUMDIRENT
261 ldd temp2,z+FAT16_BSO_NUMDIRENT+1
264 printstring "Max. entries in rootdir.: "
268 ; Calculate begin of DATA Clusters within the Volume
269 ; Num. Dir.Sektors = (Num. of Dir. entries * 32) / Bytes per Sektor
271 ; Sectorsize is fixed at 512 bytes, makes 16 entries per sector
274 ; Num. Direntries / 16
284 push temp ;save # of rootdir sectors
290 printstring " sectors)"
295 ; Print begin of Volume
296 printstring "Begin of Volume at: "
297 ldd temp, y+o_fat_parttbl+2
298 ldd temp2,y+o_fat_parttbl+3
300 ldd temp, y+o_fat_parttbl+0
301 ldd temp2,y+o_fat_parttbl+1
306 ; get num of reseved sectors from bootblock
307 ldd _tmp0,z+FAT16_BSO_RESSECT
308 ldd _tmp1,z+FAT16_BSO_RESSECT+1
310 ; Calculate begin of FAT within the Volume
311 ldd xl,y+o_fat_parttbl+0
312 ldd xh,y+o_fat_parttbl+1
313 ldd temp,y+o_fat_parttbl+2
314 ldd temp2,y+o_fat_parttbl+3
321 std y+o_fat_ptr2fat ,xl
322 std y+o_fat_ptr2fat+1,xh
323 std y+o_fat_ptr2fat+2,temp
324 std y+o_fat_ptr2fat+3,temp2
327 printstring "Begin of FAT at___: "
334 ; get num of sectors per FAT-Table from bootblock
335 ldd temp, z+FAT16_BSO_SECPERFAT
336 ldd temp2,z+FAT16_BSO_SECPERFAT+1
339 printstring "Sectors per FAT___: "
344 ; get num of FAT Tables from bootblock
345 ldd temp3,z+FAT16_BSO_NUMFATCP
348 printstring "Ammount of FAT copies: "
358 ; Calculate begin of Root- Directory within the Volume
360 ldd xl, y+o_fat_ptr2fat+0
361 ldd xh, y+o_fat_ptr2fat+1
362 ldd temp, y+o_fat_ptr2fat+2
363 ldd temp2,y+o_fat_ptr2fat+3
367 breq fat_calc_dp_lend
374 rjmp fat_calc_dp_loop
379 printstring "Begin of DIR at___: "
390 pop temp3 ;number of rootdir sectors
397 std y+o_fat_ptr2dat ,xl
398 std y+o_fat_ptr2dat+1,xh
399 std y+o_fat_ptr2dat+2,temp
400 std y+o_fat_ptr2dat+3,temp2
403 printstring "Begin of Data at__: "
410 ;-------------------------------------------------------------------------------
411 ; Here starts the scan of the directory for valid image files.
414 fat_next_sector_loop:
416 ; Get a pointer to the last+1 directory sector
428 ; Load sector from Directory
431 breq fat_look_for_images
433 ; Read error: Block not found
437 ; Looks at a read directory block for image entries
446 ldi temp2,STRLEN(FAT16_IMAGENAME_PREFIX)
448 brne fat_look_continue
452 ldi temp2,STRLEN(FAT16_IMAGENAME_SUFFIX)
454 brne fat_look_continue
457 ldd temp2,y+STRLEN(FAT16_IMAGENAME_PREFIX)
458 ldi temp,FAT16_FIRST_IMAGENAME
459 fat_look_imgname_loop:
461 breq fat_look_imgname_match
463 cpi temp,FAT16_LAST_IMAGENAME+1
464 brlo fat_look_imgname_loop
465 rjmp fat_look_continue
467 fat_look_imgname_match:
468 rcall fat_store_new_entry
474 ldi temp,high(hostbuf+HOSTSIZE)
475 cpi xl,low(hostbuf+HOSTSIZE)
477 brne fat_look_for_loop
479 fat_scan_next_sector:
482 brne fat_next_sector_loop
489 dbg_print_parttbl_raw:
496 lcall dbg_hexdump_line
497 adiw z,PARTENTRY_SIZE
498 cpi zl,low(hostparttbltop)
499 ldi temp,high(hostparttbltop)
509 ;-------------------------------------------------------------------------------
510 ; Create new Partition Entry
511 ; ============================================================================
512 ; Function: Create new Partition Entry
513 ; ============================================================================
514 ; Parameters: [in] yh,yl Directory entry
516 ; ----------------------------------------------------------------------------
520 ; ----------------------------------------------------------------------------
523 ; ============================================================================
527 ; Found a valid image
530 printstring "Found a valid Image! Y="
542 printstring "Insert entry in Tab: --> "
546 printstring "Tab before:"
548 rcall dbg_print_parttbl_raw
557 breq fat_st_insert_slot
558 sbrs temp,log2(dskType_FAT)
559 rjmp fat_st_search_next
561 ldd temp,z+PTAB_START+2
563 brlo fat_st_ins_before
566 adiw z,PARTENTRY_SIZE
567 cpi zl,low(hostparttbltop)
568 ldi temp,high(hostparttbltop)
570 brne fat_st_searchpos
577 printstring "Table is full. --> ptr: "
589 ldiw z,hostparttbltop-PARTENTRY_SIZE
590 fat_st_insert_mkslotloop:
593 breq fat_st_insert_slot
596 std z+PARTENTRY_SIZE,_tmp0
597 rjmp fat_st_insert_mkslotloop
601 ; Set Type of Partition to FAT16- Fileimage
605 ; Offset to Startcluster + 2
608 std z+PTAB_START, _tmp0
609 std z+PTAB_START+1,_tmp1
611 std z+PTAB_START+2,temp2
612 std z+PTAB_START+3,_0
614 ; Convert Filesize to ammount of sectors
615 ; (calc with 512byte/sector)
622 cpse _tmp0,_0 ;round up
634 ; store ammount of sectors in partitiontable
636 tst temp ;file size larger than 65535 sectors?
646 ; Test finding of the first sector
655 printstring "Begin of image at: "
666 ; Table counts one more entry if it was'nt allready full
679 printstring "Table now:"
680 rcall dbg_print_parttbl_raw
687 ; ============================================================================
688 ; Function: Cluster to HostSector
689 ; ============================================================================
690 ; Registers: [in] xl,xh Cluster Number
691 ; [out] yh,yl,xh,xl Sector Number on Disk
692 ; Variables: [in] fat_clustersize Ammount of Sectors per Cluster
694 ; ----------------------------------------------------------------------------
696 ; Calculates the logical Sectornumber given the physical ClusterNumber
697 ; and the size of a Cluster un sectors.
699 ; ! Only works with Clustersizes 2,4,8,16,32,64,128 !
700 ; ============================================================================
705 ; Get Offset into Data area of Disk
709 sbiw x,2 ; Substract the 2 reserved clusters
711 ldd temp,y+o_fat_clustersize
725 ; Add begin of data area to offset
727 ldd temp,y+o_fat_ptr2dat+0
729 ldd temp,y+o_fat_ptr2dat+1
731 ldd temp,y+o_fat_ptr2dat+2
733 ldd temp,y+o_fat_ptr2dat+3
739 ; ====================================================================
740 ; Function: Searches a physical Cluster, given the logical Cluster
741 ; ====================================================================
742 ; Registers: [in] xh,xl logical- Cluster
743 ; [out] yh,yl physical- Cluster
745 ; --------------------------------------------------------------------
747 ; ====================================================================
750 rcall dsk_getpartentry ; get partition entry
752 ; Get First FAT- Cluster Number of Diskimage
757 .if FAT16_DBG_FAT > 0
758 printstring "Search log. cluster "
763 printstring "Search phys. cluster "
772 breq fat_found_phsy_clust
774 ; Get Next Cluster from Fat
776 ; Trick: 512 Bytes Per Sector equals to 256 FAT- entries per Sector
777 ; so given: yl is the Offset within the FAT Sector
778 ; yh is the number off the FAT sector to Read
780 ; in zh,zl: Pointer to Word within the Sector to read
781 ; in yh..xl: Start sector number (LBA)
782 ; out zh,zl : word thats been read
787 ; Create FAT Offset Value
798 ; Add Cluster offset within sector
810 ; Check next logical Cluster
813 rjmp fat_next_phsy_clust
815 ; Found the physical cluster
816 fat_found_phsy_clust:
818 .if FAT16_DBG_FAT > 0
819 printstring "Found phys. Cluster at:"
827 ; ============================================================================
828 ; Function: This Routine searches for the Sector within an Imagefile
829 ; ============================================================================
830 ; Registers: [out] xl,xh,yl,yh Pointer to the sector on the SD-Card
831 ; [out] temp Error variable (0= No Error)
832 ; Variables: [in] hostdsk host disk #, (partition #)
833 ; [in] hostlba host block #, relative to part.start
834 ; [in] fat_last_dsk number of disk with entry in cache
835 ; [in] fat_log_clust last searched logical cluster
836 ; [in] fat_clust_offset offset within the cluster
837 ; [in] fat_clust_ptr sector of last real cluster
838 ; ----------------------------------------------------------------------------
840 ; This routine uses the variables hostdsk and hostlba to find an sector
842 ; The CP/M sector given within "hostlba" are splited to a logical cluster-
843 ; number and the subsector within this logical cluster.
844 ; logical cluster number = hostlba / fat_clustersize
845 ; The logical cluster number will be compared to the logical cluster number
846 ; within the cache. When this clusters are the same and the diskid's are
847 ; also the same, then the cached physical sector will be used.
848 ; When the clusters or the disks don't match, a seek for the physical
849 ; cluster is performed. This seek is done thru an access over the fat of
850 ; the fat16 partition. the routine starts at the first cluster of the
851 ; imagefile and goes along the linked list of clusternumber till it reaches
852 ; the searched cluster. The found clusternumber will be used to calculate
853 ; the sektor where this cluster lies on the sd card. Both the found physical
854 ; cluster and the logical cluster together with the physical sectornumber
855 ; are stored in the cache.
856 ; The last step done is to add the subsectoroffset to the found physical
857 ; sector. this gives the pointer to the sector to be read and or written.
858 ; ============================================================================
870 rcall dsk_getpartentry ; get partition entry
887 ldd xl,z+5 ; get size of disk in sectors
890 cp temp,xl ; check given sector against disksize
895 printstring ", max: "
915 ; ################# Get logical Number of Cluster within the imagefile
916 ; printstring "calc log sector"
917 ; Get logical Sectornumber from temp
921 ; Divide logical Sectornumber by size of Cluster in sectors
922 lds zl,fat_clustersize
935 rjmp fat_search_clst_lp
938 ; at this point xh and xl are carying the logical cluster number
939 ; printstring "find subsector"
940 ; ################# Get subsector within the logical cluster for later use
942 lds zl,fat_clustersize
946 breq fat_found_subsec
950 rjmp fat_search_clst_lp2
955 sts fat_clust_offset,zl
957 ; Check against last HOSTDISK searched
961 brne fat_wrong_cache_clst
963 ; Check against last Cluster searched
965 lds yh,fat_log_clust+1
969 brne fat_wrong_cache_clst
971 ; Last Cluster = searched Cluster -> get Sectornumber from cache
973 lds xh,fat_clust_ptr+1
974 lds yl,fat_clust_ptr+2
975 lds yh,fat_clust_ptr+3
979 ; Cluster is not in cache, so we must search for it
980 fat_wrong_cache_clst:
984 sts fat_log_clust+1,xh
986 ; Map Logical Cluster-Number to "Physical" Cluster Number using the FAT
987 rcall fat_find_phsy_clust
989 ; Get StartSector of "physical" Cluster
993 ; Found the physical sector
994 .if FAT16_DBG_FAT > 0
995 printstring "Found phys. Sector at:"
1003 ; Save the found Sector for later use into cache
1004 sts fat_clust_ptr ,xl
1005 sts fat_clust_ptr+1,xh
1006 sts fat_clust_ptr+2,yl
1007 sts fat_clust_ptr+3,yh
1009 ; Add- Subsector to Startsector
1011 lds zl,fat_clust_offset
1017 ; Found the physical sector
1018 .if FAT16_DBG_FAT > 0
1019 printstring "Sector with Offset at:"
1028 printstring ", abs:"
1047 ; ============================================================================
1048 ; Function: Does a Disk write operation
1049 ; ============================================================================
1050 ; Registers: [out] temp Error-Variable ( 0= No Error)
1051 ; Variables: [in] hostdsk host disk #, (partition #)
1052 ; [in] hostlba host block #, relative to part.start
1053 ; [in] hostbuf Sector to be written
1054 ; ----------------------------------------------------------------------------
1056 ; This Routine writes a Sector to the Imagefile inside an FAT16 Partition.
1057 ; ============================================================================
1062 printstring "host write "
1071 rjmp fat_rdwr_err ; skip disk change detection code
1073 ; After a second thought, the following code doesn't make sense, because
1074 ; disk change (change of one or more disk images) can not reliably detected.
1075 ; At least with the existing code.
1079 rcall mgr_init_partitions
1085 lcall mmcWriteSect ; disabled till read is functioning
1098 ; ============================================================================
1099 ; Function: Does a Disk read operation
1100 ; ============================================================================
1102 ; Variables: [in] hostdsk host disk #, (partition #)
1103 ; [in] hostlba host block #, relative to part.start
1104 ; [out] hostbuf Sector read by this routine
1105 ; ----------------------------------------------------------------------------
1107 ; This Routine reads a Sector from the Imagefile inside a FAT16 Partition.
1108 ; ============================================================================
1113 printstring "host read "
1120 .if FAT16_RWDEBUG > 0
1121 printstring "Read Image Sector:"
1140 rjmp fat_rdwr_err ; skip disk change detection code
1142 rcall mgr_init_partitions
1154 ; vim:set ts=8 noet nowrap