// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // ========== P A R T 4 ====================================== // This section is for write operations. It is much harder to write // to the card than to read from it as things such as creating // directory space, updating the fat, checking free space etc. need // to be considered // // RTC is needed for creating and modifying files, for devices without // an RTC then the words will need to be changed // This shares many words with fat-1-a, fat-2 and fat-3 so it should // have the same SID number // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // REQUIRES: // #URL-Inc "http://pin1.org/forthlib/flb/General/soft1.flb" sid=99 // #URL-lib "http://pin1.org/forthlib/flb/General/pinsel.flb" sid=100 // #URL-lib "http://pin1.org/forthlib/flb/General/SPI.flb" sid=101 // #URL-lib "http://pin1.org/forthlib/flb/SD-Card/MMC.flb" sid=102 // #URL-lib "http://pin1.org/forthlib/flb/General/rtc.flb" sid=103 // #URL-lib "http://pin1.org/forthlib/flb/SD-Card/fat-1.flb" // #URL-lib "http://pin1.org/forthlib/flb/SD-Card/fat-2.flb" // #URL-lib "http://pin1.org/forthlib/flb/SD-Card/fat-3.flb" // ========== FAT - Write =========================== // write value to cluster, value is a 16 bit word that is // written in intel format : fatClust! ( val clust# -- -1|0) fatClust // get address and sector of cluster within fat word16! // write new value d-sec Dsec! // store back to card ; // gets next free cluster in fat, starting at start, use start=0 // to find the first free fat, then use this value to find the next : fatNF ( start -- clust# [-1 is disk full] ) 1+ TotalSec d @ SectClust d @ / // total number of clusters- index swap do i fatClust@ 0= if i leave then loop dup TotalSec d @ > if drop -1 then // disk full check ; // Serarch for first blank directory entry, directory space goes from // DIR d @ to RootEntries d @, use e5 as blank or 00 // next free ditrectory entry, addr is within the current sector // 0 indicates that the directoiry is full // ** At this stage d# (device) should be set // sector will be in d-sec : nfde ( -- en#, -1|0 ) 0 // leave marker RootEntries d @ // number of directory entries for i dir-entry // conver en# to address c@ // get firct char dup &e5 = // look for either e5 or 0 swap 0= // both inducate free or if drop i -1 leave then next ; // stores update date and time in the dir entry // to the given entry address : storeDT ( add -- ) dup rtc.time m-time! rtc.date m-date! ; // stores create date and time in the dir entry // to the given entry address : storeDTC ( add -- ) dup rtc.time c-time! rtc.date c-date! ; // writes current directory entry to card, loads in correct sector // first by using the curent file handle. // This copies the directory entry in the fiile handle to the // correct directory entry on the card : dir! ( -- ) dirAdd // source address en# f @ // get entry number dir-entry // get destination address, also gets sector from card 32 move // fron file handle to directory d-sec Dsec! // store to card ; // If a file is erased then all of the clusters except the current // one needs to be set to 0 and the current cluster is set to &ffff // If the file is removed form the system then all of the clusters // including the current clutser need to be erased. This will erase // ALL of the clusters including the current one // NOTE THERE IS NO CHECK FOR GOING OVER END OF FAT : erase-clusters ( start-clust# -- [fat] ) begin dup // save previous fatClust@ // get next swap 0 swap fatClust! // write 0 to previous, available dup &fff8 > // end marker over 0= or // error checking will end if 0 encountered as well if 0 swap fatClust! -1 else 0 then // dont forget last one until ; // when opening an existing file for writing or deleting a file // the fat clusters need to be reset back to zero to free upthe // filespace, clust is the starting cluster. All clusters in theFAT // including this one will be set to 0 // NOTE This erases the contents but not the name of the file // changes to the directory : clearFile ( -- ) IsOpen f @ -1 > if 121 error then // abort if file not open dirAdd start-clust@ dup // get file starting cluster and make copy // 12 dir# @ dup // get file starting cluster and make copy erase-clusters // erase ALL clusters in file &ffff swap fatClust! // first clust to &ffff // directory 0 dirAdd fileSize! // set file size to 0 // 0 13 dir# ! // set file size to 0, dirAdd storeDT // update modified dates Dir! // update card with modified entry ; // open existing file for wtiting, this will RESET the file // in the sence that the contects will be erased : w-reset ( string handle -- ) r-open error# 0= if clearFile // clear out clusters else ." File error " error# . then ; // deletes REMOVES directory entry, sets first byte to e5, // frees up file space by setting all clusters to 0 // not this is a pure directory operation, the file is not opened so // the file handle is not used : deleteFile ( string -- ) dir-search // search in current device, fills array as wel if >r r@ dir-entry // get address, save entry # start-clust@ dup // cluster # start for this entry erase-clusters // free up clusters in fat 0 swap fatClust! // and set current cluster to 0 &e5 r> dir-entry c! // spoil name, dir-entry loads dbuff d-sec Dsec! // store to card 0 error else ." file not found" 103 error then ; // gets the last cluster in a chain, given a starting cluster // writing : last-clust ( clust -- last ) begin dup fatClust@ dup &fff7 > if drop -1 else swap drop 0 then until ; // creates directory entry where sting is in the form fred.txt // a file does not exist at this present time so the file hanlde // is not used : createFile ( string -- ) dup dir-search if ." File exists use reset" 130 error then dot2pad // convert to padded string dir$ 11 move // save nfde // gets next free dir entry if // update fat with ffff in next available cluster 0 fatNF // get next free fat cluster &ffff over fatClust! // store to fat - start-clust on stack // directory operations, en# on stack from nfde swap dir-entry >r // convert entry to an address, loads Dbuff r@ start-clust! // store to start cluster dir$ r@ 11 move // name r@ storeDTC // store DT create r@ storeDT // store update dat and time 0 r> fileSize! // set file size to 0 d-sec Dsec! // store to card else ." card full" 131 error // abort then ; // given a logical sector it retruns a new file size based // on the maximum of either the existing size or the new // calculated size : newFS ( sect -- new-filesize ) 1+ // not sure why I need an extra 1 dirAdd filesize@ // get existing file size swap BytesSec d @ * max // return largest ; // random write, file must be opened first // writes contens of fatb to the given LOGICAL sector of that file // Ls2Phys returns - 1 if the existing cluster is // exceeded so this is used to create more space. // This is more complex than it looks, the function is to get next free // store to current and update current to what was the next free // Directory size is updated but not stored, this is done in close : w-random ( handle sect [fatb] -- ) swap => f# // make sure correct dir is used IsOpen f @ -1 > if 122 error then // abort if file not open dup Ls2Phys // see if within current range dup 0 < if // this is where new file space is created drop // -1 dup SectClust d @ / // get number of clusters, save size needed ltr dirAdd start-clust@ last-clust swap // get last cluster in chain for dup fatNF dup rot fatClust! // keep updating empty clusters next &ffff swap fatClust! // last one // re-calculate logical sector dup newFS dirAdd filesize! // new file size Ls2Phys sec! // save else // can write this to existing file space sec! // save it newFS dirAdd filesize! // new file size using logical sector then ; // closes an open file by writing current date and // file size : f-close ( file -- ) dup 0 devices between if => f# // current file handle IsOpen f @ -1 > if 122 error then // abort if file not open dirAdd storeDT // updata date and time dir! // put back to dir, wrandom updates file size 0 isOpen f ! // closed else ." handle wrong value" 145 error then ; // ================= Public interface ========================= : <0>f.create ( string handle -- -1|0 ) // creates and opens file over createFile f.open ; : <0>f.reset ( string handle -- ) w-reset ; // open and new : <0>f.del ( string -- ) deleteFile ; // deletes file : <0>f.close ( handle -- ) f-close ; // closes, updates date + size : <0>f.wrandom ( handle sector -- ) w-random ; // write random // : <0>f.flush ( handle -- ) => f# f-sec sec! ; // flush any unwritten memory // Use f.open for appending