summaryrefslogtreecommitdiff
path: root/arm9/source/devices
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/source/devices
downloadbedrock-nds-722d5509178fa5bdaa488fbbd9196f21377f8775.zip
Initial commit
Diffstat (limited to 'arm9/source/devices')
-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
12 files changed, 656 insertions, 0 deletions
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