diff --git a/src/nostalgia/core/CMakeLists.txt b/src/nostalgia/core/CMakeLists.txt index 5220b9de..4adc7e91 100644 --- a/src/nostalgia/core/CMakeLists.txt +++ b/src/nostalgia/core/CMakeLists.txt @@ -23,6 +23,7 @@ endif() install( FILES + config.hpp consts.hpp context.hpp core.hpp diff --git a/src/nostalgia/core/config.hpp b/src/nostalgia/core/config.hpp new file mode 100644 index 00000000..5e977f8c --- /dev/null +++ b/src/nostalgia/core/config.hpp @@ -0,0 +1,15 @@ +/* + * Copyright 2016 - 2020 gary@drinkingtea.net + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +namespace nostalgia::core::config { + +constexpr auto GbaSpriteBufferLen = 128; + +} diff --git a/src/nostalgia/core/gba/addresses.hpp b/src/nostalgia/core/gba/addresses.hpp index a0b0be1a..d27bf51a 100644 --- a/src/nostalgia/core/gba/addresses.hpp +++ b/src/nostalgia/core/gba/addresses.hpp @@ -50,13 +50,20 @@ using interrupt_handler = void (*)(void); ///////////////////////////////////////////////////////////////// // Memory Addresses -#define MEM_PALETTE_BG reinterpret_cast(0x05000000) -#define MEM_PALETTE_SPRITE reinterpret_cast(0x05000200) +#define MEM_EWRAM_BEGIN reinterpret_cast(0x02000000) +#define MEM_EWRAM_END reinterpret_cast(0x0203FFFF) + +#define MEM_IWRAM_BEGIN reinterpret_cast(0x03000000) +#define MEM_IWRAM_END reinterpret_cast(0x03007FFF) + +#define MEM_BG_PALETTE reinterpret_cast(0x05000000) +#define MEM_SPRITE_PALETTE reinterpret_cast(0x05000200) typedef uint16_t BgMapTile[1024]; -#define MEM_BG_MAP reinterpret_cast(0x06000000) +#define MEM_BG_TILES reinterpret_cast(0x06000000) +#define MEM_BG_MAP reinterpret_cast(0x0600e000) + +#define MEM_SPRITE_TILES reinterpret_cast(0x06010000) +#define MEM_OAM reinterpret_cast(0x07000000) #define MEM_ROM reinterpret_cast(0x08000000) - -#define MEM_WRAM_BEGIN reinterpret_cast(0x02000000) -#define MEM_WRAM_END reinterpret_cast(0x0203FFFF) diff --git a/src/nostalgia/core/gba/gfx.cpp b/src/nostalgia/core/gba/gfx.cpp index 9e8acce6..207b9122 100644 --- a/src/nostalgia/core/gba/gfx.cpp +++ b/src/nostalgia/core/gba/gfx.cpp @@ -14,14 +14,12 @@ #include #include "addresses.hpp" +#include "irq.hpp" +#include "gfx.hpp" namespace nostalgia::core { -constexpr auto GBA_TILE_COLUMNS = 32; - -using SpriteAttr = uint16_t[4]; - -SpriteAttr spriteBuffer[128]; +constexpr auto GbaTileColumns = 32; struct GbaPaletteTarget { static constexpr auto TypeName = NostalgiaPalette::TypeName; @@ -88,8 +86,7 @@ ox::Error initGfx(Context*) { /* Background 0 -\|| */ /* Objects -----\||| */ /* |||| */ - REG_DISPCNT = 0x1100; - + REG_DISPCNT = 0x1101; return OxError(0); } @@ -126,11 +123,10 @@ ox::Error initConsole(Context *ctx) { ox::FileStore32 fs(rom, 32 * ox::units::MB); ctx->rom = new (ox_alloca(sizeof(ox::FileSystem32))) ox::FileSystem32(fs); } - return loadTileSheet(ctx, TileSheetSpace::Background, 0, TilesheetAddr, PaletteAddr); + return loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr); } -ox::Error loadTileSheet(Context *ctx, - TileSheetSpace, +ox::Error loadBgTileSheet(Context *ctx, int section, ox::FileAddress tilesheetAddr, ox::FileAddress paletteAddr) { @@ -139,25 +135,84 @@ ox::Error loadTileSheet(Context *ctx, auto [ts, tserr] = ctx->rom->read(tilesheetAddr); oxReturnError(tserr); GbaTileMapTarget target; - target.pal.palette = &MEM_PALETTE_BG[section]; + target.pal.palette = &MEM_BG_PALETTE[section]; target.bgCtl = &bgCtl(section); - target.tileMap = &ox::bit_cast(MEM_BG_MAP)[section * 512]; + target.tileMap = &ox::bit_cast(MEM_BG_TILES)[section * 512]; oxReturnError(ox::readMC(ts, tsStat.size, &target)); // load external palette if available if (paletteAddr) { - paletteAddr = target.defaultPalette; + auto [palStat, palStatErr] = ctx->rom->stat(paletteAddr); + oxReturnError(palStatErr); + auto [pal, palErr] = ctx->rom->read(paletteAddr); + oxReturnError(palErr); + oxReturnError(ox::readMC(pal, palStat.size, &target.pal)); } + return OxError(0); +} + +ox::Error loadSpriteTileSheet(Context *ctx, + int section, + ox::FileAddress tilesheetAddr, + ox::FileAddress paletteAddr) { + auto [tsStat, tsStatErr] = ctx->rom->stat(tilesheetAddr); + oxReturnError(tsStatErr); + auto [ts, tserr] = ctx->rom->read(tilesheetAddr); + oxReturnError(tserr); + GbaTileMapTarget target; + target.pal.palette = &MEM_SPRITE_PALETTE[section]; + target.bgCtl = &bgCtl(section); + target.tileMap = &ox::bit_cast(MEM_SPRITE_TILES)[section * 512]; + oxReturnError(ox::readMC(ts, tsStat.size, &target)); + // load external palette if available + if (paletteAddr) { + auto [palStat, palStatErr] = ctx->rom->stat(paletteAddr); + oxReturnError(palStatErr); + auto [pal, palErr] = ctx->rom->read(paletteAddr); + oxReturnError(palErr); + oxReturnError(ox::readMC(pal, palStat.size, &target.pal)); + } + return OxError(0); +} + +ox::Error loadBgPalette(Context *ctx, int section, ox::FileAddress paletteAddr) { + GbaPaletteTarget target; + target.palette = &MEM_BG_PALETTE[section]; auto [palStat, palStatErr] = ctx->rom->stat(paletteAddr); oxReturnError(palStatErr); auto [pal, palErr] = ctx->rom->read(paletteAddr); oxReturnError(palErr); - oxReturnError(ox::readMC(pal, palStat.size, &target.pal)); + oxReturnError(ox::readMC(pal, palStat.size, &target)); + return OxError(0); +} + +ox::Error loadSpritePalette(Context *ctx, int section, ox::FileAddress paletteAddr) { + GbaPaletteTarget target; + target.palette = &MEM_SPRITE_PALETTE[section]; + auto [palStat, palStatErr] = ctx->rom->stat(paletteAddr); + oxReturnError(palStatErr); + auto [pal, palErr] = ctx->rom->read(paletteAddr); + oxReturnError(palErr); + oxReturnError(ox::readMC(pal, palStat.size, &target)); return OxError(0); } // Do NOT use Context in the GBA version of this function. void setTile(Context*, int layer, int column, int row, uint8_t tile) { - MEM_BG_MAP[28 + layer][row * GBA_TILE_COLUMNS + column] = tile; + MEM_BG_MAP[layer][row * GbaTileColumns + column] = tile; +} + +void setSprite(uint8_t idx, uint8_t x, uint8_t y, uint8_t tileIdx) { + // block until g_spriteUpdates is less than buffer len + while (g_spriteUpdates >= config::GbaSpriteBufferLen); + GbaSpriteAttrUpdate oa; + oa.attr0 = static_cast(y & ox::onMask(7)) + | (static_cast(1) << 10); // enable alpha + oa.attr1 = static_cast(x); + oa.attr2 = static_cast(tileIdx & ox::onMask(8)); + oa.idx = idx; + REG_IE &= ~Int_vblank; // disable vblank interrupt handler + g_spriteBuffer[g_spriteUpdates++] = oa; + REG_IE |= Int_vblank; // enable vblank interrupt handler } } diff --git a/src/nostalgia/core/gba/gfx.hpp b/src/nostalgia/core/gba/gfx.hpp new file mode 100644 index 00000000..e73c5afc --- /dev/null +++ b/src/nostalgia/core/gba/gfx.hpp @@ -0,0 +1,29 @@ +/* + * Copyright 2016 - 2020 gary@drinkingtea.net + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include + +#include + +namespace nostalgia::core { + +struct OX_ALIGN8 GbaSpriteAttrUpdate { + uint16_t attr0 = 0; + uint16_t attr1 = 0; + uint16_t attr2 = 0; + uint16_t idx = 0; + +}; + +extern volatile uint16_t g_spriteUpdates; +extern GbaSpriteAttrUpdate g_spriteBuffer[config::GbaSpriteBufferLen]; + +} diff --git a/src/nostalgia/core/gba/irq.arm.cpp b/src/nostalgia/core/gba/irq.arm.cpp index 9133a48b..8d96232c 100644 --- a/src/nostalgia/core/gba/irq.arm.cpp +++ b/src/nostalgia/core/gba/irq.arm.cpp @@ -9,10 +9,35 @@ // NOTE: this file is compiled as ARM and not THUMB, so don't but too much in // here +#include + +#include "addresses.hpp" +#include "gfx.hpp" #include "irq.hpp" +namespace nostalgia::core { + +volatile uint16_t g_spriteUpdates = 0; +GbaSpriteAttrUpdate g_spriteBuffer[config::GbaSpriteBufferLen]; + +} + +using namespace nostalgia::core; + extern "C" { +void nostalgia_core_isr_vblank() { + // copy g_spriteUpdates to allow it to use a register instead of reading + // from memory every iteration of the loop, needed because g_spriteUpdates + // is volatile + const auto updates = g_spriteUpdates; + for (uint16_t i = 0; i < updates; ++i) { + auto &oa = g_spriteBuffer[i]; + MEM_OAM[oa.idx] = *reinterpret_cast(&oa); + } + g_spriteUpdates = 0; +} + void nostalgia_core_isr_timer0() { } @@ -25,7 +50,4 @@ void nostalgia_core_isr_timer2() { void nostalgia_core_isr_timer3() { } -void nostalgia_core_isr_vblank() { -} - } diff --git a/src/nostalgia/core/gba/irq.s b/src/nostalgia/core/gba/irq.s index 0249130d..9e92e2ed 100644 --- a/src/nostalgia/core/gba/irq.s +++ b/src/nostalgia/core/gba/irq.s @@ -97,7 +97,6 @@ isr: // Interrupt Table End // //////////////////////////////////////////////////// - isr_end: // restore lr from before the Interrupt Table pop {lr} diff --git a/src/nostalgia/core/gfx.hpp b/src/nostalgia/core/gfx.hpp index d8011e08..dea29170 100644 --- a/src/nostalgia/core/gfx.hpp +++ b/src/nostalgia/core/gfx.hpp @@ -75,7 +75,12 @@ ox::Error model(T *io, NostalgiaGraphic *ng) { /** * @param section describes which section of the selected TileSheetSpace to use (e.g. MEM_PALLETE_BG[section]) */ -[[nodiscard]] ox::Error loadTileSheet(Context *ctx, TileSheetSpace tss, int section, ox::FileAddress tilesheet, ox::FileAddress palette = nullptr); +[[nodiscard]] ox::Error loadBgTileSheet(Context *ctx, int section, ox::FileAddress tilesheet, ox::FileAddress palette = nullptr); + +[[nodiscard]] ox::Error loadSpriteTileSheet(Context *ctx, + int section, + ox::FileAddress tilesheetAddr, + ox::FileAddress paletteAddr); [[nodiscard]] Color32 toColor32(Color16 nc) noexcept; @@ -120,4 +125,6 @@ void puts(Context *ctx, int column, int row, const char *str); void setTile(Context *ctx, int layer, int column, int row, uint8_t tile); +void setSprite(uint8_t idx, uint8_t x, uint8_t y, uint8_t tileIdx); + } diff --git a/src/nostalgia/core/qt/gfx.cpp b/src/nostalgia/core/qt/gfx.cpp index 01e6f0ba..71481a20 100644 --- a/src/nostalgia/core/qt/gfx.cpp +++ b/src/nostalgia/core/qt/gfx.cpp @@ -22,11 +22,10 @@ ox::Error initConsole(Context*) { return OxError(1); } -ox::Error loadTileSheet(Context*, - TileSheetSpace, - int, - ox::FileAddress, - ox::FileAddress) { +ox::Error loadBgTileSheet(Context*, + int, + ox::FileAddress, + ox::FileAddress) { return OxError(1); } diff --git a/src/nostalgia/core/sdl/gfx.cpp b/src/nostalgia/core/sdl/gfx.cpp index dd8a8d68..cb38925f 100644 --- a/src/nostalgia/core/sdl/gfx.cpp +++ b/src/nostalgia/core/sdl/gfx.cpp @@ -72,7 +72,7 @@ ox::Error shutdownGfx(Context *ctx) { ox::Error initConsole(Context *ctx) { constexpr auto TilesheetAddr = "/TileSheets/Charset.ng"; - return loadTileSheet(ctx, TileSheetSpace::Background, 0, TilesheetAddr); + return loadBgTileSheet(ctx, 0, TilesheetAddr); } SDL_Color createSDL_Color(Color16 nc) { @@ -93,11 +93,10 @@ SDL_Palette *createSDL_Palette(const NostalgiaPalette &npal) { return pal; } -ox::Error loadTileSheet(Context *ctx, - TileSheetSpace tss, - int section, - ox::FileAddress tilesheetPath, - ox::FileAddress palettePath) { +ox::Error loadBgTileSheet(Context *ctx, + int section, + ox::FileAddress tilesheetPath, + ox::FileAddress palettePath) { auto id = ctx->implData(); auto [tilesheet, tserr] = readObj(ctx, tilesheetPath); oxReturnError(tserr); @@ -129,16 +128,21 @@ ox::Error loadTileSheet(Context *ctx, SDL_FreePalette(sdlPalette); auto sectionIdx = static_cast(section); - if (tss == TileSheetSpace::Background) { - if (id->bgTextures[sectionIdx]) { - SDL_DestroyTexture(id->bgTextures[sectionIdx]); - } - id->bgTextures[sectionIdx] = texture; + if (id->bgTextures[sectionIdx]) { + SDL_DestroyTexture(id->bgTextures[sectionIdx]); } + id->bgTextures[sectionIdx] = texture; return OxError(0); } +ox::Error loadSpriteTileSheet(Context*, + int, + ox::FileAddress, + ox::FileAddress) { + return OxError(0); +} + void drawBackground(Context *ctx, const TileMap &tm, SDL_Texture *tex) { if (tex) { constexpr auto DstSize = 8 * Scale; @@ -196,4 +200,7 @@ void setTile(Context *ctx, int layer, int column, int row, uint8_t tile) { id->bgTileMaps[z][y][x] = tile; } +void setSprite(uint8_t, uint8_t, uint8_t, uint8_t) { +} + } diff --git a/src/nostalgia/player/main.cpp b/src/nostalgia/player/main.cpp index aa7fee79..3c68eb72 100644 --- a/src/nostalgia/player/main.cpp +++ b/src/nostalgia/player/main.cpp @@ -20,7 +20,11 @@ ox::Error run(ox::FileSystem *fs) { //Zone zone; //oxReturnError(zone.init(&ctx, Bounds{0, 0, 40, 40}, "/TileSheets/Charset.ng", "/Palettes/Charset.npal")); //zone.draw(&ctx); + constexpr auto TileSheetAddr = "/TileSheets/Charset.ng"; + constexpr auto PaletteAddr = "/Palettes/Charset.npal"; + oxReturnError(core::loadSpriteTileSheet(&ctx, 0, TileSheetAddr, PaletteAddr)); oxReturnError(core::initConsole(&ctx)); + core::setSprite(0, 50, 50, 7); core::puts(&ctx, 10, 9, "DOPENESS!!!"); oxReturnError(core::run(&ctx)); oxReturnError(core::shutdownGfx(&ctx)); diff --git a/src/nostalgia/world/world.cpp b/src/nostalgia/world/world.cpp index c9ce60cc..dd50de0a 100644 --- a/src/nostalgia/world/world.cpp +++ b/src/nostalgia/world/world.cpp @@ -17,7 +17,7 @@ ox::Error Zone::init(Context *ctx, Bounds bnds, ox::FileAddress tileSheet, ox::F const auto size = static_cast(bnds.width * bnds.height); m_tiles = new Tile[size]; m_bounds = bnds; - return core::loadTileSheet(ctx, core::TileSheetSpace::Background, 0, tileSheet, palette); + return core::loadBgTileSheet(ctx, 0, tileSheet, palette); } Zone::~Zone() {