diff options
author | Ben Bridle <ben@derelict.engineering> | 2024-11-18 14:57:19 +1300 |
---|---|---|
committer | Ben Bridle <ben@derelict.engineering> | 2024-11-18 14:57:19 +1300 |
commit | 722d5509178fa5bdaa488fbbd9196f21377f8775 (patch) | |
tree | 112b39cd80cb8e074d9e71d1def4d8de33c9eefa /arm9/source/devices | |
download | bedrock-nds-722d5509178fa5bdaa488fbbd9196f21377f8775.zip |
Initial commit
Diffstat (limited to 'arm9/source/devices')
-rw-r--r-- | arm9/source/devices/clock.c | 87 | ||||
-rw-r--r-- | arm9/source/devices/clock.h | 46 | ||||
-rw-r--r-- | arm9/source/devices/input.c | 59 | ||||
-rw-r--r-- | arm9/source/devices/input.h | 16 | ||||
-rw-r--r-- | arm9/source/devices/local.c | 0 | ||||
-rw-r--r-- | arm9/source/devices/local.h | 4 | ||||
-rw-r--r-- | arm9/source/devices/math.c | 66 | ||||
-rw-r--r-- | arm9/source/devices/math.h | 19 | ||||
-rw-r--r-- | arm9/source/devices/screen.c | 284 | ||||
-rw-r--r-- | arm9/source/devices/screen.h | 66 | ||||
-rw-r--r-- | arm9/source/devices/system.c | 0 | ||||
-rw-r--r-- | arm9/source/devices/system.h | 9 |
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(×tamp); + 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 |