123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- // Test implementation of Bart's RAM File System (BRFS)
- /*
- General Idea:
- - HDD for the average home user was <100MB until after 1990
- - SPI NOR Flash provides around the same amount of storage, with very little wear
- - Very fast reads in QSPI mode
- - However, writes are extremely slow and need to be performed in pages of 256 bytes (64 words)
- - FPGC has 64MiB RAM, which is a lot even for 32 bit addressable words
- - 32MiB is already more than enough for the FPGC in its current form
- - Use the other 32MiB as a fully in RAM filesystem
- - That initializes from SPI flash and writes back to Flash at a chosen time
- */
- /*
- Implementation Idea:
- - Use superblock for info/addresses, no hard-coded sizes!
- - Allows for different storage media, easier testing on tiny size, and more future proof
- */
- /*
- Implementation Details:
- --------------------------------------------------
- | superblock | FAT | Data+Dir blocks (same size) |
- --------------------------------------------------
- 16 word superblock:
- - (1) total blocks
- - (1) bytes per block
- - (10) label [1 char per word]
- - (1) brfs version
- - (3) reserved
- 8 word Dir entries:
- - (4) filename.ext [4 chars per word -> 16 chars total]
- - (1) modify date [to be implemented when RTC]
- - (1) flags [max 32 flags, from right to left: directory, hidden]
- - (1) 1st FAT idx
- - (1) file size [in words, not bytes]
- */
- /*
- Required operations:
- - Format
- - Create directory
- - Create file
- - Open file (allow multiple files open at once)
- - Close file (update dir entry and check/update all FAT entries)
- - Set cursor
- - Get cursor
- - Read file
- - Write file
- - Delete entire file (deleting part of file is not a thing)
- - Change directory
- */
- #define word char
- #include "LIB/MATH.C"
- #include "LIB/SYS.C"
- #include "LIB/STDLIB.C"
- #define BRFS_RAM_STORAGE_ADDR 0x600000
- #define SUPERBLOCK_SIZE 16
- word *brfs_ram_storage = (word*) BRFS_RAM_STORAGE_ADDR; // RAM storage of file system
- word brfs_current_dir = 0; // Current directory index, points to block in data section
- // 16 words long
- struct brfs_superblock
- {
- word total_blocks;
- word bytes_per_block;
- word label[10]; // 1 char per word
- word brfs_version;
- word reserved[3];
- };
- // 8 words long
- struct brfs_dir_entry
- {
- word filename[4]; // 4 chars per word
- word modify_date; // TBD when RTC added to FPGC
- word flags; // 32 flags, from right to left: directory, hidden
- word fat_idx; // idx of first FAT block
- word filesize; // file size in words, not bytes
- };
- word brfs_find_next_free_block(word* fat_addr, word blocks)
- {
- word i = 0;
- word* fat_ptr = fat_addr;
- while (i < blocks)
- {
- if (*fat_ptr == 0)
- {
- return i;
- }
- fat_ptr++;
- i++;
- }
- return -1;
- }
- word brfs_find_next_free_dir_entry(word* dir_addr, word dir_entries_max)
- {
- word i = 0;
- word* dir_ptr = dir_addr;
- while (i < dir_entries_max)
- {
- if (*dir_ptr == 0)
- {
- return i;
- }
- dir_ptr += sizeof(struct brfs_dir_entry);
- i++;
- }
- return -1;
- }
- void brfs_create_single_dir_entry(struct brfs_dir_entry* dir_entry, char* filename, word fat_idx, word filesize, word flags)
- {
- // Initialize to 0
- memset(dir_entry, 0, sizeof(*dir_entry));
- // Set filename
- char compressed_filename[4] = {0,0,0,0};
- strcompress(compressed_filename, filename);
- memcpy(&(dir_entry->filename), compressed_filename, sizeof(compressed_filename));
- // Set other fields
- dir_entry->fat_idx = fat_idx;
- dir_entry->flags = flags;
- dir_entry->filesize = filesize;
- }
- void brfs_init_directory(word* dir_addr, word dir_entries_max, word dir_fat_idx, word parent_fat_idx)
- {
- // Create . entry
- struct brfs_dir_entry dir_entry;
- brfs_create_single_dir_entry(&dir_entry, ".", dir_fat_idx, dir_entries_max*sizeof(struct brfs_dir_entry), 1);
- // Copy to first data entry
- memcpy(dir_addr, &dir_entry, sizeof(dir_entry));
- // Create .. entry
- brfs_create_single_dir_entry(&dir_entry, "..", parent_fat_idx, dir_entries_max*sizeof(struct brfs_dir_entry), 1);
- // Copy to second data entry
- memcpy(dir_addr+sizeof(dir_entry), &dir_entry, sizeof(dir_entry));
- // Set FAT table
- brfs_ram_storage[SUPERBLOCK_SIZE + dir_fat_idx] = -1;
- }
- // Creates hexdump like dump
- void brfs_dump_section(word* addr, word len, word linesize)
- {
- char buf[16];
- word i;
- for (i = 0; i < len; i++)
- {
- itoah(addr[i], buf);
- if (strlen(buf+2) == 1)
- uprintc('0');
- uprint(buf+2);
- uprintc(' ');
- // newline every linesize words
- // also print last linesize words as chars if alphanum
- if (i != 0 && MATH_modU(i+1, linesize) == 0)
- {
- uprint(" ");
- word j;
- for (j = i - (linesize-1); j < i+1; j++)
- {
- if (isalnum(addr[j]) || addr[j] == ' ')
- uprintc(addr[j]);
- else
- uprintc('.');
- }
- uprintc('\n');
- }
- }
- }
- void brfs_dump(word* ram_addr, word fatsize, word datasize)
- {
- // Superblock dump
- uprintln("Superblock:");
- brfs_dump_section(ram_addr, SUPERBLOCK_SIZE, 16);
- // FAT dump
- uprintln("\nFAT:");
- brfs_dump_section(ram_addr+SUPERBLOCK_SIZE, fatsize, 8);
- // Datablock dump
- uprintln("\nData:");
- brfs_dump_section(ram_addr+SUPERBLOCK_SIZE+fatsize, datasize, 32);
- uprintc('\n');
- }
- void brfs_format(word* ram_addr, word blocks, word bytes_per_block, char* label, word full_format)
- {
- // Create a superblock
- struct brfs_superblock superblock;
- // Initialize to 0
- memset(&superblock, 0, sizeof(superblock));
- // Set values of superblock
- superblock.total_blocks = blocks;
- superblock.bytes_per_block = bytes_per_block;
- strcpy(&(superblock.label), label);
- superblock.brfs_version = 1;
- // Copy superblock to head of ram addr
- memcpy(ram_addr, &superblock, sizeof(superblock));
- // Create FAT
- memset(ram_addr + SUPERBLOCK_SIZE, 0, blocks);
- // Create Data section
- if (full_format)
- {
- memset(ram_addr + SUPERBLOCK_SIZE + blocks, 0, blocks * bytes_per_block);
- }
-
- // Initialize root dir
- word dir_entries_max = bytes_per_block / sizeof(struct brfs_dir_entry);
- brfs_init_directory(ram_addr + SUPERBLOCK_SIZE + blocks, dir_entries_max, 0, 0);
- brfs_current_dir = 0;
- }
- /*
- * Creates directory in current directory
- */
- void brfs_create_directory(word* ram_addr, char* dirname)
- {
- struct brfs_superblock* superblock = (struct brfs_superblock*) ram_addr;
- // Find first free FAT block
- word next_free_block = brfs_find_next_free_block(ram_addr + SUPERBLOCK_SIZE, superblock->total_blocks);
- if (next_free_block == -1)
- {
- uprintln("No free blocks left!");
- return;
- }
- // Find first free dir entry
- word next_free_dir_entry = brfs_find_next_free_dir_entry(
- ram_addr + SUPERBLOCK_SIZE + superblock->total_blocks + (brfs_current_dir * superblock->bytes_per_block),
- superblock->bytes_per_block / sizeof(struct brfs_dir_entry)
- );
- if (next_free_dir_entry == -1)
- {
- uprintln("No free dir entries left!");
- return;
- }
- // Create dir entry
- struct brfs_dir_entry new_entry;
- brfs_create_single_dir_entry(&new_entry, dirname, next_free_block, 0, 1);
- // Copy dir entry to first free dir entry
- memcpy(
- ram_addr + SUPERBLOCK_SIZE + superblock->total_blocks + (brfs_current_dir * superblock->bytes_per_block) + (next_free_dir_entry * sizeof(struct brfs_dir_entry)),
- &new_entry,
- sizeof(new_entry)
- );
- // Initialize directory
- word dir_entries_max = superblock->bytes_per_block / sizeof(struct brfs_dir_entry);
- brfs_init_directory(
- ram_addr + SUPERBLOCK_SIZE + superblock->total_blocks + (next_free_block * superblock->bytes_per_block),
- dir_entries_max,
- next_free_block,
- brfs_current_dir
- );
- }
- /*
- * Creates an empty file in current directory
- */
- void brfs_create_file(word* ram_addr, char* filename)
- {
- struct brfs_superblock* superblock = (struct brfs_superblock*) ram_addr;
- // Find first free FAT block
- word next_free_block = brfs_find_next_free_block(ram_addr + SUPERBLOCK_SIZE, superblock->total_blocks);
- if (next_free_block == -1)
- {
- uprintln("No free blocks left!");
- return;
- }
- // Find first free dir entry
- word next_free_dir_entry = brfs_find_next_free_dir_entry(
- ram_addr + SUPERBLOCK_SIZE + superblock->total_blocks + (brfs_current_dir * superblock->bytes_per_block),
- superblock->bytes_per_block / sizeof(struct brfs_dir_entry)
- );
- if (next_free_dir_entry == -1)
- {
- uprintln("No free dir entries left!");
- return;
- }
- // Create file entry
- struct brfs_dir_entry new_entry;
- brfs_create_single_dir_entry(&new_entry, filename, next_free_block, 0, 0);
- // Copy dir entry to first free dir entry
- memcpy(
- ram_addr + SUPERBLOCK_SIZE + superblock->total_blocks + (brfs_current_dir * superblock->bytes_per_block) + (next_free_dir_entry * sizeof(struct brfs_dir_entry)),
- &new_entry,
- sizeof(new_entry)
- );
- // Initialize file by setting data to 0
- memset(
- ram_addr + SUPERBLOCK_SIZE + superblock->total_blocks + (next_free_block * superblock->bytes_per_block),
- 0,
- superblock->bytes_per_block
- );
- // Update FAT
- ram_addr[SUPERBLOCK_SIZE + next_free_block] = -1;
- }
- int main()
- {
- // Clear UART screen:
- uprintc(0x1B);
- uprintc(0x5B);
- uprintc(0x32);
- uprintc(0x4A);
- uprintln("------------------------");
- uprintln("BRFS test implementation");
- uprintln("------------------------");
- word blocks = 8;
- word bytes_per_block = 32;
- word full_format = 1;
- brfs_format(brfs_ram_storage, blocks, bytes_per_block, "Label", full_format);
- brfs_create_file(brfs_ram_storage, "file1");
- brfs_create_directory(brfs_ram_storage, "dir1");
- brfs_dump(brfs_ram_storage, blocks, blocks*bytes_per_block);
- return 'q';
- }
- void interrupt()
- {
- // handle all interrupts
- word i = getIntID();
- switch(i)
- {
- case INTID_TIMER1:
- timer1Value = 1; // notify ending of timer1
- break;
- case INTID_TIMER2:
- break;
- case INTID_UART0:
- break;
- case INTID_GPU:
- break;
- case INTID_TIMER3:
- break;
- case INTID_PS2:
- break;
- case INTID_UART1:
- break;
- case INTID_UART2:
- break;
- }
- }
|