//------------------------------------------------------------------------
//                        FAT16 Library Low Level
//
// Author: Mattias Hansson
// Target: Commodore 64 with MMC64 interface
//         (possible future conversions for other platforms possible)
//
// Purpose: To provide lowlevel access routines for the FAT16 Library
//
// Routines:
//   lib_fat16_int_read_cluster_sector (INTERNAL)
//
//
//
//------------------------------------------------------------------------


#IFNDEF __LIB_FAT16LOW
#DEFINE __LIB_FAT16LOW = 1

GOTO lib_fat16low_skip


//------------------------------------------------------------------------
//                            FAT16 constants
//------------------------------------------------------------------------
WORD CONST LIB_FAT16_CLUSTER_FREE = $0000
WORD CONST LIB_FAT16_CLUSTER_END  = $fff0 //this value or above = clusterchain ended
BYTE CONST LIB_FAT16_DIRENTRY_SIZE = $20
BYTE CONST LIB_FAT16_DELETED_FILE = $e5
BYTE CONST LIB_FAT16_END_OF_DIR = $00
WORD CONST LIB_FAT16_SECTOR_SIZE = 512

//******Do NOT EVER access this var as a regular variable***********
//special: this is NOT a regular variable for usage!!!!!!!!!!!!!!!!!
//The "Address" defines how many files may be open simultainously!!!
//If you want to change the number of open files, change the!!!!!!!!
//address to the number you of files you want open!!!!!!!!!!!!!!!!!!
BYTE lib_fat16_max_open_files @ __LIB_FAT16_MAX_OPEN_FILES
//******Do NOT EVER access this var as a regular variable***********

BYTE CONST LIB_FAT16_FILEMODE_READ = 0
BYTE CONST LIB_FAT16_FILEMODE_WRITE = 1
//This filemode is special as it's only valid for fclose()
//This file gets set when blockwriting another size then $512
//as further writes would not be alligned with the sectors.
BYTE CONST LIB_FAT16_FILEMODE_WRITE_ENDED = 2
//BYTE CONST LIB_FAT16_FILEMODE_READWRITE = 2  //future expansion

#IFDEF __LIB_FAT16_WRITESUPPORT
//This constant controls how many consecutive clusters will be controlled
//for beeing free when mounting a partition for read/write access.
//Generally speaking this is good when having an unfragmented disk/card
//with just a little data on it, as i shortens the mount time.

//However if you'll be writing much data (like several megabytes) you'll
//probably want to set this to $ffff (no limit) since this will not add
//any performance penalty (for searching new free clusters) when you've
//reached the limit.

#IFDEF __LIB_MMC64_OPTIMIZE_SPEED
  WORD CONST LIB_FAT16_WRITE_CLUSTERS_MAX = $400
#IFEND
#IFNDEF __LIB_MMC64_OPTIMIZE_SPEED
  //Explanation: if we don't cache the reads, every single cluster
  //we search (in fat) will force the lib to re-read the sector again
  //which will take painfully much time
  #PRINT __LIB_MMC64_OPTIMIZE_SPEED combined with __LIB_FAT16_WRITESUPPORT is catastrophic performance-wise.
  WORD CONST LIB_FAT16_WRITE_CLUSTERS_MAX = $10
#IFEND


#IFEND

//------------------------------------------------------------------------
//                         FAT16 common variables
//------------------------------------------------------------------------

//Info for developers: the lib_fat16_buffer_ptr must point out an 512 byte area
//You can choose to do so by a constant or variable.
//Default is a variable that points out an area reserved inside this library
//called "lib_fat16_fatbuffer". If you change this to a constant, remark
//the "lib_fat16_fatbuffer" area to save the unused filler.

//WORD CONST lib_fat16_buffer_ptr = $c800 //change this for your particular usage
//                                        //(size needed is "one sector" = 512 bytes )

