summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorDaniel Hader <[email protected]>2026-08-23 20:25:36 -0500
committerDaniel Hader <[email protected]>2026-08-23 20:25:36 -0500
commit0532e04a9e273f2a9033a28cdbce1030a91c5bb4 (patch)
tree6e07d6bffeb834e01ff4cc18ec620ee265da8505 /source
initial commit working tile demo
Diffstat (limited to 'source')
-rw-r--r--source/input.hpp45
-rw-r--r--source/main.cpp57
-rw-r--r--source/types.hpp18
-rw-r--r--source/util.hpp191
4 files changed, 311 insertions, 0 deletions
diff --git a/source/input.hpp b/source/input.hpp
new file mode 100644
index 0000000..2a2a3a2
--- /dev/null
+++ b/source/input.hpp
@@ -0,0 +1,45 @@
+#include "types.hpp"
+
+
+
+typedef u16 KeyFlags;
+
+enum Key : KeyFlags {
+ A = 0x0001,
+ B = 0x0002,
+ Select = 0x0004,
+ Start = 0x0008,
+ Right = 0x0010,
+ Left = 0x0020,
+ Up = 0x0040,
+ Down = 0x0080,
+ R = 0x0100,
+ L = 0x0200,
+};
+
+constexpr KeyFlags KEY_MASK = 0x03FF;
+
+volatile KeyFlags* const REG_KEYINPUT = reinterpret_cast<volatile KeyFlags*>(0x04000130);
+
+static inline KeyFlags &keyflags_curr() {
+ static KeyFlags keybits_curr = 0;
+ return keybits_curr;
+}
+
+static inline KeyFlags &keyflags_prev() {
+ static KeyFlags keybits_prev = 0;
+ return keybits_prev;
+}
+
+static inline void keyflags_poll() {
+ KeyFlags &prev = keyflags_prev();
+ KeyFlags &curr = keyflags_curr();
+
+ prev = curr;
+ curr = ~*REG_KEYINPUT & KEY_MASK;
+}
+
+static inline bool key_is_down(Key key) { return keyflags_curr() & key; }
+static inline bool key_is_up(Key key) { return ~keyflags_curr() & key; }
+static inline bool key_was_down(Key key) { return keyflags_prev() & key; }
+static inline bool key_was_up(Key key) { return ~keyflags_prev() & key; }
diff --git a/source/main.cpp b/source/main.cpp
new file mode 100644
index 0000000..ec6be22
--- /dev/null
+++ b/source/main.cpp
@@ -0,0 +1,57 @@
+#include "types.hpp"
+#include "util.hpp"
+#include "input.hpp"
+
+ScreenEntry *bg0_map = screenblock_mem[28];
+
+void bad_vsync() {
+ while (*REG_VCOUNT >= 160);
+ while (*REG_VCOUNT < 160);
+}
+
+int main() {
+ auto bg_flags = BGControlFlags()
+ .set_charblock_base(0)
+ .set_screenblock_base(0)
+ .set_size(BGControlSize::Regular_64x64);
+
+ set_bg_control(0, bg_flags);
+ set_bg_offsets(0, 0, 0);
+
+ const Tile4 tiles[2] = {
+ {{0x11111111, 0x01111111, 0x01111111, 0x01111111, 0x01111111, 0x01111111, 0x01111111, 0x00000001}},
+ {{0x00000000, 0x00100100, 0x01100110, 0x00011000, 0x00011000, 0x01100110, 0x00100100, 0x00000000}},
+ };
+
+ set_tile(0, 0, tiles[0]);
+ set_tile(0, 1, tiles[1]);
+
+ set_bg_palette(0, 1, Color::from_rgb(31, 0, 0));
+ set_bg_palette(1, 1, Color::from_rgb(31, 0, 0));
+ set_bg_palette(2, 1, Color::from_rgb(31, 0, 0));
+ set_bg_palette(3, 1, Color::from_rgb(31, 0, 0));
+
+ ScreenEntry *screen_entry_ptr = bg0_map;
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 32*32; j++) {
+ *screen_entry_ptr++ = ScreenEntry().set_palette_bank(i);
+ }
+ }
+
+ set_display_control(Mode0, BG0);
+
+ s16 x = 0, y = 0;
+ while (1) {
+ bad_vsync();
+ keyflags_poll();
+
+ if (key_is_down(Key::Left)) x -= 1;
+ if (key_is_down(Key::Right)) x += 1;
+ if (key_is_down(Key::Up)) y -= 1;
+ if (key_is_down(Key::Down)) y += 1;
+
+ set_bg_offsets(0, x, y);
+ }
+
+ return 0;
+}
diff --git a/source/types.hpp b/source/types.hpp
new file mode 100644
index 0000000..222b11c
--- /dev/null
+++ b/source/types.hpp
@@ -0,0 +1,18 @@
+#ifndef TYPES_HPP
+#define TYPES_HPP
+
+typedef signed char s8;
+typedef signed short s16;
+typedef signed int s32;
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+
+typedef volatile unsigned char vu8;
+typedef volatile unsigned short vu16;
+typedef volatile unsigned int vu32;
+
+typedef u16 color_t;
+
+#endif
diff --git a/source/util.hpp b/source/util.hpp
new file mode 100644
index 0000000..7b39376
--- /dev/null
+++ b/source/util.hpp
@@ -0,0 +1,191 @@
+#ifndef UTIL_HPP
+#define UTIL_HPP
+
+#include "types.hpp"
+#include <assert.h>
+#include <type_traits>
+
+constexpr u32 SCREEN_WIDTH = 240;
+constexpr u32 SCREEN_HEIGHT = 160;
+
+vu16* const REG_VCOUNT = reinterpret_cast<vu16*>(0x04000006);
+
+enum DisplayControlMode : u16 {
+ Mode0 = 0x0000,
+ Mode1 = 0x0001,
+ Mode2 = 0x0002,
+ Mode3 = 0x0003,
+ Mode4 = 0x0004,
+ Mode5 = 0x0005,
+};
+
+enum DisplayControlLayer : u16 {
+ BG0 = 0x0100,
+ BG1 = 0x0200,
+ BG2 = 0x0400,
+ BG3 = 0x0800,
+ BG4 = 0x1000,
+};
+
+typedef u16 DisplayControlFlags;
+
+constexpr DisplayControlFlags operator|(DisplayControlMode mode, DisplayControlLayer layer) {
+ return static_cast<DisplayControlFlags>(static_cast<u16>(mode) | static_cast<u16>(layer));
+}
+
+static inline void set_display_control(DisplayControlMode mode, DisplayControlLayer layer) {
+ static volatile DisplayControlFlags* const display_control = reinterpret_cast<DisplayControlFlags*>(0x04000000);
+ *display_control = mode | layer;
+}
+
+enum BGControlColorMode : u16 {
+ Mode_4bpp = 0x0000,
+ Mode_8bpp = 0x0080,
+};
+
+enum BGControlSize : u16 {
+ Regular_32x32 = 0x0000,
+ Regular_64x32 = 0x4000,
+ Regular_32x64 = 0x8000,
+ Regular_64x64 = 0xc000,
+ Affine_16x16 = 0x0000,
+ Affine_32x32 = 0x4000,
+ Affine_64x64 = 0x8000,
+ Affine_128x128 = 0xc000,
+};
+
+struct BGControlFlags {
+ u16 flags;
+
+ constexpr BGControlFlags() : flags(0) {}
+
+ constexpr BGControlFlags &set_priority(u16 priority) {
+ assert(priority < 4);
+ this->flags = (this->flags & 0xfffc) | priority;
+ return *this;
+ }
+
+ constexpr BGControlFlags &set_charblock_base(u16 block) {
+ assert(block < 4);
+ this->flags = (this->flags & 0xfff3) | (block << 2);
+ return *this;
+ }
+
+ constexpr BGControlFlags &set_mosaic(u16 enabled) {
+ this->flags = (this->flags & 0xffbf) | (!!enabled << 6);
+ return *this;
+ }
+
+ constexpr BGControlFlags &set_color_mode(BGControlColorMode mode) {
+ this->flags = (this->flags & 0xff7f) | mode;
+ return *this;
+ }
+
+ constexpr BGControlFlags &set_screenblock_base(u16 block) {
+ assert(block < 32);
+ this->flags = (this->flags & 0xe0ff) | (block << 8);
+ return *this;
+ }
+
+ constexpr BGControlFlags &set_affine_wrapping(u16 enabled) {
+ this->flags = (this->flags & 0xdfff) | (!!enabled << 13);
+ return *this;
+ }
+
+ constexpr BGControlFlags &set_size(BGControlSize size) {
+ this->flags = (this->flags & 0x3fff) | size;
+ return *this;
+ }
+
+ constexpr operator u16() const { return this->flags; }
+};
+
+static inline void set_bg_control(u16 bg, BGControlFlags flags) {
+ assert(bg < 4);
+
+ vu16* const reg = reinterpret_cast<vu16*>(0x04000008 + 2*bg);
+ *reg = flags;
+}
+
+static inline void set_bg_offsets(u16 bg, u16 h_offset, u16 v_offset) {
+ vu16* const h_reg = reinterpret_cast<vu16*>(0x04000010 + 4*bg);
+ vu16* const v_reg = reinterpret_cast<vu16*>(0x04000012 + 4*bg);
+
+ *h_reg = h_offset;
+ *v_reg = v_offset;
+}
+
+struct Color {
+ u16 data;
+
+ static constexpr Color from_rgb(u16 r, u16 g, u16 b) {
+ assert(r < 32 && g < 32 && b < 32);
+ return Color { .data = u16(r | (g << 5) | (b << 10)) };
+ }
+};
+
+typedef Color PaletteBank[16];
+
+struct Tile4 { u32 data[8]; };
+typedef Tile4 CharBlock4[512];
+
+template <typename T>
+static inline void volatile_copy(volatile T* dst, const T* src) {
+ using U = std::conditional_t<(sizeof(T) % sizeof(u32) == 0) && (alignof(T) % alignof(u32) == 0), u32, u16>;
+ static_assert(sizeof(T) % sizeof(U) == 0);
+ static_assert(alignof(T) % alignof(U) == 0);
+ auto *d = reinterpret_cast<volatile U*>(dst);
+ auto *s = reinterpret_cast<const U*>(src);
+ for (unsigned i = 0; i < sizeof(T) / sizeof(U); i++) {
+ d[i] = s[i];
+ }
+}
+
+static inline void set_tile(u16 block, u16 index, const Tile4 &tile) {
+ // TODO bounds check
+ volatile CharBlock4 *const blocks = reinterpret_cast<volatile CharBlock4*>(0x06000000);
+ volatile_copy(&blocks[block][index], &tile);
+}
+
+static inline void set_bg_palette(u32 bank, u32 index, const Color &color) {
+ volatile PaletteBank *const banks = reinterpret_cast<volatile PaletteBank*>(0x05000000);
+ volatile_copy(&banks[bank][index], &color);
+}
+
+// pair of screen entry bytes
+struct ScreenEntry {
+ u16 data;
+
+ constexpr ScreenEntry() : data(0) {}
+ constexpr ScreenEntry &set_tile_index(u16 tid) {
+ assert(tid < 1024);
+ this->data = (this->data & ~0x03ff) | tid;
+ return *this;
+ }
+
+ constexpr ScreenEntry &set_hflip(u16 flip) {
+ this->data = (this->data & ~0x0400) | (!!flip << 10);
+ return *this;
+ }
+
+ constexpr ScreenEntry &set_vflip(u16 flip) {
+ this->data = (this->data & ~0x0800) | (!!flip << 11);
+ return *this;
+ }
+
+ constexpr ScreenEntry &set_palette_bank(u16 bank) {
+ assert(bank < 16);
+ this->data = (this->data & ~0xf000) | (bank << 12);
+ return *this;
+ }
+
+ constexpr operator u16() const { return this->data; }
+};
+
+typedef ScreenEntry ScreenBlock[1024];
+
+CharBlock4 *const tile4_mem = reinterpret_cast<CharBlock4*>(0x06000000);
+PaletteBank *const palette_bg_bank = reinterpret_cast<PaletteBank*>(0x05000000);
+ScreenBlock *const screenblock_mem = reinterpret_cast<ScreenBlock*>(0x06000000);
+
+#endif