summaryrefslogtreecommitdiff
path: root/arm9/source
diff options
context:
space:
mode:
authorBen Bridle <ben@derelict.engineering>2024-11-22 16:04:20 +1300
committerBen Bridle <ben@derelict.engineering>2024-11-22 16:15:11 +1300
commit4f8805869c469cb1b3685e03c3ea34d7654b5cb7 (patch)
treeefe555c1064b7a69738e3cf7fd107af7bdc0155f /arm9/source
parentfcbc3968bd95e4d19b37d9fa4bca51b1db8596ff (diff)
downloadbedrock-nds-4f8805869c469cb1b3685e03c3ea34d7654b5cb7.zip
Implement file device
There is still a small amount of work to be done on the file device: - Read file size only when requested - Hide '.' and '..' directories - Resize files
Diffstat (limited to 'arm9/source')
-rw-r--r--arm9/source/bang.h20
-rw-r--r--arm9/source/core.c41
-rw-r--r--arm9/source/core.h18
-rw-r--r--arm9/source/devices/file.c247
-rw-r--r--arm9/source/devices/file.h52
-rw-r--r--arm9/source/devices/system.c16
-rw-r--r--arm9/source/devices/system.h3
-rw-r--r--arm9/source/main.c1
-rw-r--r--arm9/source/types/pathbuf.c52
-rw-r--r--arm9/source/types/pathbuf.h18
10 files changed, 447 insertions, 21 deletions
diff --git a/arm9/source/bang.h b/arm9/source/bang.h
index 1e13f9f..110e243 100644
--- a/arm9/source/bang.h
+++ b/arm9/source/bang.h
@@ -1,15 +1,19 @@
#ifndef BANG_H_
#define BANG_H_
- #define SET_HIGH(v,high) v = high << 8 | (v & 0x00ff)
- #define SET_LOW(v,low) v = (v & 0xff00) | low
- #define HIGH(v) (u8)((v) >> 8)
- #define LOW(v) (u8)((v) )
+ #define SET_HIGH(v,high) v = high << 8 | (v & 0x00ff)
+ #define SET_LOW(v,low) v = (v & 0xff00) | low
+ #define HIGH(v) (u8)((v) >> 8)
+ #define LOW(v) (u8)((v) )
- #define H_HIGH(v) (u8)((v) >> 24)
- #define H_LOW(v) (u8)((v) >> 16)
- #define L_HIGH(v) (u8)((v) >> 8)
- #define L_LOW(v) (u8)((v) )
+ #define SET_H_HIGH(v,high) v = high << 24 | (v & 0x00ffffff)
+ #define SET_H_LOW(v,low) v = low << 16 | (v & 0xff00ffff)
+ #define SET_L_HIGH(v,high) v = high << 8 | (v & 0xffff00ff)
+ #define SET_L_LOW(v,low) v = low | (v & 0xffffff00)
+ #define H_HIGH(v) (u8)((v) >> 24)
+ #define H_LOW(v) (u8)((v) >> 16)
+ #define L_HIGH(v) (u8)((v) >> 8)
+ #define L_LOW(v) (u8)((v) )
#define LEFT(x) ((x) >> 4)
#define RIGHT(x) ((x) & 0xf)
diff --git a/arm9/source/core.c b/arm9/source/core.c
index 7136777..d792360 100644
--- a/arm9/source/core.c
+++ b/arm9/source/core.c
@@ -62,12 +62,9 @@ u8 dev_read(Bedrock *br, u8 port) {
case 0x03: return 0x00; // program memory size
case 0x04: return 0x00; // working stack size
case 0x05: return 0x00; // return stack size
- case 0x06: return 0xbc;
- case 0x07: return 0x00;
-
- // TODO: available devices
+ case 0x06: return devices_high();
+ case 0x07: return devices_low();
case 0x0A: return br->sys.wake;
-
// MEMORY DEVICE
case 0x10: return mem_read1(&br->mem);
case 0x11: return mem_read1(&br->mem);
@@ -137,6 +134,23 @@ u8 dev_read(Bedrock *br, u8 port) {
case 0x55: return LOW(br->scr.x);
case 0x56: return HIGH(br->scr.y);
case 0x57: return LOW(br->scr.y);
+// FILE DEVICE
+ case 0xA0: return BOOL(br->fs.open);
+ case 0xA1: return BOOL(br->fs.success);
+ case 0xA2: return pb_read(&br->fs.path);
+ case 0xA3: return BOOL(br->fs.is_dir);
+ case 0xA4: return fs_read_byte(&br->fs);
+ case 0xA5: return fs_read_byte(&br->fs);
+ case 0xA6: return pb_read(&br->fs.child_path);
+ case 0xA7: return BOOL(br->fs.child_is_dir);
+ case 0xA8: return H_HIGH(br->fs.pointer);
+ case 0xA9: return H_LOW(br->fs.pointer);
+ case 0xAA: return L_HIGH(br->fs.pointer);
+ case 0xAB: return L_LOW(br->fs.pointer);
+ case 0xAC: return H_HIGH(br->fs.length);
+ case 0xAD: return H_LOW(br->fs.length);
+ case 0xAE: return L_HIGH(br->fs.length);
+ case 0xAF: return L_LOW(br->fs.length);
default: return 0;
}
@@ -197,6 +211,23 @@ Signal dev_write(Bedrock *br, u8 port, u8 v) {
// LOCAL DEVICE
case 0x86: printf("%c", v); return 0;
case 0x87: printf("%c", v); return 0;
+// FILE DEVICE
+ case 0xA0: fs_push_entry(&br->fs,v); return 0;
+ case 0xA1: fs_push_action(&br->fs,v); return 0;
+ case 0xA2: pb_reset(&br->fs.path,v); return 0;
+ case 0xA3: fs_ascend(&br->fs); return 0;
+ case 0xA4: fs_write_byte(&br->fs,v); return 0;
+ case 0xA5: fs_write_byte(&br->fs,v); return 0;
+ case 0xA6: pb_reset(&br->fs.child_path,v); return 0;
+ case 0xA7: fs_descend(&br->fs); return 0;
+ case 0xA8: SET_H_HIGH(br->fs.pointer_write,v); return 0;
+ case 0xA9: SET_H_LOW(br->fs.pointer_write,v); return 0;
+ case 0xAA: SET_L_HIGH(br->fs.pointer_write,v); return 0;
+ case 0xAB: SET_L_LOW(br->fs.pointer_write,v); fs_seek(&br->fs); return 0;
+ case 0xAC: SET_H_HIGH(br->fs.length_write,v); return 0;
+ case 0xAD: SET_H_LOW(br->fs.length_write,v); return 0;
+ case 0xAE: SET_L_HIGH(br->fs.length_write,v); return 0;
+ case 0xAF: SET_L_LOW(br->fs.length_write,v); fs_resize(&br->fs); return 0;
default: return SIG_NONE;
}
diff --git a/arm9/source/core.h b/arm9/source/core.h
index fde13a7..ec3f2fc 100644
--- a/arm9/source/core.h
+++ b/arm9/source/core.h
@@ -2,11 +2,12 @@
#define CORE_H_
#include "devices/system.h"
+ #include "devices/memory.h"
+ #include "devices/math.h"
#include "devices/clock.h"
#include "devices/input.h"
- #include "devices/math.h"
#include "devices/screen.h"
- #include "devices/memory.h"
+ #include "devices/file.h"
#define WST br->wst
#define RST br->rst
@@ -74,12 +75,13 @@
ProgramMemory prg; // program memory
StackMemory wst, rst; // working and return stacks
- SystemDevice sys;
- MemoryDevice mem;
- MathDevice math;
- ClockDevice clk;
- InputDevice inp;
- ScreenDevice scr;
+ SystemDevice sys;
+ MemoryDevice mem;
+ MathDevice math;
+ ClockDevice clk;
+ InputDevice inp;
+ ScreenDevice scr;
+ FileDevice fs;
} Bedrock;
void reset_br(Bedrock *br);
diff --git a/arm9/source/devices/file.c b/arm9/source/devices/file.c
new file mode 100644
index 0000000..c9b822c
--- /dev/null
+++ b/arm9/source/devices/file.c
@@ -0,0 +1,247 @@
+#include "nds.h"
+#include "fat.h"
+#include "file.h"
+
+static bool FS_ENABLE = false;
+static u8 buffer[255];
+
+
+void init_filesystem() {
+ FS_ENABLE = fatInitDefault();
+}
+
+bool filesystem_enabled() {
+ return FS_ENABLE;
+}
+
+void fs_close(FileDevice *fs) {
+ fclose(fs->file);
+ fs->file = NULL;
+ closedir(fs->dir);
+ fs->dir = NULL;
+ fs->open = false;
+ fs->success = false;
+ fs->is_dir = false;
+ fs->child_is_dir = false;
+ pb_clear(&fs->path);
+ pb_clear(&fs->child_path);
+ fs->pointer = 0;
+ fs->length = 0;
+ fs->child = NULL;
+ fs->dir_i = 0;
+}
+
+void fs_clear(FileDevice *fs) {
+ pb_clear(&fs->entry);
+ pb_clear(&fs->action);
+}
+
+void fs_open_entry(FileDevice* fs, u8 *path) {
+ fs->success = false;
+ // Prevent non-absolute paths from being opened.
+ if (path[0] != '/') {
+ return;
+ }
+ // Remove trailing slash if any, without removing leading slash.
+ for (int i=2; i<255; i++) {
+ if (path[i] == 0) {
+ if (path[i-1] == '/') {
+ path[i-1] = 0;
+ }
+ break;
+ }
+ }
+ // Prevent paths with relative components from being opened.
+ for (int i=0; i<255; i++) {
+ if (path[i] == '/') {
+ if (path[i+1]=='.') {
+ // Check for '.' components.
+ if (path[i+2]=='/' || path[i+2]==0) {
+ return;
+ }
+ // Check for '..' components.
+ if (path[i+2]=='.' && (path[i+3]=='/' || path[i+3]==0)) {
+ return;
+ }
+ }
+ } else if (path[i] == 0) {
+ break;
+ }
+ }
+
+
+ DIR *tmpdir = opendir((char*) path);
+ if (tmpdir) {
+ fs_close(fs);
+ fs->dir = tmpdir;
+ fs->open = true;
+ fs->success = true;
+ fs->is_dir = true;
+ fs->dir_i = 0;
+ // Find length of directory
+ while ((fs->child = readdir(fs->dir))) {
+ fs->dir_i++;
+ };
+ fs->length = fs->dir_i;
+ fs_select_child(fs, 0);
+ pb_populate(&fs->path, path);
+ return;
+ }
+
+ // MEMORY LEAK HERE ONLY
+ // for (int i=0; i<500; i++) {
+ // fs->tmpfile = fopen((char*) path, "r+");
+ // fclose(fs->tmpfile);
+ // }
+ // END OF MEMORY LEAK CODE
+
+ // FILE CODE MUST COME AFTER DIR CODE TO AVOID MEMORY LEAK
+ FILE *tmpfile = fopen((char*) path, "r+");
+ if (tmpfile) {
+ fs_close(fs);
+ fs->file = tmpfile;
+ fs->open = true;
+ fs->success = true;
+ fs->is_dir = false;
+ // TODO: Only read length when we need to. Keep a bool around.
+ // fseek(fs->file, 0, SEEK_END);
+ // fs->length = ftell(fs->file);
+ // rewind(fs->file);
+ pb_populate(&fs->path, path);
+ return;
+ }
+}
+
+void fs_select_child(FileDevice *fs, u32 pointer) {
+ fs->pointer = pointer;
+ rewinddir(fs->dir);
+ fs->dir_i = 0;
+
+ for (fs->dir_i=0; fs->dir_i<pointer+1; fs->dir_i++) {
+ fs->child = readdir(fs->dir);
+ };
+ if (fs->child) {
+ pb_populate(&fs->child_path, (u8*) &fs->child->d_name);
+ fs->child_is_dir = fs->child->d_type == DT_DIR;
+ } else {
+ pb_clear(&fs->child_path);
+ fs->child_is_dir = false;
+ }
+}
+
+void fs_push_entry(FileDevice *fs, u8 byte) {
+ if (pb_push_byte(&fs->entry, byte)) {
+ fs_close(fs);
+ if (pb_is_terminated(&fs->entry) && fs->entry.mem[0] != 0) {
+ fs_open_entry(fs, &fs->entry.mem[0]);
+ }
+ fs_clear(fs);
+ }
+}
+
+void fs_push_action(FileDevice *fs, u8 byte) {
+ if (pb_push_byte(&fs->action, byte)) {
+ if (!pb_is_terminated(&fs->action)) return;
+ bool action = fs->action.mem[0] != 0;
+ if (fs->open) {
+ if (action) {
+ fs->success = rename((char*) fs->path.mem, (char*) fs->action.mem);
+ } else {
+ // TODO: DELETE
+ }
+ } else if (action) {
+ FILE *tmpfile = fopen((char*) fs->action.mem, "w");
+ fs->success = tmpfile != NULL;
+ fclose(tmpfile);
+ }
+ fs_close(fs);
+ fs_clear(fs);
+ }
+}
+
+
+u8 fs_read_byte(FileDevice *fs) {
+ return fgetc(fs->file);
+}
+
+void fs_write_byte(FileDevice *fs, u8 byte) {
+ fputc(byte, fs->file);
+}
+
+void fs_ascend(FileDevice *fs) {
+ fs->success = false;
+ if (fs->open) {
+ // Don't ascend if we're already at the root.
+ if (fs->path.mem[1] == 0) {
+ return;
+ }
+ // Path is guaranteed to have no trailing slash.
+ memcpy(&buffer, &fs->path.mem[0], 255);
+ u8 slash_i = 0;
+ for (int i=0; i<255; i++) {
+ if (buffer[i] == '/') {
+ slash_i = i;
+ } else if (buffer[i] == 0) {
+ // Trim to leave the slash
+ buffer[slash_i+1] = 0;
+ break;
+ }
+ }
+ fs_open_entry(fs, &buffer[0]);
+ } else {
+ fs_open_entry(fs, (u8*) "/");
+ }
+}
+
+void fs_descend(FileDevice *fs) {
+ fs->success = false;
+ if (!(fs->open && fs->is_dir && fs->child)) {
+ return;
+ }
+ memcpy(&buffer, &fs->path.mem[0], 255);
+ // Find location of terminating null in buffer.
+ int start = 0;
+ for (int i=0; i<255; i++) {
+ start = i;
+ if (buffer[i] == 0) break;
+ }
+ // Find length of child name.
+ int len = 0;
+ for (int i=0; i<255; i++) {
+ len = i;
+ if (fs->child->d_name[i] == 0) break;
+ }
+ // Check that buffer has capacity for child name.
+ if (start+len > 255) {
+ fs->success = false;
+ return;
+ }
+ // Add a slash if not at root.
+ if (start != 1) {
+ buffer[start] = '/';
+ start++;
+ }
+ // Copy child name into buffer.
+ for (int i=0; i<len; i++) {
+ buffer[start+i] = fs->child->d_name[i];
+ }
+ fs_open_entry(fs, &buffer[0]);
+}
+
+void fs_seek(FileDevice *fs) {
+ if (fs->open) {
+ if (fs->is_dir) {
+ fs_select_child(fs, fs->pointer_write);
+ } else {
+ fseek(fs->file, fs->pointer_write, SEEK_SET);
+ fs->pointer = ftell(fs->file);
+ }
+ }
+}
+
+void fs_resize(FileDevice *fs) {
+ if (fs->open && !fs->is_dir) {
+ // truncate(fs->file);
+ // TODO
+ }
+}
diff --git a/arm9/source/devices/file.h b/arm9/source/devices/file.h
new file mode 100644
index 0000000..86ca039
--- /dev/null
+++ b/arm9/source/devices/file.h
@@ -0,0 +1,52 @@
+#include <dirent.h>
+#include "../types/pathbuf.h"
+
+// #include <stdio.h>
+// #include <dirent.h>
+// #include <string.h>
+// #include <sys/stat.h>
+
+#ifndef FILE_H_
+ #define FILE_H_
+
+ typedef struct {
+ bool open; // true if an entry is open
+ bool success; // true if the previous operation was successful
+ bool is_dir; // true if the open entry is a directory
+ bool child_is_dir; // true if the selected child is a directory
+ PathBuf entry; // write buffer for entry port
+ PathBuf action; // write buffer for action port
+ PathBuf path; // path of the open entry
+ PathBuf child_path; // path of the selected child
+ u32 pointer; // pointer in the open entry, whether dir or file
+ u32 length; // length of the open entry, whether dir or file
+ u32 pointer_write; // write cache for pointer
+ u32 length_write; // write cache for length
+
+ DIR *dir; // structure pointing to current directory
+ FILE *file; // opaque ID referencing the open file
+
+ FILE *tmpfile; // to open file and keep existing file open
+ struct dirent *child; // currently-selected directory child information
+ u32 dir_i; // index of next child to read from dir
+ } FileDevice;
+
+ void init_filesystem();
+ bool filesystem_enabled();
+
+ void fs_push_entry(FileDevice *fs, u8 byte);
+ void fs_push_action(FileDevice *fs, u8 byte);
+ bool fs_push_byte(PathBuf *buf, u8 byte);
+
+ u8 fs_read_byte(FileDevice *fs);
+ void fs_write_byte(FileDevice *fs, u8 byte);
+
+ void fs_ascend(FileDevice *fs);
+ void fs_descend(FileDevice *fs);
+
+ void fs_seek(FileDevice *fs);
+ void fs_resize(FileDevice *fs);
+
+ void fs_select_child(FileDevice *fs, u32 pointer);
+
+#endif
diff --git a/arm9/source/devices/system.c b/arm9/source/devices/system.c
index e69de29..3406eed 100644
--- a/arm9/source/devices/system.c
+++ b/arm9/source/devices/system.c
@@ -0,0 +1,16 @@
+#include "nds.h"
+#include "file.h"
+#include "system.h"
+
+
+u8 devices_high() {
+ return 0b11111100;
+}
+
+u8 devices_low() {
+ u8 devices = 0;
+ if (filesystem_enabled()) {
+ devices |= 0b00100000;
+ }
+ return devices;
+}
diff --git a/arm9/source/devices/system.h b/arm9/source/devices/system.h
index 27619e3..7549f0f 100644
--- a/arm9/source/devices/system.h
+++ b/arm9/source/devices/system.h
@@ -10,4 +10,7 @@
u8 wake; // ID of wake device
} SystemDevice;
+ u8 devices_high();
+ u8 devices_low();
+
#endif
diff --git a/arm9/source/main.c b/arm9/source/main.c
index ae983b3..8be7837 100644
--- a/arm9/source/main.c
+++ b/arm9/source/main.c
@@ -68,6 +68,7 @@ int main(void) {
init_screens();
init_clock();
+ init_filesystem();
lcdMainOnBottom();
// TODO: Remove
diff --git a/arm9/source/types/pathbuf.c b/arm9/source/types/pathbuf.c
new file mode 100644
index 0000000..1a610c8
--- /dev/null
+++ b/arm9/source/types/pathbuf.c
@@ -0,0 +1,52 @@
+#include <nds.h>
+#include "pathbuf.h"
+
+u8 pb_read(PathBuf *buf) {
+ u8 output = buf->mem[buf->p];
+ if (output) buf->p++;
+ return output;
+}
+
+void pb_clear(PathBuf *buf) {
+ memset(&buf->mem, 0, 256);
+}
+
+void pb_reset(PathBuf *buf, bool to_name) {
+ buf->p = 0;
+ if (to_name) for (int i=0; i<255; i++) {
+ if (buf->mem[i] == '/') {
+ buf->p = i+1;
+ } else if (buf->mem[i] == 0) {
+ break;
+ }
+ }
+}
+
+// Push a byte to a PathBuf, return true and reset the pointer if byte is null.
+bool pb_push_byte(PathBuf *buf, u8 byte) {
+ // Stop before overwriting the pointer.
+ if (buf->p != 0xff) {
+ buf->mem[buf->p++] = byte;
+ }
+ if (byte == 0) {
+ buf->p = 0;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool pb_is_terminated(PathBuf *buf) {
+ for (int i=0; i<255; i++) {
+ if (buf->mem[i] == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void pb_populate(PathBuf *buf, u8 *path) {
+ strncpy((char*) &buf->mem[0], (char*) path, 254);
+ buf->p = 0;
+}
+
diff --git a/arm9/source/types/pathbuf.h b/arm9/source/types/pathbuf.h
new file mode 100644
index 0000000..7fd7251
--- /dev/null
+++ b/arm9/source/types/pathbuf.h
@@ -0,0 +1,18 @@
+#include <nds.h>
+
+#ifndef PATHBUF_H_
+ #define PATHBUF_H_
+
+ typedef struct {
+ u8 mem[255];
+ u8 p;
+ } PathBuf;
+
+ u8 pb_read(PathBuf *buf);
+ void pb_clear(PathBuf *buf);
+ void pb_reset(PathBuf *buf, bool to_name);
+ bool pb_push_byte(PathBuf *buf, u8 byte);
+ bool pb_is_terminated(PathBuf *buf);
+ void pb_populate(PathBuf *buf, u8 *path);
+
+#endif