From d9de4d268e9580198173524b24cc77dfa3708307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Taylor=20Ienczak=20Zanette?= Date: Sun, 7 Jan 2024 17:26:32 -0300 Subject: [PATCH] [wip] tests/pong-mode0: Add mode 0 pong example --- .clang-format | 2 +- libgba-cpp/arch/display/layers.cpp | 10 +- libgba-cpp/arch/display/layers.h | 2 +- libgba-cpp/arch/display/objects.cpp | 23 +- libgba-cpp/arch/display/objects.h | 108 +++++- libgba-cpp/arch/display/tilemap.h | 2 +- libgba-cpp/engine/graphics/tilemap.h | 101 ++++-- tests/meson.build | 15 +- tests/pong-mode0/.gitignore | 2 + tests/pong-mode0/cross_file.ini | 20 ++ tests/pong-mode0/main.cpp | 443 ++++++++++++++++++++++++ tests/pong-mode0/meson.build | 11 + tests/pong-mode0/readme.md | 21 ++ tests/pong-mode0/screenshots/pong-0.png | Bin 0 -> 575 bytes tests/pong/main.cpp | 4 +- tests/tilemap/sample_map.h | 35 -- 16 files changed, 708 insertions(+), 91 deletions(-) create mode 100644 tests/pong-mode0/.gitignore create mode 100644 tests/pong-mode0/cross_file.ini create mode 100644 tests/pong-mode0/main.cpp create mode 100644 tests/pong-mode0/meson.build create mode 100644 tests/pong-mode0/readme.md create mode 100644 tests/pong-mode0/screenshots/pong-0.png diff --git a/.clang-format b/.clang-format index 75b6536..da5709d 100644 --- a/.clang-format +++ b/.clang-format @@ -158,7 +158,7 @@ PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyIndentedWhitespace: 0 -PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyReturnTypeOnItsOwnLine: 1000000 PointerAlignment: Left PPIndentWidth: -1 QualifierAlignment: Leave diff --git a/libgba-cpp/arch/display/layers.cpp b/libgba-cpp/arch/display/layers.cpp index 585b702..6ece70e 100644 --- a/libgba-cpp/arch/display/layers.cpp +++ b/libgba-cpp/arch/display/layers.cpp @@ -1,5 +1,4 @@ #include - #include #include @@ -8,15 +7,16 @@ namespace { using gba::display::BackgroundControl; using gba::display::RawPalette; -using gba::arch::registers::display::bg_controls; - static auto const bg_address = reinterpret_cast*>(0x0500'0000); static auto& bg_palette = *new (bg_address) RawPalette<256>{}; -} +} // namespace BackgroundControl& gba::display::bg_control(gba::display::Layer layer) { - return *(reinterpret_cast(0x0400'0008) + utils::value_of(layer)); + return *( + reinterpret_cast(0x0400'0008) + + utils::value_of(layer) + ); } RawPalette<256>& gba::display::bg_palette() { diff --git a/libgba-cpp/arch/display/layers.h b/libgba-cpp/arch/display/layers.h index 74e22b9..02a4ffe 100644 --- a/libgba-cpp/arch/display/layers.h +++ b/libgba-cpp/arch/display/layers.h @@ -110,7 +110,7 @@ enum class MapSize { }; -constexpr geometry::Size extract_size(MapSize size) { +constexpr auto get_size_values(MapSize size) -> geometry::Size { switch (size) { case MapSize::TEXT_256X256: return {256, 256}; diff --git a/libgba-cpp/arch/display/objects.cpp b/libgba-cpp/arch/display/objects.cpp index 47f67e6..130ab12 100644 --- a/libgba-cpp/arch/display/objects.cpp +++ b/libgba-cpp/arch/display/objects.cpp @@ -1,20 +1,29 @@ #include +#include -using gba::display::Color; using gba::display::RawPalette; namespace { -static auto const obj_palette_address = reinterpret_cast*>(0x0500'0200); -static auto& obj_palette = - *new (obj_palette_address) RawPalette<256>{}; +static auto const obj_palette_address = + reinterpret_cast*>(0x0500'0200); +static auto& obj_palette = *new (obj_palette_address) RawPalette<256>{}; -} +static auto& oam = *new (reinterpret_cast(0x0700'0000)) + std::array{}; +static auto& sprite_tiles = *new (reinterpret_cast(0x0601'0000)) std::array{}; +// auto oam_map = array, int>, 128>{}; -namespace gba { +} // namespace -RawPalette<256>& display::obj_palette() { +auto gba::display::obj_palette() -> RawPalette<256>& { return ::obj_palette; } +auto gba::display::oam_entry(int index) -> OAMEntry& { + return ::oam[index]; } + +auto gba::display::sprite_tile(int index) -> gba::display::map::Tile& { + return sprite_tiles[index]; +}; diff --git a/libgba-cpp/arch/display/objects.h b/libgba-cpp/arch/display/objects.h index ec6c9e1..f92e8ab 100644 --- a/libgba-cpp/arch/display/objects.h +++ b/libgba-cpp/arch/display/objects.h @@ -1,7 +1,9 @@ #ifndef GBA_DRIVERS_DISPLAY_OBJ_H #define GBA_DRIVERS_DISPLAY_OBJ_H +#include #include +#include namespace gba::display { @@ -20,12 +22,10 @@ enum class ObjectMapping { MAP_TILE_MATRIX, }; - /** * Object Color Palette array. */ -RawPalette<256>& obj_palette(); - +auto obj_palette() -> RawPalette<256>&; /** * Changes object mapping mode. @@ -34,6 +34,108 @@ inline void object_mapping(ObjectMapping map) { gba::arch::registers::display::lcd_control[6] = utils::value_of(map); } +enum class ObjectMode { + NORMAL, + SEMI_TRANSPARENT, + OBJECT_WINDOW, +}; + +enum class ObjectColorMode { + COLORS_16, + COLORS_256, +}; + +enum class ObjectShape { + SQUARE, + HORIZONTAL, + VERTICAL, +}; + +enum class ObjectSize { + TINY, + SMALL, + MEDIUM, + BIG, +}; + +enum class ObjectPriority { + HIGHEST, + HIGH, + LOW, + LOWEST, +}; + +struct OAMEntry { + gba::utils::bitset attr0; + gba::utils::bitset attr1; + gba::utils::bitset attr2; + uint16_t _unused; + + auto set_x(int y) -> void { + attr1 = (attr1.to_ulong() & ~0b11111111) | (y & 0b11111111); + } + + auto set_y(int y) -> void { + attr0 = (attr0.to_ulong() & ~0b11111111) | (y & 0b11111111); + } + + auto rotation_scaling(bool enabled) -> void { + attr0[8] = enabled; + } + + auto visible(bool visible) -> void { + attr0[9] = not visible; + } + + auto mode(ObjectMode mode) -> void { + attr0 = (attr0.to_ulong() & ~0b110000000000) | + (utils::value_of(mode) << 10); + } + + auto mosaic(bool enabled) -> void { + attr0[12] = enabled; + } + + auto color_mode(ObjectColorMode mode) -> void { + attr0[13] = utils::value_of(mode); + } + + auto shape(ObjectShape shape) -> void { + attr0 = (attr0.to_ulong() & 0b0011111111111111) | + (utils::value_of(shape) << 14); + } + + auto size(ObjectSize size) -> void { + attr1 = (attr1.to_ulong() & 0b0011111111111111) | + (utils::value_of(size) << 14); + } + + auto flip_horizontally(bool flip) -> void { + attr1[12] = flip; + } + + auto flip_vertically(bool flip) -> void { + attr1[13] = flip; + } + + auto tile(int index) -> void { + attr2 = (attr2.to_ulong() & ~0b111111111) | (index & 0b111111111); + } + + auto priority(ObjectPriority priority) -> void { + attr2 = (attr2.to_ulong() & ~0b11000000000) | + (utils::value_of(priority) << 10); + } + + auto palette(int index) -> void { + attr2 = (attr2.to_ulong() & ~(0xff << 12)) | ((index & 0xff) << 12); + } +}; + +auto oam_entry(int index) -> OAMEntry&; + +auto sprite_tile(int index) -> gba::display::map::Tile&; + } #endif diff --git a/libgba-cpp/arch/display/tilemap.h b/libgba-cpp/arch/display/tilemap.h index ed76522..e9841d0 100644 --- a/libgba-cpp/arch/display/tilemap.h +++ b/libgba-cpp/arch/display/tilemap.h @@ -40,7 +40,7 @@ class Tile { Tile() = default; Tile(std::array rows): - rows_{move(rows)} + rows_{std::move(rows)} {} /** diff --git a/libgba-cpp/engine/graphics/tilemap.h b/libgba-cpp/engine/graphics/tilemap.h index f5ad479..809dd21 100644 --- a/libgba-cpp/engine/graphics/tilemap.h +++ b/libgba-cpp/engine/graphics/tilemap.h @@ -6,8 +6,8 @@ #include #include -#include "palette.h" +#include "palette.h" namespace gba::graphics { @@ -20,20 +20,17 @@ using Tile = gba::display::map::Tile; class Tileset { public: template - Tileset(const Palette& palette, - const std::array& tiles): + Tileset(const Palette& palette, const std::array& tiles): palette_{palette}, count_{N}, - tiles_{reinterpret_cast(tiles.data())} - {} + tiles_{reinterpret_cast(tiles.data())} { + } - Tileset(const Palette& palette, - const Tile tiles[], - unsigned count): + Tileset(const Palette& palette, const Tile tiles[], unsigned count): palette_{palette}, count_{count}, - tiles_{tiles} - {} + tiles_{tiles} { + } auto length() const { return count_; @@ -65,13 +62,13 @@ class Tilemap { template Tilemap(const std::array& tiles): count_{N}, - tiles_{reinterpret_cast(tiles.data())} - {} + tiles_{reinterpret_cast(tiles.data())} { + } Tilemap(const uint16_t tiles[], unsigned count): count_{count}, - tiles_{tiles} - {} + tiles_{tiles} { + } auto length() const { return count_; @@ -90,7 +87,6 @@ class Tilemap { const uint16_t* tiles_; }; - class Map { public: Map(const Tileset& tileset, @@ -104,8 +100,8 @@ class Map { layer1_{layer1}, layer2_{layer2}, layer3_{layer3}, - size_{size} - {} + size_{size} { + } const auto& tileset() const { return tileset_; @@ -113,11 +109,16 @@ class Map { const auto& layer(const gba::display::Layer layer) const { switch (layer) { - case display::Layer::BG0: return layer0_; - case display::Layer::BG1: return layer1_; - case display::Layer::BG2: return layer2_; - case display::Layer::BG3: return layer3_; - default: return layer0_; + case display::Layer::BG0: + return layer0_; + case display::Layer::BG1: + return layer1_; + case display::Layer::BG2: + return layer2_; + case display::Layer::BG3: + return layer3_; + default: + return layer0_; } } @@ -126,11 +127,11 @@ class Map { } auto width() const { - return extract_size(size_).width; + return get_size_values(size_).width; } auto height() const { - return extract_size(size_).height; + return get_size_values(size_).height; } const auto& layer0() const { @@ -149,7 +150,11 @@ class Map { /** * Loads map into map memory. */ -inline void load_tilemap(const Tilemap& tilemap, int screenblock, int charblock) { +inline void load_tilemap( + const Tilemap& tilemap, + int screenblock, + int charblock +) { const auto base = 0x400 * screenblock + 0x2000 * charblock; for (auto i = 0u; i < tilemap.length(); ++i) { display::map::tilemap()[i + base] = tilemap[i]; @@ -169,18 +174,21 @@ inline void load_tileset(const Tileset& tileset) { * Setup and load map into memory. */ inline void load_map(const Map& map) { - using gba::display::Layer; using gba::display::BGPriority; + using gba::display::Layer; using gba::display::PaletteMode; /* load the palette from the image into palette memory*/ - load_palette(map.tileset().palette()); + auto& palette = map.tileset().palette(); + //volatile auto x = *reinterpret_cast(palette.colors()); + load_palette(palette); /* load the image into char block 0 (16 bits at a time) */ load_tileset(map.tileset()); /* set all control the bits in this register */ - constexpr auto layers = std::array{Layer::BG0, Layer::BG1, Layer::BG2, Layer::BG3}; + constexpr auto layers = + std::array{Layer::BG0, Layer::BG1, Layer::BG2, Layer::BG3}; constexpr auto priorities = std::array{ BGPriority::LOWEST, BGPriority::LOW, @@ -205,6 +213,41 @@ inline void load_map(const Map& map) { } } -} +constexpr auto null_tilemap = std::array{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +} // namespace gba::graphics #endif diff --git a/tests/meson.build b/tests/meson.build index 9a3ac62..a9d8cd1 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,11 +1,12 @@ generic_test_dirs = [ - 'alphablend', - 'interrupts', - 'pong', - 'mode5', - 'mosaic', - 'sound', - 'windowing', + # 'alphablend', + # 'interrupts', + # 'pong', + 'pong-mode0', + # 'mode5', + # 'mosaic', + # 'sound', + # 'windowing', ] foreach test_dir : generic_test_dirs diff --git a/tests/pong-mode0/.gitignore b/tests/pong-mode0/.gitignore new file mode 100644 index 0000000..510ee96 --- /dev/null +++ b/tests/pong-mode0/.gitignore @@ -0,0 +1,2 @@ +build +subprojects/libgba diff --git a/tests/pong-mode0/cross_file.ini b/tests/pong-mode0/cross_file.ini new file mode 100644 index 0000000..1b3defc --- /dev/null +++ b/tests/pong-mode0/cross_file.ini @@ -0,0 +1,20 @@ +[binaries] +c = '/opt/devkitpro/devkitARM/bin/arm-none-eabi-gcc' +cpp = '/opt/devkitpro/devkitARM/bin/arm-none-eabi-g++' +ar = '/opt/devkitpro/devkitARM/bin/arm-none-eabi-ar' +strip = '/opt/devkitpro/devkitARM/bin/arm-none-eabi-strip' + +[build-in options] +cpp_eh = 'none' +cpp_rtti = false +cpp_args = [ + '-ffast-math', + '-fomit-frame-pointer', + '-mthumb-interwork', ] +default_library = 'static' + +[host_machine] +system = 'none' +cpu_family = 'arm' +cpu = 'arm7tdmi' +endian = 'little' diff --git a/tests/pong-mode0/main.cpp b/tests/pong-mode0/main.cpp new file mode 100644 index 0000000..2ec835e --- /dev/null +++ b/tests/pong-mode0/main.cpp @@ -0,0 +1,443 @@ +#include +#include +#include +#include +#include +#include + +#include "libgba-cpp/arch/display/objects.h" + +namespace test { + +// clang-format off + +const std::array raw_sample_palette = { + gba::graphics::Color + { 0, 0, 0}, {31, 31, 31}, +}; + +const std::array raw_tileset = { + /* + 1..8 9..16 17..24 25..32 + 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, + */ + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + /* ------------------------+-------------------------------+-------------------------------+------------------------ */ + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + /* ------------------------+-------------------------------+-------------------------------+------------------------ */ + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 1, 2, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + /* ------------------------+-------------------------------+-------------------------------+------------------------ */ + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 2, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 2, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 2, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 2, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 2, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 2, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 2, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, /* | */ 2, 0, 0, 0, 0, 0, 0, 0, /* | */ 0, 0, 0, 0, 0, 0, 0, 0, +}; + +const std::array sample_tiles = { + gba::display::map::Tile{{ + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + }}, + gba::display::map::Tile{{ + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + }}, + gba::display::map::Tile{{ + 0x00000000, 0x01000000, + 0x00000000, 0x01000000, + 0x00000000, 0x01000000, + 0x00000000, 0x01000000, + }}, + gba::display::map::Tile{{ + 0x00000000, 0x01000000, + 0x00000000, 0x01000000, + 0x00000000, 0x01000000, + 0x00000000, 0x01000000, + }}, + gba::display::map::Tile{{ + 0x00000001, 0x00000000, + 0x00000001, 0x00000000, + 0x00000001, 0x00000000, + 0x00000001, 0x00000000, + }}, + gba::display::map::Tile{{ + 0x00000001, 0x00000000, + 0x00000001, 0x00000000, + 0x00000001, 0x00000000, + 0x00000001, 0x00000000, + }}, +}; + +// clang-format on + +const auto sample_palette = gba::graphics::Palette{raw_sample_palette}; +const auto sample_tileset = + gba::graphics::Tileset{sample_palette, sample_tiles}; +const auto sample_tilemap = gba::graphics::Tilemap{raw_tileset}; +const auto null_tilemap = gba::graphics::Tilemap{gba::graphics::null_tilemap}; + +} // namespace test + +const static auto WHITE = gba::display::Color{31, 31, 31}; +const static auto BLACK = gba::display::Color{0, 0, 0}; +const static auto RED = gba::display::Color{31, 0, 0}; +const static auto GREEN = gba::display::Color{0, 15, 0}; + +constexpr auto PLAYER_WIDTH = 4; +constexpr auto PLAYER_HEIGHT = 16; +constexpr auto LIFE_SIZE = 8; +constexpr auto MAX_LIVES = 3; + +constexpr auto SCREEN_SIZE = gba::geometry::Size{ + gba::display::mode3::screen_width, + gba::display::mode3::screen_height, +}; + +struct Ball { + int oam_index; + gba::geometry::Point position; + gba::geometry::Point speed; +}; + +struct Player { + int oam_index; + gba::geometry::Point position; + gba::geometry::Point speed; + int lives; + + auto move() -> void { + position = { + .x = this->position.x + speed.x, + .y = this->position.y + speed.y, + }; + auto& entry = gba::display::oam_entry(oam_index); + entry.set_x(position.x); + entry.set_y(position.y); + } +}; + +auto create_lives_sprites(gba::geometry::Point const& origin, int oam_start) + -> void { + auto sprite_pos = origin; + for (auto i = 0; i < MAX_LIVES; ++i) { + auto& oam_entry = gba::display::oam_entry(oam_start + i); + oam_entry.set_x(sprite_pos.x); + oam_entry.set_y(sprite_pos.y); + oam_entry.size(gba::display::ObjectSize::TINY); + oam_entry.tile(3); + sprite_pos.x += LIFE_SIZE; + } +} + +auto update_player_lives(int lives, int oam_start) -> void { + for (auto i = 0; i < lives; ++i) { + gba::display::oam_entry(oam_start + i).tile(3); + } + for (auto i = lives; i < MAX_LIVES; ++i) { + gba::display::oam_entry(oam_start + i).tile(4); + } +} + +auto collides(Ball const& ball, gba::geometry::Rect const& hitbox) { + auto [ball_x, ball_y] = ball.position; + return ( + ball_x >= hitbox.x and ball_y >= hitbox.y and + ball_x <= hitbox.x + hitbox.width and ball_y <= hitbox.y + hitbox.height + ); +} + +struct MainScreen { + bool game_running = false; + + Ball ball = { + .oam_index = 2, + .position = + {SCREEN_SIZE.width / 2 - 1, + (SCREEN_SIZE.height / 2) % SCREEN_SIZE.height}, + .speed = {-2, -2}, + }; + + std::array players = { + Player{ + .oam_index = 0, + .position = {0, 60}, + .speed = {0, 0}, + .lives = 3, + }, + Player{ + .oam_index = 1, + .position = {SCREEN_SIZE.width - PLAYER_WIDTH - 1, 60}, + .speed = {0, -2}, + .lives = 3 + }, + }; + + auto start() -> void { + create_lives_sprites( + gba::geometry::Point{ + SCREEN_SIZE.width / 2 - LIFE_SIZE * (MAX_LIVES + 1), + 5 + }, + 3 + ); + create_lives_sprites( + gba::geometry::Point{SCREEN_SIZE.width / 2 + LIFE_SIZE, 5}, + 6 + ); + } + + auto new_match(int seed) -> void { + auto ball_hor_dir = seed % 2 == 0 ? -1 : 1; + auto ball_ver_dir = (seed / 3) % 2 == 0 ? -1 : 1; + auto ball_h_speed = (seed / 17) % 2 + 1; + auto ball_v_speed = (seed / 7) % 3 + 1; + + auto ball_dir = gba::geometry::Point{ + ball_hor_dir * ball_h_speed, + ball_ver_dir * ball_v_speed + }; + + ball = { + .oam_index = 2, + .position = + {SCREEN_SIZE.width / 2 - 1, + (SCREEN_SIZE.height / 2 + seed) % SCREEN_SIZE.height}, + .speed = ball_dir, + }; + + update_player_lives(players[0].lives, 3); + update_player_lives(players[1].lives, 6); + } + + auto update() -> void { + if (game_running) { + game_loop(); + } else if (gba::input::pressing(gba::input::Key::START)) { + game_running = true; + players[0].lives = MAX_LIVES; + players[1].lives = MAX_LIVES; + new_match(gba::utils::random()); + } + } + + auto game_loop() -> void { + using gba::geometry::Rect; + + auto seed = gba::utils::random(); + // Screen size is 240x160, so value ranges [0..239, 0..159]. + // For the upper index we must consider that the ball starts -2 pixels + // from it, because its reference is top-left and the ball has a size + // of 2x2. + if (ball.position.x <= 0 or ball.position.x >= SCREEN_SIZE.width - 3) { + ball.speed.x *= -1; + } + + if (ball.position.y <= 0 or ball.position.y >= SCREEN_SIZE.height - 3) { + ball.speed.y *= -1; + } + ball.position.x += ball.speed.x; + ball.position.y += ball.speed.y; + + if (ball.position.x >= SCREEN_SIZE.width - 3) { + players[1].lives -= 1; + update_player_lives(players[1].lives, 6); + new_match(seed); + } else if (ball.position.x == 0) { + players[0].lives -= 1; + update_player_lives(players[0].lives, 3); + new_match(seed); + } + + if (players[0].lives == 0 or players[1].lives == 0) { + game_running = false; + } + + // Update opponent position + if (players[1].position.y == 0 or + players[1].position.y >= 159 - PLAYER_HEIGHT) { + players[1].speed.y *= -1; + } + + using gba::input::Key; + + if (gba::input::pressing(Key::UP)) { + players[0].speed.y = -2; + } else if (gba::input::pressing(Key::DOWN)) { + players[0].speed.y = 2; + } else { + players[0].speed.y = 0; + } + + if (gba::input::pressing(Key::A)) { + ball.position.x = 15; + ball.position.y = 15; + } + + for (auto&& player : players) { + player.move(); + if (player.position.y + PLAYER_HEIGHT > SCREEN_SIZE.height) { + player.position.y = SCREEN_SIZE.height - PLAYER_HEIGHT; + } + if (player.position.y < 0) { + player.position.y = 0; + } + } + + auto [p0, p1] = std::array{ + players[0].position, + players[1].position, + }; + + auto players_hitbox = std::array{ + Rect{-10, p0.y, 10 + PLAYER_WIDTH, PLAYER_HEIGHT}, + Rect{ + SCREEN_SIZE.width - PLAYER_WIDTH, + p1.y, + 10 + PLAYER_WIDTH, + PLAYER_HEIGHT + }, + }; + + auto collides_with_p0 = collides(ball, players_hitbox[0]); + auto collides_with_p1 = collides(ball, players_hitbox[1]); + + if (collides_with_p0) { + ball.speed.x *= -1; + ball.speed.y += players[0].speed.y; + } + + if (collides_with_p1) { + ball.speed.x *= -1; + ball.speed.y += players[1].speed.y; + } + + auto& entry = gba::display::oam_entry(ball.oam_index); + entry.set_x(ball.position.x - 3); + entry.set_y(ball.position.y - 3); + } + + auto draw() const -> void { + gba::display::vsync(); + } + + auto post_update() -> void { + } +}; + +int main() { + using gba::geometry::Point; + using namespace gba::display; + + const auto map = gba::graphics::Map{ + test::sample_tileset, + test::sample_tilemap, + gba::graphics::null_tilemap, + gba::graphics::null_tilemap, + gba::graphics::null_tilemap, + gba::graphics::MapSize::TEXT_256X256 + }; + + force_blank(true); + + change_mode(Mode::MODE0); + layer_visible(Layer::BG1); + layer_visible(Layer::OBJ); + + auto& ball_oam_entry = gba::display::oam_entry(2); + ball_oam_entry.tile(2); + auto& player0_oam = gba::display::oam_entry(0); + player0_oam.tile(1); + player0_oam.shape(gba::display::ObjectShape::VERTICAL); + auto& player1_oam = gba::display::oam_entry(1); + player1_oam.tile(1); + player1_oam.shape(gba::display::ObjectShape::VERTICAL); + gba::display::obj_palette()[1] = WHITE; + gba::display::obj_palette()[2] = RED; + gba::display::obj_palette()[3] = GREEN; + + gba::graphics::load_map(map); + + gba::display::sprite_tile(33) = gba::display::sprite_tile(1) = + gba::display::map::Tile{{ + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + 0x00001111, + }}; + + gba::display::sprite_tile(2) = gba::display::map::Tile{{ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00011000, + 0x00011000, + 0x00000000, + 0x00000000, + 0x00000000, + }}; + + gba::display::sprite_tile(3) = gba::display::map::Tile{{ + 0x00000000, + 0x00000000, + 0x00000000, + 0x33333333, + 0x33333333, + 0x00000000, + 0x00000000, + 0x00000000, + }}; + + gba::display::sprite_tile(4) = gba::display::map::Tile{{ + 0x20000002, + 0x02000020, + 0x00200200, + 0x00022000, + 0x00022000, + 0x00200200, + 0x02000020, + 0x20000002, + }}; + + auto screen = MainScreen{}; + + force_blank(false); + + screen.start(); + while (true) { + screen.update(); + screen.draw(); + } +} diff --git a/tests/pong-mode0/meson.build b/tests/pong-mode0/meson.build new file mode 100644 index 0000000..7f5e43d --- /dev/null +++ b/tests/pong-mode0/meson.build @@ -0,0 +1,11 @@ +project('gba-pong', + 'cpp', + default_options: [ + 'cpp_std=c++20', + ]) + +libgba = dependency('libgba', fallback : ['libgba', 'libgba_dep']) + +pong = executable('pong.gba', + 'main.cpp', + dependencies : libgba) diff --git a/tests/pong-mode0/readme.md b/tests/pong-mode0/readme.md new file mode 100644 index 0000000..b95022a --- /dev/null +++ b/tests/pong-mode0/readme.md @@ -0,0 +1,21 @@ +Pong Game (mode 3) +================== + +Tests if a pong game example made for mode 3 works as expected. + +| Function | Status | +|----------------|---------| +| Draw objects | Working | +| Basic input | Working | + +Explanation +----------- + +_TODO (waiting for engine code implementation)._ + +Screenshots +----------- + +In-game: + +![Pong Game Example](screenshots/pong-0.png) diff --git a/tests/pong-mode0/screenshots/pong-0.png b/tests/pong-mode0/screenshots/pong-0.png new file mode 100644 index 0000000000000000000000000000000000000000..63bbf21f4753960f9ee154527bc682ad2bacd6d2 GIT binary patch literal 575 zcmeAS@N?(olHy`uVBq!ia0vp^AAooP2NRIoyR*!Qfq{w5)5S5QV$R#UhJDQr0t}AF z9{rDxQ|DWA`a)sU?)6&Avv{MFB^mAb{@shpUc2?T$p1sh-x(kHHL1B?Si&)DO7V9_ zyQ8u8x6+>7x)Z%j?)cV~v1TTkP+1Yj$x0bPEWTa8FX)HAm%ndTe)@`@^enjW0#89q zwd4r?cwl+|?O*xe^kZ!hcM-DUqm9Y^+_Zc8xnSol@U*PoGimEbjStmW9FNUGr(Q~C oc;!GfQ^^w?Gzb_X&3wxFWP+f-UA<-wFkvuwy85}Sb4q9e08@F-?*IS* literal 0 HcmV?d00001 diff --git a/tests/pong/main.cpp b/tests/pong/main.cpp index f35fb1e..c9f56af 100644 --- a/tests/pong/main.cpp +++ b/tests/pong/main.cpp @@ -4,8 +4,6 @@ #include #include "libgba-cpp/engine/map/objects.h" -using gba::geometry::Rect; - const static auto WHITE = gba::display::Color{31, 31, 31}; const static auto BLACK = gba::display::Color{0, 0, 0}; const static auto RED = gba::display::Color{31, 0, 0}; @@ -175,6 +173,8 @@ struct MainScreen { } auto game_loop() -> void { + using gba::geometry::Rect; + auto seed = utils::random(); // Screen size is 240x160, so value ranges [0..239, 0..159]. // For the upper index we must consider that the ball starts -2 pixels diff --git a/tests/tilemap/sample_map.h b/tests/tilemap/sample_map.h index 6dd537c..39bfc56 100644 --- a/tests/tilemap/sample_map.h +++ b/tests/tilemap/sample_map.h @@ -76,41 +76,6 @@ const auto layer0_map = std::array{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -const auto null_map = std::array{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - const auto sample_tilemap = gba::graphics::Tilemap{raw_tilemap}; const auto null_tilemap = gba::graphics::Tilemap{null_map}; const auto layer0_tilemap = gba::graphics::Tilemap{layer0_map};