WORD lib_fat16_buffer_ptr
BYTE lib_fat16_sectors_per_cluster   // needed for cluster reading routine
WORD lib_fat16_reserved_sectors      // offset from FAT16 boot record to FATs (file allocation tables)
BYTE lib_fat16_nr_of_fats            // self*sectors_per_fat = offset from FATs to the root directory
WORD lib_fat16_sectors_per_fat       // (see above)
WORD lib_fat16_rootdir_entries       // Max file-entries in the ROOT directory
                                     // ( self * size of directory entry (32) ) / 512 =
                                     // offset from ROOT directory to DATA area (where clusters begin.
                                     // N.B. First cluster here is #2 (!))
WORD lib_fat16_rootdir_sectors       // calculated size of rootdir measured in sectors.

#IFDEF __LIB_FAT16_WRITESUPPORT

WORD lib_fat16_total_clusters = 0  // ( sectors_per_fat * bytes_per_sector ($200) ) / 2 == ( sectors_per_fat * $100 ).

WORD lib_fat16_first_write_cluster = $ffff //these two are used internally by lib_fat16_int_find_unused_clusters and
WORD lib_fat16_last_write_cluster = $0000  //lib_fat16_fwriteblock to find new areas for writing bit by bit (as needed)
WORD lib_fat16_total_sectors_lo
WORD lib_fat16_total_sectors_hi

#IFEND

//Temp ZP-variables for usage in the lib.
WORD lib_fat16_tempw @ $3f
WORD lib_fat16_tempw2 @ $4b
BYTE lib_fat16_tempb @ $41

//------------------------------------------------------------------------
//          512 byte internal buffer for FAT16 operations.
//------------------------------------------------------------------------
LABEL lib_fat16_fatbuffer
ASM
  !fill 512
ENDASM

//------------------------------------------------------------------------
// lib_fat16_int_read_cluster_sector (INTERNAL)
//
// Purpose:
//   1. To read out a specific sector in a specific cluster
// or
//   2. To read out a sector in the root-dir
//
// Parameters:
//   lib_fat16_int_rc_cluster - cluster to read. special: 0 = root-dir
//   lib_fat16_int_rc_sector - sector # inside the cluster (0..lib_fat16_sectors_per_cluster-1)
//                             special: if in root-dir (0..lib_fat16_rootdir_sectors-1)
//   lib_fat16_int_rc_buffer_ptr - pointer, where to put the read sector in memory
//   lib_fat16_int_rc_status -  0 = OK. !0 = error.
//
// Preparation: lib_fat16_mount_partition
//
// Usage of lib temp variables:
//   lib_fat16_tempw2
//   lib_fat16_tempb
//------------------------------------------------------------------------

WORD lib_fat16_int_rc_cluster
WORD lib_fat16_int_rc_sector
WORD lib_fat16_int_rc_buffer_ptr
BYTE lib_fat16_int_rc_status

FUNC lib_fat16_int_read_cluster_sector ( lib_fat16_int_rc_cluster lib_fat16_int_rc_sector lib_fat16_int_rc_buffer_ptr out:lib_fat16_int_rc_status )
WORD first_pos // in bytes
WORD read_count

LET first_pos = 0
LET read_count = LIB_FAT16_SECTOR_SIZE

