[nostalgia/core/gba] Add sprite support
This commit is contained in:
parent
b32734d727
commit
7e565a3162
@ -23,6 +23,7 @@ endif()
|
||||
|
||||
install(
|
||||
FILES
|
||||
config.hpp
|
||||
consts.hpp
|
||||
context.hpp
|
||||
core.hpp
|
||||
|
15
src/nostalgia/core/config.hpp
Normal file
15
src/nostalgia/core/config.hpp
Normal 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;
|
||||
|
||||
}
|
@ -50,13 +50,20 @@ using interrupt_handler = void (*)(void);
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Memory Addresses
|
||||
|
||||
#define MEM_PALETTE_BG reinterpret_cast<uint16_t*>(0x05000000)
|
||||
#define MEM_PALETTE_SPRITE reinterpret_cast<uint16_t*>(0x05000200)
|
||||
#define MEM_EWRAM_BEGIN reinterpret_cast<uint8_t*>(0x02000000)
|
||||
#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];
|
||||
#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_WRAM_BEGIN reinterpret_cast<uint8_t*>(0x02000000)
|
||||
#define MEM_WRAM_END reinterpret_cast<uint8_t*>(0x0203FFFF)
|
||||
|
@ -14,14 +14,12 @@
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
|
||||
#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<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));
|
||||
// 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<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);
|
||||
}
|
||||
|
||||
// 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<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
|
||||
}
|
||||
|
||||
}
|
||||
|
29
src/nostalgia/core/gba/gfx.hpp
Normal file
29
src/nostalgia/core/gba/gfx.hpp
Normal 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];
|
||||
|
||||
}
|
@ -9,10 +9,35 @@
|
||||
// NOTE: this file is compiled as ARM and not THUMB, so don't but too much in
|
||||
// here
|
||||
|
||||
#include <nostalgia/core/config.hpp>
|
||||
|
||||
#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<uint64_t*>(&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() {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -97,7 +97,6 @@ isr:
|
||||
// Interrupt Table End //
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
|
||||
isr_end:
|
||||
// restore lr from before the Interrupt Table
|
||||
pop {lr}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -22,8 +22,7 @@ ox::Error initConsole(Context*) {
|
||||
return OxError(1);
|
||||
}
|
||||
|
||||
ox::Error loadTileSheet(Context*,
|
||||
TileSheetSpace,
|
||||
ox::Error loadBgTileSheet(Context*,
|
||||
int,
|
||||
ox::FileAddress,
|
||||
ox::FileAddress) {
|
||||
|
@ -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,8 +93,7 @@ SDL_Palette *createSDL_Palette(const NostalgiaPalette &npal) {
|
||||
return pal;
|
||||
}
|
||||
|
||||
ox::Error loadTileSheet(Context *ctx,
|
||||
TileSheetSpace tss,
|
||||
ox::Error loadBgTileSheet(Context *ctx,
|
||||
int section,
|
||||
ox::FileAddress tilesheetPath,
|
||||
ox::FileAddress palettePath) {
|
||||
@ -129,13 +128,18 @@ ox::Error loadTileSheet(Context *ctx,
|
||||
SDL_FreePalette(sdlPalette);
|
||||
|
||||
auto sectionIdx = static_cast<unsigned>(section);
|
||||
if (tss == TileSheetSpace::Background) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
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() {
|
||||
|
Loading…
Reference in New Issue
Block a user