[nostalgia/core/gba] Add sprite support

This commit is contained in:
Gary Talent 2020-07-13 00:14:08 -05:00
parent b32734d727
commit 7e565a3162
12 changed files with 188 additions and 43 deletions

View File

@ -23,6 +23,7 @@ endif()
install( install(
FILES FILES
config.hpp
consts.hpp consts.hpp
context.hpp context.hpp
core.hpp core.hpp

View File

@ -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;
}

View File

@ -50,13 +50,20 @@ using interrupt_handler = void (*)(void);
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// Memory Addresses // Memory Addresses
#define MEM_PALETTE_BG reinterpret_cast<uint16_t*>(0x05000000) #define MEM_EWRAM_BEGIN reinterpret_cast<uint8_t*>(0x02000000)
#define MEM_PALETTE_SPRITE reinterpret_cast<uint16_t*>(0x05000200) #define MEM_EWRAM_END reinterpret_cast<uint8_t*>(0x0203FFFF)
#define MEM_IWRAM_BEGIN reinterpret_cast<uint8_t*>(0x03000000)
#define MEM_IWRAM_END reinterpret_cast<uint8_t*>(0x03007FFF)
#define MEM_BG_PALETTE reinterpret_cast<uint16_t*>(0x05000000)
#define MEM_SPRITE_PALETTE reinterpret_cast<uint16_t*>(0x05000200)
typedef uint16_t BgMapTile[1024]; typedef uint16_t BgMapTile[1024];
#define MEM_BG_MAP reinterpret_cast<BgMapTile*>(0x06000000) #define MEM_BG_TILES reinterpret_cast<BgMapTile*>(0x06000000)
#define MEM_BG_MAP reinterpret_cast<BgMapTile*>(0x0600e000)
#define MEM_SPRITE_TILES reinterpret_cast<uint8_t*>(0x06010000)
#define MEM_OAM reinterpret_cast<uint64_t*>(0x07000000)
#define MEM_ROM reinterpret_cast<char*>(0x08000000) #define MEM_ROM reinterpret_cast<char*>(0x08000000)
#define MEM_WRAM_BEGIN reinterpret_cast<uint8_t*>(0x02000000)
#define MEM_WRAM_END reinterpret_cast<uint8_t*>(0x0203FFFF)

View File

@ -14,14 +14,12 @@
#include <nostalgia/core/gfx.hpp> #include <nostalgia/core/gfx.hpp>
#include "addresses.hpp" #include "addresses.hpp"
#include "irq.hpp"
#include "gfx.hpp"
namespace nostalgia::core { namespace nostalgia::core {
constexpr auto GBA_TILE_COLUMNS = 32; constexpr auto GbaTileColumns = 32;
using SpriteAttr = uint16_t[4];
SpriteAttr spriteBuffer[128];
struct GbaPaletteTarget { struct GbaPaletteTarget {
static constexpr auto TypeName = NostalgiaPalette::TypeName; static constexpr auto TypeName = NostalgiaPalette::TypeName;
@ -88,8 +86,7 @@ ox::Error initGfx(Context*) {
/* Background 0 -\|| */ /* Background 0 -\|| */
/* Objects -----\||| */ /* Objects -----\||| */
/* |||| */ /* |||| */
REG_DISPCNT = 0x1100; REG_DISPCNT = 0x1101;
return OxError(0); return OxError(0);
} }
@ -126,11 +123,10 @@ ox::Error initConsole(Context *ctx) {
ox::FileStore32 fs(rom, 32 * ox::units::MB); ox::FileStore32 fs(rom, 32 * ox::units::MB);
ctx->rom = new (ox_alloca(sizeof(ox::FileSystem32))) ox::FileSystem32(fs); 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, ox::Error loadBgTileSheet(Context *ctx,
TileSheetSpace,
int section, int section,
ox::FileAddress tilesheetAddr, ox::FileAddress tilesheetAddr,
ox::FileAddress paletteAddr) { ox::FileAddress paletteAddr) {
@ -139,25 +135,84 @@ ox::Error loadTileSheet(Context *ctx,
auto [ts, tserr] = ctx->rom->read(tilesheetAddr); auto [ts, tserr] = ctx->rom->read(tilesheetAddr);
oxReturnError(tserr); oxReturnError(tserr);
GbaTileMapTarget target; GbaTileMapTarget target;
target.pal.palette = &MEM_PALETTE_BG[section]; target.pal.palette = &MEM_BG_PALETTE[section];
target.bgCtl = &bgCtl(section); target.bgCtl = &bgCtl(section);
target.tileMap = &ox::bit_cast<uint16_t*>(MEM_BG_MAP)[section * 512]; target.tileMap = &ox::bit_cast<uint16_t*>(MEM_BG_TILES)[section * 512];
oxReturnError(ox::readMC(ts, tsStat.size, &target)); oxReturnError(ox::readMC(ts, tsStat.size, &target));
// load external palette if available // load external palette if available
if (paletteAddr) { if (paletteAddr) {
paletteAddr = target.defaultPalette;
}
auto [palStat, palStatErr] = ctx->rom->stat(paletteAddr); auto [palStat, palStatErr] = ctx->rom->stat(paletteAddr);
oxReturnError(palStatErr); oxReturnError(palStatErr);
auto [pal, palErr] = ctx->rom->read(paletteAddr); auto [pal, palErr] = ctx->rom->read(paletteAddr);
oxReturnError(palErr); oxReturnError(palErr);
oxReturnError(ox::readMC(pal, palStat.size, &target.pal)); 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<uint16_t*>(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));
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); return OxError(0);
} }
// Do NOT use Context in the GBA version of this function. // Do NOT use Context in the GBA version of this function.
void setTile(Context*, int layer, int column, int row, uint8_t tile) { 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<uint16_t>(y & ox::onMask<uint8_t>(7))
| (static_cast<uint16_t>(1) << 10); // enable alpha
oa.attr1 = static_cast<uint16_t>(x);
oa.attr2 = static_cast<uint16_t>(tileIdx & ox::onMask<uint16_t>(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
} }
} }

View File

@ -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 <ox/std/stddef.hpp>
#include <ox/std/types.hpp>
#include <nostalgia/core/config.hpp>
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];
}

View File

@ -9,10 +9,35 @@
// NOTE: this file is compiled as ARM and not THUMB, so don't but too much in // NOTE: this file is compiled as ARM and not THUMB, so don't but too much in
// here // here
#include <nostalgia/core/config.hpp>
#include "addresses.hpp"
#include "gfx.hpp"
#include "irq.hpp" #include "irq.hpp"
namespace nostalgia::core {
volatile uint16_t g_spriteUpdates = 0;
GbaSpriteAttrUpdate g_spriteBuffer[config::GbaSpriteBufferLen];
}
using namespace nostalgia::core;
extern "C" { 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<uint64_t*>(&oa);
}
g_spriteUpdates = 0;
}
void nostalgia_core_isr_timer0() { void nostalgia_core_isr_timer0() {
} }
@ -25,7 +50,4 @@ void nostalgia_core_isr_timer2() {
void nostalgia_core_isr_timer3() { void nostalgia_core_isr_timer3() {
} }
void nostalgia_core_isr_vblank() {
}
} }