FUNC lib_fat16_int_read_cluster_sector_part ( lib_fat16_int_rc_cluster lib_fat16_int_rc_sector lib_fat16_int_rc_buffer_ptr first_pos read_count out:lib_fat16_int_rc_status )

  BYTE spc_count
  WORD csect_offset_lo
  BYTE csect_offset_hi
  BYTE callstat   //status for internal calls


  LET lib_fat16_int_rc_status = 1 //indicate an error at first (we SUBEND out if things go wrong)

  IF lib_fat16_int_rc_cluster == 0 //the root directory
    IF lib_fat16_int_rc_sector >= lib_fat16_rootdir_sectors
      SUBEND // caller tried to make us read past the root-dir region. probably an internal error.
    ENDIF
    //Now we must add the start sector for the rootdir with the sector requested inside it, to get the right one
    ASM
      clc
      lda lib_fat16_rootdir_sector_lo
      adc lib_fat16_int_rc_sector
      sta lib_fat16_tempw2
      lda lib_fat16_rootdir_sector_lo+1
      adc lib_fat16_int_rc_sector+1
      sta lib_fat16_tempw2+1
      lda lib_fat16_rootdir_sector_hi
      adc #0
      sta lib_fat16_tempb
    ENDASM
    CALL lib_mmc64_readsector ( lib_fat16_tempw2 lib_fat16_tempb lib_fat16_int_rc_buffer_ptr callstat )
    IF callstat != 0
      SUBEND
    ENDIF
    GOTO lib_fat16_int_rc_exit_success
  ENDIF

  //Else this is a real cluster in the dataarea that's about to be read
  IF lib_fat16_int_rc_sector >= lib_fat16_sectors_per_cluster
    SUBEND // caller passed a sector offset beyond the end of the cluster. probably an internal error
  ENDIF

  //Adjust for data-area starting with cluster #2
  SUBT lib_fat16_int_rc_cluster - 2 -> lib_fat16_int_rc_cluster

  //Now we must multiply the clusternr with "nr of sectors per cluster" to
  //get a valid sector-offset into the data-area
  LET csect_offset_lo = lib_fat16_int_rc_cluster
  LET csect_offset_hi = 0
  LET spc_count = lib_fat16_sectors_per_cluster
  //First bit unimportant (1 sector per cluster and we're done here!)
  ASM
    lsr |spc_count|
  ENDASM
  WHILE spc_count
    ASM
      clc
      rol |csect_offset_lo|
      rol |csect_offset_lo|+1
      rol |csect_offset_hi|

      lsr |spc_count|
    ENDASM
  WEND

  // "real sector to access" = "start of data area" + "cluster sector offset" + lib_fat16_int_rc_sector (sector # in cluster)
  //first we do "start of data area" + "cluster sector offset"
  ASM
    clc
    lda lib_fat16_data_sector_lo
    adc |csect_offset_lo|
    sta lib_fat16_tempw2
    lda lib_fat16_data_sector_lo+1
    adc |csect_offset_lo|+1
    sta lib_fat16_tempw2+1
    lda lib_fat16_data_sector_hi
    adc |csect_offset_hi|
    sta lib_fat16_tempb
  ENDASM
  //now we add the lib_fat16_int_rc_sector
  ASM
    clc
    lda lib_fat16_tempw2
    adc |lib_fat16_int_rc_sector|
    sta lib_fat16_tempw2
    lda lib_fat16_tempw2+1
    adc #0 ; we know that "sectors per cluster" cannot exceed 128.
    sta lib_fat16_tempw2+1
    lda lib_fat16_tempb
    adc #0
    sta lib_fat16_tempb
  ENDASM

  //Mostly we read whole sectors. However if the file ends in (( size % 0x200 )!=0) the file ends someplace inside
  //a sector, and we must read the last sector partly.
  CALL lib_mmc64_readsector_part ( lib_fat16_tempw2 lib_fat16_tempb lib_fat16_int_rc_buffer_ptr first_pos read_count callstat )
  IF callstat != 0
    SUBEND
  ENDIF


LABEL lib_fat16_int_rc_exit_success

  LET lib_fat16_int_rc_status = 0
FEND

//------------------------------------------------------------------------
// lib_fat16_int_write_cluster_sector (INTERNAL)
//
// Purpose:
//   1. To write a specific sector in a specific cluster
// or
//   2. To write a sector in the root-dir
//
// Parameters:
//   lib_fat16_int_wc_cluster - cluster to write. special: 0 = root-dir
//   lib_fat16_int_wc_sector - sector # inside the cluster (0..lib_fat16_sectors_per_cluster-1)
//                             special: if in root-dir (0..lib_fat16_rootdir_sectors-1)
//   lib_fat16_int_wc_buffer_ptr - pointer, sector-data in memory to write
//   lib_fat16_int_wc_status -  0 = OK. !0 = error.
//
// Preparation: lib_fat16_mount_partition
//
// Usage of lib temp variables:
//   lib_fat16_tempw2
//   lib_fat16_tempb
//------------------------------------------------------------------------

