aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-05-24 17:02:29 +0200
committerBad Diode <bd@badd10de.dev>2021-05-24 17:02:29 +0200
commit3410761714c70480c063112e5a184c93cae39bb3 (patch)
treec955c7e6cb8a783b5a76c3e40b01c58313cd9f7d
parente07f072de45d23ab17661e3ea6bd3c707f04aa35 (diff)
downloaduxngba-3410761714c70480c063112e5a184c93cae39bb3.tar.gz
uxngba-3410761714c70480c063112e5a184c93cae39bb3.zip
Add experimental block based filesystem
-rw-r--r--src/common.h7
-rw-r--r--src/filesystem.c361
-rw-r--r--src/main.c39
3 files changed, 322 insertions, 85 deletions
diff --git a/src/common.h b/src/common.h
index 39f3ada..bfa3903 100644
--- a/src/common.h
+++ b/src/common.h
@@ -716,10 +716,13 @@ wait_vsync(void) {
716#define CLAMP(X, MIN, MAX) ((X) <= (MIN) ? (MIN) : (X) > (MAX) ? (MAX): (X)) 716#define CLAMP(X, MIN, MAX) ((X) <= (MIN) ? (MIN) : (X) > (MAX) ? (MAX): (X))
717#define LEN(ARR) (sizeof(ARR) / sizeof((ARR)[0])) 717#define LEN(ARR) (sizeof(ARR) / sizeof((ARR)[0]))
718 718
719// IWRAM allocation macros for devkitARM. 719//
720#define IWRAM_CODE __attribute__((section(".iwram"), long_call, target("arm"))) 720// Memory section macros for devkitARM.
721//
721#define IWRAM_DATA __attribute__((section(".iwram"))) 722#define IWRAM_DATA __attribute__((section(".iwram")))
723#define IWRAM_CODE __attribute__((section(".iwram"), long_call, target("arm")))
722#define EWRAM_DATA __attribute__((section(".ewram"))) 724#define EWRAM_DATA __attribute__((section(".ewram")))
725#define EWRAM_CODE __attribute__((section(".ewram"), long_call))
723#define EWRAM_BSS __attribute__((section(".sbss"))) 726#define EWRAM_BSS __attribute__((section(".sbss")))
724 727
725#endif // GBAEXP_COMMON_H 728#endif // GBAEXP_COMMON_H
diff --git a/src/filesystem.c b/src/filesystem.c
index 6737386..4a55818 100644
--- a/src/filesystem.c
+++ b/src/filesystem.c
@@ -1,29 +1,68 @@
1// We need 64 * 32 bytes (2K) of SRAM for file indexes. To avoid corruption 1// This file implements a filesystem with a minimum block size of 256 bytes. The
2// issues we ignore the first file (32 bytes). 2// maximum number of files depends on the block size. The default 1KB block size
3// Note that the filename should include the null terminator if we want to use 3// will give us 32-64 files depending on the size of MEM_CART. In case we want
4// strcmp. 4// to use a block size of 512 bytes, we will have up to 128 file available.
5#define FILE_NAME_SIZE 27 5// Blocks of 256 bytes will give us the maximum of 255 files available, since
6#define FILE_CAPACITY 4 6// a block index of 0xFF will be considered as a null block.
7#define FILE_HEADER_OFFSET 2
8#define FILE_INDEX_OFFSET 32
9#define FILE_DATA_OFFSET KB(2)
10#define FILE_MAX_SIZE KB(16)
11#define SRAM ((vu8*)(MEM_CART))
12 7
13typedef struct File { 8// A fileblock of 1KB give us a maximum of 64 files.
14 char name[FILE_NAME_SIZE + 1]; 9#define FILE_BLOCK_SIZE KB(1)
10#define FILE_MAX_FILES 64
11#define FILE_N_BLOCKS 62
12
13// With this file name size sizeof(FileIndex) will be 32 bytes. 32 * 64 files
14// give us 2KB spent on file index that we can't use for data (so maximum of 62
15// files without accounting for the block index).
16#define FILE_NAME_SIZE 30
17#define FILE_INDEX_NUM 62
18
19// Since we are reserving the first 2K bytes for the filesystem, we have 60
20// blocks available for writing data. If you were to change the previous
21// parameters, you *must* recalculate the initial block start location.
22#define FILE_DATA_START KB(2)
23
24// We must write to the SRAM using the 8bit bus.
25#define SRAM ((vu8*)(MEM_CART))
26
27// Special filesystem constants.
28enum { FS_INIT_PATTERN = 0xBA, FS_NULL = 0xFF };
29
30typedef struct FileBlock {
31 // Size used in the current block (in bytes). Should be smaller than:
32 // FILE_BLOCK_SIZE - sizeof(FileBlock)
15 u16 size; 33 u16 size;
16 u16 mem_offset; // NOTE: Unused... 34 // The index for the next block. Set to FS_NULL if there is none.
17} File; 35 u8 next_block;
36 u8 prev_block;
37} FileBlock;
38
39typedef struct FileIndex {
40 // File name.
41 char name[FILE_NAME_SIZE + 1];
42 // Index to the first block of this file. If set to FS_NULL this file
43 // has not yet been written to.
44 u8 first_block;
45} FileIndex;
18 46
19// The filesystem header. 47// The filesystem header.
20typedef struct FileSystem { 48typedef struct FileSystem {
21 u16 num_files; 49 // The first byte of the SRAM can become corrupted in some situations, like
22 u16 data_size; // NOTE: Unused... 50 // changing cartridges for example.
23 u16 data_capacity; // NOTE: Unused... 51 u8 blank;
24 File files[FILE_CAPACITY]; 52 // If the filesystem exists, this will be set to FS_INIT_PATTERN.
53 u8 initialized;
54 // Number of blocks in use.
55 u8 busy_blocks;
56 // Number of files currently existing in the filesystem.
57 u8 num_files;
58 // This stores a bitmap pattern to keep track of the blocks in use by the
59 // filesystem. The first byte maps the first 8 blocks and so on.
60 u8 used_blocks[FILE_MAX_FILES / 8];
61 // The list of possible file indexes.
62 FileIndex files[FILE_INDEX_NUM];
25} FileSystem; 63} FileSystem;
26 64
65EWRAM_BSS
27static FileSystem filesystem; 66static FileSystem filesystem;
28 67
29void 68void
@@ -41,114 +80,278 @@ _fs_write(u8 *src, size_t pos, size_t n_bytes) {
41} 80}
42 81
43void 82void
44fs_init() { 83fs_init(void) {
45 // Load header if existing. 84 // Load filesystem if existing.
46 _fs_read(&filesystem, FILE_HEADER_OFFSET, offsetof(FileSystem, files)); 85 _fs_read(&filesystem, 0, sizeof(FileSystem));
47 if (filesystem.num_files == 0xFFFF 86 if (filesystem.initialized != FS_INIT_PATTERN) {
48 && filesystem.data_capacity == 0xFFFF
49 && filesystem.data_size == 0xFFFF) {
50 // Clear SRAM. 87 // Clear SRAM.
51 for (size_t i = 0; i < KB(64); ++i) { 88 for (size_t i = 0; i < KB(64) / 8; ++i) {
52 SRAM[i] = 0; 89 SRAM[i] = 0x00;
90 }
91
92 // Initialize block headers.
93 FileBlock block = {
94 .size = 0,
95 .next_block = FS_NULL,
96 .prev_block = FS_NULL,
97 };
98 for (size_t i = 0; i < FILE_INDEX_NUM; ++i) {
99 size_t block_pos = FILE_DATA_START + FILE_BLOCK_SIZE * i;
100 _fs_write(&block, block_pos, sizeof(FileBlock));
53 } 101 }
54 102
55 // Initialize filesystem. 103 // Initialize filesystem.
56 filesystem.num_files = 0; 104 memset(&filesystem, 0, sizeof(FileSystem));
57 filesystem.data_size = 0; 105 filesystem.initialized = FS_INIT_PATTERN;
58 filesystem.data_capacity = (u16)(FILE_CAPACITY * FILE_MAX_SIZE); 106 for (size_t i = 0; i < FILE_INDEX_NUM; ++i) {
59 memset(&filesystem.files, 0, FILE_CAPACITY * sizeof(File)); 107 filesystem.files[i].first_block = FS_NULL;
60 _fs_write(&filesystem, FILE_HEADER_OFFSET, offsetof(FileSystem, files)); 108 }
61 } else { 109
62 _fs_read(&filesystem.files, FILE_INDEX_OFFSET, sizeof(File) * filesystem.num_files); 110 // Write the FS to disk.
111 _fs_write(&filesystem, 0, sizeof(FileSystem));
63 } 112 }
64} 113}
65 114
115void inline
116_fs_update_filesystem_header(void) {
117 _fs_write(&filesystem, 0, offsetof(FileSystem, files));
118};
119
120void inline
121_fs_update_file_index(u16 index) {
122 _fs_write(&filesystem.files[index],
123 offsetof(FileSystem, files) + index * sizeof(FileIndex),
124 sizeof(FileIndex));
125}
126
66typedef enum { 127typedef enum {
67 OPEN_READ, 128 FS_OPEN_READ,
68 OPEN_WRITE, 129 FS_OPEN_WRITE,
130 FS_OPEN_APPEND,
69} OpenMode; 131} OpenMode;
70 132
71int 133typedef struct File {
134 u8 index;
135 u16 cur;
136 OpenMode mode;
137} File;
138
139File
72fs_open_file(char *name, OpenMode mode) { 140fs_open_file(char *name, OpenMode mode) {
73 // Try to find an existing file. 141 // Try to find an existing file.
74 for (size_t i = 0; i < filesystem.num_files; ++i) { 142 for (size_t i = 0; i < filesystem.num_files; ++i) {
75 // TODO: Replace strcmp with vectorized fixed size char comparison. 143 // TODO: Replace strcmp with vectorized fixed size char comparison.
76 if (strcmp(name, filesystem.files[i].name) == 0) { 144 if (strcmp(name, filesystem.files[i].name) == 0) {
77 return i; 145 return (File){i, 0, mode};
78 } 146 }
79 } 147 }
80 148
81 if (mode == OPEN_READ) { 149 if (mode == FS_OPEN_READ) {
82 return -1; 150 return (File){FS_NULL, 0, mode};
83 } 151 }
84 152
85 // Create a new file if there is space. 153 // Create a new file if there is space.
86 if (filesystem.num_files < FILE_CAPACITY) { 154 if (filesystem.num_files < FILE_INDEX_NUM) {
87 size_t index = filesystem.num_files++; 155 size_t index = filesystem.num_files++;
88 size_t k = 0; 156 size_t k = 0;
89 while(*name) { 157 while(*name) {
90 filesystem.files[index].name[k++] = *name++; 158 filesystem.files[index].name[k++] = *name++;
91 } 159 }
92 filesystem.files[index].size = 0;
93 filesystem.files[index].mem_offset = 0;
94 160
95 // Update file index. 161 // Update file index and filesystem on SRAM.
96 _fs_write(&filesystem.files[index], 162 _fs_update_file_index(index);
97 FILE_INDEX_OFFSET + index * sizeof(File), 163 _fs_update_filesystem_header();
98 sizeof(File));
99 164
100 // Update header. 165 return (File){index, 0, mode};
101 _fs_write(&filesystem, FILE_HEADER_OFFSET, offsetof(FileSystem, files)); 166 }
167 return (File){FS_NULL, 0, mode};
168}
102 169
103 return index; 170u8
171_fs_init_new_block(void) {
172 // Find free block.
173 u8 block_index = 0;
174 for (size_t j = 0; j < LEN(filesystem.used_blocks); ++j) {
175 for (size_t i = 0; i < 8; ++i, block_index++) {
176 u8 blk = (filesystem.used_blocks[j] >> i) & 0x1;
177 if (blk == 0) {
178 // Initialize the block.
179 filesystem.busy_blocks++;
180 filesystem.used_blocks[j] |= (1 << i);
181 _fs_update_filesystem_header();
182 return block_index;
183 }
184 }
104 } 185 }
105 return -1; 186 return FS_NULL;
106} 187}
107 188
108size_t 189#include "text.h"
109fs_write(u8 *src, size_t n_bytes, u16 file_index, u16 offset, bool append) {
110 File *file = &filesystem.files[file_index];
111 190
112 // Check if there is enough capacity for this write operation. 191// Recursively free blocks starting at block_id. To improve performance, the
113 if (offset + n_bytes >= FILE_MAX_SIZE) { 192// filesystem header is updated in memory but not written to disk. It is
114 return 0; 193// responsability of the caller to perform the filesystem update.
194void
195_fs_free_blocks(u8 block_id) {
196 if (block_id == FS_NULL) {
197 return;
115 } 198 }
116 199
117 // Write data to file block. 200 // Read block.
118 _fs_write(src, FILE_DATA_OFFSET + FILE_MAX_SIZE * file_index + offset, n_bytes); 201 FileBlock block;
202 size_t block_pos = FILE_DATA_START + FILE_BLOCK_SIZE * block_id;
203 _fs_read(&block, block_pos, sizeof(FileBlock));
204
205 // Update block.
206 u8 next_block = block.next_block;
207 block = (FileBlock){
208 .size = 0,
209 .next_block = FS_NULL,
210 .prev_block = FS_NULL,
211 };
212 _fs_write(&block, block_pos, sizeof(FileBlock));
213
214 // Update dirty and busy blocks.
215 filesystem.busy_blocks--;
216 filesystem.used_blocks[block_id / 8] &= ~(1 << (block_id % 8));
217
218 _fs_free_blocks(next_block);
219}
220
221// Write to block as a new file.
222void
223_fs_write_to_block(u8 *src, size_t n_bytes, u8 block_id, u8 prev_block) {
224 // Read initial block.
225 FileBlock block;
226 size_t block_pos = FILE_DATA_START + FILE_BLOCK_SIZE * block_id;
227 _fs_read(&block, block_pos, sizeof(FileBlock));
228 u16 block_capacity = (FILE_BLOCK_SIZE - sizeof(FileBlock));
119 229
120 // Update file index. 230 // Write capacity.
121 if (append) { 231 u16 block_bytes = MIN(block_capacity, n_bytes);
122 if (offset + n_bytes > file->size) { 232 _fs_write(src, block_pos + sizeof(FileBlock), block_bytes);
123 file->size = offset + n_bytes; 233
234 if (n_bytes > block_capacity) {
235 if (block.next_block == FS_NULL) {
236 // Find new available block and initialize it.
237 block.next_block = _fs_init_new_block();
124 } 238 }
239 _fs_write_to_block(
240 src + block_capacity,
241 n_bytes - block_capacity,
242 block.next_block,
243 block_id);
244 } else if (block.next_block != FS_NULL){
245 // Recursively free unused blocks.
246 _fs_free_blocks(block.next_block);
247 _fs_update_filesystem_header();
248 block.next_block = FS_NULL;
249 }
250
251 // Update block header.
252 if (prev_block != FS_NULL) {
253 block.prev_block = prev_block;
254 }
255 block.size = block_bytes;
256 _fs_write(&block, block_pos, sizeof(FileBlock));
257}
258
259// void
260// _fs_append_to_block(u8 *src, size_t n_bytes, u8 block_id, u8 prev_block) {
261// // Read initial block.
262// FileBlock block;
263// size_t block_pos = FILE_DATA_START + FILE_BLOCK_SIZE * block_id;
264// _fs_read(&block, block_pos, sizeof(FileBlock));
265// u16 block_capacity = (FILE_BLOCK_SIZE - sizeof(FileBlock)) - block.size;
266
267// // Write capacity.
268// u16 block_bytes = MIN(block_capacity, n_bytes);
269// _fs_write(src, block_pos + sizeof(FileBlock), block_bytes);
270
271// // txt_printf("cap: %d\n", block_capacity);
272// // txt_printf("bytes: %d\n", n_bytes);
273// // txt_printf("id: %d\n", block_id);
274// if (n_bytes > block_capacity) {
275// if (block.next_block == FS_NULL) {
276// // Find new available block and initialize it.
277// block.next_block = _fs_init_new_block();
278// // TODO: Don't forget to set the block_prev of the next block as
279// // this one.
280// }
281// _fs_write_to_block(
282// src + block_capacity,
283// n_bytes - block_capacity,
284// block.next_block,
285// block_id);
286// }
287
288// // Update block header.
289// if (prev_block != FS_NULL) {
290// block.prev_block = prev_block;
291// }
292// block.size += block_bytes;
293// _fs_write(&block, block_pos, sizeof(FileBlock));
294// }
295
296size_t
297fs_write(u8 *src, size_t n_bytes, u16 offset, bool append, File *file) {
298 FileIndex *file_idx = &filesystem.files[file->index];
299 // TODO: Account for offset.
300
301 // If this is a new file.
302 if (file_idx->first_block == FS_NULL) {
303 // Check how many blocks will this write require and if we have enough
304 // available.
305 u16 blocks_required = n_bytes / (FILE_BLOCK_SIZE - sizeof(FileBlock));
306 u16 blocks_available = FILE_N_BLOCKS - filesystem.busy_blocks;
307 if (blocks_required > blocks_available) {
308 return 0;
309 }
310
311 // Find the first available block.
312 u8 block_id = _fs_init_new_block();
313 file_idx->first_block = block_id;
314
315 // Update file index on SRAM.
316 _fs_update_file_index(file->index);
125 } else { 317 } else {
126 file->size = offset + n_bytes; 318 // TODO: Check how many blocks will this write require and if we have
319 // enough available.
127 } 320 }
128 _fs_write(file, FILE_INDEX_OFFSET + file_index * sizeof(File), sizeof(File)); 321 // txt_printf("id: %d", file_idx->first_block);
129 322
130 // Update header. 323 // Write to block.
131 _fs_write(&filesystem, FILE_HEADER_OFFSET, offsetof(FileSystem, files)); 324 _fs_write_to_block(src, n_bytes, file_idx->first_block, FS_NULL);
325
326 // // Update file index.
327 // if (append) {
328 // if (offset + n_bytes > file->size) {
329 // file->size = offset + n_bytes;
330 // }
331 // } else {
332 // file->size = offset + n_bytes;
333 // }
334 // _fs_write(file, FILE_INDEX_OFFSET + file_index * sizeof(File), sizeof(File));
132 335
133 return n_bytes; 336 return n_bytes;
134} 337}
135 338
136size_t 339size_t
137fs_read(u8 *dst, size_t n_bytes, u16 file_index, u16 offset) { 340fs_read(u8 *dst, size_t n_bytes, u16 offset, File *file) {
138 File *file = &filesystem.files[file_index]; 341 // File *file = &filesystem.files[file_index];
139 342
140 // Check if the offset is within limits. 343 // // Check if the offset is within limits.
141 if (file->size == 0 || offset >= file->size - 1) { 344 // if (file->size == 0 || offset >= file->size - 1) {
142 return 0; 345 // return 0;
143 } 346 // }
144 347
145 // Read as much as we can. 348 // // Read as much as we can.
146 if (offset + n_bytes > file->size) { 349 // if (offset + n_bytes > file->size) {
147 n_bytes = file->size - offset; 350 // n_bytes = file->size - offset;
148 } 351 // }
149 352
150 // Copy n_bytes to destination. 353 // // Copy n_bytes to destination.
151 _fs_read(dst, FILE_DATA_OFFSET + FILE_MAX_SIZE * file_index + offset, n_bytes); 354 // _fs_read(dst, FILE_DATA_OFFSET + FILE_MAX_SIZE * file_index + offset, n_bytes);
152 355
153 return n_bytes; 356 return n_bytes;
154} 357}
diff --git a/src/main.c b/src/main.c
index e30035a..62030e5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -124,13 +124,20 @@ file_talk(Device *d, u8 b0, u8 w) {
124 u16 result = 0, length = mempeek16(d->dat, 0xa); 124 u16 result = 0, length = mempeek16(d->dat, 0xa);
125 u16 offset = mempeek16(d->dat, 0x4); 125 u16 offset = mempeek16(d->dat, 0x4);
126 u16 addr = mempeek16(d->dat, b0 - 1); 126 u16 addr = mempeek16(d->dat, b0 - 1);
127 int file_idx = fs_open_file(name, read ? OPEN_READ : OPEN_WRITE); 127 File file = fs_open_file(name, read ? FS_OPEN_READ : FS_OPEN_WRITE);
128 if (file_idx >= 0) { 128 if (file.index != FS_NULL) {
129 txt_position(9, 9);
130 // TODO: Use file.cur pointer and fs_seek instead of offset.
131 // TODO: Remove append, that should be a write mode.
129 if (read) { 132 if (read) {
130 result = fs_read(&d->mem[addr], length, file_idx, offset); 133 result = fs_read(&d->mem[addr], length, offset, &file);
131 } else { 134 } else {
132 result = fs_write(&d->mem[addr], length, file_idx, offset, offset > 0); 135 result = fs_write(&d->mem[addr], length, offset, offset > 0, &file);
136 txt_printf("WROTE: %d\n", result);
133 } 137 }
138 } else {
139 // txt_position(9, 9);
140 // txt_printf("NOT FOUND");
134 } 141 }
135 mempoke16(d->dat, 0x2, result); 142 mempoke16(d->dat, 0x2, result);
136 } 143 }
@@ -353,14 +360,38 @@ int main(void) {
353 txt_init(1, TEXT_LAYER); 360 txt_init(1, TEXT_LAYER);
354 txt_position(0,0); 361 txt_position(0,0);
355 362
363 u8 test_data_a[1020];
364 u8 test_data_b[2038];
365 memset(&test_data_a, 0xAA, sizeof(test_data_a));
366 memset(&test_data_b, 0xbb, sizeof(test_data_b));
367
368 txt_position(0, 8);
369 File file_a = fs_open_file("file_a", FS_OPEN_WRITE);
370 File file_b = fs_open_file("file_b", FS_OPEN_WRITE);
371 fs_write(&test_data_b, sizeof(test_data_b), 0, 0, &file_a);
372 fs_write(&test_data_a, sizeof(test_data_a), 0, 0, &file_a);
373 // fs_write(&test_data_a, sizeof(test_data_a), 0, 0, &file_a);
374 // fs_write(&test_data_b, sizeof(test_data_b), 0, 0, &file_b);
375
356 // Main loop. 376 // Main loop.
357 int frame_counter = 0; 377 int frame_counter = 0;
358 evaluxn(&u, 0x0100); 378 evaluxn(&u, 0x0100);
379 u32 flip_cycles = 0;
359 while(true) { 380 while(true) {
360 bios_vblank_wait(); 381 bios_vblank_wait();
382 profile_start();
361 handle_input(&u); 383 handle_input(&u);
384 u32 input_cycles = profile_stop();
385 profile_start();
362 evaluxn(&u, mempeek16(devscreen->dat, 0)); 386 evaluxn(&u, mempeek16(devscreen->dat, 0));
387 u32 eval_cycles = profile_stop();
388 txt_position(0, 8);
389 // txt_printf("INPUT: %lu \n", input_cycles);
390 // txt_printf("EVAL: %lu \n", eval_cycles);
391 // txt_printf("FLIP: %lu \n", flip_cycles);
392 profile_start();
363 flipbuf(&ppu); 393 flipbuf(&ppu);
394 flip_cycles = profile_stop();
364 frame_counter++; 395 frame_counter++;
365 } 396 }
366 397