BRFS.C 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. // Test implementation of Bart's RAM File System (BRFS)
  2. /*
  3. General Idea:
  4. - HDD for the average home user was <100MB until after 1990
  5. - SPI NOR Flash provides around the same amount of storage, with very little wear
  6. - Very fast reads in QSPI mode
  7. - However, writes are extremely slow and need to be performed in pages of 256 bytes (64 words)
  8. - FPGC has 64MiB RAM, which is a lot even for 32 bit addressable words
  9. - 32MiB is already more than enough for the FPGC in its current form
  10. - Use the other 32MiB as a fully in RAM filesystem
  11. - That initializes from SPI flash and writes back to Flash at a chosen time
  12. */
  13. /*
  14. Implementation Idea:
  15. - Use superblock for info/addresses, no hard-coded sizes!
  16. - Allows for different storage media, easier testing on tiny size, and more future proof
  17. */
  18. /*
  19. Implementation Details:
  20. --------------------------------------------------
  21. | superblock | FAT | Data+Dir blocks (same size) |
  22. --------------------------------------------------
  23. 16 word superblock:
  24. - (1) total blocks
  25. - (1) bytes per block
  26. - (10) label [1 char per word]
  27. - (1) brfs version
  28. - (3) reserved
  29. 8 word Dir entries:
  30. - (4) filename.ext [4 chars per word -> 16 chars total]
  31. - (1) modify date [to be implemented when RTC]
  32. - (1) flags [max 32 flags, from right to left: directory, hidden]
  33. - (1) 1st FAT idx
  34. - (1) file size [in words, not bytes]
  35. */
  36. /*
  37. Required operations:
  38. - Format
  39. - Create directory
  40. - Create file
  41. - Open file (allow multiple files open at once)
  42. - Close file (update dir entry and check/update all FAT entries)
  43. - Set cursor
  44. - Get cursor
  45. - Read file
  46. - Write file
  47. - Delete entire file (deleting part of file is not a thing)
  48. - Change directory
  49. */
  50. #define word char
  51. #include "LIB/MATH.C"
  52. #include "LIB/SYS.C"
  53. #include "LIB/STDLIB.C"
  54. #define BRFS_RAM_STORAGE_ADDR 0x600000
  55. #define SUPERBLOCK_SIZE 16
  56. word *brfs_ram_storage = (word*) BRFS_RAM_STORAGE_ADDR; // RAM storage of file system
  57. word brfs_current_dir = 0; // Current directory index, points to block in data section
  58. // 16 words long
  59. struct brfs_superblock
  60. {
  61. word total_blocks;
  62. word bytes_per_block;
  63. word label[10]; // 1 char per word
  64. word brfs_version;
  65. word reserved[3];
  66. };
  67. // 8 words long
  68. struct brfs_dir_entry
  69. {
  70. word filename[4]; // 4 chars per word
  71. word modify_date; // TBD when RTC added to FPGC
  72. word flags; // 32 flags, from right to left: directory, hidden
  73. word fat_idx; // idx of first FAT block
  74. word filesize; // file size in words, not bytes
  75. };
  76. word brfs_find_next_free_block(word* fat_addr, word blocks)
  77. {
  78. word i = 0;
  79. word* fat_ptr = fat_addr;
  80. while (i < blocks)
  81. {
  82. if (*fat_ptr == 0)
  83. {
  84. return i;
  85. }
  86. fat_ptr++;
  87. i++;
  88. }
  89. return -1;
  90. }
  91. word brfs_find_next_free_dir_entry(word* dir_addr, word dir_entries_max)
  92. {
  93. word i = 0;
  94. word* dir_ptr = dir_addr;
  95. while (i < dir_entries_max)
  96. {
  97. if (*dir_ptr == 0)
  98. {
  99. return i;
  100. }
  101. dir_ptr += sizeof(struct brfs_dir_entry);
  102. i++;
  103. }
  104. return -1;
  105. }
  106. void brfs_create_single_dir_entry(struct brfs_dir_entry* dir_entry, char* filename, word fat_idx, word filesize, word flags)
  107. {
  108. // Initialize to 0
  109. memset(dir_entry, 0, sizeof(*dir_entry));
  110. // Set filename
  111. char compressed_filename[4] = {0,0,0,0};
  112. strcompress(compressed_filename, filename);
  113. memcpy(&(dir_entry->filename), compressed_filename, sizeof(compressed_filename));
  114. // Set other fields
  115. dir_entry->fat_idx = fat_idx;
  116. dir_entry->flags = flags;
  117. dir_entry->filesize = filesize;
  118. }
  119. void brfs_init_directory(word* dir_addr, word dir_entries_max, word dir_fat_idx, word parent_fat_idx)
  120. {
  121. // Create . entry
  122. struct brfs_dir_entry dir_entry;
  123. brfs_create_single_dir_entry(&dir_entry, ".", dir_fat_idx, dir_entries_max*sizeof(struct brfs_dir_entry), 1);
  124. // Copy to first data entry
  125. memcpy(dir_addr, &dir_entry, sizeof(dir_entry));
  126. // Create .. entry
  127. brfs_create_single_dir_entry(&dir_entry, "..", parent_fat_idx, dir_entries_max*sizeof(struct brfs_dir_entry), 1);
  128. // Copy to second data entry
  129. memcpy(dir_addr+sizeof(dir_entry), &dir_entry, sizeof(dir_entry));
  130. // Set FAT table
  131. brfs_ram_storage[SUPERBLOCK_SIZE + dir_fat_idx] = -1;
  132. }
  133. // Creates hexdump like dump
  134. void brfs_dump_section(word* addr, word len, word linesize)
  135. {
  136. char buf[16];
  137. word i;
  138. for (i = 0; i < len; i++)
  139. {
  140. itoah(addr[i], buf);
  141. if (strlen(buf+2) == 1)
  142. uprintc('0');
  143. uprint(buf+2);
  144. uprintc(' ');
  145. // newline every linesize words
  146. // also print last linesize words as chars if alphanum
  147. if (i != 0 && MATH_modU(i+1, linesize) == 0)
  148. {
  149. uprint(" ");
  150. word j;
  151. for (j = i - (linesize-1); j < i+1; j++)
  152. {
  153. if (isalnum(addr[j]) || addr[j] == ' ')
  154. uprintc(addr[j]);
  155. else
  156. uprintc('.');
  157. }
  158. uprintc('\n');
  159. }
  160. }
  161. }
  162. void brfs_dump(word* ram_addr, word fatsize, word datasize)
  163. {
  164. // Superblock dump
  165. uprintln("Superblock:");
  166. brfs_dump_section(ram_addr, SUPERBLOCK_SIZE, 16);
  167. // FAT dump
  168. uprintln("\nFAT:");
  169. brfs_dump_section(ram_addr+SUPERBLOCK_SIZE, fatsize, 8);
  170. // Datablock dump
  171. uprintln("\nData:");
  172. brfs_dump_section(ram_addr+SUPERBLOCK_SIZE+fatsize, datasize, 32);
  173. uprintc('\n');
  174. }
  175. void brfs_format(word* ram_addr, word blocks, word bytes_per_block, char* label, word full_format)
  176. {
  177. // Create a superblock
  178. struct brfs_superblock superblock;
  179. // Initialize to 0
  180. memset(&superblock, 0, sizeof(superblock));
  181. // Set values of superblock
  182. superblock.total_blocks = blocks;
  183. superblock.bytes_per_block = bytes_per_block;
  184. strcpy(&(superblock.label), label);
  185. superblock.brfs_version = 1;
  186. // Copy superblock to head of ram addr
  187. memcpy(ram_addr, &superblock, sizeof(superblock));
  188. // Create FAT
  189. memset(ram_addr + SUPERBLOCK_SIZE, 0, blocks);
  190. // Create Data section
  191. if (full_format)
  192. {
  193. memset(ram_addr + SUPERBLOCK_SIZE + blocks, 0, blocks * bytes_per_block);
  194. }
  195. // Initialize root dir
  196. word dir_entries_max = bytes_per_block / sizeof(struct brfs_dir_entry);
  197. brfs_init_directory(ram_addr + SUPERBLOCK_SIZE + blocks, dir_entries_max, 0, 0);
  198. brfs_current_dir = 0;
  199. }
  200. /*
  201. * Creates directory in current directory
  202. */
  203. void brfs_create_directory(word* ram_addr, char* dirname)
  204. {
  205. struct brfs_superblock* superblock = (struct brfs_superblock*) ram_addr;
  206. // Find first free FAT block
  207. word next_free_block = brfs_find_next_free_block(ram_addr + SUPERBLOCK_SIZE, superblock->total_blocks);
  208. if (next_free_block == -1)
  209. {
  210. uprintln("No free blocks left!");
  211. return;
  212. }
  213. // Find first free dir entry
  214. word next_free_dir_entry = brfs_find_next_free_dir_entry(
  215. ram_addr + SUPERBLOCK_SIZE + superblock->total_blocks + (brfs_current_dir * superblock->bytes_per_block),
  216. superblock->bytes_per_block / sizeof(struct brfs_dir_entry)
  217. );
  218. if (next_free_dir_entry == -1)
  219. {
  220. uprintln("No free dir entries left!");
  221. return;
  222. }
  223. // Create dir entry
  224. struct brfs_dir_entry new_entry;
  225. brfs_create_single_dir_entry(&new_entry, dirname, next_free_block, 0, 1);
  226. // Copy dir entry to first free dir entry
  227. memcpy(
  228. ram_addr + SUPERBLOCK_SIZE + superblock->total_blocks + (brfs_current_dir * superblock->bytes_per_block) + (next_free_dir_entry * sizeof(struct brfs_dir_entry)),
  229. &new_entry,
  230. sizeof(new_entry)
  231. );
  232. // Initialize directory
  233. word dir_entries_max = superblock->bytes_per_block / sizeof(struct brfs_dir_entry);
  234. brfs_init_directory(
  235. ram_addr + SUPERBLOCK_SIZE + superblock->total_blocks + (next_free_block * superblock->bytes_per_block),
  236. dir_entries_max,
  237. next_free_block,
  238. brfs_current_dir
  239. );
  240. }
  241. /*
  242. * Creates an empty file in current directory
  243. */
  244. void brfs_create_file(word* ram_addr, char* filename)
  245. {
  246. struct brfs_superblock* superblock = (struct brfs_superblock*) ram_addr;
  247. // Find first free FAT block
  248. word next_free_block = brfs_find_next_free_block(ram_addr + SUPERBLOCK_SIZE, superblock->total_blocks);
  249. if (next_free_block == -1)
  250. {
  251. uprintln("No free blocks left!");
  252. return;
  253. }
  254. // Find first free dir entry
  255. word next_free_dir_entry = brfs_find_next_free_dir_entry(
  256. ram_addr + SUPERBLOCK_SIZE + superblock->total_blocks + (brfs_current_dir * superblock->bytes_per_block),
  257. superblock->bytes_per_block / sizeof(struct brfs_dir_entry)
  258. );
  259. if (next_free_dir_entry == -1)
  260. {
  261. uprintln("No free dir entries left!");
  262. return;
  263. }
  264. // Create file entry
  265. struct brfs_dir_entry new_entry;
  266. brfs_create_single_dir_entry(&new_entry, filename, next_free_block, 0, 0);
  267. // Copy dir entry to first free dir entry
  268. memcpy(
  269. ram_addr + SUPERBLOCK_SIZE + superblock->total_blocks + (brfs_current_dir * superblock->bytes_per_block) + (next_free_dir_entry * sizeof(struct brfs_dir_entry)),
  270. &new_entry,
  271. sizeof(new_entry)
  272. );
  273. // Initialize file by setting data to 0
  274. memset(
  275. ram_addr + SUPERBLOCK_SIZE + superblock->total_blocks + (next_free_block * superblock->bytes_per_block),
  276. 0,
  277. superblock->bytes_per_block
  278. );
  279. // Update FAT
  280. ram_addr[SUPERBLOCK_SIZE + next_free_block] = -1;
  281. }
  282. int main()
  283. {
  284. // Clear UART screen:
  285. uprintc(0x1B);
  286. uprintc(0x5B);
  287. uprintc(0x32);
  288. uprintc(0x4A);
  289. uprintln("------------------------");
  290. uprintln("BRFS test implementation");
  291. uprintln("------------------------");
  292. word blocks = 8;
  293. word bytes_per_block = 32;
  294. word full_format = 1;
  295. brfs_format(brfs_ram_storage, blocks, bytes_per_block, "Label", full_format);
  296. brfs_create_file(brfs_ram_storage, "file1");
  297. brfs_create_directory(brfs_ram_storage, "dir1");
  298. brfs_dump(brfs_ram_storage, blocks, blocks*bytes_per_block);
  299. return 'q';
  300. }
  301. void interrupt()
  302. {
  303. // handle all interrupts
  304. word i = getIntID();
  305. switch(i)
  306. {
  307. case INTID_TIMER1:
  308. timer1Value = 1; // notify ending of timer1
  309. break;
  310. case INTID_TIMER2:
  311. break;
  312. case INTID_UART0:
  313. break;
  314. case INTID_GPU:
  315. break;
  316. case INTID_TIMER3:
  317. break;
  318. case INTID_PS2:
  319. break;
  320. case INTID_UART1:
  321. break;
  322. case INTID_UART2:
  323. break;
  324. }
  325. }