#IFDEF __LIB_FAT16_WRITESUPPORT

WORD lib_fat16_int_wc_cluster
WORD lib_fat16_int_wc_sector
WORD lib_fat16_int_wc_buffer_ptr
BYTE lib_fat16_int_wc_status

FUNC lib_fat16_int_write_cluster_sector ( lib_fat16_int_wc_cluster lib_fat16_int_wc_sector lib_fat16_int_wc_buffer_ptr out:lib_fat16_int_wc_status )
WORD first_pos // in bytes
WORD write_count

LET first_pos = 0
LET write_count = LIB_FAT16_SECTOR_SIZE

FUNC lib_fat16_int_write_cluster_sector_part ( lib_fat16_int_wc_cluster lib_fat16_int_wc_sector lib_fat16_int_wc_buffer_ptr first_pos write_count out:lib_fat16_int_wc_status )

  BYTE spc_count
  WORD csect_offset_lo
  BYTE csect_offset_hi
  BYTE callstat   //status for internal calls


  LET lib_fat16_int_wc_status = 1 //indicate an error at first (we SUBEND out if things go wrong)

  IF lib_fat16_int_wc_cluster == 0 //the root directory
    IF lib_fat16_int_wc_sector >= lib_fat16_rootdir_sectors
      SUBEND // caller tried to make us read past the root-dir region. probably an internal error.
    ENDIF
    //Now we must add the start sector for the rootdir with the sector requested inside it, to get the right one
    ASM
      clc
      lda lib_fat16_rootdir_sector_lo
      adc lib_fat16_int_wc_sector
      sta lib_fat16_tempw2
      lda lib_fat16_rootdir_sector_lo+1
      adc lib_fat16_int_wc_sector+1
      sta lib_fat16_tempw2+1
      lda lib_fat16_rootdir_sector_hi
      adc #0
      sta lib_fat16_tempb
    ENDASM
    CALL lib_mmc64_writesector ( lib_fat16_tempw2 lib_fat16_tempb lib_fat16_int_wc_buffer_ptr callstat )
    IF callstat != 0
      SUBEND
    ENDIF
    GOTO lib_fat16_int_wc_exit_success
  ENDIF

  //Else this is a real cluster in the dataarea that's about to be read
  IF lib_fat16_int_wc_sector >= lib_fat16_sectors_per_cluster
    SUBEND // caller passed a sector offset beyond the end of the cluster. probably an internal error
  ENDIF

  //Adjust for data-area starting with cluster #2
  SUBT lib_fat16_int_wc_cluster - 2 -> lib_fat16_int_wc_cluster

  //Now we must multiply the clusternr with "nr of sectors per cluster" to
  //get a valid sector-offset into the data-area
  LET csect_offset_lo = lib_fat16_int_wc_cluster
  LET csect_offset_hi = 0
  LET spc_count = lib_fat16_sectors_per_cluster
  //First bit unimportant (1 sector per cluster and we're done here!)
  ASM
    lsr |spc_count|
  ENDASM
  WHILE spc_count
    ASM
      clc
      rol |csect_offset_lo|
      rol |csect_offset_lo|+1
      rol |csect_offset_hi|

      lsr |spc_count|
    ENDASM
  WEND

  // "real sector to access" = "start of data area" + "cluster sector offset" + lib_fat16_int_wc_sector (sector # in cluster)
  //first we do "start of data area" + "cluster sector offset"
  ASM
    clc
    lda lib_fat16_data_sector_lo
    adc |csect_offset_lo|
    sta lib_fat16_tempw2
    lda lib_fat16_data_sector_lo+1
    adc |csect_offset_lo|+1
    sta lib_fat16_tempw2+1
    lda lib_fat16_data_sector_hi
    adc |csect_offset_hi|
    sta lib_fat16_tempb
  ENDASM
  //now we add the lib_fat16_int_wc_sector
  ASM
    clc
    lda lib_fat16_tempw2
    adc |lib_fat16_int_wc_sector|
    sta lib_fat16_tempw2
    lda lib_fat16_tempw2+1
    adc #0 ; we know that "sectors per cluster" cannot exceed 128.
    sta lib_fat16_tempw2+1
    lda lib_fat16_tempb
    adc #0
    sta lib_fat16_tempb
  ENDASM

  //Mostly we read whole sectors. However if the file ends in (( size % 0x200 )!=0) the file ends someplace inside
  //a sector, and we must read the last sector partly.
  CALL lib_mmc64_writesector_part ( lib_fat16_tempw2 lib_fat16_tempb lib_fat16_int_wc_buffer_ptr first_pos write_count callstat )
  IF callstat != 0
    SUBEND
  ENDIF


