libpropeller
Making PropellerGCC Easier
 All Classes Files Functions Variables Enumerations Enumerator Macros Pages
sd.h
Go to the documentation of this file.
1 #ifndef LIBPROPELLER_SD_H_
2 #define LIBPROPELLER_SD_H_
3 
4 #include <propeller.h>
6 
7 
8 #define RET_IF_ERROR_NULL if(HasError()){return NULL;}
9 #define RET_IF_ERROR if(HasError()){return;}
10 #define THROW_NULL(value) {SetErrorCode((value)); return NULL;}
11 //#define THROW_FALSE(value) {SetErrorCode((value)); return false;}
12 #define THROW(value) {SetErrorCode((value)); return;}
13 
14 
62 class SD {
63 public:
64  static const int kNoError = SDSafeSPI::kNoError;
65 
66  // Mount Errors
67  static const int kErrorNotFatVolume = -20;
68  static const int kErrorBadBytesPerSector = -21;
69  static const int kErrorBadSectorsPerCluster = -22;
70  static const int kErrorNotTwoFats = -23;
71  static const int kErrorBadFatSignature = -24;
72  static const int kErrorBufNotLongwordAligned = -512;
73 
74  //Open Errors
75  static const int kErrorFileNotFound = -1; //TODO(SRLM): For some reason, if I change the value of this error code then things slow way down. Why???
76  static const int kErrorNoEmptyDirectoryEntry = -2;
77  static const int kErrorBadArgument = -3;
78  static const int kErrorNoWritePermission = -6;
79  static const int kErrorEofWhileFollowingChain = -7;
80  static const int kErrorBadClusterValue = -9;
81  static const int kErrorBadClusterNumber = -26;
82  static const int kErrorFileNotOpenForWriting = -27;
83 
84  // SdSafeSPI Errors
89  // These errors are negated since they are thrown as negative in ASM section.
92  // NOTE: errors -128 to -255 are reserved for reporting R1 response errors (SRLM ???)
95 
98  ~SD() {
99  Unmount();
100  }
101 
111  void Mount(const int basepin) {
112  Mount(basepin, (basepin + 1), (basepin + 2), (basepin + 3));
113  }
114 
122  void Mount(const int pin_do, const int pin_clk, const int pin_di, const int pin_cs) {
123  if (file_date_time_ == 0) {
124  SetDate(2010, 1, 1, 0, 0, 0);
125  }
126 
127  //SRLM Addition: check to make sure that Buf and Buf2 are longword aligned.
128  //Theoretically, this should have no runtime cost, but it looks like in CMM
129  //and -Os it takes 16 bytes. It can be commented out if you're sure that
130  //Buf and Buf2 are longword aligned.
131  if ((((int) buffer_1_) & 0b11) != 0)
133  if ((((int) buffer_2_) & 0b11) != 0)
135 
136  Unmount();
137  RET_IF_ERROR;
138 
139  sd_spi_.Start(pin_do, pin_clk, pin_di, pin_cs);
140  RET_IF_ERROR;
141 
142  last_read_ = (-1);
143  dirty_ = 0;
144  sd_spi_.ReadBlock(0, (char *) (&buffer_1_));
145  RET_IF_ERROR;
146 
147  int start;
148  if (GetFilesystemType() != kFileSystemUnknown) {
149  start = 0;
150  } else {
151  start = ReverseBytesInLong(((char *) (&buffer_1_) + 0x1C6));
152  sd_spi_.ReadBlock(start, buffer_1_);
153  RET_IF_ERROR;
154  }
155  filesystem_ = GetFilesystemType();
156  if (filesystem_ == kFileSystemUnknown) {
158  }
159  if (ReverseBytesInWord(((char *) (&buffer_1_) + 11)) != kSectorSize) {
161  }
162  int sectors_per_cluster = buffer_1_[13];
163  if (sectors_per_cluster & (sectors_per_cluster - 1)) {
165  }
166  cluster_shift_ = 0;
167  while (sectors_per_cluster > 1) {
168  (cluster_shift_++);
169  sectors_per_cluster = (Shr__(sectors_per_cluster, 1));
170  }
171  sectors_per_cluster = (1 << cluster_shift_);
172  cluster_size_ = (kSectorSize << cluster_shift_);
173  int reserved = ReverseBytesInWord(((char *) (&buffer_1_) + 14));
174  if (buffer_1_[16] != 2) {
176  }
177  int sectors = ReverseBytesInWord(((char *) (&buffer_1_) + 19));
178  if (sectors == 0) {
179  sectors = ReverseBytesInLong(((char *) (&buffer_1_) + 32));
180  }
181  fat1_ = (start + reserved);
182  if (filesystem_ == kFileSystemFAT32) {
183  int root_entries = (16 << cluster_shift_);
184  sectors_per_fat_ = ReverseBytesInLong(((char *) (&buffer_1_) + 36));
185  data_region_ = ((fat1_ + (2 * sectors_per_fat_)) - (2 * sectors_per_cluster));
186  root_directory_ = ((data_region_ + (ReverseBytesInWord(((char *) (&buffer_1_) + 44)) << cluster_shift_)) << kSectorShift);
187  root_directory_end_ = (root_directory_ + (root_entries << kDirectoryShift));
188  end_of_chain_ = 268435440;
189  } else {
190  int root_entries = ReverseBytesInWord(((char *) (&buffer_1_) + 17));
191  sectors_per_fat_ = ReverseBytesInWord(((char *) (&buffer_1_) + 22));
192  root_directory_ = ((fat1_ + (2 * sectors_per_fat_)) << kSectorShift);
193  root_directory_end_ = (root_directory_ + (root_entries << kDirectoryShift));
194  data_region_ = ((1 + (Shr__((root_directory_end_ - 1), kSectorShift))) - (2 * sectors_per_cluster));
195  end_of_chain_ = 65520;
196  }
197  if (ReverseBytesInWord(((char *) (&buffer_1_) + 510)) != 43605) {
199  }
200  total_clusters_ = (Shr__(((sectors - data_region_) + start), cluster_shift_));
201  }
202 
205  void Unmount(void) {
206  Close();
207  sd_spi_.Stop();
208  }
209 
229  void Open(const char * filename, const char file_mode) {
230  Close();
231  RET_IF_ERROR;
232 
233 
234  char cleaned_filename[11];
235 
236  int I = 0;
237  while (I < 8 && filename[0] != '\0' && filename[0] != '.') {
238  cleaned_filename[(I++)] = ConvertToUppercase((filename++)[0]);
239  }
240  while (I < 8) {
241  cleaned_filename[(I++)] = ' ';
242  }
243  while (filename[0] != '\0' && filename[0] != '.') {
244  (filename++);
245  }
246  if ((filename)[0] == '.') {
247  (filename++);
248  }
249  while ((I < 11) && ((filename)[0])) {
250  cleaned_filename[(I++)] = ConvertToUppercase((filename++)[0]);
251  }
252  while (I < 11) {
253  cleaned_filename[(I++)] = ' ';
254  }
255  int sentinel = 0;
256  int free_entry = 0;
257 
258 
259 
260  /* WARNING: Bug may be present if this loop should be working from high to low! The original
261  Spin code had the following:
262 
263  repeat dirptr from rootdir to rootdirend - DIRSIZE step DIRSIZE
264 
265  Which may go either up or down, depending on the arguments.
266  */
267  for (int directory_pointer = root_directory_;
268  directory_pointer <= root_directory_end_ - kDirectorySize;
269  directory_pointer += kDirectorySize) {
270  char * disk_filename = ReadByte(directory_pointer);
271  RET_IF_ERROR;
272 
273  if ((free_entry == 0) && (((disk_filename)[0] == 0) || ((disk_filename)[0] == 0xe5))) {
274  free_entry = directory_pointer;
275  }
276  if ((disk_filename)[0] == 0) {
277  sentinel = directory_pointer;
278  break;
279  }
280 
281  //Match the filename
282  int i = 0;
283  for (; i <= 10; i++) {
284  if (cleaned_filename[i] != (disk_filename)[i]) {
285  break;
286  }
287  }
288 
289  // If they match, then do the action
290  if ((i == 11) && (0 == ((disk_filename)[0x0b] & 0x18))) { // this always returns
291  current_cluster_ = ReverseBytesInWord((disk_filename + 0x1a));
292  if (filesystem_ == kFileSystemFAT32) {
293  current_cluster_ = (current_cluster_ + (ReverseBytesInWord((disk_filename + 0x14)) << 16));
294  }
295  first_cluster_of_file_ = current_cluster_;
296  total_filesize_ = ReverseBytesInLong((disk_filename + 0x1c));
297 
298  //Mode is Read
299  if (file_mode == 'r') {
300  OpenForRead();
301  return;
302  }
303  if ((disk_filename)[11] & 0xd9) {
305  }
306 
307  //Mode is Delete
308  if (file_mode == 'd') {
309  OpenForDelete(disk_filename);
310  return;
311  } else if (file_mode == 'w') {
312  OpenForWrite(disk_filename, directory_pointer);
313  return;
314  } else if (file_mode == 'a') {
315  OpenForAppend(directory_pointer);
316  return;
317  } else {
319  }
320 
321  }
322  }
323 
324  if (file_mode == 'd') { //If we got here it's because we didn't find anything to delete.
325  return;
326  }
327 
328  if ((file_mode != 'w') && (file_mode != 'a')) {
330  }
331  directory_entry_position_ = free_entry;
332  if (directory_entry_position_ == 0) {
334  }
335 
336  // write (or new append): create valid directory entry
337  char * S = ReadByte(directory_entry_position_);
338  RET_IF_ERROR;
339  memset((void *) S, 0, kDirectorySize);
340  memcpy((void *) S, (void *) &cleaned_filename, 11);
341  WriteReversedWord((S + 0x1a), 0);
342  WriteReversedWord((S + 0x14), 0);
343  WriteReversedLong((S + 0x0e), file_date_time_); // write create time and date
344  WriteReversedLong((S + 0x16), file_date_time_); // write last modified date and time
345  if ((directory_entry_position_ == sentinel) && ((directory_entry_position_ + kDirectorySize) < root_directory_end_)) {
346  WriteReversedWord(ReadByte((directory_entry_position_ + kDirectorySize)), 0);
347  }
348  FlushIfDirty();
349  RET_IF_ERROR;
350 
351  cluster_write_offset_ = 0;
352  current_cluster_ = 0;
353  buffer_end_ = kSectorSize;
354  }
355 
363  void Close(void) {
364  ClearError();
365  if (directory_entry_position_) {
366  Flush();
367  RET_IF_ERROR;
368  }
369  current_buffer_location_ = 0;
370  buffer_end_ = 0;
371  total_filesize_ = 0;
372  seek_position_ = 0;
373  remaining_cluster_bytes_ = 0;
374  cluster_write_offset_ = 0;
375  directory_entry_position_ = 0;
376  current_cluster_ = 0;
377  first_cluster_of_file_ = 0;
378  sd_spi_.ReleaseCard();
379  }
380 
386  int Get(void) {
387  int T;
388  if (current_buffer_location_ >= buffer_end_) {
389  T = FillBuffer();
391  if (T <= 0) {
392  return (-1);
393  }
394  }
395  return buffer_1_[(current_buffer_location_++)];
396  }
397 
408  int Get(char * read_buffer, int bytes_to_read_count) {
409  int T;
410  int R = 0;
411  while (bytes_to_read_count > 0) {
412  if (current_buffer_location_ >= buffer_end_) {
413  T = FillBuffer();
414  if (T <= 0) {
415  if (R > 0) {
416  return R;
417  }
418  return T;
419  }
420  }
421  T = (Min__((buffer_end_ - current_buffer_location_), bytes_to_read_count));
422  if (((T | (int) read_buffer) | current_buffer_location_) & 0x3) {
423  memcpy((void *) read_buffer, (void *) (void *) (((int) (&buffer_1_) + current_buffer_location_)), 1 * (T));
424  } else {
425  memmove((void *) read_buffer, (void *) (void *) (((int) (&buffer_1_) + current_buffer_location_)), 4 * ((Shr__(T, 2))));
426  }
427  current_buffer_location_ = (current_buffer_location_ + T);
428  R = (R + T);
429  read_buffer = (read_buffer + T);
430  bytes_to_read_count = (bytes_to_read_count - T);
431  }
432  return R;
433  }
434 
440  int Put(const char C) {
441  if (current_buffer_location_ == kSectorSize) {
442  if (FlushBuffer(kSectorSize, 0) < 0) {
443  return (-1);
444  }
445  }
446  buffer_1_[(current_buffer_location_++)] = C;
447  return 0;
448  }
449 
457  int Put(const char * B) {
458  return Put(B, strlen(B));
459  }
460 
469  int Put(const char * buffer, int byte_count) {
470  int total_bytes_written = 0;
471  while (byte_count > 0) {
472  if (current_buffer_location_ >= buffer_end_) {
473  FlushBuffer(current_buffer_location_, 0);
475 
476  }
477  int bytes_to_write = (Min__((buffer_end_ - current_buffer_location_), byte_count));
478  memcpy((void *) (void *) (((int) (&buffer_1_) + current_buffer_location_)), (void *) buffer, bytes_to_write);
479 
480  total_bytes_written = (total_bytes_written + bytes_to_write);
481  current_buffer_location_ = (current_buffer_location_ + bytes_to_write);
482  buffer = (buffer + bytes_to_write);
483  byte_count = (byte_count - bytes_to_write);
484  }
485  return total_bytes_written;
486  }
487 
493  void OpenRootDirectory(void) {
494  Close();
495  RET_IF_ERROR;
496 
497  int off = (root_directory_ - (data_region_ << kSectorShift));
498  current_cluster_ = (Shr__(off, (cluster_shift_ + kSectorShift)));
499  seek_position_ = (off - (current_cluster_ << (cluster_shift_ + kSectorShift)));
500  remaining_cluster_bytes_ = (root_directory_end_ - root_directory_);
501  total_filesize_ = (seek_position_ + remaining_cluster_bytes_);
502  }
503 
511  bool NextFile(char * filename) {
512  while (true) {
513  if (current_buffer_location_ >= buffer_end_) {
514  if (FillBuffer() < 0) {
515  return false;
516  }
517  if (((Shr__(seek_position_, kSectorShift)) & ((1 << cluster_shift_) - 1)) == 0) {
518  (current_cluster_++);
519  }
520  }
521 
522  unsigned char * at = (unsigned char *) ((int) &buffer_1_ + current_buffer_location_);
523 
524  if ((at)[0] == 0) {
525  return false;
526  }
527  current_buffer_location_ = (current_buffer_location_ + kDirectorySize);
528  if (((at)[0] != 0xe5)
529  && (((at)[0x0b] & 0x18) == 0)) {
530  char * lns = filename;
531 
532  for (int i = 0; i <= 10; i++) {
533  filename[0] = (at)[i];
534  filename++;
535  if (at[i] != ' ') {
536  lns = filename;
537  }
538  if (i == 7 || i == 10) {
539  filename = lns;
540  if (i == 7) {
541  filename[0] = '.';
542  filename++;
543  }
544  }
545  }
546  filename[0] = 0;
547  return true;
548  }
549  }
550  }
551 
561  int Seek(const int position) {
562  if (((directory_entry_position_) || (position < 0)) || (position > total_filesize_)) {
563  return (-1);
564  }
565  int delta = ((seek_position_ - buffer_end_) & (-cluster_size_));
566  if (position < delta) {
567  current_cluster_ = first_cluster_of_file_;
568  remaining_cluster_bytes_ = (Min__(cluster_size_, total_filesize_));
569  seek_position_ = 0;
570  current_buffer_location_ = 0;
571  buffer_end_ = 0;
572  delta = 0;
573  }
574  while (position >= (delta + cluster_size_)) {
575  current_cluster_ = NextCluster();
577 
578  seek_position_ = (seek_position_ + cluster_size_);
579  delta = (delta + cluster_size_);
580  remaining_cluster_bytes_ = (Min__(cluster_size_, (total_filesize_ - seek_position_)));
581  current_buffer_location_ = 0;
582  buffer_end_ = 0;
583  }
584  if (buffer_end_ == 0
585  || position < (seek_position_ - buffer_end_)
586  || position >= (seek_position_ - buffer_end_) + kSectorSize
587  ) { // must change buffer
588  //Warning: this section does not seem to be covered by unit tests. What's required for coverage?
589  int delta_2 = (seek_position_ + remaining_cluster_bytes_);
590  seek_position_ = (position & -kSectorSize);
591  remaining_cluster_bytes_ = (delta_2 - seek_position_);
592  FillBuffer();
594  }
595  current_buffer_location_ = position & (kSectorSize - 1);
596  return 0;
597  }
598 
616  int SetDate(const int year, const int month, const int day,
617  const int hour, const int minute, const int second) {
618  file_date_time_ = ((year - 1980) << 25) + (month << 21) + (day << 16);
619  file_date_time_ += (hour << 11) + (minute << 5) + (second >> 1);
620  return file_date_time_;
621  }
622 
628  bool HasError(void) const {
629  return (error_ != kNoError) || sd_spi_.HasError();
630  }
631 
634  void ClearError(void) {
635  error_ = kNoError;
636  sd_spi_.ClearError();
637  }
638 
643  int GetError(void) const {
644  if (error_ != kNoError) {
645  return error_;
646  } else {
647  return sd_spi_.GetError();
648  }
649  }
650 
654  int GetClusterSize(void) const {
655  return cluster_size_;
656  }
657 
664  int GetClusterCount(void) const {
665  return total_clusters_;
666  }
667 
672  int GetFilesize(void) const {
673  return total_filesize_;
674  }
675 
676 private:
677 
678  // Note: these filesystem numbers should not be changed!
679  static const int kFileSystemUnknown = 0;
680  static const int kFileSystemFAT16 = 1;
681  static const int kFileSystemFAT32 = 2;
682 
683 
684  static const int kSectorSize = 512;
685  static const int kSectorShift = 9;
686  static const int kDirectorySize = 32;
687  static const int kDirectoryShift = 5;
688 
689  SDSafeSPI sd_spi_;
690 
691  int current_cluster_;
692  int total_filesize_;
693  int seek_position_;
694  int remaining_cluster_bytes_;
695  int current_buffer_location_;
696  int buffer_end_; // The last valid character (read) or free position (write)
697  int directory_entry_position_;
698  int cluster_write_offset_;
699  int last_fat_entry_;
700  int first_cluster_of_file_;
701 
702  int filesystem_;
703  int root_directory_;
704  int root_directory_end_;
705  int data_region_;
706  int cluster_shift_;
707  int cluster_size_;
708  int fat1_;
709  int total_clusters_;
710  int sectors_per_fat_;
711  int end_of_chain_;
712  int file_date_time_;
713  int last_read_;
714  int dirty_;
715 
716 
717  int error_;
718 
719  /*
720  Buffering: two sector buffers. These two buffers must be longword
721  aligned! To ensure this, make sure they are the first byte variables
722  defined in this object.
723  *
724  * These buffers don't seem to need to be volatile (all unit tests pass
725  * whether they are or not), but for some reason the code seems to run 4%
726  * faster if they are declared volatile. So, here they are.
727  */
728  char buffer_1_[512];
729  char buffer_2_[512];
730 
735  void SetErrorCode(const int abort_code) {
736  error_ = abort_code;
737  }
738 
742  void WriteBlock(const int block_index, char * buffer_address) {
743  sd_spi_.WriteBlock(block_index, buffer_address);
744  RET_IF_ERROR;
745  if (block_index >= fat1_) {
746  if (block_index < (fat1_ + sectors_per_fat_)) {
747  sd_spi_.WriteBlock(block_index + sectors_per_fat_, buffer_address);
748  RET_IF_ERROR;
749  }
750  }
751  }
752 
753  /* If the metadata block is dirty, write it out.
754  */
755  void FlushIfDirty(void) {
756  if (dirty_) {
757  WriteBlock(last_read_, buffer_2_);
758  RET_IF_ERROR;
759  dirty_ = 0;
760  }
761  }
762 
765  void ReadBlock(const int block_index) {
766  if (block_index != last_read_) {
767  FlushIfDirty();
768  RET_IF_ERROR;
769  sd_spi_.ReadBlock(block_index, buffer_2_);
770  RET_IF_ERROR;
771  last_read_ = block_index;
772  }
773  }
774 
777  int ReverseBytesInWord(const char * data) const {
778  return ((data)[0] + ((data)[1] << 8));
779  }
780 
783  int ReverseBytesInLong(const char * data) const {
784  return (ReverseBytesInWord(data) + (ReverseBytesInWord((data + 2)) << 16));
785  }
786 
789  int ReverseBytesInCluster(const char * cluster) const {
790  if (filesystem_ == kFileSystemFAT16) {
791  return ReverseBytesInWord(cluster);
792  } else {
793  return ReverseBytesInLong(cluster);
794  }
795  }
796 
799  void WriteReversedWord(char * result, const int data) {
800  result++[0] = data;
801  result[0] = data >> 8;
802  dirty_ = 1;
803  }
804 
807  void WriteReversedLong(char * result, const int data) {
808  WriteReversedWord(result, data);
809  WriteReversedWord(result + 2, data >> 16);
810  }
811 
814  void WriteReversedCluster(char * result, const int data) {
815  // Write a cluster entry.
816  if (filesystem_ == kFileSystemFAT16) {
817  WriteReversedWord(result, data);
818  } else {
819  WriteReversedLong(result, data);
820  }
821  }
822 
823  int GetFilesystemType(void) {
824  const int kFAT1 = 'F' + ('A' << 8) + ('T' << 16) + ('1' << 24);
825  const int kFAT3 = 'F' + ('A' << 8) + ('T' << 16) + ('3' << 24);
826 
827  if ((ReverseBytesInLong(&buffer_1_[0x36]) == kFAT1) && (buffer_1_[58] == '6')) {
828  return kFileSystemFAT16;
829  }
830  if ((ReverseBytesInLong(&buffer_1_[0x52]) == kFAT3) && (buffer_1_[86] == '2')) {
831  return kFileSystemFAT32;
832  }
833  return kFileSystemUnknown;
834  }
835 
838  char * ReadByte(const int Byteloc) {
839  ReadBlock((Shr__(Byteloc, kSectorShift)));
841 
842  return ((char *) (&buffer_2_) + (Byteloc & 0x1ff));
843  }
844 
847  char * ReadFAT(const int Clust) {
848  last_fat_entry_ = ((fat1_ << kSectorShift) + (Clust << filesystem_));
849  return ReadByte(last_fat_entry_);
850  }
851 
854  int FollowFATChain(void) {
855  char * temp = ReadFAT(current_cluster_);
857  cluster_write_offset_ = last_fat_entry_;
858  return ReverseBytesInCluster(temp);
859  }
860 
863  int NextCluster(void) {
864  int result = FollowFATChain();
866  if ((result < 2) || (result >= total_clusters_)) {
868  }
869  return result;
870  }
871 
874  void FreeClusters(int cluster) {
875  while (cluster < end_of_chain_) {
876  if (cluster < 2) {
878  }
879  char * byte_pointer = ReadFAT(cluster);
880  RET_IF_ERROR;
881 
882  cluster = ReverseBytesInCluster(byte_pointer);
883  WriteReversedCluster(byte_pointer, 0);
884  }
885  FlushIfDirty();
886  RET_IF_ERROR;
887  }
888 
889  /*
890  This is just a pass-through function to allow the block layer
891  to tristate the I/O pins to the card.
892  */
893  void Release(void) {
894  sd_spi_.ReleaseCard();
895  }
896 
899  int CalculateCurrentBlockAddress(void) const {
900  return (current_cluster_ << cluster_shift_) +data_region_
901  + (Shr__(seek_position_, kSectorShift) & ((1 << cluster_shift_) - 1));
902 
903  }
904 
907  char ConvertToUppercase(const char character) const {
908  if (('a' <= character) && (character <= 'z')) {
909  return (character - 32);
910  }
911  return character;
912  }
913 
916  int FlushBuffer(int r_cnt, const int flush_metadata) {
917  if (directory_entry_position_ == 0) {
919  }
920  if (r_cnt > 0) { // must *not* allocate cluster if flushing an empty buffer
921  if (remaining_cluster_bytes_ < kSectorSize) {
922  // find a new cluster could be anywhere! If possible, stay on the
923  // same page used for the last cluster.
924  int Newcluster = (-1);
925  int Cluststart = (current_cluster_ & (~((Shr__(kSectorSize, filesystem_)) - 1)));
926  int Count = 2;
927  while (1) {
928  ReadFAT(Cluststart);
930 
931  int I;
932  {
933  int _limit__0025 = (kSectorSize - (1 << filesystem_));
934  int _step__0026 = (1 << filesystem_);
935  I = 0;
936  if (I >= _limit__0025) _step__0026 = -_step__0026;
937  do {
938  if (buffer_2_[I] == 0) {
939  if (ReverseBytesInCluster(((char *) (&buffer_2_) + I)) == 0) {
940  Newcluster = (Cluststart + (Shr__(I, filesystem_)));
941  if (Newcluster >= total_clusters_) {
942  Newcluster = (-1);
943  }
944  break;
945  }
946  }
947  I = (I + _step__0026);
948  } while (((_step__0026 > 0) && (I <= _limit__0025)) || ((_step__0026 < 0) && (I >= _limit__0025)));
949  }
950  if (Newcluster > 1) {
951  WriteReversedCluster(((char *) (&buffer_2_) + I), (end_of_chain_ + 15));
952  if (cluster_write_offset_ == 0) {
953  WriteReversedWord((ReadByte(directory_entry_position_) + 26), Newcluster);
954  cluster_write_offset_ = (directory_entry_position_ & (kSectorSize - filesystem_));
955  WriteReversedLong((((char *) (&buffer_2_) + cluster_write_offset_) + 28), (seek_position_ + current_buffer_location_));
956  if (filesystem_ == kFileSystemFAT32) {
957  WriteReversedWord((((char *) (&buffer_2_) + cluster_write_offset_) + 20), (Shr__(Newcluster, 16)));
958  }
959  } else {
960  WriteReversedCluster(ReadByte(cluster_write_offset_), Newcluster);
961  }
962  cluster_write_offset_ = (last_fat_entry_ + I);
963  current_cluster_ = Newcluster;
964  remaining_cluster_bytes_ = cluster_size_;
965  break;
966  } else {
967  Cluststart = (Cluststart + (Shr__(kSectorSize, filesystem_)));
968  if (Cluststart >= total_clusters_) {
969  Cluststart = 0;
970  (Count--);
971  if (r_cnt < 0) {
972  r_cnt = -5; //No space left on device
973  break;
974  }
975  }
976  }
977  }
978  }
979  if (remaining_cluster_bytes_ >= kSectorSize) {
980  sd_spi_.WriteBlock(CalculateCurrentBlockAddress(), (char *) (&buffer_1_));
982 
983  if (r_cnt == kSectorSize) { // full buffer, clear it
984  seek_position_ = (seek_position_ + r_cnt);
985  remaining_cluster_bytes_ = (remaining_cluster_bytes_ - r_cnt);
986  current_buffer_location_ = 0;
987  buffer_end_ = r_cnt;
988  }
989  }
990  }
991  if ((r_cnt < 0) || (flush_metadata)) { // update metadata even if error
992  ReadBlock((Shr__(directory_entry_position_, kSectorShift))); // flushes unwritten FAT too
994 
995  WriteReversedLong((((char *) (&buffer_2_) + (directory_entry_position_ & (kSectorSize - filesystem_))) + 28), (seek_position_ + current_buffer_location_));
996  FlushIfDirty();
998  }
999  if (r_cnt < 0) {
1000  THROW_NULL(r_cnt);
1001  }
1002  return r_cnt;
1003  }
1004 
1007  int Flush(void) {
1008  return FlushBuffer(current_buffer_location_, 1);
1009  }
1010 
1013  int FillBuffer(void) {
1014  if (seek_position_ >= total_filesize_) {
1015  return -1;
1016  }
1017  if (remaining_cluster_bytes_ == 0) {
1018  current_cluster_ = NextCluster();
1019  if (current_cluster_ < 0) {
1020  return current_cluster_;
1021  }
1022  remaining_cluster_bytes_ = (Min__(cluster_size_, (total_filesize_ - seek_position_)));
1023  }
1024  sd_spi_.ReadBlock(CalculateCurrentBlockAddress(), buffer_1_);
1026 
1027  int bytes_read = kSectorSize;
1028  if ((seek_position_ + bytes_read) >= total_filesize_) {
1029  bytes_read = (total_filesize_ - seek_position_);
1030  }
1031  seek_position_ = (seek_position_ + bytes_read);
1032  remaining_cluster_bytes_ = (remaining_cluster_bytes_ - bytes_read);
1033  current_buffer_location_ = 0;
1034  buffer_end_ = bytes_read;
1035  return bytes_read;
1036  }
1037 
1041  void OpenForRead(void) {
1042  remaining_cluster_bytes_ = (Min__(cluster_size_, total_filesize_));
1043  }
1044 
1049  void OpenForDelete(char * string) {
1050  WriteReversedWord(string, 0xe5);
1051  if (current_cluster_) {
1052  FreeClusters(current_cluster_);
1053  RET_IF_ERROR;
1054  }
1055  FlushIfDirty();
1056  RET_IF_ERROR;
1057  }
1058 
1061  void OpenForWrite(char * string, const int dir_pointer) {
1062  WriteReversedWord((string + 0x1a), 0);
1063  WriteReversedWord((string + 0x14), 0);
1064  WriteReversedLong((string + 0x1c), 0);
1065  cluster_write_offset_ = 0;
1066  directory_entry_position_ = dir_pointer;
1067  if (current_cluster_) {
1068  FreeClusters(current_cluster_);
1069  RET_IF_ERROR;
1070 
1071  }
1072  buffer_end_ = kSectorSize;
1073  current_cluster_ = 0;
1074  total_filesize_ = 0;
1075  remaining_cluster_bytes_ = 0;
1076  }
1077 
1084  void OpenForAppend(const int dir_pointer) {
1085  // this code will eventually be moved to seek
1086  remaining_cluster_bytes_ = total_filesize_;
1087  int free_entry = cluster_size_;
1088  if (current_cluster_ >= end_of_chain_) {
1089  current_cluster_ = 0;
1090  }
1091  while (remaining_cluster_bytes_ > free_entry) {
1092  if (current_cluster_ < 2) {
1094  }
1095  current_cluster_ = NextCluster();
1096  RET_IF_ERROR;
1097 
1098  remaining_cluster_bytes_ = (remaining_cluster_bytes_ - free_entry);
1099  }
1100  seek_position_ = (total_filesize_ & !(kSectorSize - 1));
1101  buffer_end_ = kSectorSize;
1102  current_buffer_location_ = (remaining_cluster_bytes_ & 0x1ff);
1103  cluster_write_offset_ = 0;
1104  directory_entry_position_ = dir_pointer;
1105  if (current_buffer_location_) {
1106  sd_spi_.ReadBlock(CalculateCurrentBlockAddress(), (char *) (&buffer_1_));
1107  RET_IF_ERROR;
1108 
1109  remaining_cluster_bytes_ = (free_entry - (seek_position_ & (free_entry - 1)));
1110  } else {
1111  if ((current_cluster_ < 2) || (remaining_cluster_bytes_ == free_entry)) {
1112  remaining_cluster_bytes_ = 0;
1113  } else {
1114  remaining_cluster_bytes_ = (free_entry - (seek_position_ & (free_entry - 1)));
1115  }
1116  }
1117  if (current_cluster_ >= 2) {
1118  FollowFATChain();
1119  RET_IF_ERROR;
1120 
1121  }
1122  }
1123 
1130  static int Min__(const int a, const int b) {
1131  return a < b ? a : b;
1132  }
1133 
1140  static int Shr__(const unsigned int a, const unsigned int b) {
1141  return (a >> b);
1142  }
1143 
1144 };
1145 
1146 #endif // LIBPROPELLER_SD_H_