View File

@ -97,7 +97,6 @@ isr:
// Interrupt Table End // // Interrupt Table End //
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
isr_end: isr_end:
// restore lr from before the Interrupt Table // restore lr from before the Interrupt Table
pop {lr} pop {lr}

View File

@ -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]) * @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; [[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 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);
} }

View File

@ -22,8 +22,7 @@ ox::Error initConsole(Context*) {
return OxError(1); return OxError(1);
} }
ox::Error loadTileSheet(Context*, ox::Error loadBgTileSheet(Context*,
TileSheetSpace,
int, int,
ox::FileAddress, ox::FileAddress,
ox::FileAddress) { ox::FileAddress) {

View File

@ -72,7 +72,7 @@ ox::Error shutdownGfx(Context *ctx) {
ox::Error initConsole(Context *ctx) { ox::Error initConsole(Context *ctx) {
constexpr auto TilesheetAddr = "/TileSheets/Charset.ng"; constexpr auto TilesheetAddr = "/TileSheets/Charset.ng";
return loadTileSheet(ctx, TileSheetSpace::Background, 0, TilesheetAddr); return loadBgTileSheet(ctx, 0, TilesheetAddr);
} }
SDL_Color createSDL_Color(Color16 nc) { SDL_Color createSDL_Color(Color16 nc) {
@ -93,8 +93,7 @@ SDL_Palette *createSDL_Palette(const NostalgiaPalette &npal) {
return pal; return pal;
} }
ox::Error loadTileSheet(Context *ctx, ox::Error loadBgTileSheet(Context *ctx,
TileSheetSpace tss,
int section, int section,
ox::FileAddress tilesheetPath, ox::FileAddress tilesheetPath,
ox::FileAddress palettePath) { ox::FileAddress palettePath) {
@ -129,13 +128,18 @@ ox::Error loadTileSheet(Context *ctx,
SDL_FreePalette(sdlPalette); SDL_FreePalette(sdlPalette);
auto sectionIdx = static_cast<unsigned>(section); auto sectionIdx = static_cast<unsigned>(section);
if (tss == TileSheetSpace::Background) {
if (id->bgTextures[sectionIdx]) { if (id->bgTextures[sectionIdx]) {
SDL_DestroyTexture(id->bgTextures[sectionIdx]); SDL_DestroyTexture(id->bgTextures[sectionIdx]);
} }
id->bgTextures[sectionIdx] = texture; id->bgTextures[sectionIdx] = texture;
return OxError(0);
} }
ox::Error loadSpriteTileSheet(Context*,
int,
ox::FileAddress,
ox::FileAddress) {
return OxError(0); return OxError(0);
} }
@ -196,4 +200,7 @@ void setTile(Context *ctx, int layer, int column, int row, uint8_t tile) {
id->bgTileMaps[z][y][x] = tile; id->bgTileMaps[z][y][x] = tile;
} }
void setSprite(uint8_t, uint8_t, uint8_t, uint8_t) {
}
} }

View File

@ -20,7 +20,11 @@ ox::Error run(ox::FileSystem *fs) {
//Zone zone; //Zone zone;
//oxReturnError(zone.init(&ctx, Bounds{0, 0, 40, 40}, "/TileSheets/Charset.ng", "/Palettes/Charset.npal")); //oxReturnError(zone.init(&ctx, Bounds{0, 0, 40, 40}, "/TileSheets/Charset.ng", "/Palettes/Charset.npal"));
//zone.draw(&ctx); //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)); oxReturnError(core::initConsole(&ctx));
core::setSprite(0, 50, 50, 7);
core::puts(&ctx, 10, 9, "DOPENESS!!!"); core::puts(&ctx, 10, 9, "DOPENESS!!!");
oxReturnError(core::run(&ctx)); oxReturnError(core::run(&ctx));
oxReturnError(core::shutdownGfx(&ctx)); oxReturnError(core::shutdownGfx(&ctx));

View File

@ -17,7 +17,7 @@ ox::Error Zone::init(Context *ctx, Bounds bnds, ox::FileAddress tileSheet, ox::F
const auto size = static_cast<std::size_t>(bnds.width * bnds.height); const auto size = static_cast<std::size_t>(bnds.width * bnds.height);
m_tiles = new Tile[size]; m_tiles = new Tile[size];
m_bounds = bnds; m_bounds = bnds;
return core::loadTileSheet(ctx, core::TileSheetSpace::Background, 0, tileSheet, palette); return core::loadBgTileSheet(ctx, 0, tileSheet, palette);
} }
Zone::~Zone() { Zone::~Zone() {