LABEL lib_fat16_int_wc_exit_success

  LET lib_fat16_int_wc_status = 0
FEND

#IFEND

//------------------------------------------------------------------------
// lib_fat16_int_get_cluster_link (INTERNAL)
//
// Purpose:
//   Return the next cluster nr for a given cluster.
//   In the FAT the position for the current cluster holds the link
//   to the next cluster in the "chain".
//   special values are: $0000 unallocated, $ffef-$ffff end-of-cluster-chain
//
// Parameters:
//   lib_fat16_int_nc_cluster_nr - we send in our current cluster number
//                                 and we get the next returned in the
//                                 same param
//   lib_fat16_int_nc_status - the status operation. 0 = success
//                             !0 = failure (probably internal)
//
// Preparation: lib_fat16_dirnext (caller)
//              (unfinished) lib_fat16_fopen
//
// Usage of lib temp variables:
//   lib_fat16_tempw2
//   lib_fat16_tempb
//
//
// lib_fat16_int_set_cluster_link (INTERNAL)
//
// Purpose: to update the two FAT's with a new value for the cluster-link
//
// Additional parameter:
//     lib_fat16_int_nc_target_cluster_nr = the new cluster-link to write
//
// Sorry for the clutter, but this way saves a whole lot of code to
// support write-access //Mattias
//
// A note on caching: Since a fat-sector is read, updated and written back
// at once we don't need to invalidate the "caching" of the FAT-buffer.
// (a later cached read will still be valid)
//------------------------------------------------------------------------


WORD lib_fat16_int_nc_cluster_nr
BYTE lib_fat16_int_nc_status

#IFDEF __LIB_FAT16_WRITESUPPORT

BYTE lib_fat16_int_nc_writeflag
WORD lib_fat16_int_nc_target_cluster_nr
FUNC lib_fat16_int_set_cluster_link ( lib_fat16_int_nc_cluster_nr lib_fat16_int_nc_target_cluster_nr out:lib_fat16_int_nc_status )

  LET lib_fat16_int_nc_writeflag = 1

#IFEND

