aboutsummaryrefslogtreecommitdiff
path: root/arm9/source/devices/screen.c
diff options
context:
space:
mode:
authorBen Bridle <ben@derelict.engineering>2025-09-19 13:17:14 +1200
committerBen Bridle <ben@derelict.engineering>2025-09-19 13:32:32 +1200
commitbb1aa5958d1b67707dcf0f6b08bfaf0b408bd46e (patch)
treeb26d07ed58aaf7a5230fc3e28c103d616abfa9b8 /arm9/source/devices/screen.c
parent9612c307f00c4313d73fe0c3a86c05c8d8cd514e (diff)
downloadbedrock-nds-bb1aa5958d1b67707dcf0f6b08bfaf0b408bd46e.zip
Massive rewrite
This commit rewrites the emulator halfway from scratch to make it easier to change and maintain in the future. The emulator core was rewritten to adhere to the released Bedrock specification (earlier versions implemented an older prototype specification, which is no longer relevant). This commit also adds proper support for running multiple concurrent Bedrock instances. This was previously supported in a limited manner for the on-screen keyboard, but now works for any regular program as well, with switching being performed by pressing the L or R bumper buttons. This is disabled by default, as programs will still need to be baked into the emulator and hand-loaded.
Diffstat (limited to 'arm9/source/devices/screen.c')
-rw-r--r--arm9/source/devices/screen.c421
1 files changed, 230 insertions, 191 deletions
diff --git a/arm9/source/devices/screen.c b/arm9/source/devices/screen.c
index 698b754..bca436d 100644
--- a/arm9/source/devices/screen.c
+++ b/arm9/source/devices/screen.c
@@ -1,114 +1,123 @@
+#include "screen.h"
+
+
/*
-A screen layer contains 768 tiles (32*24), requiring 24KB. Each tile is 32 bytes,
-holding 2 pixels per byte. The upper four bits of a tile byte represent the
-colour of the right pixel, and the lower four bits the colour of the left pixel.
-The colour is a 4-bit reference into a 16-colour RGB15 palette.
+Each screen layer is 24KB and contains 768 tiles (32*24). Each tile is 32
+bytes, and contains 64 pixels (8*8), holding 2 pixels per byte. The upper
+four bits of a tile byte represent the colour of the right pixel, and the
+lower four bits represent the colour of the left pixel. The colour is a
+4-bit index into a 16-colour RGB15 palette.
+
+Four screen layers are used by each physical screen: a foreground layer, a
+background layer, and then back buffers for each. Screen layers are stored
+as u16* pointers to raw video memory.
+
+Each 'tile slot' of video memory is 16KB, so we need two slots per layer. We
+can't use memory more efficiently by packing the layers more tightly into the
+available slots, because we can only draw the contents of a layer by passing
+a tile slot base address to the video system. We can, however, use the gaps to
+store our tile map, because each map slot takes only 2KB with a corresponding
+increase in address granularity.
-Screen memory contains four layers: bg, fg, then the same but for double buffers.
-Ignore double buffering for now. We keep these as u32* because a single 32-bit
-integer represents a single 8-pixel row.
-TODO: This needs to be updated. We have double buffering, and we use u16*.
+The tile map is shared by all foreground and background layers on a physical
+screen, and is stored just past the contents of the first screen layer (at the
+24KB offset in video memory). Each entry in the map is a 16-bit tile index.
+The low 10 bits of the value represent the tile index, then h-flip, then
+v-flip, then a 4-bit palette identifier. We don't flip any tiles, and we
+always use palette zero. The map is initialised with an incrementing sequence,
+and then never touched.
-The tile map is shared by the foreground and background layers, and is stored
-just past the first layer for both screens (at the 24KB offset). Each entry is
-a 16-bit tile index (low 10 bits are tile index, then h-flip, then v-flip, then
-4-bit palette identifier).
+The memory bank system of the NDS is byzantine and most vexing, with banks being
+identified by letters A through I, and in sizes of 128/128/128/128/64/16/16/32/16
+KB, respectively. The main 2D engine can use banks A/B/C/D/E for BG data (tiles),
+and the sub 2D engine can use bank C for the same.
*/
-#include <nds.h>
-#include "screen.h"
-#include "../bang.h"
-Screen scr_main = {
- .bgv = BG_TILE_RAM(BG_SLOT_VIS),
- .fgv = BG_TILE_RAM(FG_SLOT_VIS),
- .bg = BG_TILE_RAM(BG_SLOT),
- .fg = BG_TILE_RAM(FG_SLOT),
+// ------ NDS SCREENS ----------------------------------------------------------
+
+// VRAM addresses for the main 2D engine.
+NDSScreen main_screen = {
+ .bgv = BG_TILE_RAM(BG_SLOT_FRONT),
+ .fgv = BG_TILE_RAM(FG_SLOT_FRONT),
+ .bg = BG_TILE_RAM(BG_SLOT_BACK),
+ .fg = BG_TILE_RAM(FG_SLOT_BACK),
.map = BG_MAP_RAM(MAP_SLOT),
.palv = BG_PALETTE,
};
-Screen scr_sub = {
- .bgv = BG_TILE_RAM_SUB(BG_SLOT_VIS),
- .fgv = BG_TILE_RAM_SUB(FG_SLOT_VIS),
- .bg = BG_TILE_RAM_SUB(BG_SLOT),
- .fg = BG_TILE_RAM_SUB(FG_SLOT),
+
+// VRAM addresses for the sub 2D engine.
+NDSScreen sub_screen = {
+ .bgv = BG_TILE_RAM_SUB(BG_SLOT_FRONT),
+ .fgv = BG_TILE_RAM_SUB(FG_SLOT_FRONT),
+ .bg = BG_TILE_RAM_SUB(BG_SLOT_BACK),
+ .fg = BG_TILE_RAM_SUB(FG_SLOT_BACK),
.map = BG_MAP_RAM_SUB(MAP_SLOT),
.palv = BG_PALETTE_SUB,
};
-// TODO: Make an enum thing for main/sub, combine these functions
-void scr_make_main(ScreenDevice *scr) {
- scr->nds = &scr_main;
- for (int i=0; i<16; i++) {
- scr->nds->pal[i] = scr->palette[i];
- }
- scr->wake = true;
-}
-
-void scr_make_sub(ScreenDevice *scr) {
- scr->nds = &scr_sub;
- for (int i=0; i<16; i++) {
- scr->nds->pal[i] = scr->palette[i];
- }
- scr->wake = true;
-}
-
-void scr_unmake(ScreenDevice *scr) {
- if (scr->nds) {
- black_screen(scr->nds);
- scr->nds = NULL;
- }
-}
-
-void init_screens(void) {
- // Allocate VRAM for screens
+// Initialise the VRAM mappings and tile map for both NDS screens.
+void init_nds_screens(void) {
+ // Allocate VRAM for screens.
videoSetMode(DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE | MODE_0_2D);
vramSetBankA(VRAM_A_MAIN_BG);
videoSetModeSub(DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE | MODE_0_2D);
vramSetBankC(VRAM_C_SUB_BG);
-
- /* Configure screen layers to use tile graphics. */
- REG_BG0CNT = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_3 | BG_TILE_BASE(BG_SLOT_VIS) | BG_MAP_BASE(MAP_SLOT);
- REG_BG1CNT = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_2 | BG_TILE_BASE(FG_SLOT_VIS) | BG_MAP_BASE(MAP_SLOT);
- REG_BG0CNT_SUB = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_3 | BG_TILE_BASE(BG_SLOT_VIS) | BG_MAP_BASE(MAP_SLOT);
- REG_BG1CNT_SUB = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_2 | BG_TILE_BASE(FG_SLOT_VIS) | BG_MAP_BASE(MAP_SLOT);
-
- /* Populate tile maps with tile indices. */
- int i;
+ // Configure screen layers to use tile graphics.
+ REG_BG0CNT = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_3 | BG_TILE_BASE(BG_SLOT_FRONT) | BG_MAP_BASE(MAP_SLOT);
+ REG_BG1CNT = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_2 | BG_TILE_BASE(FG_SLOT_FRONT) | BG_MAP_BASE(MAP_SLOT);
+ REG_BG0CNT_SUB = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_3 | BG_TILE_BASE(BG_SLOT_FRONT) | BG_MAP_BASE(MAP_SLOT);
+ REG_BG1CNT_SUB = BG_32x32 | BG_COLOR_16 | BG_PRIORITY_2 | BG_TILE_BASE(FG_SLOT_FRONT) | BG_MAP_BASE(MAP_SLOT);
+ // Populate tile maps.
u16 *main_map = BG_MAP_RAM(12);
u16 *sub_map = BG_MAP_RAM_SUB(12);
- for (i = 0; i < TILES_SIZE; i++) {
+ for (int i=0; i<TILES_COUNT; i++) {
*(main_map++) = i;
*(sub_map++) = i;
}
}
-
-void set_palette_high(ScreenDevice *scr, u8 high) {
- SET_HIGH(scr->palette_write, high);
+// Copy the contents of the back buffer to the front buffer.
+void ndsscreen_flip_buffers(NDSScreen *nds) {
+ if (nds) {
+ dmaCopyWords(0, nds->bg, nds->bgv, LAYER_MEM);
+ dmaCopyWords(0, nds->fg, nds->fgv, LAYER_MEM);
+ for (int i=0; i<16; i++) nds->palv[i] = nds->pal[i];
+ }
}
-void set_palette_low(ScreenDevice *scr, u8 low) {
- SET_LOW(scr->palette_write, low);
- u8 i = scr->palette_write >> 12 & 0x0f;
- u8 r = scr->palette_write >> 7 & 0x1e;
- u8 g = scr->palette_write >> 3 & 0x1e;
- u8 b = scr->palette_write << 1 & 0x1e;
- scr->palette[i] = RGB15(r,g,b);
- if (scr->nds) {
- scr->nds->pal[i] = RGB15(r,g,b);
+
+// Erase both buffers and set every colour to black.
+void ndsscreen_clear(NDSScreen *nds) {
+ if (nds) {
+ dmaFillWords(0, nds->bgv, LAYER_MEM);
+ dmaFillWords(0, nds->fgv, LAYER_MEM);
+ dmaFillWords(0, nds->bg, LAYER_MEM);
+ dmaFillWords(0, nds->fg, LAYER_MEM);
+ for (int i=0; i<16; i++) nds->palv[i] = RGB15(0,0,0);
}
}
-void push_sprite(SpriteBuffer *b, u8 row) {
- b->mem[b->p] = row;
- b->p = (b->p + 1) % 16;
+
+// ------ SPRITE BUFFER --------------------------------------------------------
+
+// Reset a sprite buffer.
+void spritebuf_reset(SpriteBuffer *b) {
+ for (int i=0; i<16; i++) b->mem[i] = 0;
+ b->cached = false;
+}
+
+// Push a byte to a sprite buffer.
+void spritebuf_push(SpriteBuffer *b, u8 row) {
+ b->mem[b->p++] = row;
+ b->p %= 16;
b->cached = FALSE;
}
-void prepare_1bit_sprite(SpriteBuffer *b, u8 draw) {
+// Parse, transform, and cache a 1-bit sprite.
+void spritebuf_prepare_1bit(SpriteBuffer *b, u8 draw) {
u8 l,p,x,y;
if (b->cached && draw == b->draw) return;
-
+ // Parse and transform the sprite if not already cached.
switch (draw & 0x07) {
case 0x0: p=b->p+8; for (y=0;y<8;y++) { l=b->mem[p++ % 16]; for (x=0;x<8;x++) { b->sprite[y][x] = l>>(7-x) & 1; } }; break;
case 0x1: p=b->p+8; for (y=0;y<8;y++) { l=b->mem[p++ % 16]; for (x=0;x<8;x++) { b->sprite[y][x] = l>>( x) & 1; } }; break;
@@ -119,15 +128,15 @@ void prepare_1bit_sprite(SpriteBuffer *b, u8 draw) {
case 0x6: p=b->p; for (y=0;y<8;y++) { l=b->mem[--p % 16]; for (x=0;x<8;x++) { b->sprite[x][y] = l>>(7-x) & 1; } }; break;
case 0x7: p=b->p; for (y=0;y<8;y++) { l=b->mem[--p % 16]; for (x=0;x<8;x++) { b->sprite[x][y] = l>>( x) & 1; } }; break;
}
-
- b->cached = TRUE;
b->draw = draw;
+ b->cached = true;
}
-void prepare_2bit_sprite(SpriteBuffer *b, u8 draw) {
+// Parse, transform, and cache a 2-bit sprite.
+void spritebuf_prepare_2bit(SpriteBuffer *b, u8 draw) {
u8 l,h,i,p,s,x,y;
if (b->cached && draw == b->draw) return;
-
+ // Parse and transform the sprite if not already cached.
switch (draw & 0x07) {
case 0x0: p=b->p+8; s=p+8; for (y=0;y<8;y++) { l=b->mem[p++ % 16]; h=b->mem[s++ % 16]; for (x=0;x<8;x++) { i=(7-x); b->sprite[y][x] = (l>>i & 1) | (h>>i & 1) << 1; } }; break;
case 0x1: p=b->p+8; s=p+8; for (y=0;y<8;y++) { l=b->mem[p++ % 16]; h=b->mem[s++ % 16]; for (x=0;x<8;x++) { i=( x); b->sprite[y][x] = (l>>i & 1) | (h>>i & 1) << 1; } }; break;
@@ -138,14 +147,77 @@ void prepare_2bit_sprite(SpriteBuffer *b, u8 draw) {
case 0x6: p=b->p; s=p+8; for (y=0;y<8;y++) { l=b->mem[--p % 16]; h=b->mem[--s % 16]; for (x=0;x<8;x++) { i=(7-x); b->sprite[x][y] = (l>>i & 1) | (h>>i & 1) << 1; } }; break;
case 0x7: p=b->p; s=p+8; for (y=0;y<8;y++) { l=b->mem[--p % 16]; h=b->mem[--s % 16]; for (x=0;x<8;x++) { i=( x); b->sprite[x][y] = (l>>i & 1) | (h>>i & 1) << 1; } }; break;
}
-
- b->cached = TRUE;
b->draw = draw;
+ b->cached = true;
+}
+
+
+// ------ BEDROCK SCREEN -------------------------------------------------------
+
+// Reset a screen device.
+void screen_reset(ScreenDevice *screen) {
+ screen->x = 0;
+ screen->y = 0;
+ screen->px = 0;
+ screen->py = 0;
+ screen->selected = 0;
+ spritebuf_reset(&screen->sprite);
+ screen->colour = 0;
+ for (int i=0; i<16; i++) screen->pal[i] = 0;
+ ndsscreen_clear(screen->nds);
+ screen->wake = false;
+ screen->dirty = false;
}
+// Unmap a screen device from a screen.
+void screen_map_to_none(ScreenDevice *screen) {
+ if (screen->nds) {
+ ndsscreen_clear(screen->nds);
+ screen->nds = NULL;
+ }
+}
+
+// Map a screen device to the main screen.
+void screen_map_to_main(ScreenDevice *screen) {
+ screen->nds = &main_screen;
+ memcpy(&screen->nds->pal, &screen->pal, 32);
+ screen->wake = true;
+}
+
+// Map a screen device to the sub screen.
+void screen_map_to_sub(ScreenDevice *screen) {
+ screen->nds = &sub_screen;
+ memcpy(&screen->nds->pal, &screen->pal, 32);
+ screen->wake = true;
+}
+
+// Commit a new colour to the colour palette.
+void screen_commit_colour(ScreenDevice *screen) {
+ u8 i = screen->colour >> 12 & 0x0f;
+ // Extract 5-bit channel values.
+ u8 r = screen->colour >> 7 & 0x1e;
+ u8 g = screen->colour >> 3 & 0x1e;
+ u8 b = screen->colour << 1 & 0x1e;
+ screen->pal[i] = RGB15(r,g,b);
+ if (screen->nds) screen->nds->pal[i] = RGB15(r,g,b);
+ screen->dirty = true;
+}
+
+// Move the screen cursor.
+void screen_move_cursor(ScreenDevice *screen, u8 move) {
+ switch (move >> 6) {
+ case 0b00: screen->x += move & 0x3f; return;
+ case 0b01: screen->y += move & 0x3f; return;
+ case 0b10: screen->x -= move & 0x3f; return;
+ case 0b11: screen->y -= move & 0x3f; return;
+ }
+}
+
+
-// ---------------------------------------------------------------------------
+// ------ RAW DRAW OPERATIONS --------------------------------------------------
+// Draw a pixel to a screen layer.
void draw_pixel(u16 *layer, u16 x, u16 y, u8 colour) {
if (x < PIXELS_WIDTH && y < PIXELS_HEIGHT) {
u32 addr = \
@@ -156,109 +228,58 @@ void draw_pixel(u16 *layer, u16 x, u16 y, u8 colour) {
}
}
+// Fill a screen layer with a solid colour.
void fill_layer(u16 *layer, u8 colour) {
u8 byte = colour << 4 | colour;
u32 word = byte << 24 | byte << 16 | byte << 8 | byte;
- dmaFillWords(word, layer, TILES_MEM);
-}
-
-void erase_screen(Screen *nds) {
- if (nds) {
- dmaFillWords(0, nds->bg, TILES_MEM);
- dmaFillWords(0, nds->fg, TILES_MEM);
- }
-};
-
-void flip_buffer(Screen *nds) {
- if (nds) {
- dmaCopyWords(0, nds->bg, nds->bgv, TILES_MEM);
- dmaCopyWords(0, nds->fg, nds->fgv, TILES_MEM);
- for (int i=0; i<16; i++) {
- nds->palv[i] = nds->pal[i];
- }
- }
+ dmaFillWords(word, layer, LAYER_MEM);
}
-void black_screen(Screen *nds) {
- if (nds) {
- for (int i=0; i<16; i++) {
- nds->palv[i] = RGB15(0,0,0);
- }
- dmaFillWords(0, nds->bgv, TILES_MEM);
- dmaFillWords(0, nds->fgv, TILES_MEM);
- }
-}
-// ---------------------------------------------------------------------------
-void draw_dispatch(ScreenDevice *scr, u8 draw) {
- if (scr->nds) {
- switch (draw >> 4) {
- case 0x0: scr_draw_pixel(scr, scr->nds->bg, draw); break;
- case 0x1: scr_draw_sprite(scr, scr->nds->bg, draw); break;
- case 0x2: scr_fill_layer(scr, scr->nds->bg, draw); break;
- case 0x3: scr_draw_sprite(scr, scr->nds->bg, draw); break;
- case 0x4: scr_draw_line(scr, scr->nds->bg, draw); break;
- case 0x5: scr_draw_line(scr, scr->nds->bg, draw); break;
- case 0x6: scr_draw_rect(scr, scr->nds->bg, draw); break;
- case 0x7: scr_draw_rect(scr, scr->nds->bg, draw); break;
- case 0x8: scr_draw_pixel(scr, scr->nds->fg, draw); break;
- case 0x9: scr_draw_sprite(scr, scr->nds->fg, draw); break;
- case 0xA: scr_fill_layer(scr, scr->nds->fg, draw); break;
- case 0xB: scr_draw_sprite(scr, scr->nds->fg, draw); break;
- case 0xC: scr_draw_line(scr, scr->nds->fg, draw); break;
- case 0xD: scr_draw_line(scr, scr->nds->fg, draw); break;
- case 0xE: scr_draw_rect(scr, scr->nds->fg, draw); break;
- case 0xF: scr_draw_rect(scr, scr->nds->fg, draw); break;
- }
- scr->dirty = true;
- }
- scr->px = scr->x;
- scr->py = scr->y;
-}
+// ------ DRAW OPERATIONS ------------------------------------------------------
-void scr_draw_pixel(ScreenDevice *scr, u16 *layer, u8 draw) {
- draw_pixel(layer, scr->x, scr->y, draw&0xf);
+void op_draw_pixel(ScreenDevice *screen, u16 *layer, u8 draw) {
+ draw_pixel(layer, screen->x, screen->y, draw&0xf);
}
-void scr_fill_layer(ScreenDevice *scr, u16 *layer, u8 draw) {
+void op_fill_layer(ScreenDevice *screen, u16 *layer, u8 draw) {
fill_layer(layer, draw&0xf);
}
-void scr_draw_sprite(ScreenDevice *scr, u16 *layer, u8 draw) {
- if (draw & 0x20) { prepare_2bit_sprite(&scr->sprite, draw); }
- else { prepare_1bit_sprite(&scr->sprite, draw); }
-
- u8 colours[4] = {
- scr->colours >> 12 & 0x000f,
- scr->colours >> 8 & 0x000f,
- scr->colours >> 4 & 0x000f,
- scr->colours & 0x000f,
+void op_draw_sprite(ScreenDevice *screen, u16 *layer, u8 draw) {
+ if (draw & 0x20) { spritebuf_prepare_2bit(&screen->sprite, draw); }
+ else { spritebuf_prepare_1bit(&screen->sprite, draw); }
+ // Parse sprite colours.
+ u8 selected[4] = {
+ screen->selected >> 12 & 0x000f,
+ screen->selected >> 8 & 0x000f,
+ screen->selected >> 4 & 0x000f,
+ screen->selected & 0x000f,
};
-
if (draw & 0x08) {
- // Draw sprite with transparent background
+ // Draw sprite with transparent background.
for (u8 y=0;y<8;y++) {
for (u8 x=0;x<8;x++) {
- u8 i = scr->sprite.sprite[y][x];
- if (i) draw_pixel(layer, scr->x+x, scr->y+y, colours[i]);
+ u8 i = screen->sprite.sprite[y][x];
+ if (i) draw_pixel(layer, screen->x+x, screen->y+y, selected[i]);
}
}
} else {
- // Draw sprite with opaque background
+ // Draw sprite with opaque background.
for (u8 y=0;y<8;y++) {
for (u8 x=0;x<8;x++) {
- u8 i = scr->sprite.sprite[y][x];
- draw_pixel(layer, scr->x+x, scr->y+y, colours[i]);
+ u8 i = screen->sprite.sprite[y][x];
+ draw_pixel(layer, screen->x+x, screen->y+y, selected[i]);
}
}
}
}
-void scr_draw_line(ScreenDevice *scr, u16 *layer, u8 draw) {
- s16 x = (s16) scr->x;
- s16 y = (s16) scr->y;
- s16 x_end = (s16) scr->px;
- s16 y_end = (s16) scr->py;
+void op_draw_line(ScreenDevice *screen, u16 *layer, u8 draw) {
+ s16 x = (s16) screen->x;
+ s16 y = (s16) screen->y;
+ s16 x_end = (s16) screen->px;
+ s16 y_end = (s16) screen->py;
s32 dx = abs(x_end - x);
s32 dy = -abs(y_end - y);
@@ -267,24 +288,24 @@ void scr_draw_line(ScreenDevice *scr, u16 *layer, u8 draw) {
s32 e1 = dx + dy;
if (draw & 0x10) {
- // Draw 1-bit textured line.
- prepare_1bit_sprite(&scr->sprite, draw);
- u8 c1 = scr->colours >> 8 & 0xf;
- u8 c0 = scr->colours >> 12 & 0xf;
+ // Draw 1-bit textured line.
+ spritebuf_prepare_1bit(&screen->sprite, draw);
+ u8 c1 = screen->selected >> 8 & 0xf;
+ u8 c0 = screen->selected >> 12 & 0xf;
bool opaque = !(draw & 0x08);
while (1) {
- if (scr->sprite.sprite[y%8][x%8]) { draw_pixel(layer, x, y, c1); }
- else if (opaque) { draw_pixel(layer, x, y, c0); }
+ if (screen->sprite.sprite[y%8][x%8]) { draw_pixel(layer, x, y, c1); }
+ else if (opaque) { draw_pixel(layer, x, y, c0); }
if (x == x_end && y == y_end) return;
s32 e2 = e1 << 1;
if (e2 >= dy) { e1 += dy; x += sx; }
if (e2 <= dx) { e1 += dx; y += sy; }
}
} else {
- // Draw solid line.
- u8 colour = draw & 0xf;
+ // Draw solid line.
+ u8 c0 = draw & 0xf;
while (1) {
- draw_pixel(layer, x, y, colour);
+ draw_pixel(layer, x, y, c0);
if (x == x_end && y == y_end) return;
s32 e2 = e1 << 1;
if (e2 >= dy) { e1 += dy; x += sx; }
@@ -293,46 +314,64 @@ void scr_draw_line(ScreenDevice *scr, u16 *layer, u8 draw) {
}
}
-void scr_draw_rect(ScreenDevice *scr, u16 *layer, u8 draw) {
+void op_draw_rect(ScreenDevice *screen, u16 *layer, u8 draw) {
#define SWAP(x,y) {u8 temp=x; x=y; y=temp;}
#define CLAMP(v,m) {v>0x7fff ? 0 : v>m ? m : v}
// Get bounding box.
- u16 l = CLAMP(scr->px, PIXELS_WIDTH -1);
- u16 r = CLAMP(scr->x , PIXELS_WIDTH -1);
- u16 t = CLAMP(scr->py, PIXELS_HEIGHT-1);
- u16 b = CLAMP(scr->y , PIXELS_HEIGHT-1);
+ u16 l = CLAMP(screen->px, PIXELS_WIDTH -1);
+ u16 r = CLAMP(screen->x , PIXELS_WIDTH -1);
+ u16 t = CLAMP(screen->py, PIXELS_HEIGHT-1);
+ u16 b = CLAMP(screen->y , PIXELS_HEIGHT-1);
if (l>r) SWAP(l,r);
if (t>b) SWAP(t,b);
if (draw & 0x10) {
- // Draw 1-bit textured rectangle.
- prepare_1bit_sprite(&scr->sprite, draw);
- u8 c1 = scr->colours >> 8 & 0xf;
- u8 c0 = scr->colours >> 12 & 0xf;
+ // Draw 1-bit textured rectangle.
+ spritebuf_prepare_1bit(&screen->sprite, draw);
+ u8 c1 = screen->selected >> 8 & 0xf;
+ u8 c0 = screen->selected >> 12 & 0xf;
bool opaque = !(draw & 0x08);
for (u16 x=l; x<r+1; x++) {
for (u16 y=t; y<b+1; y++) {
- if (scr->sprite.sprite[y%8][x%8]) { draw_pixel(layer, x, y, c1); }
- else if (opaque) { draw_pixel(layer, x, y, c0); }
+ if (screen->sprite.sprite[y%8][x%8]) { draw_pixel(layer, x, y, c1); }
+ else if (opaque) { draw_pixel(layer, x, y, c0); }
}
}
} else {
- // Draw solid rectangle.
- u8 colour = draw & 0xf;
+ // Draw solid rectangle.
+ u8 c0 = draw & 0xf;
for (u16 x=l; x<r+1; x++) {
for (u16 y=t; y<b+1; y++) {
- draw_pixel(layer, x, y, colour);
+ draw_pixel(layer, x, y, c0);
}
}
}
}
-void move_cursor(ScreenDevice *scr, u8 move) {
- switch (move >> 6) {
- case 0b00: scr->x += move & 0x3f; return;
- case 0b01: scr->y += move & 0x3f; return;
- case 0b10: scr->x -= move & 0x3f; return;
- case 0b11: scr->y -= move & 0x3f; return;
+// Dispatch to the correct drawing operation.
+void screen_draw(ScreenDevice *screen, u8 draw) {
+ if (screen->nds) {
+ switch (draw >> 4) {
+ case 0x0: op_draw_pixel(screen, screen->nds->bg, draw); break;
+ case 0x1: op_draw_sprite(screen, screen->nds->bg, draw); break;
+ case 0x2: op_fill_layer(screen, screen->nds->bg, draw); break;
+ case 0x3: op_draw_sprite(screen, screen->nds->bg, draw); break;
+ case 0x4: op_draw_line(screen, screen->nds->bg, draw); break;
+ case 0x5: op_draw_line(screen, screen->nds->bg, draw); break;
+ case 0x6: op_draw_rect(screen, screen->nds->bg, draw); break;
+ case 0x7: op_draw_rect(screen, screen->nds->bg, draw); break;
+ case 0x8: op_draw_pixel(screen, screen->nds->fg, draw); break;
+ case 0x9: op_draw_sprite(screen, screen->nds->fg, draw); break;
+ case 0xA: op_fill_layer(screen, screen->nds->fg, draw); break;
+ case 0xB: op_draw_sprite(screen, screen->nds->fg, draw); break;
+ case 0xC: op_draw_line(screen, screen->nds->fg, draw); break;
+ case 0xD: op_draw_line(screen, screen->nds->fg, draw); break;
+ case 0xE: op_draw_rect(screen, screen->nds->fg, draw); break;
+ case 0xF: op_draw_rect(screen, screen->nds->fg, draw); break;
+ }
+ screen->dirty = true;
}
+ screen->px = screen->x;
+ screen->py = screen->y;
}