//------------------------------------------------------------------------ // FAT16 Library // // Author: Mattias Hansson // Copyright (c) : 2005 Mattias Hansson // License: GNU LGPL 2 // Language: 65CM v0.4+ // Dependencies: mmc64lib, multdivlib // ZP usage: $14-15, $3f-$41, $4b-$4c, $49-$4a // Target: Commodore 64 with MMC64 interface // (possible future conversions for other platforms possible) // // External functions: // lib_fat16_mount_partition // lib_fat16_dirstart // lib_fat16_dirnext // lib_fat16_chdir // lib_fat16_fopen // lib_fat16_fblockread // lib_fat16_fblockwrite // lib_fat16_fclose // lib_fat16_seek //future expansion // lib_fat16_fgetc //future expansion // lib_fat16_fputc //future expansion // // // Internal function: // lib_fat16_calc_total_clusters // lib_fat16_int_read_cluster_sector // lib_fat16_int_write_cluster_sector // lib_fat16_int_get_cluster_link // lib_fat16_int_set_cluster_link (rw-support) // lib_fat16_int_search_free_clusters (rw-support) // lib_fat16_int_get_free_cluster (rw_support) // lib_fat16_int_add_dir_entry (rw-support) // TODO: lib_fat16_int_update_dir_entry (rw-support) //internal in fclose? // // External define support: // __LIB_FAT16_MAX_OPEN_FILES - define this to the # of open files you // need in your project (default=5) // If you #DEFINE this in your own code // before #INCLUDE:ing the lib, the lib // will "obey" your setting. // // OBS: use __LIB_MMC64_READONLY instead of mocking with any R/W // settings in this lib. This lib is configured to obey that // directive also. // //------------------------------------------------------------------------ #IFNDEF __LIB_FAT16 #DEFINE __LIB_FAT16 = 1 //If you want to change this -> See above in the header. #IFNDEF __LIB_FAT16_MAX_OPEN_FILES #DEFINE __LIB_FAT16_MAX_OPEN_FILES = 5 #IFEND //This is the total size of a file descriptor record //OBS! Do NOT (re)define this one from outside!!! //NOTE: If lib is compiled for R/W this is redefined //below. #DEFINE __LIB_FAT16_FILE_DESCRIPTOR_SIZE = 14 #INCLUDE #INCLUDE //We inherit the "write support" ([Y]/n) from settings in mmc64rwlib.c65 #IFDEF __LIB_MMC64_WRITESUPPORT #PRINT Write access in fat16lib.c65 is experimental!!! #DEFINE __LIB_FAT16_WRITESUPPORT = 1 #UNDEF __LIB_FAT16_FILE_DESCRIPTOR_SIZE //note: see description of descriptor it below #DEFINE __LIB_FAT16_FILE_DESCRIPTOR_SIZE = 22 #IFEND #INCLUDE GOTO lib_fat16_skip //Precalculated sector locations WORD lib_fat16_partition_bootsector_lo BYTE lib_fat16_partition_bootsector_hi WORD lib_fat16_fat1_sector_lo BYTE lib_fat16_fat1_sector_hi WORD lib_fat16_rootdir_sector_lo BYTE lib_fat16_rootdir_sector_hi WORD lib_fat16_data_sector_lo BYTE lib_fat16_data_sector_hi //dir access variables WORD lib_fat16_active_dir_startcluster // Important if a subdir spans over several clusters. special: $0000 = root directory WORD lib_fat16_active_dir_cluster // special: $0000 = root directory WORD lib_fat16_active_dir_sector // Special if active_dir_cluster = $0000 = "root dir" this may be more than // "lib_fat16_sectors_per_cluster-1". Thats the reason that this is of type WORD. WORD lib_fat16_active_dir_entry_offset // address to the current directory-entry we shall examine in bytes. (inside the fat-buffer) // Starts @ lib_fat16_buffer_ptr and in incremented with 32 per entry BYTE lib_fat16_active_dir_bitmask // The bitmask, sent in when calling "lib_fat16_dirstart", used in "lib_fat16_dirnext" //------------------------------------------------------------------------ // File descriptor table // // An array of records representing closed or open files. // // Purpose: Having a file open and currently in a read or write process // requires the lib to hold some info about the location in the file // etc. If we want several files open at once (and heck yes we want it // since this is a freakin mass-storage device for our beloved C64) we // must multiply the descriptor records for as many files we want open // simultaniously. // // NOTE: To change how many files we can have open -> change the constant // "lib_fat16_max_open_files" under the constant declaration section. // // Description of a record: // // offset size field_description // -------------------------------------- // 0 2 Current cluster ($0002-$ffef) special $0000 - file not open // 2 1 Current sector offset in the cluster (0..127) // 3 2 Current byte position in current sector (0..511) // 5 4 File total size in bytes (0..2147483647) // 9 4 Current position in file, counted in bytes (0..2147483647) // 13 1 One of the "lib_fat16_filemode_*" constants // // The rest of the descriptor below is only avaliable if the lib is compiled // with R/W access enabled. // // 14 2 Direntry cluster (writemode only) //($0000 = rootdir) // 16 2 Direntry directory sector (writemode only) //used as fclose() must // 18 2 Direntry offset in sector (writemode only) //update the entry at close // 20 2 First cluster in file (writemode only) //To be written to fileentry at fclose() // // Total sizeof(descriptor) = 22 bytes (14 if READONLY) //------------------------------------------------------------------------ // File descriptor access constants //------------------------------------------------------------------------ BYTE CONST FAT16_DESC_CURR_CLUSTER = 0 BYTE CONST FAT16_DESC_CURR_SECTOR = 2 BYTE CONST FAT16_DESC_CURR_POS = 3 BYTE CONST FAT16_DESC_FILESIZE = 5 BYTE CONST FAT16_DESC_FILEPOS = 9 BYTE CONST FAT16_DESC_FILEMODE = 13 #IFDEF __LIB_FAT16_WRITESUPPORT BYTE CONST FAT16_DESC_DIRE_CLUSTER = 14 BYTE CONST FAT16_DESC_DIRE_SECTOR = 16 BYTE CONST FAT16_DESC_DIRE_OFFSET = 18 BYTE CONST FAT16_DESC_FIRST_CLUSTER = 20 #IFEND BYTE lib_fat16_file_descriptor_size @ __LIB_FAT16_FILE_DESCRIPTOR_SIZE ASM lib_fat16_file_descriptors !fill (lib_fat16_max_open_files*lib_fat16_file_descriptor_size), $ff ENDASM //------------------------------------------------------------------------ //lib_fat16_calc_total_clusters (INTERNAL) // // purpose: To calculate the number of the last cluster in a filesystem // // Method: // ( Total sectors - ( data-area-start - start of partition) ) / clusters_per_sector = clusters_of_data_area #IFDEF __LIB_FAT16_WRITESUPPORT FUNC lib_fat16_calc_total_clusters WORD dataarea_offset_lo BYTE dataarea_offset_hi //these two represent the dataarea size in sectors. WORD dataarea_size_lo WORD dataarea_size_hi BYTE sectors_per_cluster LET sectors_per_cluster = lib_fat16_sectors_per_cluster //data-area-start - start of partition ASM sec lda lib_fat16_data_sector_lo sbc lib_fat16_partition_bootsector_lo sta |dataarea_offset_lo| lda lib_fat16_data_sector_lo+1 sbc lib_fat16_partition_bootsector_lo+1 sta |dataarea_offset_lo|+1 lda lib_fat16_data_sector_hi sbc lib_fat16_partition_bootsector_hi sta |dataarea_offset_hi| ENDASM //Total sectors - "offset of start of dataarea in partition" ASM sec lda lib_fat16_total_sectors_lo sbc |dataarea_offset_lo| sta |dataarea_size_lo| lda lib_fat16_total_sectors_lo+1 sbc |dataarea_offset_lo|+1 sta |dataarea_size_lo|+1 lda lib_fat16_total_sectors_hi sbc |dataarea_offset_hi| sta |dataarea_size_hi| lda lib_fat16_total_sectors_hi+1 sbc #0 sta |dataarea_size_hi|+1 ENDASM //sectors_of_data_area / sectors_per_cluster = total_clusters //First bit unimportant (1 sector per cluster and we're done here!) ASM lsr |sectors_per_cluster| ENDASM WHILE sectors_per_cluster ASM clc ror |dataarea_size_hi|+1 ror |dataarea_size_hi| ror |dataarea_size_lo|+1 ror |dataarea_size_lo| lsr |sectors_per_cluster| ENDASM WEND LET lib_fat16_total_clusters = dataarea_size_lo //CALL lib_dbg_hexoutw ( lib_fat16_total_clusters ) //CALL lib_dbg_textoutlf ( " total clusters" ) FEND #IFEND //------------------------------------------------------------------------ // lib_fat16_mount_partition // // Purpose: // 1. To mount one of the 4 possible partitions on a MMC or SD card. // 2. Setup all the needed FAT pointers for work. // 3. Active directory = root-dir of the disk. // // Parameters: // lib_fat16_mp_partnr = partition nr to mount 0..3 // lib_fat16_mp_status = returns 0 if success otherwise !0 // // Preparation: lib_mmc64_cardinit (inherited from our usage of // lib_mmc64_readsector) // // Note: if the SD/MMC is superfloppy formatted "partition nr" is ignored // // Usage of lib temp variables: // lib_fat16_tempw // lib_fat16_tempw2 // lib_fat16_tempb //------------------------------------------------------------------------ BYTE lib_fat16_mp_partnr BYTE lib_fat16_mp_status FUNC lib_fat16_mount_partition ( lib_fat16_mp_partnr out:lib_fat16_mp_status ) BYTE callstat //internal status communication with lower level routines LET lib_fat16_mp_status = 1 //assume a problem //Note to developers: comment line below if using a constant buffer reference POINTER lib_fat16_buffer_ptr TO lib_fat16_fatbuffer //!!!!DEBUG CODE!!!!: To easily look at the fat-buffer while debugging we can place it where we like it here. //LET lib_fat16_buffer_ptr = $c000 //comment this line when finished debugging //Two formats exist: // 1. Removable Harddrive mode (sector0 = mbr with partition table) // 2. Superfloppy mode USB-ZIP (sector0 = partition boot record ) //We must determine what this (likely) is. CALL lib_mmc64_readsector ( 0 0 lib_fat16_buffer_ptr callstat ) IF 0 != callstat SUBEND //error! Read of MBR failed. ENDIF //First we assume that this is a "Removable Harddrive". //Calculate the address (in the buffer) to the partition record caller selected ADD $1be + lib_fat16_buffer_ptr -> lib_fat16_tempw WHILE lib_fat16_mp_partnr ADD 16 + lib_fat16_tempw -> lib_fat16_tempw DEC lib_fat16_mp_partnr WEND //Is the type of the partition = FAT16 = $06 ? PEEK lib_fat16_tempw[4] -> lib_fat16_tempb IF lib_fat16_tempb == $06 //Yes //Now we extract the partition bootsector start and store it for //future usage. (needed when calculating the rest of the data areas) GETASWORD@ lib_fat16_tempw[8] -> lib_fat16_partition_bootsector_lo PEEK lib_fat16_tempw[10] -> lib_fat16_partition_bootsector_hi //Read the boot record of the FAT16 partition CALL lib_mmc64_readsector ( lib_fat16_partition_bootsector_lo lib_fat16_partition_bootsector_hi lib_fat16_buffer_ptr callstat ) IF 0 != callstat SUBEND //error! Read of FAT16 partition boot record failed! ENDIF ELSE //No. Is this a "SuperFloppy"? (we take a wild guess) //So we set the partition boot sector to 0 LET lib_fat16_partition_bootsector_lo = 0 LET lib_fat16_partition_bootsector_hi = 0 ENDIF //MHTAG 20051205 //CALL lib_dbg_hexoutb ( lib_fat16_partition_bootsector_hi ) //CALL lib_dbg_hexoutw ( lib_fat16_partition_bootsector_lo ) //CALL lib_dbg_textoutlf ( " start of partition" ) //Get "bytes per sector". This is a sanity check for if this might be a FAT16 PBR //The code highly depends on $200 bytes per sector so it's good having this covered. LET lib_fat16_tempw = lib_fat16_buffer_ptr GETASWORD@ lib_fat16_tempw[11] -> lib_fat16_tempw2 IF lib_fat16_tempw2 != LIB_FAT16_SECTOR_SIZE SUBEND //error! "bytes per sector" is not $200 (512) (or this is not a FAT16 pbr!) ENDIF //Get "Sectors per cluster" PEEK lib_fat16_tempw[13] -> lib_fat16_sectors_per_cluster //Get "Reserved sectors" (including FAT boot sector) GETASWORD@ lib_fat16_tempw[14] -> lib_fat16_reserved_sectors //Get "nr of FATs" PEEK lib_fat16_tempw[16] -> lib_fat16_nr_of_fats //Get "max nr of rootdir entries" GETASWORD@ lib_fat16_tempw[17] -> lib_fat16_rootdir_entries //Get "sectors per fat" GETASWORD@ lib_fat16_tempw[22] -> lib_fat16_sectors_per_fat #IFDEF __LIB_FAT16_WRITESUPPORT //MHTAG 20051205 GETASWORD@ lib_fat16_tempw[34] -> lib_fat16_total_sectors_hi //CALL lib_dbg_hexoutw ( lib_fat16_total_sectors_hi ) GETASWORD@ lib_fat16_tempw[32] -> lib_fat16_total_sectors_lo //CALL lib_dbg_hexoutw ( lib_fat16_total_sectors_lo ) //CALL lib_dbg_textoutlf ( " total sectors" ) #IFEND //Now we'll do some precalculations based on the values we've read //so that we know the start sectors of all areas of the partition. //known //WORD lib_fat16_partition_bootsector_lo //BYTE lib_fat16_partition_bootsector_hi //First FAT starts after the reserved sectors // this below means fat1_sector = partition_start + reserved_sectors // in assembler due to this beeing a 24-bit number ASM clc lda lib_fat16_reserved_sectors adc lib_fat16_partition_bootsector_lo sta lib_fat16_fat1_sector_lo lda lib_fat16_reserved_sectors+1 adc lib_fat16_partition_bootsector_lo+1 sta lib_fat16_fat1_sector_lo+1 lda #0 adc lib_fat16_partition_bootsector_hi sta lib_fat16_fat1_sector_hi ENDASM //Partition root-dir starts after the FATs //So first we must figure out how much space all the FATs take up together. LET lib_fat16_tempw = lib_fat16_nr_of_fats CALL lib_multdiv_mult ( lib_fat16_tempw lib_fat16_sectors_per_fat ) //Now "total size of FATs" + "offset to the first FAT" = "root dir start" ASM clc lda lib_fat16_tempw adc lib_fat16_fat1_sector_lo sta lib_fat16_rootdir_sector_lo lda lib_fat16_tempw+1 adc lib_fat16_fat1_sector_lo+1 sta lib_fat16_rootdir_sector_lo+1 lda #0 adc lib_fat16_fat1_sector_hi sta lib_fat16_rootdir_sector_hi ENDASM //Partitions data-area starts after the root-dir. To calculate the total size of the data-area in sectors //we use this formula: ( "max root dir entries" * size of directory entry (32) ) / sector size(512) //To simplify 1 sector has space for 16, 32-byte directory entries. //So "max root dir entries" / 16 = "size of the total root-dir in sectors" LET lib_fat16_tempw = lib_fat16_rootdir_entries CALL lib_multdiv_div ( lib_fat16_tempw 16 ) LET lib_fat16_rootdir_sectors = lib_fat16_tempw //Now "total size of root dir in sectors" + "root dir start sector" = "start of data area" ASM clc lda lib_fat16_tempw adc lib_fat16_rootdir_sector_lo sta lib_fat16_data_sector_lo lda lib_fat16_tempw+1 adc lib_fat16_rootdir_sector_lo+1 sta lib_fat16_data_sector_lo+1 lda #0 adc lib_fat16_rootdir_sector_hi sta lib_fat16_data_sector_hi ENDASM //MHTAG 20051205 //CALL lib_dbg_hexoutb ( lib_fat16_data_sector_hi ) //CALL lib_dbg_hexoutw ( lib_fat16_data_sector_lo ) //CALL lib_dbg_textoutlf ( " data area start-sector" ) #IFDEF __LIB_FAT16_WRITESUPPORT CALL lib_fat16_calc_total_clusters #IFEND //Now we've calculated all the pointers that important in this partition //For the more high level FAT16 access routines //Initialize work variables //LET lib_fat16_active_cluster = $ffff LET lib_fat16_active_dir_startcluster = $0000 // startdir = root #IFDEF __LIB_FAT16_WRITESUPPORT //MHTAG 20051114 - prepare writing by finding some suitable write space when mounting LET lib_fat16_first_write_cluster = $ffff LET lib_fat16_last_write_cluster = 0 CALL lib_fat16_int_search_free_clusters ( callstat ) IF callstat != 0 SUBEND ENDIF #IFEND LET lib_fat16_mp_status = 0 //if we got this far, we've successfully mounted the partition! FEND //------------------------------------------------------------------------ // lib_fat16_dirnext // lib_fat16_dirnext_le (INTERNAL) // // Purpose: // To scan active directory for the next direntry with the same bitmask // that was passed to the latest call to lib_fat16_dirfirst // // Parameters: // lib_fat16_ds_entry_ptr - pointer to the fat entry (check FAT docs for struct layout) // NULL = 0 = not found / whole dir listed // lib_fat16_ds_status - result code: 0 - success // 1 - internal error // lib_fat16_dn_firstnamechar - $00 = last entry, else not last entry // // Preparation: lib_fat16_dirfirst // // Usage of lib temp variables: // lib_fat16_tempw (pointer to current dir-entry in sector-buffer-memory) // // Note: // When we enter this routine we're about to read the next dir // entry. We must however first look at and adjust (if needed) the current // cluster/sector/dir_entry_offset if we've passed a border in the previous // call. // //------------------------------------------------------------------------ WORD lib_fat16_dn_entry_ptr BYTE lib_fat16_dn_status BYTE lib_fat16_dn_firstnamechar #IFDEF __LIB_FAT16_WRITESUPPORT FUNC lib_fat16_dirnext_le ( out:lib_fat16_dn_entry_ptr out:lib_fat16_dn_status out:lib_fat16_dn_firstnamechar ) LET lib_fat16_dn_firstnamechar = $ff //when we write a new file we must be sure that a valid 0-entry terminated the dir. #IFEND FUNC lib_fat16_dirnext ( out:lib_fat16_dn_entry_ptr out:lib_fat16_dn_status ) BYTE callstat BYTE filetype BYTE validentry //valid directory entry found for the caller (exit condition) LET lib_fat16_dn_status = 1 LET lib_fat16_dn_entry_ptr = 0 LET validentry = 0 WHILE validentry = 0 //have we passed the end of the sector (in buffer) yet? IF lib_fat16_active_dir_entry_offset >= LIB_FAT16_SECTOR_SIZE INC lib_fat16_active_dir_sector //if this is the root directory IF lib_fat16_active_dir_startcluster == 0 IF lib_fat16_active_dir_sector >= lib_fat16_rootdir_sectors //(For all I know this is an error in the filesystem not to terminate the // directory with an entry that starts with a NULL-char). I just choose not to take action BREAK //read to the end of directory successfully. no more data ENDIF ELSE //if this is in a normal cluster IF lib_fat16_active_dir_sector >= lib_fat16_sectors_per_cluster //we've passed the last sector of it //Call will update the "lib_fat16_active_dir_cluster" to the next cluster link. CALL lib_fat16_int_get_cluster_link ( lib_fat16_active_dir_cluster callstat ) IF callstat != 0 SUBEND //internal error (or end of cluster-chain == error in filesystem) ENDIF IF lib_fat16_active_dir_cluster == LIB_FAT16_CLUSTER_FREE SUBEND //internal error (or error in filesystem), how did we end up at an free cluster while folloing a chain? ENDIF // end of cluster-chain. we are ready with this dir-scan! IF lib_fat16_active_dir_cluster >= LIB_FAT16_CLUSTER_END //(For all I know this is an error in the filesystem not to terminate the // directory with an entry that starts with a NULL-char). I just choose not to take action BREAK //read to the end of directory successfully. no more data ENDIF //since we've gone to a new cluster we restart the internal "cluster sector offset counter" LET lib_fat16_active_dir_sector = 0 ENDIF ENDIF LET lib_fat16_active_dir_entry_offset = 0 //restart "offset in sector" counter CALL lib_fat16_int_read_cluster_sector ( lib_fat16_active_dir_cluster lib_fat16_active_dir_sector lib_fat16_buffer_ptr callstat ) IF callstat != 0 SUBEND //internal error ENDIF ENDIF //calc the pointer address the the direntry in memory ADD lib_fat16_buffer_ptr + lib_fat16_active_dir_entry_offset -> lib_fat16_tempw PEEK lib_fat16_tempw -> lib_fat16_dn_firstnamechar //is it the end of the directory? IF lib_fat16_dn_firstnamechar == LIB_FAT16_END_OF_DIR //This is the correct end of an FAT16 directory. (see two "BREAK"s above) BREAK //read to the end of directory successfully. no more data ENDIF //is it a deleted entry? IF lib_fat16_dn_firstnamechar != LIB_FAT16_DELETED_FILE //no it exists! //Check the filetype of the current direntry PEEK lib_fat16_tempw[11] -> filetype IF lib_fat16_active_dir_bitmask != 0 //calc the pointer to the filetype-bitfield in the direntry AND filetype WITH lib_fat16_active_dir_bitmask -> filetype //is it a fileentry of a type we're looking for? IF filetype != 0 //found a matching entry, no more looping. LET lib_fat16_dn_entry_ptr = lib_fat16_tempw LET validentry = 1 ENDIF ELSE //bitmask = 0, we want ALL file-entries! AND filetype WITH 8 -> filetype //(but not volume labels (and long filenames)) IF filetype == 0 //no volume label - it seams ok to return LET lib_fat16_dn_entry_ptr = lib_fat16_tempw LET validentry = 1 ENDIF ENDIF ENDIF //prepare the offset to the next direntry read ADD LIB_FAT16_DIRENTRY_SIZE + lib_fat16_active_dir_entry_offset -> lib_fat16_active_dir_entry_offset WEND LET lib_fat16_dn_status = 0 FEND //------------------------------------------------------------------------ // lib_fat16_dirstart // // Purpose: // To scan active directory for the first entry that maches the given bitmask // // Parameters: // lib_fat16_ds_bitmask - a bitmask that will be AND:ed with the entries // of the active directory. If the result is not 0 // the entry is considered as a match returned to caller. // (refer to FAT documentation for the meaning of the bits). // special: value 0 lists all files and subdirs but not // volume labels (and the LFN-support in VFAT) // lib_fat16_ds_entry_ptr - pointer to the fat entry (check FAT docs for struct layout) // NULL = 0 = not found / whole dir listed // lib_fat16_ds_status - result code: 0 - success // 1 - internal error // // Preparation: lib_mmc64_cardinit (inherited from our usage of // lib_mmc64_readsector) // lib_fat16_mount_partition // // Usage of lib temp variables: // //------------------------------------------------------------------------ BYTE lib_fat16_ds_bitmask WORD lib_fat16_ds_entry_ptr BYTE lib_fat16_ds_status FUNC lib_fat16_dirstart ( lib_fat16_ds_bitmask out:lib_fat16_ds_entry_ptr out:lib_fat16_ds_status ) BYTE callstat LET lib_fat16_ds_status = 1 LET lib_fat16_active_dir_cluster = lib_fat16_active_dir_startcluster LET lib_fat16_active_dir_sector = 0 LET lib_fat16_active_dir_entry_offset = 0 LET lib_fat16_active_dir_bitmask = lib_fat16_ds_bitmask CALL lib_fat16_int_read_cluster_sector ( lib_fat16_active_dir_cluster lib_fat16_active_dir_sector lib_fat16_buffer_ptr callstat ) IF callstat != 0 SUBEND ENDIF CALL lib_fat16_dirnext ( lib_fat16_ds_entry_ptr callstat ) IF callstat != 0 SUBEND ENDIF LET lib_fat16_ds_status = 0 //set success if all went ok FEND //------------------------------------------------------------------------ // lib_fat16_chdir // // Purpose: // To change to one of the avaliable directories that exist in current // dir. // // Parameters: // lib_fat16_cd_dirname_ptr - pointer to a 11-char string. If this string // is found to be an exact match of a directory- // entry in the current dir, we will change to // this new directory. // lib_fat16_cd_status - 0 = success, !0 = unable to change dir or internal error // // Preparation: lib_fat16_mount_partition // // Usage of lib temp variables: // // Note: // In FAT16 there are always (except in the ROOT-dir) two dirs // avaliable, one called "." and the other ".." // "." points to the current dir (which just makes you go back where you are // i.e. pointless =) (but there was a need for it in DOS long ago) // ".." points back to the parent dir. // //------------------------------------------------------------------------ WORD lib_fat16_cd_dirname_ptr BYTE lib_fat16_cd_status FUNC lib_fat16_chdir ( lib_fat16_cd_dirname_ptr out:lib_fat16_cd_status ) WORD direntry @ $14 BYTE callstat BYTE charcount BYTE char1 BYTE char2 WORD dirnamecmp_ptr LET lib_fat16_cd_status = 1 //search for directories in current dir CALL lib_fat16_dirstart ( 16 direntry callstat ) IF callstat != 0 SUBEND //internal error ENDIF WHILE direntry //Compare the strings LET dirnamecmp_ptr = lib_fat16_cd_dirname_ptr LET charcount = 11 WHILE charcount PEEK dirnamecmp_ptr -> char1 PEEK direntry -> char2 IF char1 != char2 GOTO lib_fat16_cd_findnext ENDIF INC dirnamecmp_ptr INC direntry DEC charcount WEND //if we got here we have a match! So we change dir! //offset from start of direntry is now 11 and we want to 26 GETASWORD@ direntry[26-11] -> lib_fat16_active_dir_startcluster GOTO lib_fat16_cd_exitsuccess LABEL lib_fat16_cd_findnext CALL lib_fat16_dirnext ( direntry callstat ) IF callstat != 0 SUBEND //internal error ENDIF WEND //if we came here through the loop but didn't find anything the cd has failed - exit with errorcode SUBEND LABEL lib_fat16_cd_exitsuccess LET lib_fat16_cd_status = 0 FEND //------------------------------------------------------------------------ // lib_fat16_int_get_free_cluster (internal) // // purpose: This routine is used when the lib needs a new cluster to write // to, may it be for filedata or for extending a subdirectory // with an entry past the end of it's current last cluster // // The need of this routine comes because // lib_fat16_int_find_unused_clusters can only find new cluster // holes however with any fragmentation these holes are spread // between chunks data on the disk. So this routine searches // for next cluster hole as needed. // // An error is issued if diskspace is full. //------------------------------------------------------------------------ #IFDEF __LIB_FAT16_WRITESUPPORT WORD lib_fat16_int_cw_free_cluster_nr BYTE lib_fat16_int_cw_status FUNC lib_fat16_int_get_free_cluster ( out:lib_fat16_int_cw_free_cluster_nr out:lib_fat16_int_cw_status ) BYTE callstat LET lib_fat16_int_cw_status = 1 //error //Are there any more free clusters in the previously discovered (or not discovered) //"cluster hole"? IF lib_fat16_first_write_cluster > lib_fat16_last_write_cluster //No, so we try to find another "cluster hole" CALL lib_fat16_int_search_free_clusters ( callstat ) IF callstat != 0 SUBEND ENDIF ENDIF LET lib_fat16_int_cw_free_cluster_nr = lib_fat16_last_write_cluster DEC lib_fat16_last_write_cluster LET lib_fat16_int_cw_status = 0 //success FEND #IFEND //------------------------------------------------------------------------ // lib_fat16_int_add_dir_entry (INTERNAL) // // Purpose: when a new, previously not existing, file is opened for // writing, a new direntry must be added to the current // directory. This function does that. // Note: This function is only responsible for allocating a new // uninitialized entry, whereas the fopen function is responsible // to make sure that we don't add a duplicate entry in the // directory. // // Method: This function gets its input from the dirstart/dirnext done // by fopen() when searching, if the file for write exists from // before. // Then it copies the last entry forward one entry // (if possible). If all went successful it returns the pointers // to the newly allocated entry to the caller. // // Preparation: fopen() internal call // Run dirstart/dirnext until the active directory ends. // This is done in fopen() before calling this function. // // Parameters: // // lib_fat16_ae_new_entry_filename - pointer the a 11 char long filename // for the new entry. // lib_fat16_ae_new_entry_cluster - The cluster in which the new direntry // resides. // // // ZP-vars: // lib_fat16_tempw, lib_fat16_tempw2 //------------------------------------------------------------------------ #IFDEF __LIB_FAT16_WRITESUPPORT WORD lib_fat16_ae_new_entry_filename WORD lib_fat16_ae_new_entry_cluster WORD lib_fat16_ae_new_entry_sector WORD lib_fat16_ae_new_entry_offset BYTE lib_fat16_ae_status FUNC lib_fat16_int_add_dir_entry ( lib_fat16_ae_new_entry_filename out:lib_fat16_ae_new_entry_cluster out:lib_fat16_ae_new_entry_sector out:lib_fat16_ae_new_entry_offset out:lib_fat16_ae_status ) LET lib_fat16_ae_status = 1 WORD null_entry_cluster WORD null_entry_sector WORD null_entry_offset BYTE callstat //We assume caller has used looped to the last direntry with "dirnext" //NOTE!! If this is not the case we will OVERWRITE valid entries. LET lib_fat16_ae_new_entry_cluster = lib_fat16_active_dir_cluster LET lib_fat16_ae_new_entry_sector = lib_fat16_active_dir_sector LET lib_fat16_ae_new_entry_offset = lib_fat16_active_dir_entry_offset LET null_entry_cluster = lib_fat16_active_dir_cluster LET null_entry_sector = lib_fat16_active_dir_sector LET null_entry_offset = lib_fat16_active_dir_entry_offset //------------------------------------------------------- //TASK1: prepare to move the NULL-entry (end-of-dir) // (leaving the old position for the new entry) //Is the last entry the last entry in sector also? IF null_entry_offset >= LIB_FAT16_SECTOR_SIZE-LIB_FAT16_DIRENTRY_SIZE INC null_entry_sector //This is the ROOT dir? IF null_entry_cluster == 0 IF null_entry_sector >= lib_fat16_rootdir_sectors SUBEND //root directory cannot be exceeded ELSE //There are rootdir sectors left so we just take the next one. LET null_entry_offset = 0 ENDIF ELSE //this is NOT the ROOT-dir //must we allocate another cluster to fit the new entry? IF null_entry_cluster >= lib_fat16_sectors_per_cluster //Yes, so we ask for another cluster. CALL lib_fat16_int_get_free_cluster ( null_entry_cluster callstat ) IF callstat != 0 SUBEND //Could not get another cluster (disk probably full) ENDIF //Now we have the number of a free cluster, yet we have to: // a. Link the old last cluster to this new cluster // b. mark the new cluster as the last in the chain //a. CALL lib_fat16_int_set_cluster_link ( lib_fat16_ae_new_entry_cluster null_entry_cluster callstat ) IF callstat != 0 SUBEND //internal error ENDIF //b. //Note: allthough everything above > $ffef is a "cluster chain ended", some badly written drivers only understand $ffff CALL lib_fat16_int_set_cluster_link ( null_entry_cluster $ffff callstat ) IF callstat != 0 SUBEND //internal error ENDIF //Now in the new cluster we restart the internal "offsets" LET null_entry_offset = 0 LET null_entry_sector = 0 ELSE //no, we just use another sector in the current cluster LET null_entry_offset = 0 ENDIF ENDIF ELSE //last entry is not at the end of a sector. ADD LIB_FAT16_DIRENTRY_SIZE + null_entry_offset -> null_entry_offset ENDIF //When we've got here we have calculated all the pointers //to the newly created END-OF-DIR entry. //------------------------------------------------------- //TASK2 - Write a new NULL-entry. //Read up the sector where it resides. CALL lib_fat16_int_read_cluster_sector ( null_entry_cluster null_entry_sector lib_fat16_buffer_ptr callstat ) IF callstat != 0 SUBEND //internal error ENDIF //Adjust offset to where in memory the buffer is. ADD lib_fat16_buffer_ptr + null_entry_offset -> lib_fat16_tempw2 //make this a "null-entry" POKE lib_fat16_tempw2 , 0 //and write it back to disk CALL lib_fat16_int_write_cluster_sector ( null_entry_cluster null_entry_sector lib_fat16_buffer_ptr callstat ) IF callstat != 0 SUBEND //internal error ENDIF //------------------------------------------------------- //TASK3 - Create a new direntry (empty file) //Read up the sector where it resides. CALL lib_fat16_int_read_cluster_sector ( lib_fat16_ae_new_entry_cluster lib_fat16_ae_new_entry_sector lib_fat16_buffer_ptr callstat ) IF callstat != 0 SUBEND //internal error ENDIF //Adjust offset to where in memory the buffer is. ADD lib_fat16_buffer_ptr + lib_fat16_ae_new_entry_offset -> lib_fat16_tempw2 //Copy in the new filename LET lib_fat16_tempw = lib_fat16_ae_new_entry_filename ASM ldy #0 lib_fat16_ae_l1 lda (lib_fat16_tempw),y sta (lib_fat16_tempw2),y iny cpy #11 bne lib_fat16_ae_l1 ;clear the rest of the entry (NULLs) lda #0 lib_fat16_ae_l2 sta (lib_fat16_tempw2),y iny cpy #32 bne lib_fat16_ae_l2 ENDASM CALL lib_fat16_int_write_cluster_sector ( lib_fat16_ae_new_entry_cluster lib_fat16_ae_new_entry_sector lib_fat16_buffer_ptr callstat ) IF callstat != 0 SUBEND //internal error ENDIF LET lib_fat16_ae_status = 0 FEND #IFEND //------------------------------------------------------------------------ // lib_fat16_fopen // // Purpose: // To open a file in the current directory and initialize a descriptor // table entry. // A file we're opening for read must exist. // A file we're opening for write must NOT exist. // // Parameters: // descriptor_ptr - this is the fileptr the user needs to // remember for all further operations // on this file. If = 0 = NULL then // the function was unable to open the file. // lib_fat16_fo_filename_ptr - 11 char string, that must match the dir- // entry exacly (or represent the new filename // when writing). // lib_fat16_fo_filemode - use one of the filemode constants defined in // the constant section. "lib_fat16_filemode_*" // currently only "LIB_FAT16_FILEMODE_READ" is // implemented. // lib_fat16_fo_status - 0 = success. !0 = file open error or internal // error. // // Preparation: lib_fat16_mount_partition // // Dependencies: This function uses both "lib_fat16_dirstart" and // "lib_fat16_dirnext" so don't use fopen in a dir-scan // sequence. (it won't work at all as lib_fat16_dirstart // will reset the current search your are doing) // // // Usage of lib temp variables: // (none) ... used lib_fat16_tempw before. // //------------------------------------------------------------------------ WORD descriptor_ptr WORD lib_fat16_fo_filename_ptr BYTE lib_fat16_fo_filemode BYTE lib_fat16_fo_status FUNC lib_fat16_fopen ( out:descriptor_ptr lib_fat16_fo_filename_ptr lib_fat16_fo_filemode out:lib_fat16_fo_status ) WORD descr @ $49 BYTE max_files WORD clusternr WORD direntry @ $14 BYTE callstat BYTE charcount BYTE char1 BYTE char2 BYTE attributes WORD filnamecmp_ptr WORD wtemp #IFDEF __LIB_FAT16_WRITESUPPORT BYTE lastdirentrychar WORD new_entry_cluster WORD new_entry_sector WORD new_entry_offset #IFEND LET lib_fat16_fo_status = 1 LET descriptor_ptr = 0 #IFNDEF __LIB_FAT16_WRITESUPPORT //if library is compiled for readonly support, trying to opening file for write is forbidden IF lib_fat16_fo_filemode != LIB_FAT16_FILEMODE_READ SUBEND ENDIF #IFEND //------------------------------------------ //TASK1 - search for an avaliable descriptor //max files is defined as the address of "lib_fat16_max_open_files". //This is a little quirk to get it into a local variable ASM lda # clusternr IF clusternr == $ffff GOTO lib_fat16_fo_task2 ENDIF ADD descr + __LIB_FAT16_FILE_DESCRIPTOR_SIZE -> descr DEC max_files WEND SUBEND //unable to find a free filedescriptor-slot, return error //-------------------------------------------------------- //TASK2 - find the requested file in the current directory LABEL lib_fat16_fo_task2 CALL lib_fat16_dirstart ( 0 direntry callstat ) IF callstat != 0 SUBEND //internal error ENDIF WHILE direntry //Compare the strings LET filnamecmp_ptr = lib_fat16_fo_filename_ptr LET charcount = 11 WHILE charcount PEEK filnamecmp_ptr -> char1 PEEK direntry -> char2 IF char1 != char2 GOTO lib_fat16_fo_findnext ENDIF INC filnamecmp_ptr INC direntry DEC charcount WEND //if we got here we have a match! Now this must be a valid file //so we've test the attributes of it. PEEK direntry -> attributes //if the direntry is systemfile, volume label or directory we cannot open it! AND attributes WITH 4+8+16 -> attributes IF attributes != 0 SUBEND //error, direntry was not valid to open (see reasons above) ENDIF #IFDEF __LIB_FAT16_WRITESUPPORT IF lib_fat16_fo_filemode == LIB_FAT16_FILEMODE_READ #IFEND GOTO lib_fat16_fo_task4 #IFDEF __LIB_FAT16_WRITESUPPORT ELSE SUBEND //it's write error to try to overwrite an existing file. ENDIF #IFEND LABEL lib_fat16_fo_findnext #IFDEF __LIB_FAT16_WRITESUPPORT CALL lib_fat16_dirnext_le ( direntry callstat lastdirentrychar ) #IFEND #IFNDEF __LIB_FAT16_WRITESUPPORT CALL lib_fat16_dirnext ( direntry callstat ) #IFEND IF callstat != 0 SUBEND //internal error ENDIF WEND #IFDEF __LIB_FAT16_WRITESUPPORT IF lib_fat16_fo_filemode == LIB_FAT16_FILEMODE_READ #IFEND SUBEND // if we got here, we didn't find the file the caller requested for read access #IFDEF __LIB_FAT16_WRITESUPPORT ENDIF #IFEND #IFDEF __LIB_FAT16_WRITESUPPORT //------------------------------------------------------------------- //TASK3 - If writemode we need to create us a new direntry //check verification that dirnext was on the last entry in dir IF lastdirentrychar != LIB_FAT16_END_OF_DIR SUBEND //The DIR did not end with a NULL-filename. Eighter a filesystem error or //it might be the rootdir that is completely filled //We cannot however proceed to create another file in this condition. ENDIF CALL lib_fat16_int_add_dir_entry ( lib_fat16_fo_filename_ptr new_entry_cluster new_entry_sector new_entry_offset callstat ) IF callstat != 0 SUBEND //internal error ENDIF #IFEND //------------------------------------------------------------------- //TASK4 - Copy info from the direntry to the accuired(or created)file descriptor LABEL lib_fat16_fo_task4 //clear descriptor ASM ldy #0 tya lib_fat16_fo_l2 sta (|descr|),y iny cpy #lib_fat16_file_descriptor_size bne lib_fat16_fo_l2 ENDASM //Copy the startcluster to the descriptor // (direntry points to position 11 in the directory-entry // since we've scanned the filename before. therefore "X-11" GETASWORD@ direntry[26-11] -> clusternr PUTASWORD@ descr[FAT16_DESC_CURR_CLUSTER] VALUE clusternr //Copy the filesize to the descriptor. (Two words == one double word :-) GETASWORD@ direntry[26-11+2] -> wtemp PUTASWORD@ descr[FAT16_DESC_FILESIZE] VALUE wtemp GETASWORD@ direntry[26-11+4] -> wtemp PUTASWORD@ descr[FAT16_DESC_FILESIZE+2] VALUE wtemp //set filemode in descriptor POKE descr[FAT16_DESC_FILEMODE] , lib_fat16_fo_filemode #IFDEF __LIB_FAT16_WRITESUPPORT IF lib_fat16_fo_filemode == LIB_FAT16_FILEMODE_WRITE PUTASWORD@ descr[FAT16_DESC_DIRE_CLUSTER] VALUE new_entry_cluster PUTASWORD@ descr[FAT16_DESC_DIRE_SECTOR] VALUE new_entry_sector PUTASWORD@ descr[FAT16_DESC_DIRE_OFFSET] VALUE new_entry_offset ENDIF #IFEND LET descriptor_ptr = descr LET lib_fat16_fo_status = 0 FEND //------------------------------------------------------------------------ // lib_fat16_fclose // lib_fat16_fclose (returns status of operation R/W only) // // Purpose: // To close an open file specified by a descriptor pointer, the caller // accuired from a previous call to "lib_fat16_fopen" // // Parameters: // lib_fat16_fc_descriptor_ptr - the file pointer. // // Preparation: lib_fat16_mount_partition, lib_fat16_fopen // // Usage of lib temp variables: // (none) ... used lib_fat16_tempw before // //------------------------------------------------------------------------ //WORD lib_fat16_fc_descriptor_ptr WORD lib_fat16_fc_descr @ $49 #IFDEF __LIB_FAT16_WRITESUPPORT BYTE lib_fat16_fc_stat FUNC lib_fat16_fcloses ( lib_fat16_fc_descr out:lib_fat16_fc_stat ) LET lib_fat16_fc_stat = 1 #IFEND FUNC lib_fat16_fclose ( lib_fat16_fc_descr ) //LET lib_fat16_fc_descr = lib_fat16_fc_descriptor_ptr #IFDEF __LIB_FAT16_WRITESUPPORT BYTE filemode WORD direntry @ $14 WORD entry_cluster WORD entry_sector WORD entry_offset WORD startcluster WORD clusternr // <- this is the last cluster! WORD filesize_lo WORD filesize_hi BYTE callstat PEEK lib_fat16_fc_descr[FAT16_DESC_FILEMODE] -> filemode IF filemode != LIB_FAT16_FILEMODE_READ //read up the direntry GETASWORD@ lib_fat16_fc_descr[FAT16_DESC_DIRE_CLUSTER] -> entry_cluster GETASWORD@ lib_fat16_fc_descr[FAT16_DESC_DIRE_SECTOR] -> entry_sector GETASWORD@ lib_fat16_fc_descr[FAT16_DESC_DIRE_OFFSET] -> entry_offset CALL lib_fat16_int_read_cluster_sector ( entry_cluster entry_sector lib_fat16_buffer_ptr callstat ) IF callstat != 0 SUBEND ENDIF ADD lib_fat16_buffer_ptr + entry_offset -> direntry //update it GETASWORD@ lib_fat16_fc_descr[FAT16_DESC_FIRST_CLUSTER] -> startcluster GETASWORD@ lib_fat16_fc_descr[FAT16_DESC_CURR_CLUSTER] -> clusternr PUTASWORD@ direntry[26] VALUE startcluster //Copy the filesize to the descriptor. (Two words == one double word :-) GETASWORD@ lib_fat16_fc_descr[FAT16_DESC_FILEPOS] -> filesize_lo PUTASWORD@ direntry[28] VALUE filesize_lo GETASWORD@ lib_fat16_fc_descr[FAT16_DESC_FILEPOS+2] -> filesize_hi PUTASWORD@ direntry[30] VALUE filesize_hi //write down the direntry CALL lib_fat16_int_write_cluster_sector ( entry_cluster entry_sector lib_fat16_buffer_ptr callstat ) IF callstat != 0 SUBEND ENDIF //mark last cluster in file as last-in-cluster-chain // //Note: allthough everything above > $ffef is a "cluster chain ended", some badly written drivers only understand $ffff CALL lib_fat16_int_set_cluster_link ( clusternr $ffff callstat ) IF callstat != 0 SUBEND ENDIF ENDIF LET lib_fat16_fc_stat = 0 //if we got here it's a success #IFEND //we set the "current cluster" to $ffff. This //indicates for "lib_fat16_fopen" that this //descriptor location is up for grabs ASM ldy #0 ;tya lda #$ff sta (lib_fat16_fc_descr),y iny sta (lib_fat16_fc_descr),y ENDASM FEND //------------------------------------------------------------------------ // lib_fat16_fblockread // // Purpose: // Try to read a block of 512 bytes from an file that is open for reading. // If the file end before we've reached 512 read bytes the reading ends. // (Or more correct: we don't write anymore to memory-buffer, but the // sector is fully read) // // Parameters: // lib_fat16_fr_descriptor_ptr - the file pointer. // lib_fat16_fr_block_ptr - pointer to memory where to write the block // read from the file // lib_fat16_fr_read_count - The number of bytes actually read from // file. If this is not equal to 512 // it usually means we've reached the end // of the file. // // Preparation: lib_fat16_mount_partition, lib_fat16_fopen // // NOTE: this routine calls "lib_fat16_int_get_cluster_link" which // will ruin any ongoing "lib_fat16_dirstart" - "lib_fat16_dirnext" // sequencen. Don't do it. ( both use the internal FAT-buffer ) // // Usage of lib temp variables: // (none) ... used lib_fat16_tempw before // //------------------------------------------------------------------------ //WORD lib_fat16_fr_descriptor_ptr WORD lib_fat16_fr_descr @ $49 WORD lib_fat16_fr_block_ptr WORD lib_fat16_fr_read_count BYTE lib_fat16_fr_status FUNC lib_fat16_fblockread ( lib_fat16_fr_descr lib_fat16_fr_block_ptr out:lib_fat16_fr_read_count out:lib_fat16_fr_status ) WORD lib_fat16_fr_current_cluster WORD lib_fat16_fr_sector_in_cluster //note ONLY the lowbyte in this one i used (must be word for call to next_cluster_link). BYTE lib_fat16_fr_current_filemode BYTE lib_fat16_fr_callstat WORD lib_fat16_fr_filesize_lo WORD lib_fat16_fr_filesize_hi WORD lib_fat16_fr_filepos_lo WORD lib_fat16_fr_filepos_hi WORD lib_fat16_fr_fileleft_lo WORD lib_fat16_fr_fileleft_hi LET lib_fat16_fr_status = 1 //-------------------------------------- //TASK1 - is the file valid for reading - copy data //LET lib_fat16_fr_descr = lib_fat16_fr_descriptor_ptr GETASWORD@ lib_fat16_fr_descr[FAT16_DESC_CURR_CLUSTER] -> lib_fat16_fr_current_cluster PEEK lib_fat16_fr_descr[FAT16_DESC_FILEMODE] -> lib_fat16_fr_current_filemode IF lib_fat16_fr_current_filemode != LIB_FAT16_FILEMODE_READ SUBEND //error. file not open for reading. ENDIF //copy descriptor data to local variables PEEK lib_fat16_fr_descr[FAT16_DESC_CURR_SECTOR] -> lib_fat16_fr_sector_in_cluster //Copy filesize och filepos from descriptor (8 bytes for two 32-bit variables) ASM ldy #5 ldx #0 lib_fat16_fr_l1 lda (lib_fat16_fr_descr),y sta |lib_fat16_fr_filesize_lo|,x iny inx cpx #8 bne lib_fat16_fr_l1 ENDASM IF lib_fat16_fr_current_cluster == 0 //special case - empty (0-size) file is valid to open for reading but only 0 bytes can be read. IF lib_fat16_fr_filesize_lo == 0 IF lib_fat16_fr_filesize_hi == 0 LET lib_fat16_fr_read_count = 0 LET lib_fat16_fr_status = 0 //exit is set to success ENDIF ENDIF SUBEND //error (unless special above) - file is not open ENDIF //------------------------------------------- //TASK2 - Adjust clusters / sectors if needed //Did the previous read put us past the end of the cluster? IF lib_fat16_fr_sector_in_cluster >= lib_fat16_sectors_per_cluster //Call will update the "lib_fat16_active_dir_cluster" to the next cluster link. CALL lib_fat16_int_get_cluster_link ( lib_fat16_fr_current_cluster lib_fat16_fr_callstat ) IF lib_fat16_fr_callstat != 0 SUBEND //internal error ENDIF LET lib_fat16_fr_sector_in_cluster = 0 ENDIF //now if ( "File total size in bytes" - "Current position in file" ) < 512 then the sector we just read contains the EOF //subtracting 32 bit values is so very funny on 6502 :) ASM sec lda |lib_fat16_fr_filesize_lo| sbc |lib_fat16_fr_filepos_lo| sta |lib_fat16_fr_fileleft_lo| lda |lib_fat16_fr_filesize_lo|+1 sbc |lib_fat16_fr_filepos_lo|+1 sta |lib_fat16_fr_fileleft_lo|+1 lda |lib_fat16_fr_filesize_hi| sbc |lib_fat16_fr_filepos_hi| sta |lib_fat16_fr_fileleft_hi| lda |lib_fat16_fr_filesize_hi|+1 sbc |lib_fat16_fr_filepos_hi|+1 sta |lib_fat16_fr_fileleft_hi|+1 ENDASM //---------------------------------------------- //TASK3 - Do the actual read LET lib_fat16_fr_read_count = LIB_FAT16_SECTOR_SIZE IF lib_fat16_fr_fileleft_hi == 0 IF lib_fat16_fr_fileleft_lo < LIB_FAT16_SECTOR_SIZE //We've just read to the end of the file //of course we could know that from the link-table also. but we want to report //the number of bytes read to the caller. LET lib_fat16_fr_read_count = lib_fat16_fr_fileleft_lo CALL lib_fat16_int_read_cluster_sector_part ( lib_fat16_fr_current_cluster lib_fat16_fr_sector_in_cluster lib_fat16_fr_block_ptr 0 lib_fat16_fr_read_count lib_fat16_fr_callstat ) IF lib_fat16_fr_callstat != 0 SUBEND //internal error ENDIF GOTO lib_fat16_fr_skipelse1 ENDIF ENDIF CALL lib_fat16_int_read_cluster_sector ( lib_fat16_fr_current_cluster lib_fat16_fr_sector_in_cluster lib_fat16_fr_block_ptr lib_fat16_fr_callstat ) IF lib_fat16_fr_callstat != 0 SUBEND //internal error ENDIF LABEL lib_fat16_fr_skipelse1 INC lib_fat16_fr_sector_in_cluster //we've just read it so we add the offset //---------------------------------------------- //TASK4 - Recalc filepos // Write back data to the file descriptor PUTASWORD@ lib_fat16_fr_descr[FAT16_DESC_CURR_CLUSTER] VALUE lib_fat16_fr_current_cluster ASM ldy #2 lda |lib_fat16_fr_sector_in_cluster| sta (lib_fat16_fr_descr),y ; increment the position (32-bit add) ldy #9 clc lda (lib_fat16_fr_descr),y adc lib_fat16_fr_read_count ; lowbyte of the sector read sta (lib_fat16_fr_descr),y iny lda (lib_fat16_fr_descr),y adc lib_fat16_fr_read_count+1 ; hibyte of the sector read sta (lib_fat16_fr_descr),y iny lda (lib_fat16_fr_descr),y adc #0 ; does not apply (sector is max 512 bytes) sta (lib_fat16_fr_descr),y iny lda (lib_fat16_fr_descr),y adc #0 ; does not apply (sector is max 512 bytes) sta (lib_fat16_fr_descr),y ENDASM LET lib_fat16_fr_status = 0 FEND //------------------------------------------------------------------------ // lib_fat16_fblockwrite // // Purpose: // To write a block (512) of data to a file, opened for writing. // If the complete size of the file is not dividable by 512, the size of // the last block is allowed to be less than 512 bytes. // // Params: // // // //------------------------------------------------------------------------ #IFDEF __LIB_FAT16_WRITESUPPORT //WORD lib_fat16_fw_descriptor_ptr WORD lib_fat16_fw_descr @ $49 WORD lib_fat16_fw_block_ptr WORD lib_fat16_fw_block_size BYTE lib_fat16_fw_status FUNC lib_fat16_fblockwrite ( lib_fat16_fw_descr lib_fat16_fw_block_ptr lib_fat16_fw_block_size out:lib_fat16_fw_status ) WORD lib_fat16_fw_start_cluster WORD lib_fat16_fw_current_cluster WORD lib_fat16_fw_sector_in_cluster WORD lib_fat16_fw_new_cluster BYTE lib_fat16_fw_current_filemode BYTE lib_fat16_fw_callstat LET lib_fat16_fw_status = 1 //assume error //LET lib_fat16_fw_descr = lib_fat16_fw_descriptor_ptr //---------------------------------------------- //TASK1 - Read up vital data from filedescriptor PEEK lib_fat16_fw_descr[FAT16_DESC_FILEMODE] -> lib_fat16_fw_current_filemode IF lib_fat16_fw_current_filemode != LIB_FAT16_FILEMODE_WRITE SUBEND //error. file not open for writing. ENDIF GETASWORD@ lib_fat16_fw_descr[FAT16_DESC_FIRST_CLUSTER] -> lib_fat16_fw_start_cluster GETASWORD@ lib_fat16_fw_descr[FAT16_DESC_CURR_CLUSTER] -> lib_fat16_fw_current_cluster PEEK lib_fat16_fw_descr[FAT16_DESC_CURR_SECTOR] -> lib_fat16_fw_sector_in_cluster //This assures that only the last sector (block) written can be other than the size of a sector. IF lib_fat16_fw_block_size != LIB_FAT16_SECTOR_SIZE IF lib_fat16_fw_block_size > LIB_FAT16_SECTOR_SIZE SUBEND //error - not supported ENDIF //LET lib_fat16_fw_current_filemode = LIB_FAT16_FILEMODE_WRITE_ENDED POKE lib_fat16_fw_descr[FAT16_DESC_FILEMODE] , LIB_FAT16_FILEMODE_WRITE_ENDED IF lib_fat16_fw_block_size == 0 //Writing 0 bytes to a valid open write-file is OK (but nothing happens) LET lib_fat16_fw_status = 0 SUBEND ENDIF ENDIF //---------------------------------------------- //TASK2 - Is this the first block in the file? IF lib_fat16_fw_start_cluster == 0 CALL lib_fat16_int_get_free_cluster ( lib_fat16_fw_start_cluster lib_fat16_fw_callstat ) IF lib_fat16_fw_callstat != 0 SUBEND //Could not get another cluster (disk probably full) ENDIF PUTASWORD@ lib_fat16_fw_descr[FAT16_DESC_FIRST_CLUSTER] VALUE lib_fat16_fw_start_cluster LET lib_fat16_fw_current_cluster = lib_fat16_fw_start_cluster //As this file is just opened sector_in_cluster is set to 0 //LET lib_fat16_fw_sector_in_cluster = 0 ENDIF //---------------------------------------------- //TASK3 - Time to allocate another cluster? IF lib_fat16_fw_sector_in_cluster >= lib_fat16_sectors_per_cluster CALL lib_fat16_int_get_free_cluster ( lib_fat16_fw_new_cluster lib_fat16_fw_callstat ) IF lib_fat16_fw_callstat != 0 SUBEND //Could not get another cluster (disk probably full) ENDIF //Update the cluster link from last cluster to the new one. CALL lib_fat16_int_set_cluster_link ( lib_fat16_fw_current_cluster lib_fat16_fw_new_cluster lib_fat16_fw_callstat ) IF lib_fat16_fw_callstat != 0 SUBEND //internal error ENDIF LET lib_fat16_fw_sector_in_cluster = 0 LET lib_fat16_fw_current_cluster = lib_fat16_fw_new_cluster ENDIF //---------------------------------------------- //TASK4 - Write the data! CALL lib_fat16_int_write_cluster_sector_part ( lib_fat16_fw_current_cluster lib_fat16_fw_sector_in_cluster lib_fat16_fw_block_ptr 0 lib_fat16_fw_block_size lib_fat16_fw_callstat ) IF lib_fat16_fw_callstat != 0 SUBEND //internal error ENDIF INC lib_fat16_fw_sector_in_cluster //step forward for next write //---------------------------------------------- //TASK5 - Update descriptor (and filepos calc) PUTASWORD@ lib_fat16_fw_descr[FAT16_DESC_CURR_CLUSTER] VALUE lib_fat16_fw_current_cluster POKE lib_fat16_fw_descr[FAT16_DESC_CURR_SECTOR] , lib_fat16_fw_sector_in_cluster ASM ; increment the position (32-bit add) ldy #9 clc lda (lib_fat16_fw_descr),y adc lib_fat16_fw_block_size ; lowbyte of the sector read sta (lib_fat16_fw_descr),y iny lda (lib_fat16_fw_descr),y adc lib_fat16_fw_block_size+1 ; hibyte of the sector read sta (lib_fat16_fw_descr),y iny lda (lib_fat16_fw_descr),y adc #0 ; does not apply (sector is max 512 bytes) sta (lib_fat16_fw_descr),y iny lda (lib_fat16_fw_descr),y adc #0 ; does not apply (sector is max 512 bytes) sta (lib_fat16_fw_descr),y ENDASM LET lib_fat16_fw_status = 0 //if we got here it's a success FEND #IFEND LABEL lib_fat16_skip #IFEND