FUNC lib_fat16_int_get_cluster_link ( io:lib_fat16_int_nc_cluster_nr out:lib_fat16_int_nc_status )

  BYTE sect_offset
  BYTE callstat
  WORD sector_lo
  BYTE sector_hi

  //MHTAG 20050927 - this internal variable was replaced internally for lib_fat16_tempw2 (ZP-vars == better code)
  //WORD lib_fat16_int_nc_link_offset_bytes

  LET lib_fat16_int_nc_status = 1

  //Clusters in the data-area are numbered from 2 (i.e. 2 = offset 0 )
  IF lib_fat16_int_nc_cluster_nr < 2
    SUBEND
  ENDIF

  //Values from $fff0 are "bad", reserved or cluster-chain end markers. (endmark>=$fff8)
  //We don't care here, since we cannot follow longer anyhow.
  IF lib_fat16_int_nc_cluster_nr >= LIB_FAT16_CLUSTER_END
    SUBEND
  ENDIF

  // exp for the sector offset on disk for requested cluster entry:
  // "first FAT sector" + ( (cluster_nr * "sizeof cluster entry (2 bytes in FAT16 )") / 512 )
  // simplified = "first FAT sector" + ( cluster_nr / 256 ) i.e. the high byte of "cluster nr"
  // div 256 = shift left 8 bits, so we simply take the highbyte and move it to the lowbyte (see below)
  ASM
    lda lib_fat16_int_nc_cluster_nr+1
    sta |sect_offset|
  ENDASM
  //and now we add the "first FAT sector"
  ASM
    clc
    lda lib_fat16_fat1_sector_lo
    adc |sect_offset|
    sta lib_fat16_tempw2
    lda lib_fat16_fat1_sector_lo+1
    adc #0
    sta lib_fat16_tempw2+1
    lda lib_fat16_fat1_sector_hi
    adc #0
    sta lib_fat16_tempb
  ENDASM

  LET sector_lo = lib_fat16_tempw2
  LET sector_hi = lib_fat16_tempb

  //read up the sector
  CALL lib_mmc64_readsector ( sector_lo sector_hi lib_fat16_buffer_ptr callstat )
  IF callstat != 0
    SUBEND //pass the read error on to the caller.
  ENDIF

  //calculate the position of the link
  //Following the logic above the offset the the position of the link in the sector must be in the low-byte
  //However since every entry is 2 bytes long, we must multiply the lowbyte with 2 to get the offset in bytes
  //
  //MHTAG 20050927 - all usage of lib_fat16_tempw2 below in this func is replacement for lib_fat16_int_nc_link_offset_bytes
  ASM
    clc
    lda lib_fat16_int_nc_cluster_nr
    rol
    sta lib_fat16_tempw2
    lda #0
    rol
    sta lib_fat16_tempw2+1
  ENDASM

  ADD lib_fat16_tempw2 + lib_fat16_buffer_ptr -> lib_fat16_tempw2

#IFDEF __LIB_FAT16_WRITESUPPORT
  //if readaccess - skit this section
  IF lib_fat16_int_nc_writeflag == 1
		//this section updates the cluster link value and writes the fat-sector back to disk
		PUTASWORD@ lib_fat16_tempw2 VALUE lib_fat16_int_nc_target_cluster_nr

		CALL lib_mmc64_writesector ( sector_lo sector_hi lib_fat16_buffer_ptr callstat )
		IF callstat != 0
			SUBEND //pass the read error on to the caller.
		ENDIF

		//now we must update the second FAT.
		//change here to support more than 2 FATs
		//reason for decision to support only 2 = VERY standard. Even linux vfat driver relies on two FATs
		//24-bit addition of the size of one fat to get right offset
		ASM
      clc
		  lda |sector_lo|
			adc lib_fat16_sectors_per_fat
		  sta |sector_lo|
		  lda |sector_lo|+1
			adc lib_fat16_sectors_per_fat+1
		  sta |sector_lo|+1
		  lda |sector_hi|
			adc #0
		  sta |sector_hi|
    ENDASM
		//and finally the actual write.
		CALL lib_mmc64_writesector ( sector_lo sector_hi lib_fat16_buffer_ptr callstat )
		IF callstat != 0
			SUBEND //pass the read error on to the caller.
		ENDIF

		GOTO lib_fat16_int_nc_skipread
	ENDIF
#IFEND

  //return it
  GETASWORD@ lib_fat16_tempw2 -> lib_fat16_int_nc_cluster_nr

#IFDEF __LIB_FAT16_WRITESUPPORT
  LABEL lib_fat16_int_nc_skipread
  LET lib_fat16_int_nc_writeflag = 0 //reset for next access of this function
