aboutsummaryrefslogtreecommitdiff
path: root/arm9/source/devices
diff options
context:
space:
mode:
Diffstat (limited to 'arm9/source/devices')
-rw-r--r--arm9/source/devices/clock.c101
-rw-r--r--arm9/source/devices/clock.h36
-rw-r--r--arm9/source/devices/file.c422
-rw-r--r--arm9/source/devices/file.h77
-rw-r--r--arm9/source/devices/input.c93
-rw-r--r--arm9/source/devices/input.h33
-rw-r--r--arm9/source/devices/local.c11
-rw-r--r--arm9/source/devices/local.h8
-rw-r--r--arm9/source/devices/math.c143
-rw-r--r--arm9/source/devices/math.h40
-rw-r--r--arm9/source/devices/memory.c231
-rw-r--r--arm9/source/devices/memory.h60
-rw-r--r--arm9/source/devices/screen.c421
-rw-r--r--arm9/source/devices/screen.h100
-rw-r--r--arm9/source/devices/stream.c40
-rw-r--r--arm9/source/devices/stream.h33
-rw-r--r--arm9/source/devices/system.c37
-rw-r--r--arm9/source/devices/system.h15
18 files changed, 1096 insertions, 805 deletions
diff --git a/arm9/source/devices/clock.c b/arm9/source/devices/clock.c
index 6ecee1d..c05ba2b 100644
--- a/arm9/source/devices/clock.c
+++ b/arm9/source/devices/clock.c
@@ -1,55 +1,84 @@
-#include <nds.h>
-#include <time.h>
#include "clock.h"
-#include "../bang.h"
-// Uptime is the number of 1/256th second ticks since the emulator began.
+
+/*
+TODO: Add functions to set the system time and date.
+*/
+
+
+// ------ UPTIME AND DATETIME --------------------------------------------------
+
+// Uptime is the number of 1/256th second ticks elapsed since the emulator began.
static u32 uptime;
-u32 get_uptime(void) { return uptime; }
+
+// Increment the local uptime value.
void uptime_handler(void) { uptime++; }
-// Check if any timer has expired.
-bool check_timers(ClockDevice *clk) {
- bool output = FALSE;
- if (clk->t1.end && clk->t1.end <= uptime) { clk->t1.end = 0; output = TRUE; }
- if (clk->t2.end && clk->t2.end <= uptime) { clk->t2.end = 0; output = TRUE; }
- if (clk->t3.end && clk->t3.end <= uptime) { clk->t3.end = 0; output = TRUE; }
- if (clk->t4.end && clk->t4.end <= uptime) { clk->t4.end = 0; output = TRUE; }
- return output;
+// Configure timer 0 to increment the local uptime value every tick.
+void init_nds_clock(void) {
+ // Start a 256Hz timer to increment the uptime value.
+ timerStart(0, ClockDivider_1024, TIMER_FREQ_1024(256), uptime_handler);
}
-u8 get_timer_high(ClockTimer *t) {
- if (t->end > uptime) {
- t->read = t->end - uptime;
- } else {
- t->end = 0; t->read = 0;
- }
- return HIGH(t->read);
+// Return the current time and date.
+struct tm* get_datetime(void) {
+ time_t timestamp = time(NULL);
+ struct tm* datetime = localtime(&timestamp);
+ return datetime;
}
-u8 get_timer_low(ClockTimer *t) {
- return LOW(t->read);
+
+// ------ TIMERS ---------------------------------------------------------------
+
+// Reset a clock timer.
+void timer_reset(ClockTimer *timer) {
+ timer->end = 0;
+ timer->read = 0;
+ timer->write = 0;
}
-void set_timer_high(ClockTimer *t, u8 high) {
- SET_HIGH(t->write, high);
+// Update the cached read value of a timer.
+void timer_read(ClockTimer *timer) {
+ if (timer->end > uptime) {
+ timer->read = timer->end - uptime;
+ } else {
+ timer->read = 0;
+ timer->end = 0;
+ }
}
-void set_timer_low(ClockTimer *t, u8 low) {
- SET_LOW(t->write, low);
- if (t->write) {
- t->end = uptime + t->write;
+
+// Update the value of a timer using the cached write value.
+void timer_write(ClockTimer *timer) {
+ if (timer->write) {
+ timer->end = uptime + timer->write;
} else {
- t->end = 0;
+ timer->end = 0;
}
}
-void init_clock(void) {
- // Start a 256Hz timer to increment the uptime value.
- timerStart(0, ClockDivider_1024, TIMER_FREQ_1024(256), uptime_handler);
+
+// ------ CLOCK ----------------------------------------------------------------
+
+// Reset the clock device.
+void clock_reset(ClockDevice *clock) {
+ clock->start = uptime;
+ timer_reset(&clock->t1);
+ timer_reset(&clock->t2);
+ timer_reset(&clock->t3);
+ timer_reset(&clock->t4);
}
-struct tm* get_datetime(void) {
- time_t timestamp = time(NULL);
- struct tm* datetime = localtime(&timestamp);
- return datetime;
+// Update the cached uptime value.
+void clock_uptime_read(ClockDevice *clock) {
+ clock->uptime = uptime - clock->start;
+}
+
+// Return true if any timer has expired.
+bool clock_check_timers(ClockDevice *clock) {
+ bool output = false;
+ if (clock->t1.end && clock->t1.end <= uptime) { clock->t1.end = 0; output = true; }
+ if (clock->t2.end && clock->t2.end <= uptime) { clock->t2.end = 0; output = true; }
+ if (clock->t3.end && clock->t3.end <= uptime) { clock->t3.end = 0; output = true; }
+ if (clock->t4.end && clock->t4.end <= uptime) { clock->t4.end = 0; output = true; }
+ return output;
}
diff --git a/arm9/source/devices/clock.h b/arm9/source/devices/clock.h
index 04c4d5d..4dc4b41 100644
--- a/arm9/source/devices/clock.h
+++ b/arm9/source/devices/clock.h
@@ -1,18 +1,24 @@
-#include <time.h>
-
#ifndef CLOCK_H_
#define CLOCK_H_
+ #include <time.h>
+ #include "../bang.h"
+
+
+ // A 16-bit countdown timer.
typedef struct {
- u32 end; // real end time
- u16 read, write; // read write caches
+ u32 end; // End time as an uptime value, zero if inactive
+ u16 read, write; // Read write caches for remaining duration
} ClockTimer;
+ // Bedrock clock device.
typedef struct {
- ClockTimer t1, t2, t3, t4; // timers
- u32 start; // uptime offset
+ ClockTimer t1, t2, t3, t4; // Timers
+ u16 uptime; // Read cache for uptime
+ u32 start; // Uptime offset
} ClockDevice;
+ // Extract fields from a tm struct.
#define YEAR(tm) (tm->tm_year - 100)
#define MONTH(tm) (tm->tm_mon)
#define DAY(tm) (tm->tm_mday - 1)
@@ -20,16 +26,16 @@
#define MINUTE(tm) (tm->tm_min)
#define SECOND(tm) (tm->tm_sec)
- u32 get_uptime(void);
- void uptime_handler(void);
- void init_clock(void);
+ // Functions.
+ void init_nds_clock(void);
struct tm* get_datetime(void);
- bool check_timers(ClockDevice *clk);
-
- u8 get_timer_high(ClockTimer *t);
- u8 get_timer_low( ClockTimer *t);
- void set_timer_high(ClockTimer *t, u8 high);
- void set_timer_low( ClockTimer *t, u8 low);
+ // Timer methods.
+ void timer_read(ClockTimer *timer);
+ void timer_write(ClockTimer *timer);
+ // Clock methods.
+ void clock_reset(ClockDevice *clock);
+ void clock_uptime_read(ClockDevice *clock);
+ bool clock_check_timers(ClockDevice *clock);
#endif
diff --git a/arm9/source/devices/file.c b/arm9/source/devices/file.c
index dcc987d..546ca0c 100644
--- a/arm9/source/devices/file.c
+++ b/arm9/source/devices/file.c
@@ -1,279 +1,323 @@
-#include "nds.h"
-#include "fat.h"
#include "file.h"
-#include <sys/stat.h>
+
+/*
+TODO: I believe that multiple open files are supported, but I'm not so sure
+about multiple open directories. I'm not sure what the limit is for maximum
+open files or directories, or whether it's possible to accidentally exceed
+this limit with multiple Bedrock instances.
+
+TODO: I need to thoroughly test every function of the device on hardware.
+There's a lot that I'm not sure about.
+*/
+
+
+// True if the filesystem is available.
static bool FS_ENABLE = false;
-static u8 buffer[255];
+
+// Scratch buffer for manipulating paths when ascending and descending.
+static u8 scratch[256];
-void init_filesystem() {
+// Attempt to initialise the filesystem.
+void init_nds_filesystem(void) {
FS_ENABLE = fatInitDefault();
}
-bool filesystem_enabled() {
+// Return whether the filesystem is available.
+bool nds_filesystem_enabled() {
return FS_ENABLE;
}
-bool is_valid_path(u8 *path) {
- // Prevent non-absolute paths from being opened.
- if (path[0] != '/') {
- return false;
- }
- // 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 false;
- }
- // Check for '..' components.
- if (path[i+2]=='.' && (path[i+3]=='/' || path[i+3]==0)) {
- return false;
- }
- }
- } else if (path[i] == 0) {
- break;
- }
+// Rewind the seek position of the current directory.
+void fs_rewind_dir(FileDevice *fs) {
+ if (fs->dir) {
+ rewinddir(fs->dir);
+ fs->dir_i = 0;
}
- return true;
}
+// Reset the current entry to its original state.
+void fs_reset_entry(FileDevice *fs) {
+ if (fs->file) fseek(fs->file, 0, SEEK_SET);
+ fs_rewind_dir(fs);
+ fs->child = NULL;
+ pathbuf_clear(&fs->child_path);
+ fs->address = 0;
+ fs->length = 0;
+ fs->length_cached = false;
+}
+
+// Close the current entry, if any. Don't clear the open and action buffers,
+// they'll be needed for opening a file or performing an action after this
+// method returns. They'll need to be cleared manually.
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;
+ if (fs->file) {
+ fclose(fs->file);
+ fs->file = NULL;
+ }
+ if (fs->dir) {
+ closedir(fs->dir);
+ fs->dir = NULL;
+ fs->dir_i = 0;
+ fs->is_root = false;
+ fs->child = NULL;
+ }
+ // Clear variables and buffers.
+ pathbuf_clear(&fs->path);
+ pathbuf_clear(&fs->child_path);
+ fs->address = 0;
fs->length = 0;
- fs->child = NULL;
- fs->dir_i = 0;
- fs->at_root = false;
+ fs->length_cached = false;
}
+// Clear the open and action buffers.
void fs_clear(FileDevice *fs) {
- pb_clear(&fs->entry);
- pb_clear(&fs->action);
+ pathbuf_clear(&fs->open);
+ pathbuf_clear(&fs->action);
}
-void fs_open_entry(FileDevice* fs, u8 *path) {
- fs->success = false;
- if (!is_valid_path(path)) return;
+// Reset a file device.
+void fs_reset(FileDevice *fs) {
+ fs_close(fs);
+ fs_clear(fs);
+ fs->address_write = 0;
+ fs->length_write = 0;
+ fs->error = false;
+}
+
+// Calculate the length of the current entry.
+void fs_calculate_length(FileDevice *fs) {
+ if (fs->length_cached) return;
+ if (fs->dir) {
+ // Find directory length by reading directory entries until null is
+ // returned. If the current directory is not the root directory,
+ // decrease the length by two to ignore the . and .. dirs.
+ fs_rewind_dir(fs);
+ for (fs->length=0; readdir(fs->dir); fs->length++);
+ if (!fs->is_root) fs->length -= 2;
+ fs_rewind_dir(fs);
+ } else if (fs->file) {
+ // Find file length by seeking to the end of the file.
+ fseek(fs->file, 0, SEEK_END);
+ fs->length = ftell(fs->file);
+ // Restore previous seek position.
+ fseek(fs->file, fs->address, SEEK_SET);
+ }
+ fs->length_cached = true;
+}
+
+// Returns true if an entry is open.
+bool fs_get_open(FileDevice *fs) {
+ return fs->file || fs->dir;
+}
+
+// Returns true if the error flag is set, then clears the error flag.
+bool fs_get_error(FileDevice *fs) {
+ if (fs->error) {
+ fs->error = false;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// Returns true if a directory is open.
+bool fs_get_type(FileDevice *fs) {
+ return fs->dir;
+}
+
+// Returns true if a child is selected and that child is a directory.
+bool fs_get_child_type(FileDevice *fs) {
+ return fs->child && fs->child->d_type == DT_DIR;
+}
+// Attempt to open the given path as a filesystem entry, keeping the original
+// entry open on failure. Path must be valid.
+void fs_open_entry(FileDevice* fs, u8 *path) {
+ // Attempt to open the path as a directory.
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++;
- };
- // If this is not the root directory, reduce len by 2 for . and .. dirs.
- if (path[1] == 0) { fs->at_root = true; } else { fs->dir_i -= 2; }
- fs->length = fs->dir_i;
- fs_select_child(fs, 0);
- pb_populate(&fs->path, path);
+ pathbuf_populate(&fs->path, path);
+ if (pathbuf_is_root(&fs->path)) fs->is_root = true;
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
+ // Attempt to open the path as a file. Moving this to before the
+ // directory check will cause a memory leak. Specifically, the memory
+ // leak is caused when opening and closing a file many thousands of times.
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);
+ pathbuf_populate(&fs->path, path);
return;
}
+ // Could not open entry.
+ fs->error = true;
}
-void fs_select_child(FileDevice *fs, u32 pointer) {
- // Increment past the . and .. directories.
- if (!fs->at_root) {
- pointer += 2;
- }
- fs->pointer = pointer;
- rewinddir(fs->dir);
-
- for (fs->dir_i=0; fs->dir_i<pointer+1; fs->dir_i++) {
+// Select the nth child of the current directory. Directory must be open.
+void fs_select_child(FileDevice *fs, u32 n) {
+ fs->address = n;
+ // Ignore the . and .. entries.
+ if (!fs->is_root) n += 2;
+ // Iterate to the nth child.
+ for (fs_rewind_dir(fs); fs->dir_i<=n; 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;
+ pathbuf_populate(&fs->child_path, (u8*) &fs->child->d_name);
} else {
- pb_clear(&fs->child_path);
- fs->child_is_dir = false;
+ pathbuf_clear(&fs->child_path);
+ fs->error = true;
}
}
-void fs_push_entry(FileDevice *fs, u8 byte) {
- if (pb_push_byte(&fs->entry, byte)) {
+// Push a byte to the open port.
+void fs_push_open(FileDevice *fs, u8 byte) {
+ if (pathbuf_push(&fs->open, byte)) {
fs_close(fs);
- if (fs->entry.mem[0] != 0) {
- fs->success = false;
- if (pb_is_terminated(&fs->entry)) {
- fs_open_entry(fs, fs->entry.mem);
+ if (!pathbuf_is_empty(&fs->open)) {
+ if (pathbuf_is_valid(&fs->open)) {
+ fs_open_entry(fs, (u8*) &fs->open.mem);
+ } else {
+ fs->error = true;
}
}
fs_clear(fs);
}
}
+// Push a byte to the action port.
void fs_push_action(FileDevice *fs, u8 byte) {
- if (pb_push_byte(&fs->action, byte)) {
- fs->success = false;
- bool action = fs->action.mem[0] != 0;
- if (fs->open) {
- if (action) {
- if (!pb_is_terminated(&fs->action)) return;
- if (!is_valid_path(fs->action.mem)) return;
- fs->success = rename((char*) fs->path.mem, (char*) fs->action.mem);
+ if (pathbuf_push(&fs->action, byte)) {
+ bool is_entry = !pathbuf_is_empty(&fs->path);
+ bool is_action = !pathbuf_is_empty(&fs->action);
+ if (is_entry) {
+ if (is_action) {
+ if (!pathbuf_is_valid(&fs->action)) return;
+ // Move open entry to action path.
+ if (rename((char*) &fs->path.mem, (char*) &fs->action.mem)) {
+ fs->error = true;
+ };
} else {
- // TODO: DELETE
+ // TODO: Delete the open entry. The remove() function will
+ // delete a file or an empty directory, so we will need to
+ // create a function that can recursively delete all entries
+ // within a directory.
+ }
+ } else {
+ if (is_action) {
+ if (!pathbuf_is_valid(&fs->action)) return;
+ // Create all parent directories.
+ for (u8 *p=&fs->action.mem[1]; *p; p++) {
+ // Temporarily null out each path separator to get the
+ // path of each parent directory.
+ if (*p == '/') {
+ *p = 0; mkdir((char*) &fs->action.mem, 0777); *p = '/';
+ }
+ }
+ // Create a new file at action path.
+ FILE *tmpfile = fopen((char*) &fs->action.mem, "w");
+ if (!tmpfile) fs->error = true;
+ fclose(tmpfile);
}
- } else if (action) {
- if (!pb_is_terminated(&fs->action)) return;
- if (!is_valid_path(fs->action.mem)) return;
- create_parents(fs->action.mem);
- FILE *tmpfile = fopen((char*) fs->action.mem, "w");
- fs->success = tmpfile != NULL;
- fclose(tmpfile);
}
fs_close(fs);
fs_clear(fs);
}
}
-// Recursively create parent directories.
-void create_parents(u8 *path) {
- // Iterate over characters left-to-right.
- for (u8 *p=path+1; *p; p++) {
- if (*p == '/') {
- *p = 0;
- mkdir((char*) path, 0777);
- *p = '/';
- }
- }
- return;
-}
-
-
+// Read a byte from the file head.
u8 fs_read_byte(FileDevice *fs) {
+ // TODO: Check that this returns 0 if fs->file is NULL. Look into
+ // setting fs->error on error.
+ fs->address++;
return fgetc(fs->file);
}
+// Write a byte to the file head.
void fs_write_byte(FileDevice *fs, u8 byte) {
+ // TODO: Look into setting fs->error on error.
+ fs->address++;
fputc(byte, fs->file);
}
+// Attempt to ascend to the parent directory.
void fs_ascend(FileDevice *fs) {
- fs->success = false;
- if (fs->open) {
+ if (!pathbuf_is_empty(&fs->path)) {
// Don't ascend if we're already at the root.
- if (fs->path.mem[1] == 0) {
+ if (fs->is_root) {
+ fs_reset_entry(fs);
+ fs->error = true;
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;
- }
+ // Find and null the final slash in the path to get the path of the
+ // parent directory. Path is guaranteed not to have a trailing slash.
+ // If the final slash is at index 0, we need to null past the slash
+ // instead, getting the root directory.
+ memcpy(&scratch, &fs->path.mem, 256);
+ u8 slash_i = 1;
+ for (int i=1; i<256; i++) {
+ if (scratch[i] == '/') slash_i = i;
+ if (scratch[i] == 0 ) break;
}
- fs_open_entry(fs, &buffer[0]);
+ scratch[slash_i] = 0;
+ fs_open_entry(fs, (u8*) &scratch);
+ fs_reset_entry(fs);
} else {
+ // Open the default directory if no entry is open.
fs_open_entry(fs, (u8*) "/");
+ fs_reset_entry(fs);
}
}
+// Attempt to descend to the selected child entry.
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];
+ if (fs->child) {
+ memcpy(&scratch, &fs->path.mem, 256);
+ // Find length of current path.
+ int path_len = 0;
+ while (scratch[path_len]) path_len++;
+ // Find length of child name.
+ int name_len = 0;
+ while (fs->child->d_name[name_len]) name_len++;
+ // Test that buffer has capacity for child name.
+ if (path_len+1+name_len > 255) {
+ fs_reset_entry(fs);
+ fs->error = true;
+ return;
+ }
+ // Append a slash to the path if not at root.
+ if (path_len != 1) scratch[path_len++] = '/';
+ // Copy child name into buffer.
+ for (int i=0; i<name_len; i++) {
+ scratch[path_len+i] = fs->child->d_name[i];
+ }
+ fs_open_entry(fs, (u8*) &scratch);
+ fs_reset_entry(fs);
+ } else {
+ fs->error = true;
}
- fs_open_entry(fs, &buffer[0]);
}
+// Seek within the current entry.
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);
- }
+ if (fs->dir) {
+ fs_select_child(fs, fs->address_write);
+ } else if (fs->file) {
+ fseek(fs->file, fs->address_write, SEEK_SET);
+ fs->address = ftell(fs->file);
}
}
+// Resize the current entry.
void fs_resize(FileDevice *fs) {
- if (fs->open && !fs->is_dir) {
+ if (fs->file) {
// truncate(fs->file);
- // TODO
+ // TODO: Figure out how to resize a file.
}
}
diff --git a/arm9/source/devices/file.h b/arm9/source/devices/file.h
index f295e89..3f27bda 100644
--- a/arm9/source/devices/file.h
+++ b/arm9/source/devices/file.h
@@ -1,55 +1,54 @@
-#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
+ #include <dirent.h>
+ #include <sys/stat.h>
+ #include "fat.h"
+ #include "nds.h"
+ #include "../bang.h"
+ #include "../types/pathbuf.h"
- 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
- bool at_root; // current entry is the root directory
- } FileDevice;
+ // Bedrock file device.
+ typedef struct {
+ FILE *file; // Opaque ID referencing current file (if any)
+ // See ANSI C89 Standard, page 126
+ DIR *dir; // Structure referencing current directory (if any)
+ // See 'libnds/libs/libnds/include/sys/dirent.h:41'
+ u32 dir_i; // Index of next child to be read from current directory
+ bool is_root; // True if the current directory is the root directory.
+ struct dirent *child; // Information about selected child (file or dir)
+ // See 'libnds/libs/libnds/include/sys/dirent.h:17'
- void init_filesystem();
- bool filesystem_enabled();
+ PathBuf open; // Write buffer for open port
+ PathBuf action; // Write buffer for action port
+ PathBuf path; // Path of current entry
+ PathBuf child_path; // Path of selected child
+ u32 address; // Address into current entry (file or dir)
+ u32 length; // Length of current entry (file or dir)
+ bool length_cached; // True if length has been calculated.
+ u32 address_write; // Write cache for address
+ u32 length_write; // Write cache for length
+ bool error; // True if error occurred since last read
+ } FileDevice;
- void create_parents(u8 *path);
+ // Functions.
+ void init_nds_filesystem();
+ bool nds_filesystem_enabled();
- void fs_push_entry(FileDevice *fs, u8 byte);
+ // Methods.
+ void fs_reset(FileDevice *fs);
+ void fs_calculate_length(FileDevice *fs);
+ bool fs_get_open(FileDevice *fs);
+ bool fs_get_error(FileDevice *fs);
+ bool fs_get_type(FileDevice *fs);
+ bool fs_get_child_type(FileDevice *fs);
+ void fs_push_open(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/input.c b/arm9/source/devices/input.c
index e5ac5be..401f053 100644
--- a/arm9/source/devices/input.c
+++ b/arm9/source/devices/input.c
@@ -1,14 +1,40 @@
-#include <nds.h>
-#include "../bang.h"
#include "input.h"
+#include "../config.h"
-void inp_receive_byte(InputDevice *inp, u8 byte) {
- cb_write_byte(&inp->keybuffer, byte);
- inp->wake = true;
+
+// Reset an input device.
+void input_reset(InputDevice *input) {
+ input->pointer = false;
+ input->keyboard = false;
+ input->x = 0;
+ input->y = 0;
+ input->x_read = 0;
+ input->y_read = 0;
+ circbuf_clear(&input->keybuffer);
+ input->gamepad = 0;
+ input->wake = false;
+}
+
+// Update the cached read value for the pointer x coordinate.
+void input_read_x(InputDevice *input) {
+ input->x_read = input->x;
+}
+
+// Update the cached read value for the pointer y coordinate.
+void input_read_y(InputDevice *input) {
+ input->y_read = input->y;
}
-// Read gamepad state into an input device.
-void inp_read_gamepad(InputDevice *inp) {
+// Receive a byte from a keyboard.
+// TODO: Make this function Unicode-aware by receiving a u32 instead of
+// a u8, so that the keybuffer can be checked for capacity before pushing.
+void input_receive_key(InputDevice *input, u8 byte) {
+ circbuf_write(&input->keybuffer, byte);
+ input->wake = true;
+}
+
+// Read the current gamepad state.
+void input_update_gamepad(InputDevice *input) {
u32 held = keysHeld();
u8 gamepad = (
TEST(held, KEY_UP) << 7
@@ -20,45 +46,34 @@ void inp_read_gamepad(InputDevice *inp) {
| TEST(held, KEY_X) << 1
| TEST(held, KEY_Y) << 0
);
- if (gamepad != inp->gamepad) {
- inp->wake = TRUE;
- inp->gamepad = gamepad;
+ // If the L and R bumper keys aren't being used to switch between
+ // instances, map them to be alternate X and Y keys.
+ if (!SWITCH_BETWEEN_INSTANCES) {
+ gamepad |= TEST(held, KEY_L) << 1;
+ gamepad |= TEST(held, KEY_R) << 0;
}
-}
-
-// Read navigation state into an input device.
-void inp_read_navigation(InputDevice *inp) {
- u32 held = keysHeld();
- u8 navigation = (
- TEST(held, KEY_UP) << 7
- | TEST(held, KEY_DOWN) << 6
- | TEST(held, KEY_LEFT) << 5
- | TEST(held, KEY_RIGHT) << 4
- | TEST(held, KEY_A) << 3
- | TEST(held, KEY_B) << 2
- | TEST(held, KEY_R) << 1
- | TEST(held, KEY_L) << 0
- );
- if (navigation != inp->navigation) {
- inp->wake = TRUE;
- inp->navigation = navigation;
+ if (gamepad != input->gamepad) {
+ input->wake = true;
+ input->gamepad = gamepad;
}
}
-// Read touchscreen state into an input device.
-void inp_read_touch(InputDevice *inp) {
+// Read the current touchscreen state.
+void input_update_touch(InputDevice *input) {
+ // Test if pointer is active.
bool pointer = TEST(keysHeld(), KEY_TOUCH);
- if (pointer != inp->pointer) {
- inp->wake = TRUE;
- inp->pointer = pointer;
+ if (pointer != input->pointer) {
+ input->wake = true;
+ input->pointer = pointer;
}
+ // Update pointer position.
if (pointer) {
- touchPosition pos;
- touchRead(&pos);
- if (pos.px != inp->x || pos.py != inp->y) {
- inp->wake = TRUE;
- inp->x = pos.px;
- inp->y = pos.py;
+ touchPosition position;
+ touchRead(&position);
+ if (position.px != input->x || position.py != input->y) {
+ input->wake = true;
+ input->x = position.px;
+ input->y = position.py;
}
}
}
diff --git a/arm9/source/devices/input.h b/arm9/source/devices/input.h
index 4f91ed5..6df947b 100644
--- a/arm9/source/devices/input.h
+++ b/arm9/source/devices/input.h
@@ -1,21 +1,26 @@
-#include <nds.h>
-#include "../types/circbuf.h"
-
#ifndef INPUT_H_
#define INPUT_H_
+ #include "../bang.h"
+ #include "../types/circbuf.h"
+
+
+ // Bedrock input device.
typedef struct {
- bool pointer; // pointer active
- bool keyboard; // keyboard active
- CircBuf keybuffer; // queued keypresses
- u16 x,y; // pointer position
- u8 navigation; // navigation state
- u8 gamepad; // gamepad state
- bool wake; // wake flag
+ bool pointer; // Pointer active
+ bool keyboard; // Keyboard active
+ u16 x, y; // Pointer position
+ u16 x_read, y_read; // Read caches for pointer position
+ CircBuf keybuffer; // Queued keypresses
+ u8 gamepad; // Gamepad state
+ bool wake; // Wake flag
} InputDevice;
- void inp_receive_byte(InputDevice *inp, u8 byte);
- void inp_read_gamepad(InputDevice *inp);
- void inp_read_navigation(InputDevice *inp);
- void inp_read_touch(InputDevice *inp);
+ // Methods.
+ void input_reset(InputDevice *input);
+ void input_read_x(InputDevice *input);
+ void input_read_y(InputDevice *input);
+ void input_receive_key(InputDevice *input, u8 byte);
+ void input_update_gamepad(InputDevice *input);
+ void input_update_touch(InputDevice *input);
#endif
diff --git a/arm9/source/devices/local.c b/arm9/source/devices/local.c
deleted file mode 100644
index e13a1f8..0000000
--- a/arm9/source/devices/local.c
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "../main.h"
-#include "local.h"
-
-void std_write(u8 byte) {
- if (byte) {
- receive_keyboard_byte(byte);
- } else {
- close_keyboard();
-
- }
-}
diff --git a/arm9/source/devices/local.h b/arm9/source/devices/local.h
deleted file mode 100644
index 979f5ce..0000000
--- a/arm9/source/devices/local.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#include <nds.h>
-
-#ifndef LOCAL_H_
- #define LOCAL_H_
-
- void std_write(u8 byte);
-
-#endif
diff --git a/arm9/source/devices/math.c b/arm9/source/devices/math.c
index bb592e0..33aeb63 100644
--- a/arm9/source/devices/math.c
+++ b/arm9/source/devices/math.c
@@ -1,66 +1,119 @@
-#include <nds.h>
-#include <math.h>
#include "math.h"
-#include "../bang.h"
-#define CLEAR \
- math->sqrt_rc = FALSE;\
- math->atan_rc = FALSE;\
- math->prod_rc = FALSE;\
- math->quot_rc = FALSE;\
- math->rem_rc = FALSE;
-void set_op1_high(MathDevice *math, u8 high) { SET_HIGH(math->op1, high); CLEAR; }
-void set_op1_low( MathDevice *math, u8 low) { SET_LOW(math->op1, low); CLEAR; }
-void set_op2_high(MathDevice *math, u8 high) { SET_HIGH(math->op2, high); CLEAR; }
-void set_op2_low( MathDevice *math, u8 low) { SET_LOW(math->op2, low); CLEAR; }
+// Angle conversion constants.
+const double fromRadians = 10430.378350470453; // 65536 / 2Ï€
+const double toRadians = 0.00009587379924285257; // 2Ï€ / 65536
-u16 get_sqrt(MathDevice *math) {
- if (!math->sqrt_rc) {
- u32 input = math->op1 << 16 | math->op2;
- math->sqrt = sqrt32(input);
- math->sqrt_rc = TRUE;
+
+// Reset the math device.
+void math_reset(MathDevice *math) {
+ math->x = 0;
+ math->y = 0;
+ math->r = 0;
+ math->t = 0;
+ math_clear_cartesian(math);
+ math_clear_polar(math);
+}
+
+// Clear all values calculated from cartesian coordinates.
+void math_clear_cartesian(MathDevice *math) {
+ math->r_cached = false;
+ math->t_cached = false;
+ math->prod_cached = false;
+ math->quot_cached = false;
+ math->rem_cached = false;
+}
+
+// Clear all values calculated from polar coordinates.
+void math_clear_polar(MathDevice *math) {
+ math->x_cached = false;
+ math->y_cached = false;
+}
+
+// Calculate the x coordinate of the polar point.
+s16 math_get_x(MathDevice *math) {
+ if (!math->x_cached) {
+ double x = cos(math->t * toRadians) * math->r;
+ if (-32768.0 <= x && x <= 32768.0) {
+ math->x_read = x;
+ } else {
+ math->x_read = 0;
+ }
+ math->x_cached = true;
+ }
+ return math->x_read;
+}
+
+// Calculate the y coordinate of the polar point.
+s16 math_get_y(MathDevice *math) {
+ if (!math->y_cached) {
+ double y = sin(math->t * toRadians) * math->r;
+ if (-32768.0 <= y && y <= 32767.0) {
+ math->y_read = y;
+ } else {
+ math->y_read = 0;
+ }
+ math->y_cached = true;
+ }
+ return math->y_read;
+}
+
+// Calculate the r coordinate of the cartesian point.
+u16 math_get_r(MathDevice *math) {
+ if (!math->r_cached) {
+ u32 x2 = math->x * math->x;
+ u32 y2 = math->y * math->y;
+ math->r_read = sqrt32(x2 + y2);
+ math->r_cached = true;
}
- return math->sqrt;
+ return math->r_read;
}
-u16 get_atan(MathDevice *math) {
- if (!math->atan_rc) {
- if (math->op1 || math->op2) {
- double op1 = (s16) math->op1;
- double op2 = (s16) math->op2;
- const double scale = 10430.378350470453; // PI * 32768
- math->atan = (s32) (atan2(op1, op2) * scale);
+// Calculate the t coordinate of the cartesian point.
+u16 math_get_t(MathDevice *math) {
+ if (!math->t_cached) {
+ if (math->x || math->y) {
+ math->t_read = atan2(math->y, math->x) * fromRadians;
+ } else {
+ math->t_read = 0;
}
- math->atan_rc = TRUE;
+ math->t_cached = true;
}
- return math->atan;
+ return math->t_read;
}
-u32 get_prod(MathDevice *math) {
- if (!math->prod_rc) {
- math->prod = math->op1 * math->op2;
- math->prod_rc = TRUE;
+// Calculate the product of x by y.
+u32 math_get_prod(MathDevice *math) {
+ if (!math->prod_cached) {
+ math->prod_read = (u16)math->x * (u16)math->y;
+ math->prod_cached = true;
}
- return math->prod;
+ return math->prod_read;
}
-u16 get_quot(MathDevice *math) {
- if (!math->quot_rc) {
- if (math->op2) {
- math->quot = div32(math->op1, math->op2);
+// Calculate the quotient of x by y.
+u16 math_get_quot(MathDevice *math) {
+ if (!math->quot_cached) {
+ if (math->y) {
+ math->quot_read = div32((u16)math->x, (u16)math->y);
+ } else {
+ math->quot_read = 0;
}
- math->quot_rc = TRUE;
+ math->quot_cached = true;
}
- return math->quot;
+ return math->quot_read;
}
-u16 get_rem(MathDevice *math) {
- if (!math->rem_rc) {
- if (math->op2) {
- math->rem = mod32(math->op1, math->op2);
+// Calculate the remainder of x by y.
+u16 math_get_rem(MathDevice *math) {
+ if (!math->rem_cached) {
+ if (math->y) {
+ math->rem_read = mod32((u16)math->x, (u16)math->y);
+ } else {
+ math->rem_read = 0;
}
- math->rem_rc = TRUE;
+ math->rem_cached = true;
}
- return math->rem;
+ return math->rem_read;
}
diff --git a/arm9/source/devices/math.h b/arm9/source/devices/math.h
index ae52a10..6b173b6 100644
--- a/arm9/source/devices/math.h
+++ b/arm9/source/devices/math.h
@@ -1,19 +1,33 @@
#ifndef MATH_H_
#define MATH_H_
+ #include <math.h>
+ #include "../bang.h"
+
+
+ // Bedrock math device.
typedef struct {
- u16 op1, op2;
- u16 sqrt, atan; u32 prod; u16 quot, rem; // read
- bool sqrt_rc, atan_rc, prod_rc, quot_rc, rem_rc; // read cached
- } MathDevice ;
+ s16 x, y; // Cartesian write
+ u16 r, t; // Polar write
+ s16 x_read, y_read; // Cartesian read
+ bool x_cached, y_cached; // Cartesian cached
+ u16 r_read, t_read; // Polar read
+ bool r_cached, t_cached; // Polar cached
+ u32 prod_read; // Multiplication read
+ bool prod_cached; // Multiplication cached
+ u16 quot_read, rem_read; // Division read
+ bool quot_cached, rem_cached; // Division cached
+ } MathDevice;
- void set_op1_high(MathDevice *math, u8 high);
- void set_op1_low(MathDevice *math, u8 low);
- void set_op2_high(MathDevice *math, u8 high);
- void set_op2_low(MathDevice *math, u8 low);
- u16 get_sqrt(MathDevice *math);
- u16 get_atan(MathDevice *math);
- u32 get_prod(MathDevice *math);
- u16 get_quot(MathDevice *math);
- u16 get_rem(MathDevice *math);
+ // Methods.
+ void math_reset(MathDevice *math);
+ void math_clear_cartesian(MathDevice *math);
+ void math_clear_polar(MathDevice *math);
+ s16 math_get_x(MathDevice *math);
+ s16 math_get_y(MathDevice *math);
+ u16 math_get_r(MathDevice *math);
+ u16 math_get_t(MathDevice *math);
+ u32 math_get_prod(MathDevice *math);
+ u16 math_get_quot(MathDevice *math);
+ u16 math_get_rem(MathDevice *math);
#endif
diff --git a/arm9/source/devices/memory.c b/arm9/source/devices/memory.c
index 915aec2..c9e86d7 100644
--- a/arm9/source/devices/memory.c
+++ b/arm9/source/devices/memory.c
@@ -1,143 +1,158 @@
-#include "nds.h"
#include "memory.h"
-static u8 heap[HEAP_SIZE][256]; // the page heap.
-static u8 unallocated[256]; // page to use for all unallocated reads and writes.
-static u8 heap_index[HEAP_SIZE];
-u8 mem_read1(MemoryDevice *mem) {
- u8 output = mem->point1[mem->byte1++];
- if (mem->byte1 == 0) {
- mem->page1++;
- mem_get_page1(mem);
- }
- return output;
+/*
+The 'heap' array is the global pool of memory from which pages are allocated
+and deallocated, used by every Bedrock instance. The 'heap_index' array tracks
+the owner of each page of memory: the value of each entry is the ID of the
+instance that owns / has allocated the corresponding page in the heap. An ID
+of zero indicates that the page is free / hasn't been allocated yet.
+
+If multiple instances are repeatedly allocating and deallocating pages, it's
+expected that the allocations will become very fragmented, with pages owned
+by each instance interleaving with one another. To find the heap address that
+corresponds with an allocated page address, the heap will need to be traversed,
+which makes random access very expensive. To mitigate this, the heap address
+of every page accessible from the current offset value (which determines the
+base page to address from) is cached in an array of 256 pointers, and the
+pointer corresponding with the current page is further cached as a pointer.
+
+The 'unallocated' array acts as a sort of null page, with every access of an
+unallocated page being routed instead to that page. This allows the read and
+write methods to omit a test to check whether a page is allocated or not;
+effectively, every page is always allocated. This is allowed because the
+Bedrock specification says that an unallocated read or write causes undefined
+behaviour, allowing us to optimise away the test.
+
+TODO: If one instance allocates memory, then a second instance allocates memory,
+then the first instance deallocates, then the second instance allocates some
+more, the pages allocated to the second instance will be out of order, but the
+code so far assumes that all pages are in the correct order (with gaps or not).
+There needs to be either a page reordering pass (time expensive) or an array to
+track the allocation order of pages for each instance (space expensive). The
+former approach would be the best, because it's unlikely that this would be a
+frequent occurrence. I'll need to shuffle pages around using memcpy and the null
+page. Only touch the pages of the current instance, to avoid having to recalculate
+the page caches of every instance. We can determine if pages will need to be
+shuffled / defragmented by checking if an unallocated page is traversed before
+the final allocated page is found.
+*/
+
+
+// Size of the heap in pages. The value was chosen by steadily increasing it
+// until the emulator refused to compile, and then easing back a bit.
+#define MAX_PAGES (12000)
+
+// The heap.
+static u8 heap[MAX_PAGES][256]; // the page heap.
+
+// Null page to use for unallocated reads and writes.
+static u8 unallocated[256];
+
+// Map heap pages to instances. An ID of zero represents an unallocated page.
+static u8 heap_index[MAX_PAGES];
+
+
+// Reset a read-write head on the memory device.
+void memory_head_reset(MemoryHead *head) {
+ head->offset = 0;
+ head->byte = 0;
+ head->point = (u8*) &unallocated;
+ head->page = 0;
+ for (int i=0; i<256; i++) head->cache[i] = (u8*) &unallocated;
}
-u8 mem_read2(MemoryDevice *mem) {
- u8 output = mem->point2[mem->byte2++];
- if (mem->byte2 == 0) {
- mem->page2++;
- mem_get_page2(mem);
- }
- return output;
+// Reset the memory device.
+void memory_reset(MemoryDevice *mem) {
+ mem->count = 0;
+ mem->copy = 0;
+ memory_allocate(mem);
+ memory_head_reset(&mem->head1);
+ memory_head_reset(&mem->head2);
}
-void mem_write1(MemoryDevice *mem, u8 value) {
- mem->point1[mem->byte1++] = value;
- if (mem->byte1 == 0) {
- mem->page1++;
- mem_get_page1(mem);
+// Read a byte from a memory head.
+u8 memory_read(MemoryHead *head) {
+ u8 output = head->point[head->byte++];
+ if (head->byte == 0) {
+ head->page++;
+ memory_refresh_page(head);
}
+ return output;
}
-void mem_write2(MemoryDevice *mem, u8 value) {
- mem->point2[mem->byte2++] = value;
- if (mem->byte2 == 0) {
- mem->page2++;
- mem_get_page2(mem);
+// Write a byte to a memory head.
+void memory_write(MemoryHead *head, u8 value) {
+ head->point[head->byte++] = value;
+ if (head->byte == 0) {
+ head->page++;
+ memory_refresh_page(head);
}
}
-
-void mem_load_cache1(MemoryDevice *mem) {
- // Set all cached pointers to unallocated.
- for (int i=0; i<256; i++) {
- mem->cache1[i] = (u8*) &unallocated;
- }
- // Iterate over all heap pages, gather our ones.
- int count = 0;
- int cached = 0;
- for (int i=0; i<HEAP_SIZE; i++) {
- if (heap_index[i] == mem->id) {
- if (count >= mem->offset1) {
- mem->cache1[cached] = (u8*) &heap[i];
- cached++;
- }
- count++;
- if (cached == 256) break;
- }
- }
+void memory_refresh_page(MemoryHead *head) {
+ head->point = head->cache[head->page];
}
-void mem_load_cache2(MemoryDevice *mem) {
- // Set all cached pointers to unallocated.
- for (int i=0; i<256; i++) {
- mem->cache2[i] = (u8*) &unallocated;
- }
- // Iterate over all heap pages, gather our ones.
- int count = 0;
- int cached = 0;
- for (int i=0; i<HEAP_SIZE; i++) {
- if (heap_index[i] == mem->id) {
- if (count >= mem->offset2) {
- mem->cache2[cached] = (u8*) &heap[i];
- cached++;
+// Recalculate the pointers stored in the page cache.
+void memory_refresh_cache(MemoryDevice *mem, MemoryHead *head) {
+ // Point every page in the cache to the unallocated page.
+ for (int i=0; i<256; i++) head->cache[i] = (u8*) &unallocated;
+ // Iterate over every page in the heap, gather all pages allocated to
+ // this instance that belong to the current offset.
+ int n = 0;
+ int cache_i = 0;
+ for (int heap_i=0; heap_i<MAX_PAGES; heap_i++) {
+ if (heap_index[heap_i] == mem->id) {
+ if (n++ >= head->offset) {
+ head->cache[cache_i++] = (u8*) &heap[heap_i];
+ if (cache_i == 256) break;
}
- count++;
- if (cached == 256) break;
}
}
}
-// Fetch the page1 pointer from cache1.
-void mem_get_page1(MemoryDevice *mem) {
- mem->point1 = mem->cache1[mem->page1];
-}
-
-// Fetch the page2 pointer from cache2.
-void mem_get_page2(MemoryDevice *mem) {
- mem->point2 = mem->cache2[mem->page2];
-}
-
-void mem_allocate(MemoryDevice *mem) {
- int count = 0;
-
- // ALLOCATING
- if (mem->count_write > mem->count) {
- int to_allocate = mem->count_write - mem->count;
- for (int i=0; i<HEAP_SIZE; i++) {
+// Allocate mem->count pages of memory.
+void memory_allocate(MemoryDevice *mem) {
+ // Track how many pages have been allocated/deallocated so far.
+ int n = 0;
+ // Allocate memory.
+ if (mem->count > mem->allocated) {
+ int to_allocate = mem->count - mem->allocated;
+ for (int i=0; i<MAX_PAGES; i++) {
if (heap_index[i] == 0) {
heap_index[i] = mem->id;
- count++;
- if (count == to_allocate) {
- break;
- }
+ if (++n == to_allocate) break;
}
}
- // DEALLOCATING
- } else if (mem->count_write < mem->count) {
- for (int i=0; i<HEAP_SIZE; i++) {
+ // Deallocate memory.
+ } else if (mem->count < mem->allocated) {
+ for (int i=0; i<MAX_PAGES; i++) {
if (heap_index[i] == mem->id) {
- count++;
- if (count > mem->count_write) {
- heap_index[i] = 0;
+ if (++n > mem->count) {
+ // Clear the page when it's deallocated.
memset(heap[i], 0, 256);
+ heap_index[i] = 0;
}
}
}
}
-
- // Count the number of pages allocated to us.
- mem->count = 0;
- for (int i=0; i<HEAP_SIZE; i++) {
- if (heap_index[i]==mem->id) mem->count++;
+ // Re-count the number of pages allocated to this instance.
+ mem->allocated = 0;
+ for (int i=0; i<MAX_PAGES; i++) {
+ if (heap_index[i]==mem->id) mem->allocated++;
}
- // Reload the pointer caches for each head.
- mem_load_cache1(mem);
- mem_load_cache2(mem);
+ // Refresh the page caches for both heads.
+ memory_refresh_cache(mem, &mem->head1);
+ memory_refresh_cache(mem, &mem->head2);
}
-
-void mem_do_copy(MemoryDevice *mem) {
- int src = mem->offset2;
- int dest = mem->offset1;
- if (src == dest) return;
- for (int i=0; i<mem->copy_write; i++) {
- if (src>=mem->count || dest>=mem->count) {
- return;
- }
- memcpy(&heap[dest++], &heap[src++], 256);
+// Copy mem->count pages from head 2 to head 1.
+void memory_copy(MemoryDevice *mem) {
+ if (mem->head1.offset == mem->head2.offset) return;
+ for (int n=0; n<mem->copy; n++) {
+ if (mem->head1.cache[n] == (u8*) &unallocated) return;
+ if (mem->head2.cache[n] == (u8*) &unallocated) return;
+ memcpy(mem->head1.cache[n], mem->head2.cache[n], 256);
}
}
-
diff --git a/arm9/source/devices/memory.h b/arm9/source/devices/memory.h
index 0abff16..814f47d 100644
--- a/arm9/source/devices/memory.h
+++ b/arm9/source/devices/memory.h
@@ -1,42 +1,34 @@
#ifndef MEMORY_H_
#define MEMORY_H_
- #define HEAP_SIZE (4096*3)
+ #include "../bang.h"
- typedef struct {
- u8 id; // Unique non-zero identifier for this memory device.
-
- u16 offset1; // Bedrock offset value for head 1
- u8 page1; // Bedrock address value for head 1
- u8 byte1; // Bedrock address value for head 1
- u16 offset2; // Bedrock offset value for head 2
- u8 page2; // Bedrock address value for head 2
- u8 byte2; // Bedrock address value for head 2
-
- u16 count_write; // write cache for page count
- u16 count; // number of pages allocated for this bedrock
- u16 copy_write; // write cache for page copy length
-
- u8 *point1; // pointer to current page for head 1
- u8 *point2; // pointer to current page for head 2
-
- u8* cache1[256];
- u8* cache2[256];
- } MemoryDevice ;
- u8 mem_read1(MemoryDevice *mem);
- u8 mem_read2(MemoryDevice *mem);
- void mem_write1(MemoryDevice *mem, u8 value);
- void mem_write2(MemoryDevice *mem, u8 value);
-
- void mem_load_cache1(MemoryDevice *mem);
- void mem_load_cache2(MemoryDevice *mem);
- void mem_get_page1(MemoryDevice *mem);
- void mem_get_page2(MemoryDevice *mem);
-
-
- void mem_allocate(MemoryDevice *mem);
- void mem_do_copy(MemoryDevice *mem);
+ // Read-write head for memory device.
+ typedef struct {
+ u16 offset; // Index of base page.
+ u8 page; // Index of current page from offset.
+ u8 byte; // Index of current byte in page.
+ u8 *point; // Pointer to current page.
+ u8 *cache[256]; // Pointers to all pages for the current offset.
+ } MemoryHead;
+ // Bedrock memory device.
+ typedef struct {
+ u8 id; // Non-zero ID of this Bedrock instance.
+ u16 allocated; // Number of pages allocated to this instance
+ u16 count; // Write cache for allocation count
+ u16 copy; // Write cache for copy length
+ MemoryHead head1;
+ MemoryHead head2;
+ } MemoryDevice;
+ // Methods.
+ void memory_reset(MemoryDevice *mem);
+ u8 memory_read(MemoryHead *head);
+ void memory_write(MemoryHead *head, u8 value);
+ void memory_refresh_page(MemoryHead *head);
+ void memory_refresh_cache(MemoryDevice *mem, MemoryHead *head);
+ void memory_allocate(MemoryDevice *mem);
+ void memory_copy(MemoryDevice *mem);
#endif
diff --git a/arm9/source/devices/screen.c b/arm9/source/devices/screen.c
index 698b754..bca436d 100644
--- a/arm9/source/devices/screen.c
+++ b/arm9/source/devices/screen.c
@@ -1,114 +1,123 @@
+#include "screen.h"
+
+
/*
-A screen layer contains 768 tiles (32*24), requiring 24KB. Each tile is 32 bytes,
-holding 2 pixels per byte. The upper four bits of a tile byte represent the
-colour of the right pixel, and the lower four bits the colour of the left pixel.
-The colour is a 4-bit reference into a 16-colour RGB15 palette.
+Each screen layer is 24KB and contains 768 tiles (32*24). Each tile is 32
+bytes, and contains 64 pixels (8*8), holding 2 pixels per byte. The upper
+four bits of a tile byte represent the colour of the right pixel, and the
+lower four bits represent the colour of the left pixel. The colour is a
+4-bit index into a 16-colour RGB15 palette.
+
+Four screen layers are used by each physical screen: a foreground layer, a
+background layer, and then back buffers for each. Screen layers are stored
+as u16* pointers to raw video memory.
+
+Each 'tile slot' of video memory is 16KB, so we need two slots per layer. We
+can't use memory more efficiently by packing the layers more tightly into the
+available slots, because we can only draw the contents of a layer by passing
+a tile slot base address to the video system. We can, however, use the gaps to
+store our tile map, because each map slot takes only 2KB with a corresponding
+increase in address granularity.
-Screen memory contains four layers: bg, fg, then the same but for double buffers.
-Ignore double buffering for now. We keep these as u32* because a single 32-bit
-integer represents a single 8-pixel row.
-TODO: This needs to be updated. We have double buffering, and we use u16*.
+The tile map is shared by all foreground and background layers on a physical
+screen, and is stored just past the contents of the first screen layer (at the
+24KB offset in video memory). Each entry in the map is a 16-bit tile index.
+The low 10 bits of the value represent the tile index, then h-flip, then
+v-flip, then a 4-bit palette identifier. We don't flip any tiles, and we
+always use palette zero. The map is initialised with an incrementing sequence,
+and then never touched.
-The tile map is shared by the foreground and background layers, and is stored
-just past the first layer for both screens (at the 24KB offset). Each entry is
-a 16-bit tile index (low 10 bits are tile index, then h-flip, then v-flip, then
-4-bit palette identifier).
+The memory bank system of the NDS is byzantine and most vexing, with banks being
+identified by letters A through I, and in sizes of 128/128/128/128/64/16/16/32/16
+KB, respectively. The main 2D engine can use banks A/B/C/D/E for BG data (tiles),
+and the sub 2D engine can use bank C for the same.
*/
-#include <nds.h>
-#include "screen.h"
-#include "../bang.h"
-Screen scr_main = {
- .bgv = BG_TILE_RAM(BG_SLOT_VIS),
- .fgv = BG_TILE_RAM(FG_SLOT_VIS),
- .bg = BG_TILE_RAM(BG_SLOT),
- .fg = BG_TILE_RAM(FG_SLOT),
+// ------ NDS SCREENS ----------------------------------------------------------
+
+// VRAM addresses for the main 2D engine.
+NDSScreen main_screen = {
+ .bgv = BG_TILE_RAM(BG_SLOT_FRONT),
+ .fgv = BG_TILE_RAM(FG_SLOT_FRONT),
+ .bg = BG_TILE_RAM(BG_SLOT_BACK),
+ .fg = BG_TILE_RAM(FG_SLOT_BACK),
.map = BG_MAP_RAM(MAP_SLOT),
.palv = BG_PALETTE,
};
-Screen scr_sub = {
- .bgv = BG_TILE_RAM_SUB(BG_SLOT_VIS),
- .fgv = BG_TILE_RAM_SUB(FG_SLOT_VIS),
- .bg = BG_TILE_RAM_SUB(BG_SLOT),
- .fg = BG_TILE_RAM_SUB(FG_SLOT),
+
+// VRAM addresses for the sub 2D engine.
+NDSScreen sub_screen = {
+ .bgv = BG_TILE_RAM_SUB(BG_SLOT_FRONT),
+ .fgv = BG_TILE_RAM_SUB(FG_SLOT_FRONT),
+ .bg = BG_TILE_RAM_SUB(BG_SLOT_BACK),
+ .fg = BG_TILE_RAM_SUB(FG_SLOT_BACK),
.map = BG_MAP_RAM_SUB(MAP_SLOT),
.palv = BG_PALETTE_SUB,
};
-// TODO: Make an enum thing for main/sub, combine these functions
-void scr_make_main(ScreenDevice *scr) {
- scr->nds = &scr_main;
- for (int i=0; i<16; i++) {
- scr->nds->pal[i] = scr->palette[i];
- }
- scr->wake = true;
-}
-
-void scr_make_sub(ScreenDevice *scr) {
- scr->nds = &scr_sub;
- for (int i=0; i<16; i++) {
- scr->nds->pal[i] = scr->palette[i];
- }
- scr->wake = true;
-}
-
-void scr_unmake(ScreenDevice *scr) {
- if (scr->nds) {
- black_screen(scr->nds);
- scr->nds = NULL;
- }
-}
-
-void init_screens(void) {
- // Allocate VRAM for screens
+// Initialise the VRAM mappings and tile map for both NDS screens.
+void init_nds_screens(void) {
+ // Allocate VRAM for screens.
videoSetMode(DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE | MODE_0_2D);
vramSetBankA(VRAM_A_MAIN_BG);
videoSetModeSub(DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE | MODE_0_2D);
vramSetBankC(VRAM_C_SUB_BG);
-
- /* Configure screen layers to use tile graphics. */
- REG_BG0CNT = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_3 | BG_TILE_BASE(BG_SLOT_VIS) | BG_MAP_BASE(MAP_SLOT);
- REG_BG1CNT = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_2 | BG_TILE_BASE(FG_SLOT_VIS) | BG_MAP_BASE(MAP_SLOT);
- REG_BG0CNT_SUB = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_3 | BG_TILE_BASE(BG_SLOT_VIS) | BG_MAP_BASE(MAP_SLOT);
- REG_BG1CNT_SUB = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_2 | BG_TILE_BASE(FG_SLOT_VIS) | BG_MAP_BASE(MAP_SLOT);
-
- /* Populate tile maps with tile indices. */
- int i;
+ // Configure screen layers to use tile graphics.
+ REG_BG0CNT = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_3 | BG_TILE_BASE(BG_SLOT_FRONT) | BG_MAP_BASE(MAP_SLOT);
+ REG_BG1CNT = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_2 | BG_TILE_BASE(FG_SLOT_FRONT) | BG_MAP_BASE(MAP_SLOT);
+ REG_BG0CNT_SUB = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_3 | BG_TILE_BASE(BG_SLOT_FRONT) | BG_MAP_BASE(MAP_SLOT);
+ REG_BG1CNT_SUB = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_2 | BG_TILE_BASE(FG_SLOT_FRONT) | BG_MAP_BASE(MAP_SLOT);
+ // Populate tile maps.
u16 *main_map = BG_MAP_RAM(12);
u16 *sub_map = BG_MAP_RAM_SUB(12);
- for (i = 0; i < TILES_SIZE; i++) {
+ for (int i=0; i<TILES_COUNT; i++) {
*(main_map++) = i;
*(sub_map++) = i;
}
}
-
-void set_palette_high(ScreenDevice *scr, u8 high) {
- SET_HIGH(scr->palette_write, high);
+// Copy the contents of the back buffer to the front buffer.
+void ndsscreen_flip_buffers(NDSScreen *nds) {
+ if (nds) {
+ dmaCopyWords(0, nds->bg, nds->bgv, LAYER_MEM);
+ dmaCopyWords(0, nds->fg, nds->fgv, LAYER_MEM);
+ for (int i=0; i<16; i++) nds->palv[i] = nds->pal[i];
+ }
}
-void set_palette_low(ScreenDevice *scr, u8 low) {
- SET_LOW(scr->palette_write, low);
- u8 i = scr->palette_write >> 12 & 0x0f;
- u8 r = scr->palette_write >> 7 & 0x1e;
- u8 g = scr->palette_write >> 3 & 0x1e;
- u8 b = scr->palette_write << 1 & 0x1e;
- scr->palette[i] = RGB15(r,g,b);
- if (scr->nds) {
- scr->nds->pal[i] = RGB15(r,g,b);
+
+// Erase both buffers and set every colour to black.
+void ndsscreen_clear(NDSScreen *nds) {
+ if (nds) {
+ dmaFillWords(0, nds->bgv, LAYER_MEM);
+ dmaFillWords(0, nds->fgv, LAYER_MEM);
+ dmaFillWords(0, nds->bg, LAYER_MEM);
+ dmaFillWords(0, nds->fg, LAYER_MEM);
+ for (int i=0; i<16; i++) nds->palv[i] = RGB15(0,0,0);
}
}
-void push_sprite(SpriteBuffer *b, u8 row) {
- b->mem[b->p] = row;
- b->p = (b->p + 1) % 16;
+
+// ------ SPRITE BUFFER --------------------------------------------------------
+
+// Reset a sprite buffer.
+void spritebuf_reset(SpriteBuffer *b) {
+ for (int i=0; i<16; i++) b->mem[i] = 0;
+ b->cached = false;
+}
+
+// Push a byte to a sprite buffer.
+void spritebuf_push(SpriteBuffer *b, u8 row) {
+ b->mem[b->p++] = row;
+ b->p %= 16;
b->cached = FALSE;
}
-void prepare_1bit_sprite(SpriteBuffer *b, u8 draw) {
+// Parse, transform, and cache a 1-bit sprite.
+void spritebuf_prepare_1bit(SpriteBuffer *b, u8 draw) {
u8 l,p,x,y;
if (b->cached && draw == b->draw) return;
-
+ // Parse and transform the sprite if not already cached.
switch (draw & 0x07) {
case 0x0: p=b->p+8; for (y=0;y<8;y++) { l=b->mem[p++ % 16]; for (x=0;x<8;x++) { b->sprite[y][x] = l>>(7-x) & 1; } }; break;
case 0x1: p=b->p+8; for (y=0;y<8;y++) { l=b->mem[p++ % 16]; for (x=0;x<8;x++) { b->sprite[y][x] = l>>( x) & 1; } }; break;
@@ -119,15 +128,15 @@ void prepare_1bit_sprite(SpriteBuffer *b, u8 draw) {
case 0x6: p=b->p; for (y=0;y<8;y++) { l=b->mem[--p % 16]; for (x=0;x<8;x++) { b->sprite[x][y] = l>>(7-x) & 1; } }; break;
case 0x7: p=b->p; for (y=0;y<8;y++) { l=b->mem[--p % 16]; for (x=0;x<8;x++) { b->sprite[x][y] = l>>( x) & 1; } }; break;
}
-
- b->cached = TRUE;
b->draw = draw;
+ b->cached = true;
}
-void prepare_2bit_sprite(SpriteBuffer *b, u8 draw) {
+// Parse, transform, and cache a 2-bit sprite.
+void spritebuf_prepare_2bit(SpriteBuffer *b, u8 draw) {
u8 l,h,i,p,s,x,y;
if (b->cached && draw == b->draw) return;
-
+ // Parse and transform the sprite if not already cached.
switch (draw & 0x07) {
case 0x0: p=b->p+8; s=p+8; for (y=0;y<8;y++) { l=b->mem[p++ % 16]; h=b->mem[s++ % 16]; for (x=0;x<8;x++) { i=(7-x); b->sprite[y][x] = (l>>i & 1) | (h>>i & 1) << 1; } }; break;
case 0x1: p=b->p+8; s=p+8; for (y=0;y<8;y++) { l=b->mem[p++ % 16]; h=b->mem[s++ % 16]; for (x=0;x<8;x++) { i=( x); b->sprite[y][x] = (l>>i & 1) | (h>>i & 1) << 1; } }; break;
@@ -138,14 +147,77 @@ void prepare_2bit_sprite(SpriteBuffer *b, u8 draw) {
case 0x6: p=b->p; s=p+8; for (y=0;y<8;y++) { l=b->mem[--p % 16]; h=b->mem[--s % 16]; for (x=0;x<8;x++) { i=(7-x); b->sprite[x][y] = (l>>i & 1) | (h>>i & 1) << 1; } }; break;
case 0x7: p=b->p; s=p+8; for (y=0;y<8;y++) { l=b->mem[--p % 16]; h=b->mem[--s % 16]; for (x=0;x<8;x++) { i=( x); b->sprite[x][y] = (l>>i & 1) | (h>>i & 1) << 1; } }; break;
}
-
- b->cached = TRUE;
b->draw = draw;
+ b->cached = true;
+}
+
+
+// ------ BEDROCK SCREEN -------------------------------------------------------
+
+// Reset a screen device.
+void screen_reset(ScreenDevice *screen) {
+ screen->x = 0;
+ screen->y = 0;
+ screen->px = 0;
+ screen->py = 0;
+ screen->selected = 0;
+ spritebuf_reset(&screen->sprite);
+ screen->colour = 0;
+ for (int i=0; i<16; i++) screen->pal[i] = 0;
+ ndsscreen_clear(screen->nds);
+ screen->wake = false;
+ screen->dirty = false;
}
+// Unmap a screen device from a screen.
+void screen_map_to_none(ScreenDevice *screen) {
+ if (screen->nds) {
+ ndsscreen_clear(screen->nds);
+ screen->nds = NULL;
+ }
+}
+
+// Map a screen device to the main screen.
+void screen_map_to_main(ScreenDevice *screen) {
+ screen->nds = &main_screen;
+ memcpy(&screen->nds->pal, &screen->pal, 32);
+ screen->wake = true;
+}
+
+// Map a screen device to the sub screen.
+void screen_map_to_sub(ScreenDevice *screen) {
+ screen->nds = &sub_screen;
+ memcpy(&screen->nds->pal, &screen->pal, 32);
+ screen->wake = true;
+}
+
+// Commit a new colour to the colour palette.
+void screen_commit_colour(ScreenDevice *screen) {
+ u8 i = screen->colour >> 12 & 0x0f;
+ // Extract 5-bit channel values.
+ u8 r = screen->colour >> 7 & 0x1e;
+ u8 g = screen->colour >> 3 & 0x1e;
+ u8 b = screen->colour << 1 & 0x1e;
+ screen->pal[i] = RGB15(r,g,b);
+ if (screen->nds) screen->nds->pal[i] = RGB15(r,g,b);
+ screen->dirty = true;
+}
+
+// Move the screen cursor.
+void screen_move_cursor(ScreenDevice *screen, u8 move) {
+ switch (move >> 6) {
+ case 0b00: screen->x += move & 0x3f; return;
+ case 0b01: screen->y += move & 0x3f; return;
+ case 0b10: screen->x -= move & 0x3f; return;
+ case 0b11: screen->y -= move & 0x3f; return;
+ }
+}
+
+
-// ---------------------------------------------------------------------------
+// ------ RAW DRAW OPERATIONS --------------------------------------------------
+// Draw a pixel to a screen layer.
void draw_pixel(u16 *layer, u16 x, u16 y, u8 colour) {
if (x < PIXELS_WIDTH && y < PIXELS_HEIGHT) {
u32 addr = \
@@ -156,109 +228,58 @@ void draw_pixel(u16 *layer, u16 x, u16 y, u8 colour) {
}
}
+// Fill a screen layer with a solid colour.
void fill_layer(u16 *layer, u8 colour) {
u8 byte = colour << 4 | colour;
u32 word = byte << 24 | byte << 16 | byte << 8 | byte;
- dmaFillWords(word, layer, TILES_MEM);
-}
-
-void erase_screen(Screen *nds) {
- if (nds) {
- dmaFillWords(0, nds->bg, TILES_MEM);
- dmaFillWords(0, nds->fg, TILES_MEM);
- }
-};
-
-void flip_buffer(Screen *nds) {
- if (nds) {
- dmaCopyWords(0, nds->bg, nds->bgv, TILES_MEM);
- dmaCopyWords(0, nds->fg, nds->fgv, TILES_MEM);
- for (int i=0; i<16; i++) {
- nds->palv[i] = nds->pal[i];
- }
- }
+ dmaFillWords(word, layer, LAYER_MEM);
}
-void black_screen(Screen *nds) {
- if (nds) {
- for (int i=0; i<16; i++) {
- nds->palv[i] = RGB15(0,0,0);
- }
- dmaFillWords(0, nds->bgv, TILES_MEM);
- dmaFillWords(0, nds->fgv, TILES_MEM);
- }
-}
-// ---------------------------------------------------------------------------
-void draw_dispatch(ScreenDevice *scr, u8 draw) {
- if (scr->nds) {
- switch (draw >> 4) {
- case 0x0: scr_draw_pixel(scr, scr->nds->bg, draw); break;
- case 0x1: scr_draw_sprite(scr, scr->nds->bg, draw); break;
- case 0x2: scr_fill_layer(scr, scr->nds->bg, draw); break;
- case 0x3: scr_draw_sprite(scr, scr->nds->bg, draw); break;
- case 0x4: scr_draw_line(scr, scr->nds->bg, draw); break;
- case 0x5: scr_draw_line(scr, scr->nds->bg, draw); break;
- case 0x6: scr_draw_rect(scr, scr->nds->bg, draw); break;
- case 0x7: scr_draw_rect(scr, scr->nds->bg, draw); break;
- case 0x8: scr_draw_pixel(scr, scr->nds->fg, draw); break;
- case 0x9: scr_draw_sprite(scr, scr->nds->fg, draw); break;
- case 0xA: scr_fill_layer(scr, scr->nds->fg, draw); break;
- case 0xB: scr_draw_sprite(scr, scr->nds->fg, draw); break;
- case 0xC: scr_draw_line(scr, scr->nds->fg, draw); break;
- case 0xD: scr_draw_line(scr, scr->nds->fg, draw); break;
- case 0xE: scr_draw_rect(scr, scr->nds->fg, draw); break;
- case 0xF: scr_draw_rect(scr, scr->nds->fg, draw); break;
- }
- scr->dirty = true;
- }
- scr->px = scr->x;
- scr->py = scr->y;
-}
+// ------ DRAW OPERATIONS ------------------------------------------------------
-void scr_draw_pixel(ScreenDevice *scr, u16 *layer, u8 draw) {
- draw_pixel(layer, scr->x, scr->y, draw&0xf);
+void op_draw_pixel(ScreenDevice *screen, u16 *layer, u8 draw) {
+ draw_pixel(layer, screen->x, screen->y, draw&0xf);
}
-void scr_fill_layer(ScreenDevice *scr, u16 *layer, u8 draw) {
+void op_fill_layer(ScreenDevice *screen, u16 *layer, u8 draw) {
fill_layer(layer, draw&0xf);
}
-void scr_draw_sprite(ScreenDevice *scr, u16 *layer, u8 draw) {
- if (draw & 0x20) { prepare_2bit_sprite(&scr->sprite, draw); }
- else { prepare_1bit_sprite(&scr->sprite, draw); }
-
- u8 colours[4] = {
- scr->colours >> 12 & 0x000f,
- scr->colours >> 8 & 0x000f,
- scr->colours >> 4 & 0x000f,
- scr->colours & 0x000f,
+void op_draw_sprite(ScreenDevice *screen, u16 *layer, u8 draw) {
+ if (draw & 0x20) { spritebuf_prepare_2bit(&screen->sprite, draw); }
+ else { spritebuf_prepare_1bit(&screen->sprite, draw); }
+ // Parse sprite colours.
+ u8 selected[4] = {
+ screen->selected >> 12 & 0x000f,
+ screen->selected >> 8 & 0x000f,
+ screen->selected >> 4 & 0x000f,
+ screen->selected & 0x000f,
};
-
if (draw & 0x08) {
- // Draw sprite with transparent background
+ // Draw sprite with transparent background.
for (u8 y=0;y<8;y++) {
for (u8 x=0;x<8;x++) {
- u8 i = scr->sprite.sprite[y][x];
- if (i) draw_pixel(layer, scr->x+x, scr->y+y, colours[i]);
+ u8 i = screen->sprite.sprite[y][x];
+ if (i) draw_pixel(layer, screen->x+x, screen->y+y, selected[i]);
}
}
} else {
- // Draw sprite with opaque background
+ // Draw sprite with opaque background.
for (u8 y=0;y<8;y++) {
for (u8 x=0;x<8;x++) {
- u8 i = scr->sprite.sprite[y][x];
- draw_pixel(layer, scr->x+x, scr->y+y, colours[i]);
+ u8 i = screen->sprite.sprite[y][x];
+ draw_pixel(layer, screen->x+x, screen->y+y, selected[i]);
}
}
}
}
-void scr_draw_line(ScreenDevice *scr, u16 *layer, u8 draw) {
- s16 x = (s16) scr->x;
- s16 y = (s16) scr->y;
- s16 x_end = (s16) scr->px;
- s16 y_end = (s16) scr->py;
+void op_draw_line(ScreenDevice *screen, u16 *layer, u8 draw) {
+ s16 x = (s16) screen->x;
+ s16 y = (s16) screen->y;
+ s16 x_end = (s16) screen->px;
+ s16 y_end = (s16) screen->py;
s32 dx = abs(x_end - x);
s32 dy = -abs(y_end - y);
@@ -267,24 +288,24 @@ void scr_draw_line(ScreenDevice *scr, u16 *layer, u8 draw) {
s32 e1 = dx + dy;
if (draw & 0x10) {
- // Draw 1-bit textured line.
- prepare_1bit_sprite(&scr->sprite, draw);
- u8 c1 = scr->colours >> 8 & 0xf;
- u8 c0 = scr->colours >> 12 & 0xf;
+ // Draw 1-bit textured line.
+ spritebuf_prepare_1bit(&screen->sprite, draw);
+ u8 c1 = screen->selected >> 8 & 0xf;
+ u8 c0 = screen->selected >> 12 & 0xf;
bool opaque = !(draw & 0x08);
while (1) {
- if (scr->sprite.sprite[y%8][x%8]) { draw_pixel(layer, x, y, c1); }
- else if (opaque) { draw_pixel(layer, x, y, c0); }
+ if (screen->sprite.sprite[y%8][x%8]) { draw_pixel(layer, x, y, c1); }
+ else if (opaque) { draw_pixel(layer, x, y, c0); }
if (x == x_end && y == y_end) return;
s32 e2 = e1 << 1;
if (e2 >= dy) { e1 += dy; x += sx; }
if (e2 <= dx) { e1 += dx; y += sy; }
}
} else {
- // Draw solid line.
- u8 colour = draw & 0xf;
+ // Draw solid line.
+ u8 c0 = draw & 0xf;
while (1) {
- draw_pixel(layer, x, y, colour);
+ draw_pixel(layer, x, y, c0);
if (x == x_end && y == y_end) return;
s32 e2 = e1 << 1;
if (e2 >= dy) { e1 += dy; x += sx; }
@@ -293,46 +314,64 @@ void scr_draw_line(ScreenDevice *scr, u16 *layer, u8 draw) {
}
}
-void scr_draw_rect(ScreenDevice *scr, u16 *layer, u8 draw) {
+void op_draw_rect(ScreenDevice *screen, u16 *layer, u8 draw) {
#define SWAP(x,y) {u8 temp=x; x=y; y=temp;}
#define CLAMP(v,m) {v>0x7fff ? 0 : v>m ? m : v}
// Get bounding box.
- u16 l = CLAMP(scr->px, PIXELS_WIDTH -1);
- u16 r = CLAMP(scr->x , PIXELS_WIDTH -1);
- u16 t = CLAMP(scr->py, PIXELS_HEIGHT-1);
- u16 b = CLAMP(scr->y , PIXELS_HEIGHT-1);
+ u16 l = CLAMP(screen->px, PIXELS_WIDTH -1);
+ u16 r = CLAMP(screen->x , PIXELS_WIDTH -1);
+ u16 t = CLAMP(screen->py, PIXELS_HEIGHT-1);
+ u16 b = CLAMP(screen->y , PIXELS_HEIGHT-1);
if (l>r) SWAP(l,r);
if (t>b) SWAP(t,b);
if (draw & 0x10) {
- // Draw 1-bit textured rectangle.
- prepare_1bit_sprite(&scr->sprite, draw);
- u8 c1 = scr->colours >> 8 & 0xf;
- u8 c0 = scr->colours >> 12 & 0xf;
+ // Draw 1-bit textured rectangle.
+ spritebuf_prepare_1bit(&screen->sprite, draw);
+ u8 c1 = screen->selected >> 8 & 0xf;
+ u8 c0 = screen->selected >> 12 & 0xf;
bool opaque = !(draw & 0x08);
for (u16 x=l; x<r+1; x++) {
for (u16 y=t; y<b+1; y++) {
- if (scr->sprite.sprite[y%8][x%8]) { draw_pixel(layer, x, y, c1); }
- else if (opaque) { draw_pixel(layer, x, y, c0); }
+ if (screen->sprite.sprite[y%8][x%8]) { draw_pixel(layer, x, y, c1); }
+ else if (opaque) { draw_pixel(layer, x, y, c0); }
}
}
} else {
- // Draw solid rectangle.
- u8 colour = draw & 0xf;
+ // Draw solid rectangle.
+ u8 c0 = draw & 0xf;
for (u16 x=l; x<r+1; x++) {
for (u16 y=t; y<b+1; y++) {
- draw_pixel(layer, x, y, colour);
+ draw_pixel(layer, x, y, c0);
}
}
}
}
-void move_cursor(ScreenDevice *scr, u8 move) {
- switch (move >> 6) {
- case 0b00: scr->x += move & 0x3f; return;
- case 0b01: scr->y += move & 0x3f; return;
- case 0b10: scr->x -= move & 0x3f; return;
- case 0b11: scr->y -= move & 0x3f; return;
+// Dispatch to the correct drawing operation.
+void screen_draw(ScreenDevice *screen, u8 draw) {
+ if (screen->nds) {
+ switch (draw >> 4) {
+ case 0x0: op_draw_pixel(screen, screen->nds->bg, draw); break;
+ case 0x1: op_draw_sprite(screen, screen->nds->bg, draw); break;
+ case 0x2: op_fill_layer(screen, screen->nds->bg, draw); break;
+ case 0x3: op_draw_sprite(screen, screen->nds->bg, draw); break;
+ case 0x4: op_draw_line(screen, screen->nds->bg, draw); break;
+ case 0x5: op_draw_line(screen, screen->nds->bg, draw); break;
+ case 0x6: op_draw_rect(screen, screen->nds->bg, draw); break;
+ case 0x7: op_draw_rect(screen, screen->nds->bg, draw); break;
+ case 0x8: op_draw_pixel(screen, screen->nds->fg, draw); break;
+ case 0x9: op_draw_sprite(screen, screen->nds->fg, draw); break;
+ case 0xA: op_fill_layer(screen, screen->nds->fg, draw); break;
+ case 0xB: op_draw_sprite(screen, screen->nds->fg, draw); break;
+ case 0xC: op_draw_line(screen, screen->nds->fg, draw); break;
+ case 0xD: op_draw_line(screen, screen->nds->fg, draw); break;
+ case 0xE: op_draw_rect(screen, screen->nds->fg, draw); break;
+ case 0xF: op_draw_rect(screen, screen->nds->fg, draw); break;
+ }
+ screen->dirty = true;
}
+ screen->px = screen->x;
+ screen->py = screen->y;
}
diff --git a/arm9/source/devices/screen.h b/arm9/source/devices/screen.h
index 1ff58e5..48411b7 100644
--- a/arm9/source/devices/screen.h
+++ b/arm9/source/devices/screen.h
@@ -1,70 +1,68 @@
#ifndef SCREEN_H_
#define SCREEN_H_
+ #include "../bang.h"
+
+
+ // Dimension constants.
#define PIXELS_WIDTH 256
#define PIXELS_HEIGHT 192
- #define PIXELS_SIZE 49152
+ #define PIXELS_COUNT (PIXELS_WIDTH*PIXELS_HEIGHT)
#define TILES_WIDTH 32
#define TILES_HEIGHT 24
- #define TILES_SIZE 768
- #define TILES_MEM 24576 // size of a screen layer in bytes
+ #define TILES_COUNT (TILES_WIDTH*TILES_HEIGHT)
+ #define LAYER_MEM (TILES_COUNT*32)
- #define BG_SLOT_VIS 0 // tile base for visible background tiles
- #define FG_SLOT_VIS 2 // tile base for visible foreground tiles
- #define BG_SLOT 4 // tile base for background tiles
- #define FG_SLOT 6 // tile base for foreground tiles
- #define MAP_SLOT 12 // map base for tile map
+ // VRAM addresses.
+ #define BG_SLOT_FRONT 0 // Tile base for background tiles in the front buffer
+ #define FG_SLOT_FRONT 2 // Tile base for foreground tiles in the front buffer
+ #define BG_SLOT_BACK 4 // Tile base for background tiles in the back buffer
+ #define FG_SLOT_BACK 6 // Tile base for foreground tiles in the back buffer
+ #define MAP_SLOT 12 // Map base for tile map
+ // Sprite buffer for the screen device.
typedef struct {
- u8 mem[16]; // buffer memory
- u8 p; // buffer pointer
- u8 sprite[8][8]; // transformed sprite
- bool cached; // true if sprite has been transformed
- u8 draw; // the cached draw byte
+ u8 mem[16]; // Buffer memory
+ u8 p; // Buffer pointer
+ u8 sprite[8][8]; // Cached transformed sprite
+ u8 draw; // Cached draw byte
+ bool cached; // A transformed sprite has been cached
} SpriteBuffer;
+ // Hardware screen on the NDS.
typedef struct {
- u16 *bgv, *fgv; // visible tile memory
- u16 *bg, *fg; // buffered tile memory
- u16 *map; // tile map (never changes)
- u16 *palv; // visible colour palette
- u16 pal[16]; // buffered colour palette
- } Screen;
+ u16 *bgv, *fgv; // Visible tile memory (front buffer)
+ u16 *bg, *fg; // Buffered tile memory (back buffer)
+ u16 *map; // Tile map (never changes)
+ u16 *palv; // Visible colour palette (front buffer)
+ u16 pal[16]; // Buffered colour palette (back buffer)
+ } NDSScreen;
+ // Bedrock screen device.
typedef struct {
- u16 x,y; // cursor position
- u16 px,py; // previous cursor position
- u16 colours; // sprite colours
- SpriteBuffer sprite; // sprite buffer
- u16 palette_write; // palette write cache
- u16 palette[16]; // palette as RGB15 colours
- Screen *nds; // NDS VRAM pointers
- bool wake; // wake flag
- bool dirty; // dirty flag
+ u16 x,y; // Current cursor position
+ u16 px,py; // Previous cursor position
+ u16 selected; // Selected sprite colours
+ SpriteBuffer sprite; // Sprite buffer
+ u16 colour; // Palette colour write cache
+ u16 pal[16]; // Palette as RGB15 colours
+ NDSScreen *nds; // Pointer to hardware screen to draw to.
+ bool wake; // Wake flag
+ bool dirty; // Dirty flag
} ScreenDevice;
- void init_screens(void);
- void scr_make_main(ScreenDevice *scr);
- void scr_make_sub(ScreenDevice *scr);
- void scr_unmake(ScreenDevice *scr);
-
- void set_palette_high(ScreenDevice *scr, u8 high);
- void set_palette_low(ScreenDevice *scr, u8 low);
- void draw_pixel(u16 *layer, u16 x, u16 y, u8 colour);
- void fill_layer(u16 *layer, u8 colour);
- void erase_screen(Screen *nds);
- void flip_buffer(Screen *nds);
- void black_screen(Screen *nds);
-
- void scr_draw_pixel( ScreenDevice *scr, u16 *layer, u8 draw);
- void scr_draw_sprite(ScreenDevice *scr, u16 *layer, u8 draw);
- void scr_fill_layer( ScreenDevice *scr, u16 *layer, u8 draw);
- void scr_draw_line( ScreenDevice *scr, u16 *layer, u8 draw);
- void scr_draw_rect( ScreenDevice *scr, u16 *layer, u8 draw);
-
- void draw_dispatch(ScreenDevice *scr, u8 draw);
- void move_cursor(ScreenDevice *scr, u8 move);
-
- void push_sprite(SpriteBuffer *b, u8 row);
+ // Functions.
+ void init_nds_screens(void);
+ // Methods.
+ void screen_reset(ScreenDevice *screen);
+ void ndsscreen_flip_buffers(NDSScreen *nds);
+ void ndsscreen_clear(NDSScreen *nds);
+ void spritebuf_push(SpriteBuffer *b, u8 row);
+ void screen_map_to_none(ScreenDevice *screen);
+ void screen_map_to_main(ScreenDevice *screen);
+ void screen_map_to_sub(ScreenDevice *screen);
+ void screen_commit_colour(ScreenDevice *screen);
+ void screen_move_cursor(ScreenDevice *screen, u8 move);
+ void screen_draw(ScreenDevice *screen, u8 draw);
#endif
diff --git a/arm9/source/devices/stream.c b/arm9/source/devices/stream.c
new file mode 100644
index 0000000..7329380
--- /dev/null
+++ b/arm9/source/devices/stream.c
@@ -0,0 +1,40 @@
+#include "stream.h"
+
+/*
+This is a skeleton of a device at the moment. The local.output.connected flag
+// of the keyboard program is manually set in main.c so that only the keyboard
+can send bytes to the main instance, otherwise user programs that write to the
+stream device will send keyboard bytes to themselves.
+*/
+
+
+void channel_reset(Channel *channel) {
+ channel->connected = false;
+ channel->transmitting = false;
+}
+
+void bytestream_reset(Bytestream *bstream) {
+ channel_reset(&bstream->input);
+ channel_reset(&bstream->output);
+}
+
+// Reset a stream device.
+void stream_reset(StreamDevice *stream) {
+ bytestream_reset(&stream->local);
+ bytestream_reset(&stream->remote);
+}
+
+// Write a byte to the stream of the main instance. This byte is expected to
+// be consumed by a keyboard program running on the sub instance.
+void stream_write(StreamDevice *stream, u8 byte) {
+ if (stream->local.output.connected) {
+ receive_keyboard_byte(byte);
+ }
+}
+
+// End the current transmission. Currently this will only hide the keyboard.
+void stream_end(StreamDevice *stream) {
+ if (stream->local.output.connected) {
+ close_keyboard();
+ }
+}
diff --git a/arm9/source/devices/stream.h b/arm9/source/devices/stream.h
new file mode 100644
index 0000000..2ee463c
--- /dev/null
+++ b/arm9/source/devices/stream.h
@@ -0,0 +1,33 @@
+// TODO: Implement this properly.
+
+
+#ifndef STREAM_H_
+ #define STREAM_H_
+
+ #include "../bang.h"
+
+ typedef struct {
+ bool connected;
+ bool transmitting;
+ } Channel;
+
+ typedef struct {
+ Channel input;
+ Channel output;
+ } Bytestream;
+
+ // Bedrock stream device.
+ typedef struct {
+ Bytestream local;
+ Bytestream remote;
+ } StreamDevice;
+
+ // Methods.
+ void stream_reset(StreamDevice *stream);
+ void stream_write(StreamDevice *stream, u8 byte);
+ void stream_end(StreamDevice *stream);
+
+ // Duplicate declarations from main.
+ void receive_keyboard_byte(u8 byte);
+ void close_keyboard(void);
+#endif
diff --git a/arm9/source/devices/system.c b/arm9/source/devices/system.c
index 3406eed..d2aeb7f 100644
--- a/arm9/source/devices/system.c
+++ b/arm9/source/devices/system.c
@@ -1,16 +1,35 @@
-#include "nds.h"
#include "file.h"
#include "system.h"
-u8 devices_high() {
- return 0b11111100;
+// Reset a system device.
+void system_reset(SystemDevice *system) {
+ readbuf_set_pointer(&system->name);
+ readbuf_set_pointer(&system->authors);
}
-u8 devices_low() {
- u8 devices = 0;
- if (filesystem_enabled()) {
- devices |= 0b00100000;
- }
- return devices;
+// Return a bitmask representing the connected state of each device.
+u16 connected_devices(void) {
+ bool devices[16] = {
+ /* SYSTEM */ true,
+ /* MEMORY */ true,
+ /* MATH */ true,
+ /* CLOCK */ true,
+ /* INPUT */ true,
+ /* SCREEN */ true,
+ /* TONE */ false,
+ /* SAMPLER */ false,
+ /* STREAM */ true,
+ /* FILE */ nds_filesystem_enabled(),
+ /* CLIPBOARD */ false,
+ /* REGISTRY */ false,
+ /* CUSTOM 1 */ false,
+ /* CUSTOM 2 */ false,
+ /* CUSTOM 3 */ false,
+ /* CUSTOM 4 */ false,
+ };
+ u16 mask = 0;
+ for (int i=0; i<16; i++) mask |= devices[i] << (15-i);
+ return mask;
}
+
diff --git a/arm9/source/devices/system.h b/arm9/source/devices/system.h
index 7549f0f..029d2e6 100644
--- a/arm9/source/devices/system.h
+++ b/arm9/source/devices/system.h
@@ -1,16 +1,25 @@
-#include "../types/readbuf.h"
+// TODO: Wrap more system device functionality in functions here.
#ifndef SYSTEM_H_
#define SYSTEM_H_
+ #include "../bang.h"
+ #include "../types/readbuf.h"
+ #include "../types/wakequeue.h"
+
+
+ // Bedrock system device.
typedef struct {
ReadBuf name;
ReadBuf authors;
u16 sleep; // device mask for waking
u8 wake; // ID of wake device
+ WakeQueue queue;
} SystemDevice;
- u8 devices_high();
- u8 devices_low();
+ // Functions.
+ u16 connected_devices(void);
+ // Methods.
+ void system_reset(SystemDevice *system);
#endif