diff options
author | Ben Bridle <ben@derelict.engineering> | 2025-09-19 13:17:14 +1200 |
---|---|---|
committer | Ben Bridle <ben@derelict.engineering> | 2025-09-19 13:32:32 +1200 |
commit | bb1aa5958d1b67707dcf0f6b08bfaf0b408bd46e (patch) | |
tree | b26d07ed58aaf7a5230fc3e28c103d616abfa9b8 /arm9/source | |
parent | 9612c307f00c4313d73fe0c3a86c05c8d8cd514e (diff) | |
download | bedrock-nds-bb1aa5958d1b67707dcf0f6b08bfaf0b408bd46e.zip |
Massive rewrite
This commit rewrites the emulator halfway from scratch to make it
easier to change and maintain in the future. The emulator core was
rewritten to adhere to the released Bedrock specification (earlier
versions implemented an older prototype specification, which is no
longer relevant).
This commit also adds proper support for running multiple concurrent
Bedrock instances. This was previously supported in a limited manner
for the on-screen keyboard, but now works for any regular program as
well, with switching being performed by pressing the L or R bumper
buttons. This is disabled by default, as programs will still need to
be baked into the emulator and hand-loaded.
Diffstat (limited to 'arm9/source')
37 files changed, 2107 insertions, 1589 deletions
diff --git a/arm9/source/bang.c b/arm9/source/bang.c index 8686dd3..6065267 100644 --- a/arm9/source/bang.c +++ b/arm9/source/bang.c @@ -1,51 +1,26 @@ -#include <nds.h> #include "bang.h" -static const u8 rev_lookup[] = { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, - 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, - 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, - 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, - 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, - 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, - 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, - 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, - 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, - 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, - 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, - 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, - 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, - 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, - 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, - 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, - 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, - 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, - 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, - 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, -}; -u8 rev(u8 x) { - return rev_lookup[x]; +// Left-rotate a byte. +u8 ROL1(u8 v, u8 d) { + d %= 8; + return v << d | v >> (8-d); } -u8 shc(u8 x, u8 y) { - y = (u8)(RIGHT(y) - LEFT(y)) % 8; - return ((x >> y | (x << (8-y)))); +// Left-rotate a double. +u16 ROLD(u16 v, u8 d) { + d %= 16; + return v << d | v >> (16-d); } -u16 shcd(u16 x, u8 y) { - y = (u16)(RIGHT(y) - LEFT(y)) % 16; - return ((x >> y | (x << (16-y)))); +// Right-rotate a byte. +u8 ROR1(u8 v, u8 d) { + d %= 8; + return v << (8-d) | v >> d; +} + +// Right-rotate a double. +u16 RORD(u16 v, u8 d) { + d %= 16; + return v << (16-d) | v >> d; } diff --git a/arm9/source/bang.h b/arm9/source/bang.h index 110e243..b85e9cd 100644 --- a/arm9/source/bang.h +++ b/arm9/source/bang.h @@ -1,31 +1,39 @@ #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) ) + #include <nds.h> - #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) - - #define SHF(x,y) (x >> RIGHT(y) << LEFT(y)) - #define TAL(x) __builtin_popcount(x) - - #define TEST(x,m) ((x & m) == m) + // Read and write individual bytes from a 32-bit value. + #define SETHH(v,h) v = h << 24 | (v & 0x00ffffff) + #define SETHL(v,l) v = l << 16 | (v & 0xff00ffff) + #define SETLH(v,h) v = h << 8 | (v & 0xffff00ff) + #define SETLL(v,l) v = l | (v & 0xffffff00) + #define GETHH(v) (u8)((v) >> 24) + #define GETHL(v) (u8)((v) >> 16) + #define GETLH(v) (u8)((v) >> 8) + #define GETLL(v) (u8)((v) ) + // Aliases for use on 16-bit values. + #define SETH(v,h) SETLH(v,h) + #define SETL(v,l) SETLL(v,l) + #define GETH(v) GETLH(v) + #define GETL(v) GETLL(v) + // Test if all bits of a mask are set in a value. + #define TEST(v,m) ((v & m) == m) + // Convert a C-style boolean to a Bedrock-style boolean. #define BOOL(v) ((u8)-(v)) + // Merge two bytes together as a double. #define DOUBLE(h,l) ((u16)(h) << 8 | (u16)(l)) + // Extract the high and low bytes of a double. + #define HIGH(v) (u8)(v >> 8) + #define LOW(v) (u8)(v) + // Find the highest or lowest value of a pair. + #define MAX(x,y) (x>y ? x : y) + #define MIN(x,y) (x<y ? x : y) - u8 rev(u8 x); - u8 shc(u8 x, u8 y); - u16 shcd(u16 x, u8 y); + // Functions. + u8 ROL1(u8 v, u8 d); + u16 ROLD(u16 v, u8 d); + u8 ROR1(u8 v, u8 d); + u16 RORD(u16 v, u8 d); #endif diff --git a/arm9/source/config.h b/arm9/source/config.h new file mode 100644 index 0000000..ae27fba --- /dev/null +++ b/arm9/source/config.h @@ -0,0 +1,22 @@ + +#ifndef CONFIG_H_ + #define CONFIG_H_ + + #include "bang.h" + + + // Allow the user to switch between instances with the L and R buttons. + #define SWITCH_BETWEEN_INSTANCES false + + // The number of Bedrock instances to initialise. This will determine + // the maximum number of programs that can run simultaneously (including + // the keyboard program). Should be at least 2. + #define NUM_INSTANCES 2 + + // Number of instances reserved for user programs. + #define USER_INSTANCES (NUM_INSTANCES-1) + + // Index of the keyboard instance, which will display on the bottom + // screen when the main instance is on the top screen. + #define KEY_INSTANCE (NUM_INSTANCES-1) +#endif diff --git a/arm9/source/core.c b/arm9/source/core.c index 26c7bb0..21587b4 100644 --- a/arm9/source/core.c +++ b/arm9/source/core.c @@ -1,528 +1,419 @@ -#include <nds.h> -#include "bang.h" #include "core.h" -#include "debug.h" -void reset_br(Bedrock *br) { - // TODO +// Reset a Bedrock instance. +void br_reset(Bedrock *br) { + br->ip = 0; + br->wst.p = 0; + br->rst.p = 0; + dev_reset(&br->dev); } -// Load a program into an instance. -void start_br(Bedrock *br, u8 program[], int size) { - reset_br(br); - memcpy(&br->prg.mem, program, size); - br->alive = TRUE; - br->awake = TRUE; - // br->scr.nds = &scr_main; +// Load a new program into a Bedrock instance. +void br_load(Bedrock *br, u8 bytecode[], int length) { + br_reset(br); + memcpy(&br->mem, bytecode, MIN(length, 65536)); + br->alive = true; + br->awake = true; } -void run_br(Bedrock *br) { - if (br->awake) { - switch (evaluate(br, 65535)) { - case SIG_HALT: br->alive = FALSE; black_screen(br->scr.nds); br = NULL; return; - case SIG_SLEEP: br->awake = FALSE; return; - case SIG_DB1: debug_stacks(br); return; - case SIG_DB4: debug_assert(br); return; - default: return; - } - } -} - -void rouse_br(Bedrock *br) { - if (TEST(br->sys.sleep, 0x8000)) { // SYSTEM - br->awake = TRUE; - br->sys.wake = 0x0; - return; - } - if (TEST(br->sys.sleep, 0x0400)) { // SCREEN - if (br->scr.wake) { - br->scr.wake = FALSE; - br->awake = TRUE; - br->sys.wake = 0x5; +// Attempt to wake a Bedrock instance. +// TODO: Redo this. +void br_rouse(Bedrock *br) { + if (TEST(br->dev.system.sleep, 0x0400)) { // SCREEN + if (br->dev.screen.wake) { + br->dev.screen.wake = false; + br->dev.system.wake = 0x5; + br->awake = true; return; } } - if (TEST(br->sys.sleep, 0x0800)) { // INPUT - if (br->inp.wake) { - br->inp.wake = FALSE; - br->awake = TRUE; - br->sys.wake = 0x4; + if (TEST(br->dev.system.sleep, 0x0800)) { // INPUT + if (br->dev.input.wake) { + br->dev.input.wake = false; + br->dev.system.wake = 0x4; + br->awake = true; return; } } - if (TEST(br->sys.sleep, 0x1000)) { // CLOCK - if (check_timers(&br->clk)) { - br->awake = TRUE; - br->sys.wake = 0x3; + if (TEST(br->dev.system.sleep, 0x1000)) { // CLOCK + if (clock_check_timers(&br->dev.clock)) { + br->dev.system.wake = 0x3; + br->awake = true; return; } } -} - -u8 dev_read(Bedrock *br, u8 port) { - switch(port) { -// SYSTEM DEVICE - // TODO: name and author - case 0x00: return rb_read(&br->sys.name); - case 0x01: return rb_read(&br->sys.authors); - case 0x02: return 0x00; // program memory size - case 0x03: return 0x00; // program memory size - case 0x04: return 0x00; // working stack size - case 0x05: return 0x00; // return stack size - 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); - case 0x12: return HIGH(br->mem.offset1); - case 0x13: return LOW(br->mem.offset1); - case 0x14: return br->mem.page1; - case 0x15: return br->mem.byte1; - case 0x16: return HIGH(br->mem.count); - case 0x17: return LOW(br->mem.count); - case 0x18: return mem_read2(&br->mem); - case 0x19: return mem_read2(&br->mem); - case 0x1A: return HIGH(br->mem.offset2); - case 0x1B: return LOW(br->mem.offset2); - case 0x1C: return br->mem.page2; - case 0x1D: return br->mem.byte2; -// MATH DEVICE - case 0x20: return HIGH(br->math.op1); - case 0x21: return LOW(br->math.op1); - case 0x22: return HIGH(br->math.op2); - case 0x23: return LOW(br->math.op2); - case 0x24: return HIGH(get_sqrt(&br->math)); - case 0x25: return LOW(get_sqrt(&br->math)); - case 0x26: return HIGH(get_atan(&br->math)); - case 0x27: return LOW(get_atan(&br->math)); - case 0x28: return H_HIGH(get_prod(&br->math)); - case 0x29: return H_LOW(get_prod(&br->math)); - case 0x2A: return L_HIGH(get_prod(&br->math)); - case 0x2B: return L_LOW(get_prod(&br->math)); - case 0x2C: return HIGH(get_quot(&br->math)); - case 0x2D: return LOW(get_quot(&br->math)); - case 0x2E: return HIGH( get_rem(&br->math)); - case 0x2F: return LOW( get_rem(&br->math)); -// CLOCK DEVICE - case 0x30: return YEAR(get_datetime()); - case 0x31: return MONTH(get_datetime()); - case 0x32: return DAY(get_datetime()); - case 0x33: return HOUR(get_datetime()); - case 0x34: return MINUTE(get_datetime()); - case 0x35: return SECOND(get_datetime()); - case 0x36: return HIGH(get_uptime()-br->clk.start); - case 0x37: return LOW(get_uptime()-br->clk.start); - case 0x38: return get_timer_high(&br->clk.t1); - case 0x39: return get_timer_low( &br->clk.t1); - case 0x3A: return get_timer_high(&br->clk.t2); - case 0x3B: return get_timer_low( &br->clk.t2); - case 0x3C: return get_timer_high(&br->clk.t3); - case 0x3D: return get_timer_low( &br->clk.t3); - case 0x3E: return get_timer_high(&br->clk.t4); - case 0x3F: return get_timer_low( &br->clk.t4); -// INPUT DEVICE - case 0x40: return BOOL(br->inp.pointer); - case 0x41: return br->inp.pointer << 7; - case 0x44: return HIGH(br->inp.x); - case 0x45: return LOW(br->inp.x); - case 0x46: return HIGH(br->inp.y); - case 0x47: return LOW(br->inp.y); - case 0x48: return BOOL(is_keyboard_open()); - case 0x49: return cb_read_byte(&br->inp.keybuffer); - case 0x4A: return br->inp.navigation; - case 0x4C: return br->inp.gamepad; -// SCREEN DEVICE - case 0x50: return HIGH(PIXELS_WIDTH); - case 0x51: return LOW(PIXELS_WIDTH); - case 0x52: return HIGH(PIXELS_HEIGHT); - case 0x53: return LOW(PIXELS_HEIGHT); - case 0x54: return HIGH(br->scr.x); - 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; - } -} - -Signal dev_write(Bedrock *br, u8 port, u8 v) { - switch(port) { -// SYSTEM DEVICE - case 0x00: rb_reset(&br->sys.name); return 0; - case 0x01: rb_reset(&br->sys.authors); return 0; - case 0x08: SET_HIGH(br->sys.sleep,v); return 0; - case 0x09: SET_LOW(br->sys.sleep,v); return SIG_SLEEP; -// MEMORY DEVICE - case 0x10: mem_write1(&br->mem,v); return 0; - case 0x11: mem_write1(&br->mem,v); return 0; - case 0x12: SET_HIGH(br->mem.offset1,v); mem_load_cache1(&br->mem); return 0; - case 0x13: SET_LOW(br->mem.offset1,v); mem_load_cache1(&br->mem); return 0; - case 0x14: br->mem.page1=v; mem_get_page1(&br->mem); return 0; - case 0x15: br->mem.byte1=v; return 0; - case 0x16: SET_HIGH(br->mem.count_write,v); return 0; - case 0x17: SET_LOW(br->mem.count_write,v); mem_allocate(&br->mem); return 0; - case 0x18: mem_write2(&br->mem,v); return 0; - case 0x19: mem_write2(&br->mem,v); return 0; - case 0x1A: SET_HIGH(br->mem.offset2,v); mem_load_cache2(&br->mem); return 0; - case 0x1B: SET_LOW(br->mem.offset2,v); mem_load_cache2(&br->mem); return 0; - case 0x1C: br->mem.page2=v; mem_get_page2(&br->mem); return 0; - case 0x1D: br->mem.byte2=v; return 0; - case 0x1E: SET_HIGH(br->mem.copy_write,v); return 0; - case 0x1F: SET_LOW(br->mem.copy_write,v); mem_do_copy(&br->mem); return 0; -// MATH DEVICE - case 0x20: set_op1_high(&br->math,v); return 0; - case 0x21: set_op1_low( &br->math,v); return 0; - case 0x22: set_op2_high(&br->math,v); return 0; - case 0x23: set_op2_low( &br->math,v); return 0; -// CLOCK DEVICE - // TODO: Set time and date - case 0x38: set_timer_high(&br->clk.t1,v); return 0; - case 0x39: set_timer_low( &br->clk.t1,v); return 0; - case 0x3A: set_timer_high(&br->clk.t2,v); return 0; - case 0x3B: set_timer_low( &br->clk.t2,v); return 0; - case 0x3C: set_timer_high(&br->clk.t3,v); return 0; - case 0x3D: set_timer_low( &br->clk.t3,v); return 0; - case 0x3E: set_timer_high(&br->clk.t4,v); return 0; - case 0x3F: set_timer_low( &br->clk.t4,v); return 0; -// INPUT DEVICE - case 0x48: v ? open_keyboard() : close_keyboard(); return 0; - case 0x49: cb_clear(&br->inp.keybuffer); return 0; -// SCREEN DEVICE - case 0x54: SET_HIGH(br->scr.x,v); return 0; - case 0x55: SET_LOW( br->scr.x,v); return 0; - case 0x56: SET_HIGH(br->scr.y,v); return 0; - case 0x57: SET_LOW( br->scr.y,v); return 0; - case 0x58: set_palette_high(&br->scr,v); return 0; - case 0x59: set_palette_low( &br->scr,v); return 0; - case 0x5A: SET_HIGH(br->scr.colours,v); return 0; - case 0x5B: SET_LOW( br->scr.colours,v); return 0; - case 0x5C: push_sprite(&br->scr.sprite,v); return 0; - case 0x5D: push_sprite(&br->scr.sprite,v); return 0; - case 0x5E: draw_dispatch(&br->scr,v); return 0; - case 0x5F: move_cursor(&br->scr,v); return 0; -// LOCAL DEVICE - case 0x86: std_write(v); return 0; - case 0x87: std_write(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; + // System device is always chosen last. + if (TEST(br->dev.system.sleep, 0x8000)) { // SYSTEM + br->dev.system.wake = 0x0; + br->awake = true; + return; } } -Signal dev_write_16(Bedrock *br, u8 port, u8 high, u8 low) { - Signal s1 = dev_write(br, port, high); - Signal s2 = dev_write(br, port+1, low); - if (s1) { return s1; } else { return s2; } +// Write a double to a device bus. +Signal dev_write_double(DeviceBus *d, u8 port, u8 h, u8 l) { + Signal s1 = dev_write(d, port, h); + Signal s2 = dev_write(d, port+1, l); + return s1 ? s1 : s2; } -Signal evaluate(Bedrock *br, u16 count) { +// Evaluate a Bedrock instance until a signal is emitted. +Signal br_eval(Bedrock *br, int count) { #define SIGNAL(s) if (s) { return s; } else { continue; }; - register u8 a, b, c, d, e, f; - register u16 x, y; + // Helpers. + #define WST br->wst + #define RST br->rst + #define MEM br->mem + #define IP br->ip + #define WSTV(i) WST.mem[WST.p+(i)] + #define RSTV(i) RST.mem[RST.p+(i)] + // Push values to a stack. + #define WPSH1(x) WST.mem[WST.p++] = (x); + #define RPSH1(x) RST.mem[RST.p++] = (x); + #define WPSH2(x,y) WPSH1(x); WPSH1(y); + #define RPSH2(x,y) RPSH1(x); RPSH1(y); + #define WPSHD(d) WPSH1(HIGH((d))); WPSH1(LOW((d))); + #define RPSHD(d) RPSH1(HIGH((d))); RPSH1(LOW((d))); + #define WPSHB(b) WPSH1(BOOL(b)); + #define RPSHB(b) RPSH1(BOOL(b)); + // Pop values from a stack. + #define WPOP1(x) (x) = WST.mem[--WST.p]; + #define RPOP1(x) (x) = RST.mem[--RST.p]; + #define WPOP2(x,y) WPOP1(y); WPOP1(x); + #define RPOP2(x,y) RPOP1(y); RPOP1(x); + #define WPOPD(d) d = DOUBLE(WSTV(-2), WSTV(-1)); WST.p-=2; + #define RPOPD(d) d = DOUBLE(RSTV(-2), RSTV(-1)); RST.p-=2; + // Pop values without affecting the stack pointer. + #define WGET1(x) x = WSTV(-1); + #define RGET1(x) x = RSTV(-1); + #define WGET2(x,y) x = WSTV(-2); y = WSTV(-1); + #define RGET2(x,y) x = RSTV(-2); y = RSTV(-1); + #define WGETD(x) x = DOUBLE(WSTV(-2), WSTV(-1)); + #define RGETD(x) x = DOUBLE(RSTV(-2), RSTV(-1)); + // Read the next value in memory. + #define MPOP1(x) (x) = MEM[IP++]; + #define MPOP2(x,y) MPOP1(x); MPOP1(y); + #define MPOPD(d) d = DOUBLE(MEM[IP], MEM[IP+1]); IP+=2; + // Read from a device port. + #define DGET1(p) dev_read(&br->dev, p) + #define DSET2(p,h,l) dev_write_double(&br->dev, p, h, l) + + register u16 u, v, w, x, y, z; register Signal sig; - for (u16 i=0; i < count; i++) { - switch (MLIT) { + while (count--) { + MPOP1(x); + switch (x) { /* HLT */ case 0x00: return SIG_HALT; -/* JMP */ case 0x01: WPOP2(a,b); PC=DOUBLE(a,b); continue; -/* JCN */ case 0x02: WPOP2(b,c); WPOP1(a); if(a) PC=DOUBLE(b,c); continue; -/* JCK */ case 0x03: WPOP2(b,c); WGET1(a); if(a) PC=DOUBLE(b,c); continue; -/* LDA */ case 0x04: WPOP2(a,b); x=DOUBLE(a,b); WPSH1(MEM[x]); continue; -/* STA */ case 0x05: WPOPD(x); WPOP1(a); MEM[x]=a; continue; -/* LDD */ case 0x06: WPOP1(a); WPSH1(dev_read(br,a)); continue; -/* STD */ case 0x07: WPOP1(b); WPOP1(a); sig=dev_write(br,b,a); SIGNAL(sig); -/* PSH */ case 0x08: RPOP1(a); WPSH1(a); continue; -/* POP */ case 0x09: WST.p-=1; continue; -/* CPY */ case 0x0A: RGET1(a); WPSH1(a); continue; -/* SPL */ case 0x0B: WPOP1(a); WPSH2(LEFT(a),RIGHT(a)); continue; -/* DUP */ case 0x0C: WGET1(a); WPSH1(a); continue; -/* OVR */ case 0x0D: WPOP1(b); WGET1(a); WPSH1(b); WPSH1(a); continue; -/* SWP */ case 0x0E: WPOP1(b); WPOP1(a); WPSH1(b); WPSH1(a); continue; -/* ROT */ case 0x0F: WPOP1(c); WPOP1(b); WPOP1(a); WPSH1(b); WPSH1(c); WPSH1(a); continue; -/* ADD */ case 0x10: WPOP1(b); WSTV(-1)+=b; continue; -/* SUB */ case 0x11: WPOP1(b); WSTV(-1)-=b; continue; +/* PSH */ case 0x01: RPOP1(x); WPSH1(x); continue; +/* POP */ case 0x02: WST.p-=1; continue; +/* CPY */ case 0x03: RGET1(x); WPSH1(x); continue; +/* DUP */ case 0x04: WGET1(x); WPSH1(x); continue; +/* OVR */ case 0x05: WPOP1(y); WGET1(x); WPSH1(y); WPSH1(x); continue; +/* SWP */ case 0x06: WPOP1(y); WPOP1(x); WPSH1(y); WPSH1(x); continue; +/* ROT */ case 0x07: WPOP1(z); WPOP1(y); WPOP1(x); WPSH1(y); WPSH1(z); WPSH1(x); continue; +/* JMP */ case 0x08: WPOPD(x); IP=x; continue; +/* JMS */ case 0x09: WPOPD(x); RPSHD(IP); IP=x; continue; +/* JCN */ case 0x0A: WPOPD(y); WPOP1(x); if(x) IP=y; continue; +/* JCS */ case 0x0B: WPOPD(y); WPOP1(x); if(x) { RPSHD(IP); IP=y; } continue; +/* LDA */ case 0x0C: WPOPD(x); WPSH1(MEM[x]); continue; +/* STA */ case 0x0D: WPOPD(y); WPOP1(x); MEM[y]=x; continue; +/* LDD */ case 0x0E: WPOP1(x); WPSH1(dev_read(&br->dev,x)); continue; +/* STD */ case 0x0F: WPOP1(y); WPOP1(x); sig=dev_write(&br->dev,y,x); SIGNAL(sig); +/* ADD */ case 0x10: WPOP1(x); WSTV(-1)+=x; continue; +/* SUB */ case 0x11: WPOP1(x); WSTV(-1)-=x; continue; /* INC */ case 0x12: WSTV(-1)+=1; continue; /* DEC */ case 0x13: WSTV(-1)-=1; continue; -/* LTH */ case 0x14: WPOP1(b); WPOP1(a); WPSH1(BOOL(a<b)); continue; -/* GTH */ case 0x15: WPOP1(b); WPOP1(a); WPSH1(BOOL(a>b)); continue; -/* EQU */ case 0x16: WPOP1(b); WPOP1(a); WPSH1(BOOL(a==b)); continue; -/* NQK */ case 0x17: b=WSTV(-1); a=WSTV(-2); WPSH1(BOOL(a!=b)); continue; -/* IOR */ case 0x18: WPOP1(b); WSTV(-1)|=b; continue; -/* XOR */ case 0x19: WPOP1(b); WSTV(-1)^=b; continue; -/* AND */ case 0x1A: WPOP1(b); WSTV(-1)&=b; continue; -/* NOT */ case 0x1B: WSTV(-1)=~WSTV(-1); continue; -/* SHF */ case 0x1C: WPOP1(b); WPOP1(a); x=SHF(a,b); WPSH1(x); continue; -/* SHC */ case 0x1D: WPOP1(b); WPOP1(a); x=shc(a,b); WPSH1(x); continue; -/* TAL */ case 0x1E: WSTV(-1)=TAL(WSTV(-1)); continue; -/* REV */ case 0x1F: WSTV(-1)=rev(WSTV(-1)); continue; +/* LTH */ case 0x14: WPOP1(y); WPOP1(x); WPSHB(x<y); continue; +/* GTH */ case 0x15: WPOP1(y); WPOP1(x); WPSHB(x>y); continue; +/* EQU */ case 0x16: WPOP1(y); WPOP1(x); WPSHB(x==y); continue; +/* NQK */ case 0x17: y=WSTV(-1); x=WSTV(-2); WPSHB(x!=y); continue; +/* SHL */ case 0x18: WPOP1(y); WPOP1(x); WPSH1(x<<y); continue; +/* SHR */ case 0x19: WPOP1(y); WPOP1(x); WPSH1(x>>y); continue; +/* ROL */ case 0x1A: WPOP1(y); WPOP1(x); WPSH1(ROL1(x,y)); continue; +/* ROR */ case 0x1B: WPOP1(y); WPOP1(x); WPSH1(ROR1(x,y)); continue; +/* IOR */ case 0x1C: WPOP1(x); WSTV(-1)|=x; continue; +/* XOR */ case 0x1D: WPOP1(x); WSTV(-1)^=x; continue; +/* AND */ case 0x1E: WPOP1(x); WSTV(-1)&=x; continue; +/* NOT */ case 0x1F: WSTV(-1)=~WSTV(-1); continue; /* NOP */ case 0x20: continue; -/* JMS */ case 0x21: WPOP2(a,b); RPSHD(PC); PC=DOUBLE(a,b); continue; -/* JCS */ case 0x22: WPOP2(b,c); WPOP1(a); if(a) {RPSHD(PC); PC=DOUBLE(b,c);} continue; -/* JCK* */ case 0x23: WPOP2(c,d); WGETD(x); if(x) {PC=DOUBLE(c,d);} continue; -/* LDA* */ case 0x24: WPOP2(a,b); x=DOUBLE(a,b); WPSH1(MEM[x]); WPSH1(MEM[x+1]); continue; -/* STA* */ case 0x25: WPOPD(x); WPOP2(a,b); MEM[x]=a; MEM[x+1]=b; continue; -/* LDD* */ case 0x26: WPOP1(a); WPSH1(dev_read(br,a)); WPSH1(dev_read(br,a+1)); continue; -/* STD* */ case 0x27: WPOP1(c); WPOP2(a,b); sig=dev_write_16(br,c,a,b); SIGNAL(sig); -/* PSH* */ case 0x28: RPOP2(a,b); WPSH2(a,b); continue; -/* POP* */ case 0x29: WST.p-=2; continue; -/* CPY* */ case 0x2A: RGET2(a,b); WPSH2(a,b); continue; -/* SPL* */ case 0x2B: WPOP2(a,b); WPSH2(LEFT(a),RIGHT(a)); WPSH2(LEFT(b),RIGHT(b)); continue; -/* DUP* */ case 0x2C: WGET2(a,b); WPSH2(a,b); continue; -/* OVR* */ case 0x2D: WPOP2(c,d); WGET2(a,b); WPSH2(c,d); WPSH2(a,b); continue; -/* SWP* */ case 0x2E: WPOP2(c,d); WPOP2(a,b); WPSH2(c,d); WPSH2(a,b); continue; -/* ROT* */ case 0x2F: WPOP2(e,f); WPOP2(c,d); WPOP2(a,b); WPSH2(c,d); WPSH2(e,f); WPSH2(a,b); continue; -/* ADD* */ case 0x30: WPOPD(y); WPOPD(x); WPSHD(x+y); continue; -/* SUB* */ case 0x31: WPOPD(y); WPOPD(x); WPSHD(x-y); continue; -/* INC* */ case 0x32: WPOPD(x); WPSHD(x+1); continue; -/* DEC* */ case 0x33: WPOPD(x); WPSHD(x-1); continue; -/* LTH* */ case 0x34: WPOPD(y); WPOPD(x); WPSH1(BOOL(x<y)); continue; -/* GTH* */ case 0x35: WPOPD(y); WPOPD(x); WPSH1(BOOL(x>y)); continue; -/* EQU* */ case 0x36: WPOP2(c,d); WPOP2(a,b); WPSH1(BOOL(a==c && b==d)); continue; -/* NQK* */ case 0x37: d=WSTV(-1); c=WSTV(-2); b=WSTV(-3); a=WSTV(-4); WPSH1(BOOL(a!=c || b!=d)); continue; -/* IOR* */ case 0x38: WPOP2(c,d); WSTV(-1)|=d; WSTV(-2)|=c; continue; -/* XOR* */ case 0x39: WPOP2(c,d); WSTV(-1)^=d; WSTV(-2)^=c; continue; -/* AND* */ case 0x3A: WPOP2(c,d); WSTV(-1)&=d; WSTV(-2)&=c; continue; -/* NOT* */ case 0x3B: WSTV(-1)=~WSTV(-1); WSTV(-2)=~WSTV(-2); continue; -/* SHF* */ case 0x3C: WPOP1(c); WPOPD(x); x=SHF(x,c); WPSHD(x); continue; -/* SHC* */ case 0x3D: WPOP1(c); WPOPD(x); x=shcd(x,c); WPSHD(x); continue; -/* TAL* */ case 0x3E: WPOP2(a,b); WPSH1(TAL(a)+TAL(b)); continue; -/* REV* */ case 0x3F: WPOP2(a,b); WPSH1(rev(b)); WPSH1(rev(a)); continue; +/* PSH: */ case 0x21: MPOP1(x); WPSH1(x); continue; +/* POP: */ case 0x22: IP+=1; continue; +/* CPY: */ case 0x23: MPOP1(x); RPSH1(x); WPSH1(x); continue; +/* DUP: */ case 0x24: MPOP1(x); WPSH1(x); WPSH1(x); continue; +/* OVR: */ case 0x25: MPOP1(y); WGET1(x); WPSH1(y); WPSH1(x); continue; +/* SWP: */ case 0x26: MPOP1(y); WPOP1(x); WPSH1(y); WPSH1(x); continue; +/* ROT: */ case 0x27: MPOP1(z); WPOP1(y); WPOP1(x); WPSH1(y); WPSH1(z); WPSH1(x); continue; +/* JMP: */ case 0x28: MPOPD(x); IP=x; continue; +/* JMS: */ case 0x29: MPOPD(x); RPSHD(IP); IP=x; continue; +/* JCN: */ case 0x2A: MPOPD(y); WPOP1(x); if(x) IP=y; continue; +/* JCS: */ case 0x2B: MPOPD(y); WPOP1(x); if(x) { RPSHD(IP); IP=y; } continue; +/* LDA: */ case 0x2C: MPOPD(x); WPSH1(MEM[x]); continue; +/* STA: */ case 0x2D: MPOPD(y); WPOP1(x); MEM[y]=x; continue; +/* LDD: */ case 0x2E: MPOP1(x); WPSH1(dev_read(&br->dev,x)); continue; +/* STD: */ case 0x2F: MPOP1(y); WPOP1(x); sig=dev_write(&br->dev,y,x); SIGNAL(sig); +/* ADD: */ case 0x30: MPOP1(x); WSTV(-1)+=x; continue; +/* SUB: */ case 0x31: MPOP1(x); WSTV(-1)-=x; continue; +/* INC: */ case 0x32: MPOP1(x); WPSH1(x+1); continue; +/* DEC: */ case 0x33: MPOP1(x); WPSH1(x-1); continue; +/* LTH: */ case 0x34: MPOP1(y); WPOP1(x); WPSHB(x<y); continue; +/* GTH: */ case 0x35: MPOP1(y); WPOP1(x); WPSHB(x>y); continue; +/* EQU: */ case 0x36: MPOP1(y); WPOP1(x); WPSHB(x==y); continue; +/* NQK: */ case 0x37: MPOP1(y); WGET1(x); WPSH1(y); WPSHB(x!=y); continue; +/* SHL: */ case 0x38: MPOP1(y); WPOP1(x); WPSH1(x<<y); continue; +/* SHR: */ case 0x39: MPOP1(y); WPOP1(x); WPSH1(x>>y); continue; +/* ROL: */ case 0x3A: MPOP1(y); WPOP1(x); WPSH1(ROL1(x,y)); continue; +/* ROR: */ case 0x3B: MPOP1(y); WPOP1(x); WPSH1(ROR1(x,y)); continue; +/* IOR: */ case 0x3C: MPOP1(x); WSTV(-1)|=x; continue; +/* XOR: */ case 0x3D: MPOP1(x); WSTV(-1)^=x; continue; +/* AND: */ case 0x3E: MPOP1(x); WSTV(-1)&=x; continue; +/* NOT: */ case 0x3F: MPOP1(x); WPSH1(~x); continue; /* DB1 */ case 0x40: return SIG_DB1; -/* JMP: */ case 0x41: MLIT2(a,b); PC=DOUBLE(a,b); continue; -/* JCN: */ case 0x42: MLIT2(b,c); WPOP1(a); if(a) PC=DOUBLE(b,c); continue; -/* JCK: */ case 0x43: MLIT2(b,c); WGET1(a); if(a) PC=DOUBLE(b,c); continue; -/* LDA: */ case 0x44: MLIT2(a,b); x=DOUBLE(a,b); WPSH1(MEM[x]); continue; -/* STA: */ case 0x45: MLITD(x); WPOP1(a); MEM[x]=a; continue; -/* LDD: */ case 0x46: MLIT1(a); WPSH1(dev_read(br,a)); continue; -/* STD: */ case 0x47: MLIT1(b); WPOP1(a); sig=dev_write(br,b,a); SIGNAL(sig); -/* PSH: */ case 0x48: MLIT1(a); WPSH1(a); continue; -/* POP: */ case 0x49: PC+=1; continue; -/* CPY: */ case 0x4A: MLIT1(a); RPSH1(a); WPSH1(a); continue; -/* SPL: */ case 0x4B: MLIT1(a); WPSH2(LEFT(a),RIGHT(a)); continue; -/* DUP: */ case 0x4C: MLIT1(a); WPSH1(a); WPSH1(a); continue; -/* OVR: */ case 0x4D: MLIT1(b); WGET1(a); WPSH1(b); WPSH1(a); continue; -/* SWP: */ case 0x4E: MLIT1(b); WPOP1(a); WPSH1(b); WPSH1(a); continue; -/* ROT: */ case 0x4F: MLIT1(c); WPOP1(b); WPOP1(a); WPSH1(b); WPSH1(c); WPSH1(a); continue; -/* ADD: */ case 0x50: MLIT1(b); WSTV(-1)+=b; continue; -/* SUB: */ case 0x51: MLIT1(b); WSTV(-1)-=b; continue; -/* INC: */ case 0x52: MLIT1(a); WPSH1(a+1); continue; -/* DEC: */ case 0x53: MLIT1(a); WPSH1(a-1); continue; -/* LTH: */ case 0x54: MLIT1(b); WPOP1(a); WPSH1(BOOL(a<b)); continue; -/* GTH: */ case 0x55: MLIT1(b); WPOP1(a); WPSH1(BOOL(a>b)); continue; -/* EQU: */ case 0x56: MLIT1(b); WPOP1(a); WPSH1(BOOL(a==b)); continue; -/* NQK: */ case 0x57: MLIT1(b); WGET1(a); WPSH1(b); WPSH1(BOOL(a!=b)); continue; -/* IOR: */ case 0x58: MLIT1(b); WSTV(-1)|=b; continue; -/* XOR: */ case 0x59: MLIT1(b); WSTV(-1)^=b; continue; -/* AND: */ case 0x5A: MLIT1(b); WSTV(-1)&=b; continue; -/* NOT: */ case 0x5B: MLIT1(a); WPSH1(~a); continue; -/* SHF: */ case 0x5C: MLIT1(b); WPOP1(a); x=SHF(a,b); WPSH1(x); continue; -/* SHC: */ case 0x5D: MLIT1(b); WPOP1(a); x=shc(a,b); WPSH1(x); continue; -/* TAL: */ case 0x5E: MLIT1(a); WPSH1(TAL(a)); continue; -/* REV: */ case 0x5F: MLIT1(a); WPSH1(rev(a)); continue; +/* PSH* */ case 0x41: RPOP2(x,y); WPSH2(x,y); continue; +/* POP* */ case 0x42: WST.p-=2; continue; +/* CPY* */ case 0x43: RGET2(x,y); WPSH2(x,y); continue; +/* DUP* */ case 0x44: WGET2(x,y); WPSH2(x,y); continue; +/* OVR* */ case 0x45: WPOP2(y,z); WGET2(w,x); WPSH2(y,z); WPSH2(w,x); continue; +/* SWP* */ case 0x46: WPOP2(y,z); WPOP2(w,x); WPSH2(y,z); WPSH2(w,x); continue; +/* ROT* */ case 0x47: WPOP2(y,z); WPOP2(w,x); WPOP2(u,v); WPSH2(w,x); WPSH2(y,z); WPSH2(u,v); continue; +/* JMP* */ case 0x48: WPOPD(x); IP=x; continue; +/* JMS* */ case 0x49: WPOPD(x); RPSHD(IP); IP=x; continue; +/* JCN* */ case 0x4A: WPOPD(y); WPOPD(x); if(x) IP=y; continue; +/* JCS* */ case 0x4B: WPOPD(y); WPOPD(x); if(x) { RPSHD(IP); IP=y; } continue; +/* LDA* */ case 0x4C: WPOPD(x); WPSH2(MEM[x], MEM[x+1]); continue; +/* STA* */ case 0x4D: WPOPD(z); WPOP2(x,y); MEM[z]=x; MEM[z+1]=y; continue; +/* LDD* */ case 0x4E: WPOP1(x); WPSH1(DGET1(x)); WPSH1(DGET1(x+1)); continue; +/* STD* */ case 0x4F: WPOP1(z); WPOP2(x,y); sig=DSET2(z,x,y); SIGNAL(sig); +/* ADD* */ case 0x50: WPOPD(y); WPOPD(x); WPSHD(x+y); continue; +/* SUB* */ case 0x51: WPOPD(y); WPOPD(x); WPSHD(x-y); continue; +/* INC* */ case 0x52: WPOPD(x); WPSHD(x+1); continue; +/* DEC* */ case 0x53: WPOPD(x); WPSHD(x-1); continue; +/* LTH* */ case 0x54: WPOPD(y); WPOPD(x); WPSHB(x<y); continue; +/* GTH* */ case 0x55: WPOPD(y); WPOPD(x); WPSHB(x>y); continue; +/* EQU* */ case 0x56: WPOP2(y,z); WPOP2(w,x); WPSHB(w==y && x==z); continue; +/* NQK* */ case 0x57: z=WSTV(-1); y=WSTV(-2); x=WSTV(-3); w=WSTV(-4); WPSHB(w!=y || x!=z); continue; +/* SHL* */ case 0x58: WPOP1(y); WPOPD(x); WPSHD(x<<y); continue; +/* SHR* */ case 0x59: WPOP1(y); WPOPD(x); WPSHD(x>>y); continue; +/* ROL* */ case 0x5A: WPOP1(y); WPOPD(x); WPSHD(ROLD(x,y)); continue; +/* ROR* */ case 0x5B: WPOP1(y); WPOPD(x); WPSHD(RORD(x,y)); continue; +/* IOR* */ case 0x5C: WPOP2(x,y); WSTV(-1)|=y; WSTV(-2)|=x; continue; +/* XOR* */ case 0x5D: WPOP2(x,y); WSTV(-1)^=y; WSTV(-2)^=x; continue; +/* AND* */ case 0x5E: WPOP2(x,y); WSTV(-1)&=y; WSTV(-2)&=x; continue; +/* NOT* */ case 0x5F: WSTV(-1)=~WSTV(-1); WSTV(-2)=~WSTV(-2); continue; /* DB2 */ case 0x60: return SIG_DB2; -/* JMS: */ case 0x61: MLIT2(a,b); RPSHD(PC); PC=DOUBLE(a,b); continue; -/* JCS: */ case 0x62: MLIT2(b,c); WPOP1(a); if(a) {RPSHD(PC); PC=DOUBLE(b,c);} continue; -/* JCK:* */ case 0x63: MLIT2(c,d); WGETD(x); if(x) {PC=DOUBLE(c,d);} continue; -/* LDA:* */ case 0x64: MLIT2(a,b); x=DOUBLE(a,b); WPSH1(MEM[x]); WPSH1(MEM[x+1]); continue; -/* STA:* */ case 0x65: MLITD(x); WPOP2(a,b); MEM[x]=a; MEM[x+1]=b; continue; -/* LDD:* */ case 0x66: MLIT1(a); WPSH1(dev_read(br,a)); WPSH1(dev_read(br,a+1)); continue; -/* STD:* */ case 0x67: MLIT1(c); WPOP2(a,b); sig=dev_write_16(br,c,a,b); SIGNAL(sig); -/* PSH:* */ case 0x68: MLIT2(a,b); WPSH2(a,b); continue; -/* POP:* */ case 0x69: PC+=2; continue; -/* CPY:* */ case 0x6A: MLIT2(a,b); RPSH2(a,b); WPSH2(a,b); continue; -/* SPL:* */ case 0x6B: MLIT2(a,b); WPSH2(LEFT(a),RIGHT(a)); WPSH2(LEFT(b),RIGHT(b)); continue; -/* DUP:* */ case 0x6C: MLIT2(a,b); WPSH2(a,b); WPSH2(a,b); continue; -/* OVR:* */ case 0x6D: MLIT2(c,d); WGET2(a,b); WPSH2(c,d); WPSH2(a,b); continue; -/* SWP:* */ case 0x6E: MLIT2(c,d); WPOP2(a,b); WPSH2(c,d); WPSH2(a,b); continue; -/* ROT:* */ case 0x6F: MLIT2(e,f); WPOP2(c,d); WPOP2(a,b); WPSH2(c,d); WPSH2(e,f); WPSH2(a,b); continue; -/* ADD:* */ case 0x70: MLITD(y); WPOPD(x); WPSHD(x+y); continue; -/* SUB:* */ case 0x71: MLITD(y); WPOPD(x); WPSHD(x-y); continue; -/* INC:* */ case 0x72: MLITD(x); WPSHD(x+1); continue; -/* DEC:* */ case 0x73: MLITD(x); WPSHD(x-1); continue; -/* LTH:* */ case 0x74: MLITD(y); WPOPD(x); WPSH1(BOOL(x<y)); continue; -/* GTH:* */ case 0x75: MLITD(y); WPOPD(x); WPSH1(BOOL(x>y)); continue; -/* EQU:* */ case 0x76: MLIT2(c,d); WPOP2(a,b); WPSH1(BOOL(a==c && b==d)); continue; -/* NQK:* */ case 0x77: MLIT2(c,d); WGET2(a,b); WPSH2(c,d); WPSH1(BOOL(a!=c || b!=d)); continue; -/* IOR:* */ case 0x78: MLIT2(c,d); WSTV(-1)|=d; WSTV(-2)|=c; continue; -/* XOR:* */ case 0x79: MLIT2(c,d); WSTV(-1)^=d; WSTV(-2)^=c; continue; -/* AND:* */ case 0x7A: MLIT2(c,d); WSTV(-1)&=d; WSTV(-2)&=c; continue; -/* NOT:* */ case 0x7B: MLIT2(a,b); WPSH2(~a,~b); continue; -/* SHF:* */ case 0x7C: MLIT1(c); WPOPD(x); x=SHF(x,c); WPSHD(x); continue; -/* SHC:* */ case 0x7D: MLIT1(c); WPOPD(x); x=shcd(x,c); WPSHD(x); continue; -/* TAL:* */ case 0x7E: MLIT2(a,b); WPSH1(TAL(a)+TAL(b)); continue; -/* REV:* */ case 0x7F: MLIT2(a,b); WPSH1(rev(b)); WPSH1(rev(a)); continue; +/* PSH*: */ case 0x61: MPOP2(x,y); WPSH2(x,y); continue; +/* POP*: */ case 0x62: IP+=2; continue; +/* CPY*: */ case 0x63: MPOP2(x,y); RPSH2(x,y); WPSH2(x,y); continue; +/* DUP*: */ case 0x64: MPOP2(x,y); WPSH2(x,y); WPSH2(x,y); continue; +/* OVR*: */ case 0x65: MPOP2(y,z); WGET2(w,x); WPSH2(y,z); WPSH2(w,x); continue; +/* SWP*: */ case 0x66: MPOP2(y,z); WPOP2(w,x); WPSH2(y,z); WPSH2(w,x); continue; +/* ROT*: */ case 0x67: MPOP2(y,z); WPOP2(w,x); WPOP2(u,v); WPSH2(w,x); WPSH2(y,z); WPSH2(u,v); continue; +/* JMP*: */ case 0x68: MPOPD(x); IP=x; continue; +/* JMS*: */ case 0x69: MPOPD(x); RPSHD(IP); IP=x; continue; +/* JCN*: */ case 0x6A: MPOPD(y); WPOPD(x); if(x) IP=y; continue; +/* JCS*: */ case 0x6B: MPOPD(y); WPOPD(x); if(x) { RPSHD(IP); IP=y; } continue; +/* LDA*: */ case 0x6C: MPOPD(x); WPSH2(MEM[x], MEM[x+1]); continue; +/* STA*: */ case 0x6D: MPOPD(z); WPOP2(x,y); MEM[z]=x; MEM[z+1]=y; continue; +/* LDD*: */ case 0x6E: MPOP1(x); WPSH1(DGET1(x)); WPSH1(DGET1(x+1)); continue; +/* STD*: */ case 0x6F: MPOP1(z); WPOP2(x,y); sig=DSET2(z,x,y); SIGNAL(sig); +/* ADD*: */ case 0x70: MPOPD(y); WPOPD(x); WPSHD(x+y); continue; +/* SUB*: */ case 0x71: MPOPD(y); WPOPD(x); WPSHD(x-y); continue; +/* INC*: */ case 0x72: MPOPD(x); WPSHD(x+1); continue; +/* DEC*: */ case 0x73: MPOPD(x); WPSHD(x-1); continue; +/* LTH*: */ case 0x74: MPOPD(y); WPOPD(x); WPSHB(x<y); continue; +/* GTH*: */ case 0x75: MPOPD(y); WPOPD(x); WPSHB(x>y); continue; +/* EQU*: */ case 0x76: MPOP2(y,z); WPOP2(w,x); WPSHB(w==y && x==z); continue; +/* NQK*: */ case 0x77: MPOP2(y,z); WGET2(w,x); WPSH2(y,z); WPSHB(w!=y || x!=z); continue; +/* SHL*: */ case 0x78: MPOP1(y); WPOPD(x); WPSHD(x<<y); continue; +/* SHR*: */ case 0x79: MPOP1(y); WPOPD(x); WPSHD(x>>y); continue; +/* ROL*: */ case 0x7A: MPOP1(y); WPOPD(x); WPSHD(ROLD(x,y)); continue; +/* ROR*: */ case 0x7B: MPOP1(y); WPOPD(x); WPSHD(RORD(x,y)); continue; +/* IOR*: */ case 0x7C: MPOP2(x,y); WSTV(-1)|=y; WSTV(-2)|=x; continue; +/* XOR*: */ case 0x7D: MPOP2(x,y); WSTV(-1)^=y; WSTV(-2)^=x; continue; +/* AND*: */ case 0x7E: MPOP2(x,y); WSTV(-1)&=y; WSTV(-2)&=x; continue; +/* NOT*: */ case 0x7F: MPOP2(x,y); WPSH2(~x,~y); continue; /* DB3 */ case 0x80: return SIG_DB3; -/* JMPr */ case 0x81: RPOP2(a,b); PC=DOUBLE(a,b); continue; -/* JCNr */ case 0x82: RPOP2(b,c); RPOP1(a); if(a) PC=DOUBLE(b,c); continue; -/* JCKr */ case 0x83: RPOP2(b,c); RGET1(a); if(a) PC=DOUBLE(b,c); continue; -/* LDAr */ case 0x84: RPOP2(a,b); x=DOUBLE(a,b); RPSH1(MEM[x]); continue; -/* STAr */ case 0x85: RPOPD(x); RPOP1(a); MEM[x]=a; continue; -/* LDDr */ case 0x86: RPOP1(a); RPSH1(dev_read(br,a)); continue; -/* STDr */ case 0x87: RPOP1(b); RPOP1(a); sig=dev_write(br,b,a); SIGNAL(sig); -/* PSHr */ case 0x88: WPOP1(a); RPSH1(a); continue; -/* POPr */ case 0x89: RST.p-=1; continue; -/* CPYr */ case 0x8A: WGET1(a); RPSH1(a); continue; -/* SPLr */ case 0x8B: RPOP1(a); RPSH2(LEFT(a),RIGHT(a)); continue; -/* DUPr */ case 0x8C: RGET1(a); RPSH1(a); continue; -/* OVRr */ case 0x8D: RPOP1(b); RGET1(a); RPSH1(b); RPSH1(a); continue; -/* SWPr */ case 0x8E: RPOP1(b); RPOP1(a); RPSH1(b); RPSH1(a); continue; -/* ROTr */ case 0x8F: RPOP1(c); RPOP1(b); RPOP1(a); RPSH1(b); RPSH1(c); RPSH1(a); continue; -/* ADDr */ case 0x90: RPOP1(b); RSTV(-1)+=b; continue; -/* SUBr */ case 0x91: RPOP1(b); RSTV(-1)-=b; continue; +/* PSHr */ case 0x81: WPOP1(x); RPSH1(x); continue; +/* POPr */ case 0x82: RST.p-=1; continue; +/* CPYr */ case 0x83: WGET1(x); RPSH1(x); continue; +/* DUPr */ case 0x84: RGET1(x); RPSH1(x); continue; +/* OVRr */ case 0x85: RPOP1(y); RGET1(x); RPSH1(y); RPSH1(x); continue; +/* SWPr */ case 0x86: RPOP1(y); RPOP1(x); RPSH1(y); RPSH1(x); continue; +/* ROTr */ case 0x87: RPOP1(z); RPOP1(y); RPOP1(x); RPSH1(y); RPSH1(z); RPSH1(x); continue; +/* JMPr */ case 0x88: RPOPD(x); IP=x; continue; +/* JMSr */ case 0x89: RPOPD(x); WPSHD(IP); IP=x; continue; +/* JCNr */ case 0x8A: RPOPD(y); RPOP1(x); if(x) IP=y; continue; +/* JCSr */ case 0x8B: RPOPD(y); RPOP1(x); if(x) { WPSHD(IP); IP=y; } continue; +/* LDAr */ case 0x8C: RPOPD(x); RPSH1(MEM[x]); continue; +/* STAr */ case 0x8D: RPOPD(y); RPOP1(x); MEM[y]=x; continue; +/* LDDr */ case 0x8E: RPOP1(x); RPSH1(dev_read(&br->dev,x)); continue; +/* STDr */ case 0x8F: RPOP1(y); RPOP1(x); sig=dev_write(&br->dev,y,x); SIGNAL(sig); +/* ADDr */ case 0x90: RPOP1(x); RSTV(-1)+=x; continue; +/* SUBr */ case 0x91: RPOP1(x); RSTV(-1)-=x; continue; /* INCr */ case 0x92: RSTV(-1)+=1; continue; /* DECr */ case 0x93: RSTV(-1)-=1; continue; -/* LTHr */ case 0x94: RPOP1(b); RPOP1(a); RPSH1(BOOL(a<b)); continue; -/* GTHr */ case 0x95: RPOP1(b); RPOP1(a); RPSH1(BOOL(a>b)); continue; -/* EQUr */ case 0x96: RPOP1(b); RPOP1(a); RPSH1(BOOL(a==b)); continue; -/* NQKr */ case 0x97: b=RSTV(-1); a=RSTV(-2); RPSH1(BOOL(a!=b)); continue; -/* IORr */ case 0x98: RPOP1(b); RSTV(-1)|=b; continue; -/* XORr */ case 0x99: RPOP1(b); RSTV(-1)^=b; continue; -/* ANDr */ case 0x9A: RPOP1(b); RSTV(-1)&=b; continue; -/* NOTr */ case 0x9B: RSTV(-1)=~RSTV(-1); continue; -/* SHFr */ case 0x9C: RPOP1(b); RPOP1(a); x=SHF(a,b); RPSH1(x); continue; -/* SHCr */ case 0x9D: RPOP1(b); RPOP1(a); x=shc(a,b); RPSH1(x); continue; -/* TALr */ case 0x9E: RSTV(-1)=TAL(RSTV(-1)); continue; -/* REVr */ case 0x9F: RSTV(-1)=rev(RSTV(-1)); continue; +/* LTHr */ case 0x94: RPOP1(y); RPOP1(x); RPSHB(x<y); continue; +/* GTHr */ case 0x95: RPOP1(y); RPOP1(x); RPSHB(x>y); continue; +/* EQUr */ case 0x96: RPOP1(y); RPOP1(x); RPSHB(x==y); continue; +/* NQKr */ case 0x97: y=RSTV(-1); x=RSTV(-2); RPSHB(x!=y); continue; +/* SHLr */ case 0x98: RPOP1(y); RPOP1(x); RPSH1(x<<y); continue; +/* SHRr */ case 0x99: RPOP1(y); RPOP1(x); RPSH1(x>>y); continue; +/* ROLr */ case 0x9A: RPOP1(y); RPOP1(x); RPSH1(ROL1(x,y)); continue; +/* RORr */ case 0x9B: RPOP1(y); RPOP1(x); RPSH1(ROR1(x,y)); continue; +/* IORr */ case 0x9C: RPOP1(x); RSTV(-1)|=x; continue; +/* XORr */ case 0x9D: RPOP1(x); RSTV(-1)^=x; continue; +/* ANDr */ case 0x9E: RPOP1(x); RSTV(-1)&=x; continue; +/* NOTr */ case 0x9F: RSTV(-1)=~RSTV(-1); continue; /* DB4 */ case 0xA0: return SIG_DB4; -/* JMSr */ case 0xA1: RPOP2(a,b); WPSHD(PC); PC=DOUBLE(a,b); continue; -/* JCSr */ case 0xA2: RPOP2(b,c); RPOP1(a); if(a) {WPSHD(PC); PC=DOUBLE(b,c);} continue; -/* JCKr* */ case 0xA3: RPOP2(c,d); RGETD(x); if(x) {PC=DOUBLE(c,d);} continue; -/* LDAr* */ case 0xA4: RPOP2(a,b); x=DOUBLE(a,b); RPSH1(MEM[x]); RPSH1(MEM[x+1]); continue; -/* STAr* */ case 0xA5: RPOPD(x); RPOP2(a,b); MEM[x]=a; MEM[x+1]=b; continue; -/* LDDr* */ case 0xA6: RPOP1(a); RPSH1(dev_read(br,a)); RPSH1(dev_read(br,a+1)); continue; -/* STDr* */ case 0xA7: RPOP1(c); RPOP2(a,b); sig=dev_write_16(br,c,a,b); SIGNAL(sig); -/* PSHr* */ case 0xA8: WPOP2(a,b); RPSH2(a,b); continue; -/* POPr* */ case 0xA9: RST.p-=2; continue; -/* CPYr* */ case 0xAA: WGET2(a,b); RPSH2(a,b); continue; -/* SPLr* */ case 0xAB: RPOP2(a,b); RPSH2(LEFT(a),RIGHT(a)); RPSH2(LEFT(b),RIGHT(b)); continue; -/* DUPr* */ case 0xAC: RGET2(a,b); RPSH2(a,b); continue; -/* OVRr* */ case 0xAD: RPOP2(c,d); RGET2(a,b); RPSH2(c,d); RPSH2(a,b); continue; -/* SWPr* */ case 0xAE: RPOP2(c,d); RPOP2(a,b); RPSH2(c,d); RPSH2(a,b); continue; -/* ROTr* */ case 0xAF: RPOP2(e,f); RPOP2(c,d); RPOP2(a,b); RPSH2(c,d); RPSH2(e,f); RPSH2(a,b); continue; -/* ADDr* */ case 0xB0: RPOPD(y); RPOPD(x); RPSHD(x+y); continue; -/* SUBr* */ case 0xB1: RPOPD(y); RPOPD(x); RPSHD(x-y); continue; -/* INCr* */ case 0xB2: RPOPD(x); RPSHD(x+1); continue; -/* DECr* */ case 0xB3: RPOPD(x); RPSHD(x-1); continue; -/* LTHr* */ case 0xB4: RPOPD(y); RPOPD(x); RPSH1(BOOL(x<y)); continue; -/* GTHr* */ case 0xB5: RPOPD(y); RPOPD(x); RPSH1(BOOL(x>y)); continue; -/* EQUr* */ case 0xB6: RPOP2(c,d); RPOP2(a,b); RPSH1(BOOL(a==c && b==d)); continue; -/* NQKr* */ case 0xB7: d=RSTV(-1); c=RSTV(-2); b=RSTV(-3); a=RSTV(-4); RPSH1(BOOL(a!=c || b!=d)); continue; -/* IORr* */ case 0xB8: RPOP2(c,d); RSTV(-1)|=d; RSTV(-2)|=c; continue; -/* XORr* */ case 0xB9: RPOP2(c,d); RSTV(-1)^=d; RSTV(-2)^=c; continue; -/* ANDr* */ case 0xBA: RPOP2(c,d); RSTV(-1)&=d; RSTV(-2)&=c; continue; -/* NOTr* */ case 0xBB: RSTV(-1)=~RSTV(-1); RSTV(-2)=~RSTV(-2); continue; -/* SHFr* */ case 0xBC: RPOP1(c); RPOPD(x); x=SHF(x,c); RPSHD(x); continue; -/* SHCr* */ case 0xBD: RPOP1(c); RPOPD(x); x=shcd(x,c); RPSHD(x); continue; -/* TALr* */ case 0xBE: RPOP2(a,b); RPSH1(TAL(a)+TAL(b)); continue; -/* REVr* */ case 0xBF: RPOP2(a,b); RPSH1(rev(b)); RPSH1(rev(a)); continue; +/* PSHr: */ case 0xA1: MPOP1(x); RPSH1(x); continue; +/* POPr: */ case 0xA2: IP+=1; continue; +/* CPYr: */ case 0xA3: MPOP1(x); WPSH1(x); RPSH1(x); continue; +/* DUPr: */ case 0xA4: MPOP1(x); RPSH1(x); RPSH1(x); continue; +/* OVRr: */ case 0xA5: MPOP1(y); RGET1(x); RPSH1(y); RPSH1(x); continue; +/* SWPr: */ case 0xA6: MPOP1(y); RPOP1(x); RPSH1(y); RPSH1(x); continue; +/* ROTr: */ case 0xA7: MPOP1(z); RPOP1(y); RPOP1(x); RPSH1(y); RPSH1(z); RPSH1(x); continue; +/* JMPr: */ case 0xA8: MPOPD(x); IP=x; continue; +/* JMSr: */ case 0xA9: MPOPD(x); WPSHD(IP); IP=x; continue; +/* JCNr: */ case 0xAA: MPOPD(y); RPOP1(x); if(x) IP=y; continue; +/* JCSr: */ case 0xAB: MPOPD(y); RPOP1(x); if(x) { WPSHD(IP); IP=y; } continue; +/* LDAr: */ case 0xAC: MPOPD(x); RPSH1(MEM[x]); continue; +/* STAr: */ case 0xAD: MPOPD(y); RPOP1(x); MEM[y]=x; continue; +/* LDDr: */ case 0xAE: MPOP1(x); RPSH1(dev_read(&br->dev,x)); continue; +/* STDr: */ case 0xAF: MPOP1(y); RPOP1(x); sig=dev_write(&br->dev,y,x); SIGNAL(sig); +/* ADDr: */ case 0xB0: MPOP1(x); RSTV(-1)+=x; continue; +/* SUBr: */ case 0xB1: MPOP1(x); RSTV(-1)-=x; continue; +/* INCr: */ case 0xB2: MPOP1(x); RPSH1(x+1); continue; +/* DECr: */ case 0xB3: MPOP1(x); RPSH1(x-1); continue; +/* LTHr: */ case 0xB4: MPOP1(y); RPOP1(x); RPSHB(x<y); continue; +/* GTHr: */ case 0xB5: MPOP1(y); RPOP1(x); RPSHB(x>y); continue; +/* EQUr: */ case 0xB6: MPOP1(y); RPOP1(x); RPSHB(x==y); continue; +/* NQKr: */ case 0xB7: MPOP1(y); RGET1(x); RPSH1(y); RPSHB(x!=y); continue; +/* SHLr: */ case 0xB8: MPOP1(y); RPOP1(x); RPSH1(x<<y); continue; +/* SHRr: */ case 0xB9: MPOP1(y); RPOP1(x); RPSH1(x>>y); continue; +/* ROLr: */ case 0xBA: MPOP1(y); RPOP1(x); RPSH1(ROL1(x,y)); continue; +/* RORr: */ case 0xBB: MPOP1(y); RPOP1(x); RPSH1(ROR1(x,y)); continue; +/* IORr: */ case 0xBC: MPOP1(x); RSTV(-1)|=x; continue; +/* XORr: */ case 0xBD: MPOP1(x); RSTV(-1)^=x; continue; +/* ANDr: */ case 0xBE: MPOP1(x); RSTV(-1)&=x; continue; +/* NOTr: */ case 0xBF: MPOP1(x); RPSH1(~x); continue; /* DB5 */ case 0xC0: return SIG_DB5; -/* JMPr: */ case 0xC1: MLIT2(a,b); PC=DOUBLE(a,b); continue; -/* JCNr: */ case 0xC2: MLIT2(b,c); RPOP1(a); if(a) PC=DOUBLE(b,c); continue; -/* JCKr: */ case 0xC3: MLIT2(b,c); RGET1(a); if(a) PC=DOUBLE(b,c); continue; -/* LDAr: */ case 0xC4: MLIT2(a,b); x=DOUBLE(a,b); RPSH1(MEM[x]); continue; -/* STAr: */ case 0xC5: MLITD(x); RPOP1(a); MEM[x]=a; continue; -/* LDDr: */ case 0xC6: MLIT1(a); RPSH1(dev_read(br,a)); continue; -/* STDr: */ case 0xC7: MLIT1(b); RPOP1(a); sig=dev_write(br,b,a); SIGNAL(sig); -/* PSHr: */ case 0xC8: MLIT1(a); RPSH1(a); continue; -/* POPr: */ case 0xC9: PC+=1; continue; -/* CPYr: */ case 0xCA: MLIT1(a); WPSH1(a); RPSH1(a); continue; -/* SPLr: */ case 0xCB: MLIT1(a); RPSH2(LEFT(a),RIGHT(a)); continue; -/* DUPr: */ case 0xCC: MLIT1(a); RPSH1(a); RPSH1(a); continue; -/* OVRr: */ case 0xCD: MLIT1(b); RGET1(a); RPSH1(b); RPSH1(a); continue; -/* SWPr: */ case 0xCE: MLIT1(b); RPOP1(a); RPSH1(b); RPSH1(a); continue; -/* ROTr: */ case 0xCF: MLIT1(c); RPOP1(b); RPOP1(a); RPSH1(b); RPSH1(c); RPSH1(a); continue; -/* ADDr: */ case 0xD0: MLIT1(b); RSTV(-1)+=b; continue; -/* SUBr: */ case 0xD1: MLIT1(b); RSTV(-1)-=b; continue; -/* INCr: */ case 0xD2: MLIT1(a); RPSH1(a+1); continue; -/* DECr: */ case 0xD3: MLIT1(a); RPSH1(a-1); continue; -/* LTHr: */ case 0xD4: MLIT1(b); RPOP1(a); RPSH1(BOOL(a<b)); continue; -/* GTHr: */ case 0xD5: MLIT1(b); RPOP1(a); RPSH1(BOOL(a>b)); continue; -/* EQUr: */ case 0xD6: MLIT1(b); RPOP1(a); RPSH1(BOOL(a==b)); continue; -/* NQKr: */ case 0xD7: MLIT1(b); RGET1(a); RPSH1(b); RPSH1(BOOL(a!=b)); continue; -/* IORr: */ case 0xD8: MLIT1(b); RSTV(-1)|=b; continue; -/* XORr: */ case 0xD9: MLIT1(b); RSTV(-1)^=b; continue; -/* ANDr: */ case 0xDA: MLIT1(b); RSTV(-1)&=b; continue; -/* NOTr: */ case 0xDB: MLIT1(a); RPSH1(~a); continue; -/* SHFr: */ case 0xDC: MLIT1(b); RPOP1(a); x=SHF(a,b); RPSH1(x); continue; -/* SHCr: */ case 0xDD: MLIT1(b); RPOP1(a); x=shc(a,b); RPSH1(x); continue; -/* TALr: */ case 0xDE: MLIT1(a); RPSH1(TAL(a)); continue; -/* REVr: */ case 0xDF: MLIT1(a); RPSH1(rev(a)); continue; +/* PSHr* */ case 0xC1: WPOP2(x,y); RPSH2(x,y); continue; +/* POPr* */ case 0xC2: RST.p-=2; continue; +/* CPYr* */ case 0xC3: WGET2(x,y); RPSH2(x,y); continue; +/* DUPr* */ case 0xC4: RGET2(x,y); RPSH2(x,y); continue; +/* OVRr* */ case 0xC5: RPOP2(y,z); RGET2(w,x); RPSH2(y,z); RPSH2(w,x); continue; +/* SWPr* */ case 0xC6: RPOP2(y,z); RPOP2(w,x); RPSH2(y,z); RPSH2(w,x); continue; +/* ROTr* */ case 0xC7: RPOP2(y,z); RPOP2(w,x); RPOP2(u,v); RPSH2(w,x); RPSH2(y,z); RPSH2(u,v); continue; +/* JMPr* */ case 0xC8: RPOPD(x); IP=x; continue; +/* JMSr* */ case 0xC9: RPOPD(x); WPSHD(IP); IP=x; continue; +/* JCNr* */ case 0xCA: RPOPD(y); RPOPD(x); if(x) IP=y; continue; +/* JCSr* */ case 0xCB: RPOPD(y); RPOPD(x); if(x) { WPSHD(IP); IP=y; } continue; +/* LDAr* */ case 0xCC: RPOPD(x); RPSH2(MEM[x], MEM[x+1]); continue; +/* STAr* */ case 0xCD: RPOPD(z); RPOP2(x,y); MEM[z]=x; MEM[z+1]=y; continue; +/* LDDr* */ case 0xCE: RPOP1(x); RPSH1(DGET1(x)); RPSH1(DGET1(x+1)); continue; +/* STDr* */ case 0xCF: RPOP1(z); RPOP2(x,y); sig=DSET2(z,x,y); SIGNAL(sig); +/* ADDr* */ case 0xD0: RPOPD(y); RPOPD(x); RPSHD(x+y); continue; +/* SUBr* */ case 0xD1: RPOPD(y); RPOPD(x); RPSHD(x-y); continue; +/* INCr* */ case 0xD2: RPOPD(x); RPSHD(x+1); continue; +/* DECr* */ case 0xD3: RPOPD(x); RPSHD(x-1); continue; +/* LTHr* */ case 0xD4: RPOPD(y); RPOPD(x); RPSHB(x<y); continue; +/* GTHr* */ case 0xD5: RPOPD(y); RPOPD(x); RPSHB(x>y); continue; +/* EQUr* */ case 0xD6: RPOP2(y,z); RPOP2(w,x); RPSHB(w==y && x==z); continue; +/* NQKr* */ case 0xD7: z=RSTV(-1); y=RSTV(-2); x=RSTV(-3); w=RSTV(-4); RPSHB(w!=y || x!=z); continue; +/* SHLr* */ case 0xD8: RPOP1(y); RPOPD(x); RPSHD(x<<y); continue; +/* SHRr* */ case 0xD9: RPOP1(y); RPOPD(x); RPSHD(x>>y); continue; +/* ROLr* */ case 0xDA: RPOP1(y); RPOPD(x); RPSHD(ROLD(x,y)); continue; +/* RORr* */ case 0xDB: RPOP1(y); RPOPD(x); RPSHD(RORD(x,y)); continue; +/* IORr* */ case 0xDC: RPOP2(x,y); RSTV(-1)|=y; RSTV(-2)|=x; continue; +/* XORr* */ case 0xDD: RPOP2(x,y); RSTV(-1)^=y; RSTV(-2)^=x; continue; +/* ANDr* */ case 0xDE: RPOP2(x,y); RSTV(-1)&=y; RSTV(-2)&=x; continue; +/* NOTr* */ case 0xDF: RSTV(-1)=~RSTV(-1); RSTV(-2)=~RSTV(-2); continue; /* DB6 */ case 0xE0: return SIG_DB6; -/* JMSr */ case 0xE1: MLIT2(a,b); WPSHD(PC); PC=DOUBLE(a,b); continue; -/* JCSr */ case 0xE2: MLIT2(b,c); RPOP1(a); if(a) {WPSHD(PC); PC=DOUBLE(b,c);} continue; -/* JCKr* */ case 0xE3: MLIT2(c,d); RGETD(x); if(x) {PC=DOUBLE(c,d);} continue; -/* LDAr* */ case 0xE4: MLIT2(a,b); x=DOUBLE(a,b); RPSH1(MEM[x]); RPSH1(MEM[x+1]); continue; -/* STAr* */ case 0xE5: MLITD(x); RPOP2(a,b); MEM[x]=a; MEM[x+1]=b; continue; -/* LDDr* */ case 0xE6: MLIT1(a); RPSH1(dev_read(br,a)); RPSH1(dev_read(br,a+1)); continue; -/* STDr* */ case 0xE7: MLIT1(c); RPOP2(a,b); sig=dev_write_16(br,c,a,b); SIGNAL(sig); -/* PSHr* */ case 0xE8: MLIT2(a,b); RPSH2(a,b); continue; -/* POPr* */ case 0xE9: PC+=2; continue; -/* CPYr* */ case 0xEA: MLIT2(a,b); WPSH2(a,b); RPSH2(a,b); continue; -/* SPLr* */ case 0xEB: MLIT2(a,b); RPSH2(LEFT(a),RIGHT(a)); RPSH2(LEFT(b),RIGHT(b)); continue; -/* DUPr* */ case 0xEC: MLIT2(a,b); RPSH2(a,b); RPSH2(a,b); continue; -/* OVRr* */ case 0xED: MLIT2(c,d); RGET2(a,b); RPSH2(c,d); RPSH2(a,b); continue; -/* SWPr* */ case 0xEE: MLIT2(c,d); RPOP2(a,b); RPSH2(c,d); RPSH2(a,b); continue; -/* ROTr* */ case 0xEF: MLIT2(e,f); RPOP2(c,d); RPOP2(a,b); RPSH2(c,d); RPSH2(e,f); RPSH2(a,b); continue; -/* ADDr* */ case 0xF0: MLITD(y); RPOPD(x); RPSHD(x+y); continue; -/* SUBr* */ case 0xF1: MLITD(y); RPOPD(x); RPSHD(x-y); continue; -/* INCr* */ case 0xF2: MLITD(x); RPSHD(x+1); continue; -/* DECr* */ case 0xF3: MLITD(x); RPSHD(x-1); continue; -/* LTHr* */ case 0xF4: MLITD(y); RPOPD(x); RPSH1(BOOL(x<y)); continue; -/* GTHr* */ case 0xF5: MLITD(y); RPOPD(x); RPSH1(BOOL(x>y)); continue; -/* EQUr* */ case 0xF6: MLIT2(c,d); RPOP2(a,b); RPSH1(BOOL(a==c && b==d)); continue; -/* NQKr* */ case 0xF7: MLIT2(c,d); RGET2(a,b); RPSH2(c,d); RPSH1(BOOL(a!=c || b!=d)); continue; -/* IORr* */ case 0xF8: MLIT2(c,d); RSTV(-1)|=d; RSTV(-2)|=c; continue; -/* XORr* */ case 0xF9: MLIT2(c,d); RSTV(-1)^=d; RSTV(-2)^=c; continue; -/* ANDr* */ case 0xFA: MLIT2(c,d); RSTV(-1)&=d; RSTV(-2)&=c; continue; -/* NOTr* */ case 0xFB: MLIT2(a,b); RPSH2(~a,~b); continue; -/* SHFr* */ case 0xFC: MLIT1(c); RPOPD(x); x=SHF(x,c); RPSHD(x); continue; -/* SHCr* */ case 0xFD: MLIT1(c); RPOPD(x); x=shcd(x,c); RPSHD(x); continue; -/* TALr* */ case 0xFE: MLIT2(a,b); RPSH1(TAL(a)+TAL(b)); continue; -/* REVr* */ case 0xFF: MLIT2(a,b); RPSH1(rev(b)); RPSH1(rev(a)); continue; +/* PSHr*: */ case 0xE1: MPOP2(x,y); RPSH2(x,y); continue; +/* POPr*: */ case 0xE2: IP+=2; continue; +/* CPYr*: */ case 0xE3: MPOP2(x,y); WPSH2(x,y); RPSH2(x,y); continue; +/* DUPr*: */ case 0xE4: MPOP2(x,y); RPSH2(x,y); RPSH2(x,y); continue; +/* OVRr*: */ case 0xE5: MPOP2(y,z); RGET2(w,x); RPSH2(y,z); RPSH2(w,x); continue; +/* SWPr*: */ case 0xE6: MPOP2(y,z); RPOP2(w,x); RPSH2(y,z); RPSH2(w,x); continue; +/* ROTr*: */ case 0xE7: MPOP2(y,z); RPOP2(w,x); RPOP2(u,v); RPSH2(w,x); RPSH2(y,z); RPSH2(u,v); continue; +/* JMPr*: */ case 0xE8: MPOPD(x); IP=x; continue; +/* JMSr*: */ case 0xE9: MPOPD(x); WPSHD(IP); IP=x; continue; +/* JCNr*: */ case 0xEA: MPOPD(y); RPOPD(x); if(x) IP=y; continue; +/* JCSr*: */ case 0xEB: MPOPD(y); RPOPD(x); if(x) { WPSHD(IP); IP=y; } continue; +/* LDAr*: */ case 0xEC: MPOPD(x); RPSH2(MEM[x], MEM[x+1]); continue; +/* STAr*: */ case 0xED: MPOPD(z); RPOP2(x,y); MEM[z]=x; MEM[z+1]=y; continue; +/* LDDr*: */ case 0xEE: MPOP1(x); RPSH1(DGET1(x)); RPSH1(DGET1(x+1)); continue; +/* STDr*: */ case 0xEF: MPOP1(z); RPOP2(x,y); sig=DSET2(z,x,y); SIGNAL(sig); +/* ADDr*: */ case 0xF0: MPOPD(y); RPOPD(x); RPSHD(x+y); continue; +/* SUBr*: */ case 0xF1: MPOPD(y); RPOPD(x); RPSHD(x-y); continue; +/* INCr*: */ case 0xF2: MPOPD(x); RPSHD(x+1); continue; +/* DECr*: */ case 0xF3: MPOPD(x); RPSHD(x-1); continue; +/* LTHr*: */ case 0xF4: MPOPD(y); RPOPD(x); RPSHB(x<y); continue; +/* GTHr*: */ case 0xF5: MPOPD(y); RPOPD(x); RPSHB(x>y); continue; +/* EQUr*: */ case 0xF6: MPOP2(y,z); RPOP2(w,x); RPSHB(w==y && x==z); continue; +/* NQKr*: */ case 0xF7: MPOP2(y,z); RGET2(w,x); RPSH2(y,z); RPSHB(w!=y || x!=z); continue; +/* SHLr*: */ case 0xF8: MPOP1(y); RPOPD(x); RPSHD(x<<y); continue; +/* SHRr*: */ case 0xF9: MPOP1(y); RPOPD(x); RPSHD(x>>y); continue; +/* ROLr*: */ case 0xFA: MPOP1(y); RPOPD(x); RPSHD(ROLD(x,y)); continue; +/* RORr*: */ case 0xFB: MPOP1(y); RPOPD(x); RPSHD(RORD(x,y)); continue; +/* IORr*: */ case 0xFC: MPOP2(x,y); RSTV(-1)|=y; RSTV(-2)|=x; continue; +/* XORr*: */ case 0xFD: MPOP2(x,y); RSTV(-1)^=y; RSTV(-2)^=x; continue; +/* ANDr*: */ case 0xFE: MPOP2(x,y); RSTV(-1)&=y; RSTV(-2)&=x; continue; +/* NOTr*: */ case 0xFF: MPOP2(x,y); RPSH2(~x,~y); continue; } } return SIG_NONE; } + +// Print a stack to the debug console. +void debug_print_stack(Stack *stack) { + for (int i=0; i<stack->p; i++) { + printf("%02x ", stack->mem[i]); + } +} + +// Print the state of a Bedrock instance to the debug console. +void debug_print_state(Bedrock *br) { + printf("\nP:0x%04x", br->ip); + printf("\nW:"); debug_print_stack(&br->wst); + printf("\nR:"); debug_print_stack(&br->rst); + printf("\n"); +} + +// Print the state of a Bedrock instance to the debug console. +void debug_print_brief(Bedrock *br) { + printf("[%02X %02X]", br->wst.p, br->rst.p); +} + +// Assert the state of a Bedrock instance, print to the debug console.. +void debug_print_assert(Bedrock *br) { + if (br->wst.mem[0] == 0xff && br->wst.p == 1 && br->rst.p == 0) { + printf("."); + } else { + printf("X"); + } +} + +// Run a Bedrock instance for a number of cycles. +void br_run(Bedrock *br) { + if (br->alive && br->awake) { + switch (br_eval(br, 50000)) { + case SIG_HALT: br->alive = false; + ndsscreen_clear(br->dev.screen.nds); return; + case SIG_SLEEP: br->awake = false; return; + case SIG_DB1: debug_print_state(br); return; + case SIG_DB2: debug_print_brief(br); return; + case SIG_DB4: debug_print_assert(br); return; + default: return; + } + } +} diff --git a/arm9/source/core.h b/arm9/source/core.h index 765be3b..2eccc0a 100644 --- a/arm9/source/core.h +++ b/arm9/source/core.h @@ -1,99 +1,27 @@ -#include <nds.h> -#include "main.h" - #ifndef CORE_H_ #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/screen.h" - #include "devices/local.h" - #include "devices/file.h" - - #define WST br->wst - #define RST br->rst - #define MEM br->prg.mem - #define PC br->prg.p - - #define WSTV(i) WST.mem[WST.p+(i)] - #define RSTV(i) RST.mem[RST.p+(i)] - - #define MLIT MEM[PC++] - #define MLIT1(x) x = MLIT; - #define MLIT2(x,y) x = MLIT; y = MLIT; - #define MLITD(d) d = (u16)(MEM[PC] << 8) | (u16)(MEM[PC+1]); PC+=2; - - #define WPSH WST.mem[WST.p++] - #define RPSH RST.mem[RST.p++] - #define WPSH1(x) WPSH = (x); - #define RPSH1(x) RPSH = (x); - #define WPSH2(x,y) WPSH1(x); WPSH1(y); - #define RPSH2(x,y) RPSH1(x); RPSH1(y); - #define WPSHD(d) WPSH1(HIGH((d))); WPSH1(LOW((d))); - #define RPSHD(d) RPSH1(HIGH((d))); RPSH1(LOW((d))); - - #define WPOP WST.mem[--WST.p] - #define RPOP RST.mem[--RST.p] - #define WPOP1(x) x = WPOP; - #define RPOP1(x) x = RPOP; - #define WPOP2(x,y) WPOP1(y); WPOP1(x); - #define RPOP2(x,y) RPOP1(y); RPOP1(x); - #define WPOPD(d) d = (u16)(WSTV(-2) << 8) | (u16)(WSTV(-1)); WST.p-=2; - #define RPOPD(d) d = (u16)(RSTV(-2) << 8) | (u16)(RSTV(-1)); RST.p-=2; + #include "bang.h" + #include "dev.h" - #define WGET1(x) x = WSTV(-1); - #define RGET1(x) x = RSTV(-1); - #define WGET2(x,y) x = WSTV(-2); y = WSTV(-1); - #define RGET2(x,y) x = RSTV(-2); y = RSTV(-1); - #define WGETD(x) x = DOUBLE(WSTV(-2),WSTV(-1)); - #define RGETD(x) x = DOUBLE(RSTV(-2),RSTV(-1)); - - typedef enum { - SIG_NONE, - SIG_HALT, - SIG_SLEEP, - SIG_DB1, - SIG_DB2, - SIG_DB3, - SIG_DB4, - SIG_DB5, - SIG_DB6, - } Signal; - - typedef struct { - u8 mem [65536]; - u16 p; - } ProgramMemory; typedef struct { - u8 mem[256]; // stack memory - u8 p; // stack pointer - } StackMemory; + u8 mem[256]; + u8 p; + } Stack; typedef struct { - bool alive; // true when program is loaded - bool awake; // true when program is not asleep - ProgramMemory prg; // program memory - StackMemory wst, rst; // working and return stacks - - SystemDevice sys; - MemoryDevice mem; - MathDevice math; - ClockDevice clk; - InputDevice inp; - ScreenDevice scr; - FileDevice fs; + bool alive; // True when program is not halted + bool awake; // True when program is not asleep + u8 mem[65536]; // Program memory + u16 ip; // Instruction pointer + Stack wst, rst; // Working and return stacks + DeviceBus dev; // Device bus } Bedrock; - void reset_br(Bedrock *br); - void start_br(Bedrock *br, u8 program[], int size); - void run_br(Bedrock *br); - void rouse_br(Bedrock *br); - u8 dev_read(Bedrock *br, u8 port); - Signal dev_write(Bedrock *br, u8 port, u8 val); - Signal evaluate( Bedrock *br, u16 count); - + // Methods. + void br_reset(Bedrock *br); + void br_load(Bedrock *br, u8 program[], int size); + void br_rouse(Bedrock *br); + void br_run(Bedrock *br); #endif diff --git a/arm9/source/debug.c b/arm9/source/debug.c deleted file mode 100644 index eae5192..0000000 --- a/arm9/source/debug.c +++ /dev/null @@ -1,24 +0,0 @@ -#include <nds.h> -#include "core.h" - -void debug_stacks(Bedrock *br) { - u8 i; - printf("\nP:0x%04x I:0x%02x", PC, MEM[PC]); - printf("\nW:"); - for (i=0; i<WST.p; i++) { - printf("%02x ", WST.mem[i]); - } - printf("\nR:"); - for (i=0; i<RST.p; i++) { - printf("%02x ", RST.mem[i]); - } - printf("\n"); -} - -void debug_assert(Bedrock *br) { - if (WST.mem[0] == 0xff && WST.p == 1 && RST.p == 0) { - printf("."); - } else { - printf("X"); - } -} diff --git a/arm9/source/debug.h b/arm9/source/debug.h deleted file mode 100644 index 36bfca3..0000000 --- a/arm9/source/debug.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef DEBUG_H_ - #define DEBUG_H_ - - void debug_stacks(Bedrock *br); - void debug_assert(Bedrock *br); - -#endif diff --git a/arm9/source/dev.c b/arm9/source/dev.c new file mode 100644 index 0000000..1b9a1dc --- /dev/null +++ b/arm9/source/dev.c @@ -0,0 +1,213 @@ +#include "dev.h" + + +// Reset every device on a device bus. +void dev_reset(DeviceBus *d) { + system_reset(&d->system); + memory_reset(&d->memory); + math_reset(&d->math); + clock_reset(&d->clock); + input_reset(&d->input); + screen_reset(&d->screen); + stream_reset(&d->stream); + fs_reset(&d->file); +} + +// Read a byte from a device bus. +u8 dev_read(DeviceBus *d, u8 port) { + switch(port) { + // SYSTEM DEVICE + case 0x02: return d->system.wake; + case 0x08: return readbuf_read(&d->system.name); + case 0x09: return readbuf_read(&d->system.authors); + case 0x0E: return GETH(connected_devices()); + case 0x0F: return GETL(connected_devices()); + // MEMORY DEVICE + case 0x10: return GETH(d->memory.allocated); + case 0x11: return GETL(d->memory.allocated); + case 0x12: return GETH(d->memory.head1.offset); + case 0x13: return GETL(d->memory.head1.offset); + case 0x14: return d->memory.head1.page; + case 0x15: return d->memory.head1.byte; + case 0x16: + case 0x17: return memory_read(&d->memory.head1); + case 0x1A: return GETH(d->memory.head2.offset); + case 0x1B: return GETL(d->memory.head2.offset); + case 0x1C: return d->memory.head2.page; + case 0x1D: return d->memory.head2.byte; + case 0x1E: + case 0x1F: return memory_read(&d->memory.head2); + // MATH DEVICE + case 0x20: return GETH(math_get_x(&d->math)); + case 0x21: return GETL(math_get_x(&d->math)); + case 0x22: return GETH(math_get_y(&d->math)); + case 0x23: return GETL(math_get_y(&d->math)); + case 0x24: return GETH(math_get_r(&d->math)); + case 0x25: return GETL(math_get_r(&d->math)); + case 0x26: return GETH(math_get_t(&d->math)); + case 0x27: return GETL(math_get_t(&d->math)); + case 0x28: return GETHH(math_get_prod(&d->math)); + case 0x29: return GETHL(math_get_prod(&d->math)); + case 0x2A: return GETLH(math_get_prod(&d->math)); + case 0x2B: return GETLL(math_get_prod(&d->math)); + case 0x2C: return GETH(math_get_quot(&d->math)); + case 0x2D: return GETL(math_get_quot(&d->math)); + case 0x2E: return GETH(math_get_rem(&d->math)); + case 0x2F: return GETL(math_get_rem(&d->math)); + // CLOCK DEVICE + case 0x30: return YEAR(get_datetime()); + case 0x31: return MONTH(get_datetime()); + case 0x32: return DAY(get_datetime()); + case 0x33: return HOUR(get_datetime()); + case 0x34: return MINUTE(get_datetime()); + case 0x35: return SECOND(get_datetime()); + case 0x36: clock_uptime_read(&d->clock); + return GETH(d->clock.uptime); + case 0x37: return GETL(d->clock.uptime); + case 0x38: timer_read(&d->clock.t1); + return GETH(d->clock.t1.read); + case 0x39: return GETL(d->clock.t1.read); + case 0x3A: timer_read(&d->clock.t2); + return GETH(d->clock.t2.read); + case 0x3B: return GETL(d->clock.t2.read); + case 0x3C: timer_read(&d->clock.t3); + return GETH(d->clock.t3.read); + case 0x3D: return GETL(d->clock.t3.read); + case 0x3E: timer_read(&d->clock.t4); + return GETH(d->clock.t4.read); + case 0x3F: return GETL(d->clock.t4.read); + // INPUT DEVICE + case 0x40: input_read_x(&d->input); + return GETH(d->input.x_read); + case 0x41: return GETL(d->input.x_read); + case 0x42: input_read_y(&d->input); + return GETH(d->input.y_read); + case 0x43: return GETL(d->input.y_read); + case 0x48: return BOOL(d->input.pointer); + case 0x49: return d->input.pointer << 7; + case 0x4A: return circbuf_read(&d->input.keybuffer); + case 0x4C: return d->input.gamepad; + // SCREEN DEVICE + case 0x50: return GETH(d->screen.x); + case 0x51: return GETL(d->screen.x); + case 0x52: return GETH(d->screen.y); + case 0x53: return GETL(d->screen.y); + case 0x54: return GETH(PIXELS_WIDTH); + case 0x55: return GETL(PIXELS_WIDTH); + case 0x56: return GETH(PIXELS_HEIGHT); + case 0x57: return GETL(PIXELS_HEIGHT); + // FILE DEVICE + case 0x90: return BOOL(fs_get_open(&d->file)); + case 0x91: return BOOL(fs_get_error(&d->file)); + case 0x92: + case 0x93: return fs_read_byte(&d->file); + case 0x94: return pathbuf_read(&d->file.path); + case 0x95: return BOOL(fs_get_type(&d->file)); + case 0x96: return pathbuf_read(&d->file.child_path); + case 0x97: return BOOL(fs_get_child_type(&d->file)); + case 0x98: return GETHH(d->file.address); + case 0x99: return GETHL(d->file.address); + case 0x9A: return GETLH(d->file.address); + case 0x9B: return GETLL(d->file.address); + case 0x9C: fs_calculate_length(&d->file); return GETHH(d->file.length); + case 0x9D: fs_calculate_length(&d->file); return GETHL(d->file.length); + case 0x9E: fs_calculate_length(&d->file); return GETLH(d->file.length); + case 0x9F: fs_calculate_length(&d->file); return GETLL(d->file.length); + // DEFAULT + default: return 0; + } +} + +// Write a byte to a device bus. +Signal dev_write(DeviceBus *d, u8 port, u8 v) { + switch(port) { + // SYSTEM DEVICE + case 0x00: SETH(d->system.sleep, v); break; + case 0x01: SETL(d->system.sleep, v); return SIG_SLEEP; + case 0x03: if (v) { return SIG_FORK; } else { return SIG_RESET; } + case 0x08: readbuf_set_pointer(&d->system.name); break; + case 0x09: readbuf_set_pointer(&d->system.authors); break; + // MEMORY DEVICE + case 0x10: SETH(d->memory.count, v); break; + case 0x11: SETL(d->memory.count, v); memory_allocate(&d->memory); break; + case 0x12: SETH(d->memory.head1.offset, v); + memory_refresh_cache(&d->memory, &d->memory.head1); break; + case 0x13: SETL(d->memory.head1.offset, v); + memory_refresh_cache(&d->memory, &d->memory.head1); break; + case 0x14: d->memory.head1.page = v; + memory_refresh_page(&d->memory.head1); break; + case 0x15: d->memory.head1.byte = v; break; + case 0x16: + case 0x17: memory_write(&d->memory.head1, v); break; + case 0x18: SETH(d->memory.copy, v); break; + case 0x19: SETL(d->memory.copy, v); memory_copy(&d->memory); break; + case 0x1A: SETH(d->memory.head2.offset, v); + memory_refresh_cache(&d->memory, &d->memory.head2); break; + case 0x1B: SETL(d->memory.head2.offset, v); + memory_refresh_cache(&d->memory, &d->memory.head2); break; + case 0x1C: d->memory.head2.page = v; + memory_refresh_page(&d->memory.head2); break; + case 0x1D: d->memory.head2.byte = v; break; + case 0x1E: + case 0x1F: memory_write(&d->memory.head2, v); break; + // MATH DEVICE + case 0x20: math_clear_cartesian(&d->math); SETH(d->math.x, v); break; + case 0x21: math_clear_cartesian(&d->math); SETL(d->math.x, v); break; + case 0x22: math_clear_cartesian(&d->math); SETH(d->math.y, v); break; + case 0x23: math_clear_cartesian(&d->math); SETL(d->math.y, v); break; + case 0x24: math_clear_polar(&d->math); SETH(d->math.r, v); break; + case 0x25: math_clear_polar(&d->math); SETL(d->math.r, v); break; + case 0x26: math_clear_polar(&d->math); SETH(d->math.t, v); break; + case 0x27: math_clear_polar(&d->math); SETL(d->math.t, v); break; + // CLOCK DEVICE + case 0x38: SETH(d->clock.t1.write, v); break; + case 0x39: SETL(d->clock.t1.write, v); timer_write(&d->clock.t1); break; + case 0x3A: SETH(d->clock.t2.write, v); break; + case 0x3B: SETL(d->clock.t2.write, v); timer_write(&d->clock.t2); break; + case 0x3C: SETH(d->clock.t3.write, v); break; + case 0x3D: SETL(d->clock.t3.write, v); timer_write(&d->clock.t3); break; + case 0x3E: SETH(d->clock.t4.write, v); break; + case 0x3F: SETL(d->clock.t4.write, v); timer_write(&d->clock.t4); break; + // INPUT DEVICE + case 0x4A: circbuf_clear(&d->input.keybuffer); + v ? open_keyboard() : close_keyboard(); break; + // SCREEN DEVICE + case 0x50: SETH(d->screen.x, v); break; + case 0x51: SETL(d->screen.x, v); break; + case 0x52: SETH(d->screen.y, v); break; + case 0x53: SETL(d->screen.y, v); break; + case 0x58: SETH(d->screen.colour, v); break; + case 0x59: SETL(d->screen.colour, v); + screen_commit_colour(&d->screen); break; + case 0x5A: SETH(d->screen.selected, v); break; + case 0x5B: SETL(d->screen.selected, v); break; + case 0x5C: + case 0x5D: spritebuf_push(&d->screen.sprite, v); break; + case 0x5E: screen_draw(&d->screen, v); break; + case 0x5F: screen_move_cursor(&d->screen, v); break; + // STREAM DEVICE + case 0x83: stream_end(&d->stream); break; + case 0x86: + case 0x87: stream_write(&d->stream, v); break; + // FILE DEVICE + case 0x90: fs_push_open(&d->file, v); break; + case 0x91: fs_push_action(&d->file, v); break; + case 0x92: + case 0x93: fs_write_byte(&d->file, v); break; + case 0x94: pathbuf_set_pointer(&d->file.path, v); break; + case 0x95: fs_ascend(&d->file); break; + case 0x96: pathbuf_set_pointer(&d->file.child_path, v); break; + case 0x97: fs_descend(&d->file); break; + case 0x98: SETHH(d->file.address_write, v); break; + case 0x99: SETHL(d->file.address_write, v); break; + case 0x9A: SETLH(d->file.address_write, v); break; + case 0x9B: SETLL(d->file.address_write, v); fs_seek(&d->file); break; + case 0x9C: SETHH(d->file.length_write, v); break; + case 0x9D: SETHL(d->file.length_write, v); break; + case 0x9E: SETLH(d->file.length_write, v); break; + case 0x9F: SETLL(d->file.length_write, v); fs_resize(&d->file); break; + // DEFAULT + default: break; + } + return SIG_NONE; +} diff --git a/arm9/source/dev.h b/arm9/source/dev.h new file mode 100644 index 0000000..272baea --- /dev/null +++ b/arm9/source/dev.h @@ -0,0 +1,49 @@ +#ifndef DEV_H_ + #define DEV_H_ + + #include "devices/system.h" + #include "devices/memory.h" + #include "devices/math.h" + #include "devices/clock.h" + #include "devices/input.h" + #include "devices/screen.h" + #include "devices/stream.h" + #include "devices/file.h" + + + // Signals that can be emitted by a device. + typedef enum { + SIG_NONE = 0, + SIG_HALT, + SIG_DB1, + SIG_DB2, + SIG_DB3, + SIG_DB4, + SIG_DB5, + SIG_DB6, + SIG_SLEEP, + SIG_RESET, + SIG_FORK, + } Signal; + + // Bedrock device bus. + typedef struct { + SystemDevice system; + MemoryDevice memory; + MathDevice math; + ClockDevice clock; + InputDevice input; + ScreenDevice screen; + StreamDevice stream; + FileDevice file; + } DeviceBus; + + // Methods. + void dev_reset(DeviceBus *d); + u8 dev_read(DeviceBus *d, u8 port); + Signal dev_write(DeviceBus *d, u8 port, u8 v); + + // Duplicate declarations from main. + void open_keyboard(void); + void close_keyboard(void); +#endif 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(×tamp); + 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(×tamp); - 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 diff --git a/arm9/source/main.c b/arm9/source/main.c index 56103da..9dc612c 100644 --- a/arm9/source/main.c +++ b/arm9/source/main.c @@ -1,122 +1,179 @@ -#include <stdio.h> -#include <time.h> -#include <nds.h> -#include "devices/clock.h" -#include "devices/math.h" -#include "devices/screen.h" -#include "bang.h" -#include "core.h" #include "main.h" +#include "config.h" -#define NUM_BR 5 -Bedrock br[NUM_BR] = {}; +char *system_name = "bedrock-nds/0.1.0"; +char *system_authors = "Ben Bridle"; + +// Reserve memory for all Bedrock instances up front. +Bedrock br[NUM_INSTANCES] = {}; +// The instance currently visible on the main screen. +u32 MAIN_INSTANCE = 0; + +// The NDS has two 2D engines, called the main engine and the sub engine. +// These can be freely allocated to either of the hardware screens, there +// is effectively no difference between the two engines for our purposes. +// The br_main pointer will point to the Bedrock instance currently using +// the main engine (which will always be the current user instance), and +// the br_sub pointer to the instance using the sub engine (which will +// always be the keyboard instance). Bedrock *br_main; Bedrock *br_sub; -bool main_on_bottom = TRUE; +// The main 2D engine always starts on the bottom hardware screen. +bool main_on_bottom = true; -char *system_name = "bedrock-nds/0.1.0-alpha"; -char *system_authors = "Ben Bridle"; +// Bake some Bedrock programs into the emulator. u8 main_program[] = { - #include "../include/cobalt.br.inc" + // #include "../include/alphabet-snake.br.inc" + // #include "../include/clock.br.inc" + #include "../include/cobalt-demo.br.inc" + // #include "../include/system-info.br.inc" }; u8 keyboard_program[] = { #include "../include/keyboard.br.inc" }; -// Change to the next screen layout. -void change_layout(void) { +// Swap the hardware screens. +void swap_screens(void) { lcdSwap(); main_on_bottom = !main_on_bottom; + // Hide the sub program if the main program is on the bottom screen. if (main_on_bottom) { - scr_unmake(&br_sub->scr); + screen_map_to_none(&br_sub->dev.screen); } else { - scr_make_sub(&br_sub->scr); + screen_map_to_sub(&br_sub->dev.screen); } } +// TODO void receive_keyboard_byte(u8 byte) { if (br_main) { - inp_receive_byte(&br_main->inp, byte); + input_receive_key(&br_main->dev.input, byte); } } -bool is_keyboard_open(void) { - return !main_on_bottom; -} - +// TODO void open_keyboard(void) { if (main_on_bottom) { - change_layout(); + swap_screens(); + // Deactivate the pointer on the main instance. + br_main->dev.input.pointer = false; + br_main->dev.input.wake = true; + // Connect the keyboard. + br_sub->dev.stream.local.output.connected = true; } } +// TODO void close_keyboard(void) { if (!main_on_bottom) { - change_layout(); + swap_screens(); + // Disconnect the keyboard. + br_sub->dev.stream.local.output.connected = false; } } +// Make a neighbouring user instance become the new active user instance. +void change_main_instance(int delta) { + screen_map_to_none(&br[MAIN_INSTANCE].dev.screen); + MAIN_INSTANCE = (MAIN_INSTANCE + delta) % USER_INSTANCES; + screen_map_to_main(&br[MAIN_INSTANCE].dev.screen); + br[MAIN_INSTANCE].dev.screen.wake = true; + br_main = &br[MAIN_INSTANCE]; +} + // Scan for input and handle emulator-specific keys. void receive_input(void) { + // This should be the only place in the entire codebase where the + // scanKeys function is called. scanKeys(); - if (keysDown() & KEY_SELECT) change_layout(); + // Swap screens when the select key is pressed. + if (keysDown() & KEY_SELECT) swap_screens(); + if (SWITCH_BETWEEN_INSTANCES) { + if (keysDown() & KEY_L) change_main_instance(-1); + if (keysDown() & KEY_R) change_main_instance(1); + } } +// ------ MAIN ----------------------------------------------------------------- + int main(void) { #define ALIVE(br) (br && br->alive) #define DEAD(br) (br && !br->alive) #define AWAKE(br) (br && br->alive && br->awake) #define ASLEEP(br) (br && br->alive && !br->awake) - // Set memory identifiers and system device strings. - for (int i=0; i<NUM_BR; i++) { - br[i].mem.id = i+1; - br[i].sys.name.mem = (u8*) &system_name[0]; - br[i].sys.authors.mem = (u8*) &system_authors[0]; + // Initialise all Bedrock instances. + for (int i=0; i<NUM_INSTANCES; i++) { + br_reset(&br[i]); + br[i].dev.memory.id = i+1; + readbuf_populate(&br[i].dev.system.name, (u8*) system_name); + readbuf_populate(&br[i].dev.system.authors, (u8*) system_authors); } - init_screens(); - init_clock(); - init_filesystem(); + // Initialise functions used by devices. + init_nds_clock(); + init_nds_screens(); + init_nds_filesystem(); + + // Start with the main screen on the bottom. lcdMainOnBottom(); - // Load program - start_br(&br[0], main_program, sizeof(main_program)); - start_br(&br[1], keyboard_program, sizeof(keyboard_program)); - br_main = &br[0]; - br_sub = &br[1]; - scr_make_main(&br_main->scr); + // Initialise the debug console. + // TODO: Remove. + // consoleDemoInit(); + + // Load program. + br_main = &br[MAIN_INSTANCE]; + br_sub = &br[KEY_INSTANCE]; + br_load(&br[MAIN_INSTANCE], main_program, sizeof(main_program)); + br_load(&br[KEY_INSTANCE], keyboard_program, sizeof(keyboard_program)); + + // We map the main instance to the main screen, but we don't map the + // sub instance because we don't want it to be visible just yet. + screen_map_to_main(&br_main->dev.screen); + + // ------ EVENT LOOP ------------------------------------------------------- while (1) { - if (AWAKE(br_main)) run_br(br_main); - if (AWAKE(br_sub)) run_br(br_sub); + if (AWAKE(br_main)) br_run(br_main); + if (AWAKE(br_sub)) br_run(br_sub); if (DEAD(br_main)) br_main = NULL; if (DEAD(br_sub)) br_sub = NULL; - bool main_flip = ASLEEP(br_main) && br_main->scr.dirty; - bool sub_flip = ASLEEP(br_sub) && br_sub->scr.dirty; - if (main_flip || sub_flip) swiWaitForVBlank(); - if (main_flip) { flip_buffer(br_main->scr.nds); br_main->scr.dirty = false; } - if (sub_flip) { flip_buffer(br_sub->scr.nds); br_sub->scr.dirty = false; } + // Check if screen buffers need to be flipped. + // Only flip buffers if the instance is asleep. + bool flip_main = ASLEEP(br_main) && br_main->dev.screen.dirty; + bool flip_sub = ASLEEP(br_sub) && br_sub->dev.screen.dirty; + // Wait for next frame to avoid running too fast. + if (flip_main || flip_sub) swiWaitForVBlank(); + if (flip_main) { + ndsscreen_flip_buffers(br_main->dev.screen.nds); + br_main->dev.screen.dirty = false; + } + if (flip_sub) { + ndsscreen_flip_buffers(br_sub->dev.screen.nds); + br_sub->dev.screen.dirty = false; + } rouse: receive_input(); + // Receive gamepad and touch input if main is on bottom screen. if (ALIVE(br_main) && main_on_bottom) { - inp_read_navigation(&br_main->inp); - inp_read_gamepad(&br_main->inp); - inp_read_touch(&br_main->inp); + input_update_gamepad(&br_main->dev.input); + input_update_touch(&br_main->dev.input); } + // Receive gamepad and touch input if sub is on bottom screen. if (ALIVE(br_sub) && !main_on_bottom) { - inp_read_navigation(&br_sub->inp); - inp_read_gamepad(&br_sub->inp); - inp_read_touch(&br_sub->inp); + input_update_gamepad(&br_sub->dev.input); + input_update_touch(&br_sub->dev.input); } - if (ASLEEP(br_main)) rouse_br(br_main); - if (ASLEEP(br_sub)) rouse_br(br_sub); + if (ASLEEP(br_main)) br_rouse(br_main); + if (ASLEEP(br_sub)) br_rouse(br_sub); + if (ASLEEP(br_main) && ASLEEP(br_sub)) { // Sleep until next keypad event or clock tick. swiIntrWait(1, IRQ_KEYS | IRQ_TIMER0); @@ -124,6 +181,7 @@ int main(void) { } if (!br_main) { + printf("SHUTTING DOWN\n"); systemShutDown(); return 0; } diff --git a/arm9/source/main.h b/arm9/source/main.h index 14aa0dd..bd2a8c3 100644 --- a/arm9/source/main.h +++ b/arm9/source/main.h @@ -1,12 +1,15 @@ -#include <nds.h> - #ifndef MAIN_H_ #define MAIN_H_ + #include <stdio.h> + #include <time.h> + #include "bang.h" + #include "core.h" + + + // Functions. void receive_keyboard_byte(u8 byte); - bool is_keyboard_open(void); void open_keyboard(void); void close_keyboard(void); void change_layout(void); - #endif diff --git a/arm9/source/types/circbuf.c b/arm9/source/types/circbuf.c index 532a5a0..0398254 100644 --- a/arm9/source/types/circbuf.c +++ b/arm9/source/types/circbuf.c @@ -1,6 +1,8 @@ #include "circbuf.h" -u8 cb_read_byte(CircBuf *buf) { + +// Read a byte from a circular buffer, or zero if the buffer is empty. +u8 circbuf_read(CircBuf *buf) { if (buf->front != buf->back) { return buf->mem[buf->front++]; } else { @@ -8,13 +10,15 @@ u8 cb_read_byte(CircBuf *buf) { } } -void cb_write_byte(CircBuf *buf, u8 byte) { +// Write a byte to a circular buffer if the buffer is not full. +void circbuf_write(CircBuf *buf, u8 byte) { if (((buf->back+1)&0xff) != buf->front) { buf->mem[buf->back++] = byte; } } -void cb_clear(CircBuf *buf) { +// Clear a circular buffer. +void circbuf_clear(CircBuf *buf) { buf->front = 0; buf->back = 0; } diff --git a/arm9/source/types/circbuf.h b/arm9/source/types/circbuf.h index 305f8bf..15b3b63 100644 --- a/arm9/source/types/circbuf.h +++ b/arm9/source/types/circbuf.h @@ -1,16 +1,18 @@ -#include <nds.h> - #ifndef CIRCBUF_H_ #define CIRCBUF_H_ + #include "../bang.h" + + + // A 256-byte circular buffer. typedef struct { u8 mem[256]; - u8 front; // start of buffer, read from here until back - u8 back; // end of buffer, write past here until front + u8 front; // start of buffer, read from here up to back + u8 back; // end of buffer, write past here up to front } CircBuf; - u8 cb_read_byte(CircBuf *buf); - void cb_write_byte(CircBuf *buf, u8 byte); - void cb_clear(CircBuf *buf); - + // Methods. + u8 circbuf_read(CircBuf *buf); + void circbuf_write(CircBuf *buf, u8 byte); + void circbuf_clear(CircBuf *buf); #endif diff --git a/arm9/source/types/pathbuf.c b/arm9/source/types/pathbuf.c index 1a610c8..e94b955 100644 --- a/arm9/source/types/pathbuf.c +++ b/arm9/source/types/pathbuf.c @@ -1,52 +1,99 @@ -#include <nds.h> #include "pathbuf.h" -u8 pb_read(PathBuf *buf) { + +// Read the next byte of the path buffer, or zero if all bytes have been read. +u8 pathbuf_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); +// Clear a path buffer. +void pathbuf_clear(PathBuf *buf) { + for (int i=0; i<256; i++) buf->mem[i] = 0; + buf->p = 0; } -void pb_reset(PathBuf *buf, bool to_name) { +// Move the pointer to either the start of the path buffer or to the start of +// the final path component. +void pathbuf_set_pointer(PathBuf *buf, bool to_final_component) { 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; + if (to_final_component) { + 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; - } +// Populate a path buffer with a bytestring. +void pathbuf_populate(PathBuf *buf, u8 *path) { + pathbuf_clear(buf); + u8 c; + for (int i=0; i<256; i++) { + c = path[i]; + buf->mem[i] = c; + if (c == 0) return; + }; } -bool pb_is_terminated(PathBuf *buf) { - for (int i=0; i<255; i++) { - if (buf->mem[i] == 0) { - return true; - } - } - return false; +// Push a byte to a path buffer, return true and reset pointer if byte is null. +bool pathbuf_push(PathBuf *buf, u8 byte) { + buf->mem[buf->p++] = byte; + return byte == 0; } -void pb_populate(PathBuf *buf, u8 *path) { - strncpy((char*) &buf->mem[0], (char*) path, 254); - buf->p = 0; +// Check if a path buffer is empty. +bool pathbuf_is_empty(PathBuf *buf) { + return buf->mem[0] == 0; +} + +// Check if a path buffer contains the path of the root directory. +bool pathbuf_is_root(PathBuf *buf) { + return buf->mem[0] == '/' && buf->mem[1] == 0; +} + +// Check if a path buffer contains a valid path. +bool pathbuf_is_valid(PathBuf *buf) { + // Test that path begins with a forward slash and ends with a null byte. + if (buf->mem[0x00] != '/') return false; + if (buf->mem[0xFF] != 0 ) return false; + // Find the length of the path. + int len = 0; + for (; len<256; len++) if (buf->mem[len] == 0) break; + // End early if path is the root directory. + if (len == 1) return true; + // Test that path contains no control characters. + for (int i=0; i<len; i++) if (buf->mem[i] < 0x20) return false; + // Test that path contains no empty or relative components. This also + // happens to test that it doesn't end with a slash. + bool is_empty = false; + bool all_dots = false; + int dot_count = 0; + u8 c; + for (int i=0; i<=len; i++) { + c = buf->mem[i]; + if (c == '/' || c == 0) { + // Test if component is empty. + if (is_empty) return false; + // Test if component is one or two periods. + if (all_dots && dot_count < 3) return false; + // Reset flags. + is_empty = true; + all_dots = true; + dot_count = 0; + } else { + is_empty = false; + if (c == '.') { + dot_count++; + } else { + all_dots = false; + } + } + } + return true; } diff --git a/arm9/source/types/pathbuf.h b/arm9/source/types/pathbuf.h index 7fd7251..a926262 100644 --- a/arm9/source/types/pathbuf.h +++ b/arm9/source/types/pathbuf.h @@ -1,18 +1,22 @@ -#include <nds.h> - #ifndef PATHBUF_H_ #define PATHBUF_H_ + #include "../bang.h" + + + // A 255-byte path buffer. typedef struct { - u8 mem[255]; + u8 mem[256]; 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); - + // Methods. + u8 pathbuf_read(PathBuf *buf); + void pathbuf_clear(PathBuf *buf); + void pathbuf_set_pointer(PathBuf *buf, bool to_final_component); + void pathbuf_populate(PathBuf *buf, u8 *path); + bool pathbuf_push(PathBuf *buf, u8 byte); + bool pathbuf_is_empty(PathBuf *buf); + bool pathbuf_is_root(PathBuf *buf); + bool pathbuf_is_valid(PathBuf *buf); #endif diff --git a/arm9/source/types/readbuf.c b/arm9/source/types/readbuf.c index eaee27a..750ac4d 100644 --- a/arm9/source/types/readbuf.c +++ b/arm9/source/types/readbuf.c @@ -1,12 +1,20 @@ -#include <nds.h> #include "readbuf.h" -u8 rb_read(ReadBuf *buf) { + +// Populate a read buffer with a pointer to a string. +void readbuf_populate(ReadBuf *buf, u8 *string) { + buf->mem = string; + buf->p = 0; +} + +// Read the next byte of a read buffer, or zero if all bytes have been read. +u8 readbuf_read(ReadBuf *buf) { u8 output = buf->mem[buf->p]; if (output) buf->p++; return output; } -void rb_reset(ReadBuf *buf) { +// Move the pointer to the start of the buffer. +void readbuf_set_pointer(ReadBuf *buf) { buf->p = 0; } diff --git a/arm9/source/types/readbuf.h b/arm9/source/types/readbuf.h index 9077a69..a88934b 100644 --- a/arm9/source/types/readbuf.h +++ b/arm9/source/types/readbuf.h @@ -1,14 +1,17 @@ -#include <nds.h> - #ifndef READBUF_H_ #define READBUF_H_ + #include "../bang.h" + + + // A variable-length read buffer. typedef struct { u8 *mem; u8 p; } ReadBuf; - u8 rb_read(ReadBuf *buf); - void rb_reset(ReadBuf *buf); - + // Methods. + void readbuf_populate(ReadBuf *buf, u8 *string); + u8 readbuf_read(ReadBuf *buf); + void readbuf_set_pointer(ReadBuf *buf); #endif diff --git a/arm9/source/types/wakequeue.c b/arm9/source/types/wakequeue.c new file mode 100644 index 0000000..8876123 --- /dev/null +++ b/arm9/source/types/wakequeue.c @@ -0,0 +1,30 @@ +#include "wakequeue.h" + +/* +TODO: This is unused at the moment. It will eventually be used for keeping +track of which device last woke a Bedrock instance, to optimally choose +which device to use when waking from sleep. +*/ + +// Reset a wake queue. +void wakequeue_reset(WakeQueue *queue) { + // Populate the queue with fifteen slot numbers. Slot zero is excluded, + // as it always comes last in the wake order. + for (int i=0; i<15; i++) queue->mem[i] = i+1; +} + +// TODO: Not quite done yet. +bool wakequeue_wake(WakeQueue *queue, u16 device_list) { + // Test each entry in the queue against the device list. + for (int i=0; i<15; i++) { + u8 slot_number = queue->mem[i]; + if (TEST(device_list, 0x8000 >> slot_number)) { + // Move the matched entry to the back of the queue. + for (; i<14; i++) queue->mem[i] = queue->mem[i+1]; + queue->mem[14] = slot_number; + return true; + } + } + // Test system device last. + return TEST(device_list, 0x8000); +} diff --git a/arm9/source/types/wakequeue.h b/arm9/source/types/wakequeue.h new file mode 100644 index 0000000..ed07d6d --- /dev/null +++ b/arm9/source/types/wakequeue.h @@ -0,0 +1,13 @@ +#ifndef WAKEQUEUE_H_ + #define WAKEQUEUE_H_ + + #include "../bang.h" + + + // A variable-length read buffer. + typedef struct { + u8 mem[15]; // List of slot numbers + } WakeQueue; + + // Methods. +#endif |