summaryrefslogtreecommitdiff
path: root/arm9
diff options
context:
space:
mode:
authorBen Bridle <ben@derelict.engineering>2024-11-18 14:57:19 +1300
committerBen Bridle <ben@derelict.engineering>2024-11-18 14:57:19 +1300
commit722d5509178fa5bdaa488fbbd9196f21377f8775 (patch)
tree112b39cd80cb8e074d9e71d1def4d8de33c9eefa /arm9
downloadbedrock-nds-722d5509178fa5bdaa488fbbd9196f21377f8775.zip
Initial commit
Diffstat (limited to 'arm9')
-rw-r--r--arm9/source/bang.c51
-rw-r--r--arm9/source/bang.h27
-rw-r--r--arm9/source/core.c437
-rw-r--r--arm9/source/core.h85
-rw-r--r--arm9/source/devices/clock.c87
-rw-r--r--arm9/source/devices/clock.h46
-rw-r--r--arm9/source/devices/input.c59
-rw-r--r--arm9/source/devices/input.h16
-rw-r--r--arm9/source/devices/local.c0
-rw-r--r--arm9/source/devices/local.h4
-rw-r--r--arm9/source/devices/math.c66
-rw-r--r--arm9/source/devices/math.h19
-rw-r--r--arm9/source/devices/screen.c284
-rw-r--r--arm9/source/devices/screen.h66
-rw-r--r--arm9/source/devices/system.c0
-rw-r--r--arm9/source/devices/system.h9
-rw-r--r--arm9/source/main.c118
-rw-r--r--arm9/source/main_debug.c24
-rw-r--r--arm9/source/main_debug.h7
19 files changed, 1405 insertions, 0 deletions
diff --git a/arm9/source/bang.c b/arm9/source/bang.c
new file mode 100644
index 0000000..8686dd3
--- /dev/null
+++ b/arm9/source/bang.c
@@ -0,0 +1,51 @@
+#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];
+}
+
+u8 shc(u8 x, u8 y) {
+ y = (u8)(RIGHT(y) - LEFT(y)) % 8;
+ return ((x >> y | (x << (8-y))));
+}
+
+u16 shcd(u16 x, u8 y) {
+ y = (u16)(RIGHT(y) - LEFT(y)) % 16;
+ return ((x >> y | (x << (16-y))));
+}
diff --git a/arm9/source/bang.h b/arm9/source/bang.h
new file mode 100644
index 0000000..1e13f9f
--- /dev/null
+++ b/arm9/source/bang.h
@@ -0,0 +1,27 @@
+#ifndef BANG_H_
+ #define BANG_H_
+
+ #define SET_HIGH(v,high) v = high << 8 | (v & 0x00ff)
+ #define SET_LOW(v,low) v = (v & 0xff00) | low
+ #define HIGH(v) (u8)((v) >> 8)
+ #define LOW(v) (u8)((v) )
+
+ #define 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)
+ #define BOOL(v) ((u8)-(v))
+ #define DOUBLE(h,l) ((u16)(h) << 8 | (u16)(l))
+
+ u8 rev(u8 x);
+ u8 shc(u8 x, u8 y);
+ u16 shcd(u16 x, u8 y);
+#endif
diff --git a/arm9/source/core.c b/arm9/source/core.c
new file mode 100644
index 0000000..cb112e0
--- /dev/null
+++ b/arm9/source/core.c
@@ -0,0 +1,437 @@
+#include <nds.h>
+#include "bang.h"
+#include "core.h"
+
+
+void reset_br(Bedrock *br) {
+ *br = (Bedrock) {0};
+}
+
+// Load a program into an instance.
+void start_br(Bedrock *br, u8 program[], int size) {
+ reset_br(br);
+ memcpy(&br->mem, program, size);
+ br->alive = TRUE;
+ br->awake = TRUE;
+ // br->scr.nds = &scr_main;
+}
+
+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, 0x0800)) { // INPUT
+ if (br->inp.wake) {
+ br->inp.wake = FALSE;
+ br->awake = TRUE;
+ br->sys.wake = 0x4;
+ return;
+ }
+ }
+ if (TEST(br->sys.sleep, 0x1000)) { // CLOCK
+ if (check_timers(&br->clk)) {
+ br->awake = TRUE;
+ br->sys.wake = 0x3;
+ return;
+ }
+ }
+}
+
+u8 dev_read(Bedrock *br, u8 port) {
+ switch(port) {
+// SYSTEM DEVICE
+ // TODO: name and author
+ 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 0xbc;
+ case 0x07: return 0x00;
+
+ // TODO: available devices
+ case 0x0A: return br->sys.wake;
+
+// 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_timer1_high(&br->clk);
+ case 0x39: return get_timer1_low( &br->clk);
+ case 0x3A: return get_timer2_high(&br->clk);
+ case 0x3B: return get_timer2_low( &br->clk);
+ case 0x3C: return get_timer3_high(&br->clk);
+ case 0x3D: return get_timer3_low( &br->clk);
+ case 0x3E: return get_timer4_high(&br->clk);
+ case 0x3F: return get_timer4_low( &br->clk);
+// 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(br->inp.keyboard);
+ // TODO: Keyboard input
+ 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);
+
+ default: return 0;
+ }
+}
+
+Signal dev_write(Bedrock *br, u8 port, u8 v) {
+ switch(port) {
+// SYSTEM DEVICE
+ case 0x08: SET_HIGH(br->sys.sleep,v); return 0;
+ case 0x09: SET_LOW(br->sys.sleep,v); return SIG_SLEEP;
+// 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_timer1_high(&br->clk,v); return 0;
+ case 0x39: set_timer1_low( &br->clk,v); return 0;
+ case 0x3A: set_timer2_high(&br->clk,v); return 0;
+ case 0x3B: set_timer2_low( &br->clk,v); return 0;
+ case 0x3C: set_timer3_high(&br->clk,v); return 0;
+ case 0x3D: set_timer3_low( &br->clk,v); return 0;
+ case 0x3E: set_timer4_high(&br->clk,v); return 0;
+ case 0x3F: set_timer4_low( &br->clk,v); 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: printf("%c", v); return 0;
+ case 0x87: printf("%c", v); return 0;
+
+ default: return SIG_NONE;
+ }
+}
+
+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; }
+}
+
+Signal evaluate(Bedrock *br, u16 count) {
+ #define SIGNAL(s) if (s) { return s; } else { continue; };
+
+ register u8 a, b, c, d, e, f;
+ register u16 x, y;
+ register Signal sig;
+
+ for (u16 i=0; i < count; i++) {
+ switch (MLIT) {
+/* 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;
+/* 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;
+
+/* 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;
+
+/* 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;
+
+/* 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;
+
+/* 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;
+/* 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;
+
+/* 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;
+
+/* 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;
+
+/* 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;
+ }
+ }
+ return SIG_NONE;
+}
diff --git a/arm9/source/core.h b/arm9/source/core.h
new file mode 100644
index 0000000..e5494ca
--- /dev/null
+++ b/arm9/source/core.h
@@ -0,0 +1,85 @@
+#ifndef CORE_H_
+ #define CORE_H_
+
+ #include "devices/clock.h"
+ #include "devices/input.h"
+ #include "devices/math.h"
+ #include "devices/screen.h"
+ #include "devices/system.h"
+
+ #define WST br->wst
+ #define RST br->rst
+ #define MEM br->mem
+ #define PC br->pc
+
+ #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;
+
+ #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[256]; // stack memory
+ u8 p; // stack pointer
+ } Stack;
+
+ typedef struct {
+ bool alive; // true when program is loaded
+ bool awake; // true when program is not asleep
+ u8 mem[65536]; // program memory
+ u16 pc; // program counter
+ Stack wst, rst; // working and return stacks
+ SystemDevice sys;
+ MathDevice math;
+ ClockDevice clk;
+ InputDevice inp;
+ ScreenDevice scr;
+ } Bedrock;
+
+ void reset_br(Bedrock *br);
+ void start_br(Bedrock *br, u8 program[], int size);
+ 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);
+
+#endif
diff --git a/arm9/source/devices/clock.c b/arm9/source/devices/clock.c
new file mode 100644
index 0000000..6f3ce85
--- /dev/null
+++ b/arm9/source/devices/clock.c
@@ -0,0 +1,87 @@
+#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.
+static u32 uptime;
+u32 get_uptime(void) { return uptime; }
+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;
+}
+
+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);
+}
+
+u8 get_timer_low(ClockTimer *t) {
+ return LOW(t->read);
+}
+
+void set_timer_high(ClockTimer *t, u8 high) {
+ SET_HIGH(t->write, high);
+}
+void set_timer_low(ClockTimer *t, u8 low) {
+ SET_LOW(t->write, low);
+ if (t->write) {
+ t->end = uptime + t->write;
+ } else {
+ t->end = 0;
+ }
+}
+
+u8 get_timer1_high(ClockDevice *clock) { return get_timer_high(&clock->t1); }
+u8 get_timer1_low( ClockDevice *clock) { return get_timer_low( &clock->t1); }
+u8 get_timer2_high(ClockDevice *clock) { return get_timer_high(&clock->t2); }
+u8 get_timer2_low( ClockDevice *clock) { return get_timer_low( &clock->t2); }
+u8 get_timer3_high(ClockDevice *clock) { return get_timer_high(&clock->t3); }
+u8 get_timer3_low( ClockDevice *clock) { return get_timer_low( &clock->t3); }
+u8 get_timer4_high(ClockDevice *clock) { return get_timer_high(&clock->t4); }
+u8 get_timer4_low( ClockDevice *clock) { return get_timer_low( &clock->t4); }
+
+void set_timer1_high(ClockDevice *clock, u8 high) { set_timer_high(&clock->t1, high); }
+void set_timer1_low( ClockDevice *clock, u8 low) { set_timer_low( &clock->t1, low); }
+void set_timer2_high(ClockDevice *clock, u8 high) { set_timer_high(&clock->t2, high); }
+void set_timer2_low( ClockDevice *clock, u8 low) { set_timer_low( &clock->t2, low); }
+void set_timer3_high(ClockDevice *clock, u8 high) { set_timer_high(&clock->t3, high); }
+void set_timer3_low( ClockDevice *clock, u8 low) { set_timer_low( &clock->t3, low); }
+void set_timer4_high(ClockDevice *clock, u8 high) { set_timer_high(&clock->t4, high); }
+void set_timer4_low( ClockDevice *clock, u8 low) { set_timer_low( &clock->t4, low); }
+
+
+void init_clock(void) {
+ // Start a 256Hz timer to increment the uptime value.
+ timerStart(0, ClockDivider_1024, TIMER_FREQ_1024(256), uptime_handler);
+}
+
+struct tm* get_datetime(void) {
+ time_t timestamp = time(NULL);
+ struct tm* datetime = localtime(&timestamp);
+ return datetime;
+}
diff --git a/arm9/source/devices/clock.h b/arm9/source/devices/clock.h
new file mode 100644
index 0000000..892479f
--- /dev/null
+++ b/arm9/source/devices/clock.h
@@ -0,0 +1,46 @@
+#ifndef CLOCK_H_
+ #define CLOCK_H_
+
+ #include <time.h>
+
+ typedef struct {
+ u32 end; // real end time
+ u16 read, write; // read write caches
+ } ClockTimer;
+
+ typedef struct {
+ ClockTimer t1, t2, t3, t4; // timers
+ u32 start; // uptime offset
+ } ClockDevice;
+
+ #define YEAR(tm) (tm->tm_year - 100)
+ #define MONTH(tm) (tm->tm_mon)
+ #define DAY(tm) (tm->tm_mday - 1)
+ #define HOUR(tm) (tm->tm_hour)
+ #define MINUTE(tm) (tm->tm_min)
+ #define SECOND(tm) (tm->tm_sec)
+
+ u32 get_uptime(void);
+ void uptime_handler(void);
+ void init_clock(void);
+ struct tm* get_datetime(void);
+
+ bool check_timers(ClockDevice *clk);
+
+ u8 get_timer1_high(ClockDevice *clock);
+ u8 get_timer1_low( ClockDevice *clock);
+ u8 get_timer2_high(ClockDevice *clock);
+ u8 get_timer2_low( ClockDevice *clock);
+ u8 get_timer3_high(ClockDevice *clock);
+ u8 get_timer3_low( ClockDevice *clock);
+ u8 get_timer4_high(ClockDevice *clock);
+ u8 get_timer4_low( ClockDevice *clock);
+ void set_timer1_high(ClockDevice *clock, u8 high);
+ void set_timer1_low( ClockDevice *clock, u8 low);
+ void set_timer2_high(ClockDevice *clock, u8 high);
+ void set_timer2_low( ClockDevice *clock, u8 low);
+ void set_timer3_high(ClockDevice *clock, u8 high);
+ void set_timer3_low( ClockDevice *clock, u8 low);
+ void set_timer4_high(ClockDevice *clock, u8 high);
+ void set_timer4_low( ClockDevice *clock, u8 low);
+#endif
diff --git a/arm9/source/devices/input.c b/arm9/source/devices/input.c
new file mode 100644
index 0000000..a39f448
--- /dev/null
+++ b/arm9/source/devices/input.c
@@ -0,0 +1,59 @@
+#include <nds.h>
+#include "../bang.h"
+#include "input.h"
+
+// Read gamepad state into an input device.
+void inp_read_gamepad(InputDevice *inp) {
+ u32 held = keysHeld();
+ u8 gamepad = (
+ 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_X) << 1
+ | TEST(held, KEY_Y) << 0
+ );
+ if (gamepad != inp->gamepad) {
+ inp->wake = TRUE;
+ inp->gamepad = gamepad;
+ }
+}
+
+// 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;
+ }
+}
+
+// Read touchscreen state into an input device.
+void inp_read_touch(InputDevice *inp) {
+ bool pointer = TEST(keysHeld(), KEY_TOUCH);
+ if (pointer != inp->pointer) {
+ inp->wake = TRUE;
+ inp->pointer = pointer;
+ }
+ 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;
+ }
+ }
+}
diff --git a/arm9/source/devices/input.h b/arm9/source/devices/input.h
new file mode 100644
index 0000000..e62bdd3
--- /dev/null
+++ b/arm9/source/devices/input.h
@@ -0,0 +1,16 @@
+#ifndef INPUT_H_
+ #define INPUT_H_
+
+ typedef struct {
+ bool pointer; // pointer active
+ bool keyboard; // keyboard active
+ u16 x,y; // pointer position
+ u8 navigation; // navigation state
+ u8 gamepad; // gamepad state
+ bool wake; // wake flag
+ } InputDevice;
+
+ void inp_read_gamepad(InputDevice *inp);
+ void inp_read_navigation(InputDevice *inp);
+ void inp_read_touch(InputDevice *inp);
+#endif
diff --git a/arm9/source/devices/local.c b/arm9/source/devices/local.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/arm9/source/devices/local.c
diff --git a/arm9/source/devices/local.h b/arm9/source/devices/local.h
new file mode 100644
index 0000000..bf1393c
--- /dev/null
+++ b/arm9/source/devices/local.h
@@ -0,0 +1,4 @@
+#ifndef LOCAL_H_
+ #define LOCAL_H_
+
+#endif
diff --git a/arm9/source/devices/math.c b/arm9/source/devices/math.c
new file mode 100644
index 0000000..bb592e0
--- /dev/null
+++ b/arm9/source/devices/math.c
@@ -0,0 +1,66 @@
+#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; }
+
+u16 get_sqrt(MathDevice *math) {
+ if (!math->sqrt_rc) {
+ u32 input = math->op1 << 16 | math->op2;
+ math->sqrt = sqrt32(input);
+ math->sqrt_rc = TRUE;
+ }
+ return math->sqrt;
+}
+
+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);
+ }
+ math->atan_rc = TRUE;
+ }
+ return math->atan;
+}
+
+u32 get_prod(MathDevice *math) {
+ if (!math->prod_rc) {
+ math->prod = math->op1 * math->op2;
+ math->prod_rc = TRUE;
+ }
+ return math->prod;
+}
+
+u16 get_quot(MathDevice *math) {
+ if (!math->quot_rc) {
+ if (math->op2) {
+ math->quot = div32(math->op1, math->op2);
+ }
+ math->quot_rc = TRUE;
+ }
+ return math->quot;
+}
+
+u16 get_rem(MathDevice *math) {
+ if (!math->rem_rc) {
+ if (math->op2) {
+ math->rem = mod32(math->op1, math->op2);
+ }
+ math->rem_rc = TRUE;
+ }
+ return math->rem;
+}
diff --git a/arm9/source/devices/math.h b/arm9/source/devices/math.h
new file mode 100644
index 0000000..ae52a10
--- /dev/null
+++ b/arm9/source/devices/math.h
@@ -0,0 +1,19 @@
+#ifndef MATH_H_
+ #define MATH_H_
+
+ 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 ;
+
+ 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);
+#endif
diff --git a/arm9/source/devices/screen.c b/arm9/source/devices/screen.c
new file mode 100644
index 0000000..492bb05
--- /dev/null
+++ b/arm9/source/devices/screen.c
@@ -0,0 +1,284 @@
+/*
+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.
+
+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 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).
+*/
+
+#include <nds.h>
+#include "screen.h"
+#include "../bang.h"
+
+void init_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;
+ u16 *main_map = BG_MAP_RAM(12);
+ u16 *sub_map = BG_MAP_RAM_SUB(12);
+ for (i = 0; i < TILES_SIZE; i++) {
+ *(main_map++) = i;
+ *(sub_map++) = i;
+ }
+}
+
+
+void set_palette_high(ScreenDevice *scr, u8 high) {
+ SET_HIGH(scr->palette_write, high);
+}
+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;
+ // TODO: With 4-bit we multiply by 17, find equivalent
+ // here to get best value of least-significant bit.
+ scr->palette[i] = RGB15(r,g,b);
+ scr->nds->pal[i] = RGB15(r,g,b);
+}
+
+void push_sprite(SpriteBuffer *b, u8 row) {
+ b->mem[b->p] = row;
+ b->p = (b->p + 1) % 16;
+ b->cached = FALSE;
+}
+
+void prepare_1bit_sprite(SpriteBuffer *b, u8 draw) {
+ u8 l,p,x,y;
+ if (b->cached && draw == b->draw) return;
+
+ 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;
+ case 0x2: p=b->p; 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 0x3: p=b->p; 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;
+ case 0x4: p=b->p+8; 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 0x5: p=b->p+8; 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;
+ 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;
+}
+
+void prepare_2bit_sprite(SpriteBuffer *b, u8 draw) {
+ u8 l,h,i,p,s,x,y;
+ if (b->cached && draw == b->draw) return;
+
+ 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;
+ case 0x2: 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[y][x] = (l>>i & 1) | (h>>i & 1) << 1; } }; break;
+ case 0x3: 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[y][x] = (l>>i & 1) | (h>>i & 1) << 1; } }; break;
+ case 0x4: 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[x][y] = (l>>i & 1) | (h>>i & 1) << 1; } }; break;
+ case 0x5: 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[x][y] = (l>>i & 1) | (h>>i & 1) << 1; } }; break;
+ 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;
+}
+
+
+// ---------------------------------------------------------------------------
+
+void draw_pixel(u16 *layer, u16 x, u16 y, u8 colour) {
+ if (x < PIXELS_WIDTH && y < PIXELS_HEIGHT) {
+ u32 addr = \
+ (x >> 2 & 0x0001) + (x << 1 & 0xfff0) + \
+ (y << 1 & 0x000f) + (y << 6 & 0xfe00);
+ u16 shift = (x & 0x3) << 2;
+ layer[addr] = (layer[addr] & ~(0xf << shift)) | (colour << shift);
+ }
+}
+
+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) {
+ dmaFillWords(0, nds->bg, TILES_MEM);
+ dmaFillWords(0, nds->fg, TILES_MEM);
+};
+
+void flip_buffer(Screen *nds) {
+ dmaCopyWords(0, nds->bg, nds->bgv, TILES_MEM);
+ dmaCopyWords(0, nds->fg, nds->fgv, TILES_MEM);
+}
+
+void black_screen(Screen *nds) {
+ nds->pal[0] = RGB15(8,0,0);
+ dmaFillWords(0, nds->bgv, TILES_MEM);
+ dmaFillWords(0, nds->fgv, TILES_MEM);
+}
+// ---------------------------------------------------------------------------
+
+void draw_dispatch(ScreenDevice *scr, u8 draw) {
+ 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->px = scr->x;
+ scr->py = scr->y;
+ scr->dirty = true;
+}
+
+void scr_draw_pixel(ScreenDevice *scr, u16 *layer, u8 draw) {
+ draw_pixel(layer, scr->x, scr->y, draw&0xf);
+}
+
+void scr_fill_layer(ScreenDevice *scr, 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,
+ };
+
+ if (draw & 0x08) {
+ // 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]);
+ }
+ }
+ } else {
+ // 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]);
+ }
+ }
+ }
+}
+
+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;
+
+ s16 dx = abs(x_end - x);
+ s16 dy = -abs(y_end - y);
+ s16 sx = x < x_end ? 1 : -1;
+ s16 sy = y < y_end ? 1 : -1;
+ s16 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;
+ 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 (x == x_end && y == y_end) return;
+ s16 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;
+ while (1) {
+ draw_pixel(layer, x, y, colour);
+ if (x == x_end && y == y_end) return;
+ s16 e2 = e1 << 1;
+ if (e2 >= dy) { e1 += dy; x += sx; }
+ if (e2 <= dx) { e1 += dx; y += sy; }
+ }
+ }
+}
+
+void scr_draw_rect(ScreenDevice *scr, 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);
+ 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;
+ 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); }
+ }
+ }
+ } else {
+ // Draw solid rectangle.
+ u8 colour = draw & 0xf;
+ for (u16 x=l; x<r+1; x++) {
+ for (u16 y=t; y<b+1; y++) {
+ draw_pixel(layer, x, y, colour);
+ }
+ }
+ }
+}
+
+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;
+ }
+}
diff --git a/arm9/source/devices/screen.h b/arm9/source/devices/screen.h
new file mode 100644
index 0000000..a406051
--- /dev/null
+++ b/arm9/source/devices/screen.h
@@ -0,0 +1,66 @@
+#ifndef SCREEN_H_
+ #define SCREEN_H_
+
+ #define PIXELS_WIDTH 256
+ #define PIXELS_HEIGHT 192
+ #define PIXELS_SIZE 49152
+ #define TILES_WIDTH 32
+ #define TILES_HEIGHT 24
+ #define TILES_SIZE 768
+ #define TILES_MEM 24576 // size of a screen layer in bytes
+
+ #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
+
+ 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
+ } SpriteBuffer;
+
+ typedef struct {
+ u16 *bgv, *fgv; // visible tile memory
+ u16 *bg, *fg; // tile memory
+ u16 *map; // tile map
+ u16 *pal; // colour palette
+ } Screen;
+
+ 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
+ } ScreenDevice;
+
+ void init_screens(void);
+
+ 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);
+
+#endif
diff --git a/arm9/source/devices/system.c b/arm9/source/devices/system.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/arm9/source/devices/system.c
diff --git a/arm9/source/devices/system.h b/arm9/source/devices/system.h
new file mode 100644
index 0000000..b18b5a2
--- /dev/null
+++ b/arm9/source/devices/system.h
@@ -0,0 +1,9 @@
+#ifndef SYSTEM_H_
+ #define SYSTEM_H_
+
+ typedef struct {
+ u16 sleep; // device mask for waking
+ u8 wake; // ID of wake device
+ } SystemDevice;
+
+#endif
diff --git a/arm9/source/main.c b/arm9/source/main.c
new file mode 100644
index 0000000..a8d3273
--- /dev/null
+++ b/arm9/source/main.c
@@ -0,0 +1,118 @@
+#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_debug.h"
+
+Bedrock br[5] = {};
+Bedrock *br_main;
+Bedrock *br_sub;
+bool main_on_bottom = TRUE;
+
+u8 main_program[] = {
+ #include "../include/sysinfo.br.inc"
+};
+u8 keyboard_program[] = {
+ #include "../include/sysinfo.br.inc"
+};
+
+// Change to the next screen layout.
+void change_layout(void) {
+ lcdSwap();
+ main_on_bottom = !main_on_bottom;
+}
+
+// Scan for input and handle emulator-specific keys.
+void receive_input(void) {
+ scanKeys();
+ if (keysDown() & KEY_SELECT) change_layout();
+}
+
+
+void run_br(Bedrock *br) {
+ if (br->awake) {
+ switch (evaluate(br, 1000)) {
+ 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;
+ }
+ }
+}
+
+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),
+ .map = BG_MAP_RAM(MAP_SLOT),
+ .pal = 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),
+ .map = BG_MAP_RAM_SUB(MAP_SLOT),
+ .pal = BG_PALETTE_SUB,
+};
+
+int main(void) {
+ #define ALIVE(br) (br && br->alive)
+ #define AWAKE(br) (br && br->alive && br->awake)
+ #define ASLEEP(br) (br && br->alive && !br->awake)
+
+ init_screens();
+ init_clock();
+ lcdMainOnBottom();
+
+ // TODO: Remove
+ // consoleDemoInit();
+
+ // 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];
+ br_main->scr.nds = &scr_main;
+ br_sub->scr.nds = &scr_sub;
+
+ while (1) {
+ if (AWAKE(br_main)) run_br(br_main);
+ if (AWAKE(br_sub)) run_br(br_sub);
+
+ 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; }
+
+ rouse:
+ receive_input();
+ if (ALIVE(br_main)) {
+ inp_read_navigation(&br_main->inp);
+ inp_read_gamepad(&br_main->inp);
+ if (main_on_bottom) inp_read_touch(&br_main->inp);
+ }
+ if (ALIVE(br_sub)) {
+ inp_read_navigation(&br_sub->inp);
+ inp_read_gamepad(&br_sub->inp);
+ if (!main_on_bottom) inp_read_touch(&br_sub->inp);
+ }
+ if (ASLEEP(br_main)) rouse_br(br_main);
+ if (ASLEEP(br_sub)) rouse_br(br_sub);
+ if (ASLEEP(br_main) && ASLEEP(br_sub)) {
+ // Sleep until next keypad event or clock tick.
+ swiIntrWait(1, IRQ_KEYS | IRQ_TIMER0);
+ goto rouse;
+ }
+
+ if (!br_main && !br_sub) return 0;
+ }
+}
+
diff --git a/arm9/source/main_debug.c b/arm9/source/main_debug.c
new file mode 100644
index 0000000..eae5192
--- /dev/null
+++ b/arm9/source/main_debug.c
@@ -0,0 +1,24 @@
+#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/main_debug.h b/arm9/source/main_debug.h
new file mode 100644
index 0000000..55e60b9
--- /dev/null
+++ b/arm9/source/main_debug.h
@@ -0,0 +1,7 @@
+#ifndef MAIN_DEBUG_H_
+ #define MAIN_DEBUG_H_
+
+ void debug_stacks(Bedrock *br);
+ void debug_assert(Bedrock *br);
+
+#endif