#IFEND

  LET lib_fat16_int_nc_status = 0
FEND

//------------------------------------------------------------------------
// lib_fat16_int_search_free_clusters
//
// Purpose: To find clusters on the mounted drive that are free to allocate
//   for new filedata.
//
// Usage: When mounting a partition for R/W access this routine is called
// to find unused clusters, in the event that a write is issued. The
// routine searches for a "cluster hole" of max LIB_FAT16_WRITE_CLUSTERS_MAX
// continous clusters (to reduce search time). It updates the variables
// lib_fat16_first_write_cluster and lib_fat16_last_write_cluster with
// the limits of the found "cluster hole" (range of unallocated clusters)
//
// If writing has completely filled previously discovered "cluster hole"
// the write routines may call this routine again to try to find more
// unclaimed disc space to fill.
//
// Method:
// Space for writing is searched from the end of the FAT (last clusters)
// and backwards until we find free clusters. When we find a free cluster
// we set the "lib_fat16_last_write_cluster" marker and continue backwards
// for a while (until we reach a count or until we hit data again. This is
// where we set the "lib_fat16_first_write_cluster" marker.
// After this operation we know about an area between the two markers that
// consists of free clusters, ready to use for writing file-data
//------------------------------------------------------------------------

#IFDEF __LIB_FAT16_WRITESUPPORT

BYTE lib_fat16_int_fu_status

FUNC lib_fat16_int_search_free_clusters ( out:lib_fat16_int_fu_status )

  WORD active_cluster
  WORD active_link
  WORD cluster_search_max
  BYTE callstat

  LET lib_fat16_int_fu_status = 1 //default = error exit

  //-----------------------------------------------------
  //TASK1 - find out where to start the search
  LET active_cluster = lib_fat16_first_write_cluster
  IF active_cluster == $ffff //if free area is uninitialized yet
    LET active_cluster = lib_fat16_total_clusters //start the search at the end of the fat
  ENDIF

  //-----------------------------------------------------
	//TASK2 - rewind until we reach:
	// a) free clusters
	// b) start of fat

	WHILE 1
    LET active_link = active_cluster
  	CALL lib_fat16_int_get_cluster_link ( active_link callstat )
		IF callstat != 0
      SUBEND //internal error
		ENDIF
  	IF active_link == 0
	    BREAK
  	ENDIF
    DEC active_cluster
	WEND

  IF active_cluster < 2 //disk is full. Unable to find a single free cluster!
	  SUBEND
	ENDIF

  LET lib_fat16_last_write_cluster = active_cluster

  //-----------------------------------------------------
  //TASK3 - rewind until we reach the end of the free area.

  //LET active_link = active_cluster
	LET cluster_search_max = LIB_FAT16_WRITE_CLUSTERS_MAX
  WHILE active_link == LIB_FAT16_CLUSTER_FREE
	  DEC cluster_search_max
		IF cluster_search_max == 0
		  BREAK
		ENDIF
    DEC active_cluster
    LET active_link = active_cluster
		CALL lib_fat16_int_get_cluster_link ( active_link callstat )
		IF callstat != 0
		  SUBEND //internal error
		ENDIF
	WEND

	IF active_cluster < 2
    LET active_cluster = 1
	ENDIF

  INC active_cluster //this one was used so we need the next one (the first free)
  LET lib_fat16_first_write_cluster = active_cluster

  //now we should have found a area free for writing.

  //MHTAG 20051114 - debug writes out the free area to the screen
  //CALL lib_dbg_hexoutw ( lib_fat16_first_write_cluster )
  //CALL lib_dbg_outlf
  //CALL lib_dbg_hexoutw ( lib_fat16_last_write_cluster )
  //CALL lib_dbg_outlf



  LET lib_fat16_int_fu_status = 0 //if we got this far it's a success

FEND

#IFEND

LABEL lib_fat16low_skip

#IFEND
