[nostalgia] Break part of core out into Turbine and TeaGBA libraries
This commit is contained in:
@@ -6,27 +6,26 @@
|
||||
#include <ox/mc/mc.hpp>
|
||||
#include <ox/std/array.hpp>
|
||||
|
||||
#include <teagba/addresses.hpp>
|
||||
#include <teagba/gfx.hpp>
|
||||
#include <teagba/registers.hpp>
|
||||
|
||||
#include <keel/media.hpp>
|
||||
#include <turbine/turbine.hpp>
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <nostalgia/core/gfx.hpp>
|
||||
|
||||
#include "addresses.hpp"
|
||||
#include "bios.hpp"
|
||||
#include "gfx.hpp"
|
||||
#include "irq.hpp"
|
||||
#include "registers.hpp"
|
||||
|
||||
namespace nostalgia::core {
|
||||
|
||||
struct BgCbbData {
|
||||
unsigned bpp = 4;
|
||||
};
|
||||
static ox::Array<BgCbbData, 4> g_cbbData;
|
||||
|
||||
constexpr auto GbaTileColumns = 32;
|
||||
constexpr auto GbaTileRows = 32;
|
||||
|
||||
constexpr uint16_t DispStat_irq_vblank = 1 << 3;
|
||||
constexpr uint16_t DispStat_irq_hblank = 1 << 4;
|
||||
constexpr uint16_t DispStat_irq_vcount = 1 << 5;
|
||||
|
||||
struct GbaPaletteTarget {
|
||||
static constexpr auto TypeName = Palette::TypeName;
|
||||
static constexpr auto TypeVersion = Palette::TypeVersion;
|
||||
@@ -68,40 +67,14 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaTileMapTarget> auto *t)
|
||||
return io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
|
||||
}
|
||||
|
||||
ox::Error initGfx(Context*) noexcept {
|
||||
REG_DISPCTL = DispCtl_Mode0
|
||||
| DispCtl_SpriteMap1D
|
||||
| DispCtl_Obj;
|
||||
// tell display to trigger vblank interrupts
|
||||
REG_DISPSTAT = REG_DISPSTAT | DispStat_irq_vblank;
|
||||
// enable vblank interrupt
|
||||
REG_IE = REG_IE | Int_vblank;
|
||||
ox::Error initGfx(Context*, const InitParams&) noexcept {
|
||||
for (auto bgCtl = ®_BG0CTL; bgCtl <= ®_BG3CTL; bgCtl += 2) {
|
||||
bgSetSbb(bgCtl, 28);
|
||||
teagba::bgSetSbb(bgCtl, 28);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error shutdownGfx(Context*) noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
void setWindowTitle(Context*, ox::CRStringView) noexcept {
|
||||
}
|
||||
|
||||
void focusWindow(Context*) noexcept {
|
||||
}
|
||||
|
||||
int getScreenWidth(Context*) noexcept {
|
||||
return 240;
|
||||
}
|
||||
|
||||
int getScreenHeight(Context*) noexcept {
|
||||
return 160;
|
||||
}
|
||||
|
||||
ox::Size getScreenSize(Context*) noexcept {
|
||||
return {240, 160};
|
||||
void shutdownGfx(Context*) noexcept {
|
||||
}
|
||||
|
||||
uint8_t bgStatus(Context*) noexcept {
|
||||
@@ -129,30 +102,35 @@ ox::Error initConsole(Context *ctx) noexcept {
|
||||
constexpr ox::FileAddress PaletteAddr("/Palettes/Charset.npal");
|
||||
setBgStatus(ctx, 0b0001);
|
||||
if (!ctx) {
|
||||
ctx = new (ox_alloca(sizeof(Context))) Context();
|
||||
ctx = new(ox_alloca(sizeof(Context))) Context;
|
||||
oxRequire(rom, keel::loadRom());
|
||||
ox::FileStore32 fs(rom, 32 * ox::units::MB);
|
||||
auto romFs = new (ox_alloca(sizeof(ox::FileSystem32))) ox::FileSystem32(fs);
|
||||
new (&ctx->rom) ox::UniquePtr<ox::FileSystem>(romFs);
|
||||
auto romFs = new(ox_alloca(sizeof(ox::FileSystem32))) ox::FileSystem32(fs);
|
||||
oxRequireM(tctx, turbine::init(ox::UPtr<ox::FileSystem>(romFs)));
|
||||
ctx->turbineCtx = tctx.release();
|
||||
oxReturnError(loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr));
|
||||
setBgCbb(ctx, 0, 0);
|
||||
return {};
|
||||
} else {
|
||||
oxReturnError(loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr));
|
||||
setBgCbb(ctx, 0, 0);
|
||||
return {};
|
||||
}
|
||||
oxReturnError(loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr));
|
||||
setBgCbb(ctx, 0, 0);
|
||||
return {};
|
||||
}
|
||||
|
||||
void setBgCbb(Context*, unsigned bgIdx, unsigned cbb) noexcept {
|
||||
auto &bgCtl = regBgCtl(bgIdx);
|
||||
const auto &cbbData = g_cbbData[cbb];
|
||||
bgSetBpp(&bgCtl, cbbData.bpp);
|
||||
bgSetCbb(&bgCtl, cbb);
|
||||
teagba::bgSetBpp(&bgCtl, cbbData.bpp);
|
||||
teagba::bgSetCbb(&bgCtl, cbb);
|
||||
}
|
||||
|
||||
ox::Error loadBgTileSheet(Context *ctx,
|
||||
unsigned cbb,
|
||||
const ox::FileAddress &tilesheetAddr,
|
||||
const ox::FileAddress &paletteAddr) noexcept {
|
||||
oxRequire(tsStat, ctx->rom->stat(tilesheetAddr));
|
||||
oxRequire(ts, static_cast<ox::MemFS*>(ctx->rom.get())->directAccess(tilesheetAddr));
|
||||
oxRequire(tsStat, ctx->rom().stat(tilesheetAddr));
|
||||
oxRequire(ts, static_cast<ox::MemFS*>(&ctx->rom())->directAccess(tilesheetAddr));
|
||||
GbaTileMapTarget target;
|
||||
target.pal.palette = MEM_BG_PALETTE;
|
||||
target.cbbData = &g_cbbData[cbb];
|
||||
@@ -160,15 +138,15 @@ ox::Error loadBgTileSheet(Context *ctx,
|
||||
oxReturnError(ox::readMC(ts, tsStat.size, &target));
|
||||
// load external palette if available
|
||||
if (paletteAddr) {
|
||||
oxRequire(palStat, ctx->rom->stat(paletteAddr));
|
||||
oxRequire(pal, static_cast<ox::MemFS*>(ctx->rom.get())->directAccess(paletteAddr));
|
||||
oxRequire(palStat, ctx->rom().stat(paletteAddr));
|
||||
oxRequire(pal, static_cast<ox::MemFS*>(&ctx->rom())->directAccess(paletteAddr));
|
||||
oxReturnError(ox::readMC(pal, palStat.size, &target.pal));
|
||||
}
|
||||
// update bpp of all bgs with the updated cbb
|
||||
const auto bpp = g_cbbData[cbb].bpp;
|
||||
iterateBgCtl([bpp, cbb](auto bgCtl) {
|
||||
if (bgCbb(bgCtl) == cbb) {
|
||||
bgSetBpp(bgCtl, bpp);
|
||||
teagba::iterateBgCtl([bpp, cbb](auto bgCtl) {
|
||||
if (teagba::bgCbb(bgCtl) == cbb) {
|
||||
teagba::bgSetBpp(bgCtl, bpp);
|
||||
}
|
||||
});
|
||||
return {};
|
||||
@@ -177,16 +155,16 @@ ox::Error loadBgTileSheet(Context *ctx,
|
||||
ox::Error loadSpriteTileSheet(Context *ctx,
|
||||
const ox::FileAddress &tilesheetAddr,
|
||||
const ox::FileAddress &paletteAddr) noexcept {
|
||||
oxRequire(tsStat, ctx->rom->stat(tilesheetAddr));
|
||||
oxRequire(ts, static_cast<ox::MemFS*>(ctx->rom.get())->directAccess(tilesheetAddr));
|
||||
oxRequire(tsStat, ctx->rom().stat(tilesheetAddr));
|
||||
oxRequire(ts, static_cast<ox::MemFS*>(&ctx->rom())->directAccess(tilesheetAddr));
|
||||
GbaTileMapTarget target;
|
||||
target.pal.palette = MEM_SPRITE_PALETTE;
|
||||
target.tileMap = MEM_SPRITE_TILES;
|
||||
oxReturnError(ox::readMC(ts, tsStat.size, &target));
|
||||
// load external palette if available
|
||||
if (paletteAddr) {
|
||||
oxRequire(palStat, ctx->rom->stat(paletteAddr));
|
||||
oxRequire(pal, static_cast<ox::MemFS*>(ctx->rom.get())->directAccess(paletteAddr));
|
||||
oxRequire(palStat, ctx->rom().stat(paletteAddr));
|
||||
oxRequire(pal, static_cast<ox::MemFS*>(&ctx->rom())->directAccess(paletteAddr));
|
||||
oxReturnError(ox::readMC(pal, palStat.size, &target.pal));
|
||||
}
|
||||
return {};
|
||||
@@ -195,8 +173,8 @@ ox::Error loadSpriteTileSheet(Context *ctx,
|
||||
ox::Error loadBgPalette(Context *ctx, unsigned, const ox::FileAddress &paletteAddr) noexcept {
|
||||
GbaPaletteTarget target;
|
||||
target.palette = MEM_BG_PALETTE;
|
||||
oxRequire(palStat, ctx->rom->stat(paletteAddr));
|
||||
oxRequire(pal, static_cast<ox::MemFS*>(ctx->rom.get())->directAccess(paletteAddr));
|
||||
oxRequire(palStat, ctx->rom().stat(paletteAddr));
|
||||
oxRequire(pal, static_cast<ox::MemFS*>(&ctx->rom())->directAccess(paletteAddr));
|
||||
oxReturnError(ox::readMC(pal, palStat.size, &target));
|
||||
return {};
|
||||
}
|
||||
@@ -204,13 +182,12 @@ ox::Error loadBgPalette(Context *ctx, unsigned, const ox::FileAddress &paletteAd
|
||||
ox::Error loadSpritePalette(Context *ctx, unsigned cbb, const ox::FileAddress &paletteAddr) noexcept {
|
||||
GbaPaletteTarget target;
|
||||
target.palette = &MEM_SPRITE_PALETTE[cbb];
|
||||
oxRequire(palStat, ctx->rom->stat(paletteAddr));
|
||||
oxRequire(pal, static_cast<ox::MemFS*>(ctx->rom.get())->directAccess(paletteAddr));
|
||||
oxRequire(palStat, ctx->rom().stat(paletteAddr));
|
||||
oxRequire(pal, static_cast<ox::MemFS*>(&ctx->rom())->directAccess(paletteAddr));
|
||||
oxReturnError(ox::readMC(pal, palStat.size, &target));
|
||||
return {};
|
||||
}
|
||||
|
||||
// Do NOT use Context in the GBA version of this function.
|
||||
void puts(Context *ctx, int column, int row, ox::CRStringView str) noexcept {
|
||||
const auto col = static_cast<unsigned>(column);
|
||||
for (auto i = 0u; i < str.bytes(); i++) {
|
||||
@@ -231,25 +208,11 @@ void clearTileLayer(Context*, unsigned bgIdx) noexcept {
|
||||
|
||||
[[maybe_unused]]
|
||||
void hideSprite(Context*, unsigned idx) noexcept {
|
||||
oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow");
|
||||
GbaSpriteAttrUpdate oa;
|
||||
//oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow");
|
||||
teagba::GbaSpriteAttrUpdate oa;
|
||||
oa.attr0 = 2 << 8;
|
||||
oa.idx = idx;
|
||||
// block until g_spriteUpdates is less than buffer len
|
||||
if (g_spriteUpdates >= config::GbaSpriteBufferLen) [[unlikely]] {
|
||||
nostalgia_core_vblankintrwait();
|
||||
}
|
||||
if constexpr(config::GbaEventLoopTimerBased) {
|
||||
REG_IE = REG_IE & ~Int_vblank; // disable vblank interrupt handler
|
||||
g_spriteBuffer[g_spriteUpdates] = oa;
|
||||
REG_IE = REG_IE | Int_vblank; // enable vblank interrupt handler
|
||||
} else {
|
||||
const auto ie = REG_IE; // disable vblank interrupt handler
|
||||
REG_IE = REG_IE & static_cast<uint16_t>(~Int_vblank); // disable vblank interrupt handler
|
||||
g_spriteBuffer[g_spriteUpdates] = oa;
|
||||
REG_IE = ie; // enable vblank interrupt handler
|
||||
}
|
||||
g_spriteUpdates = g_spriteUpdates + 1;
|
||||
teagba::addSpriteUpdate(oa);
|
||||
}
|
||||
|
||||
void setSprite(Context*,
|
||||
@@ -260,8 +223,8 @@ void setSprite(Context*,
|
||||
unsigned spriteShape,
|
||||
unsigned spriteSize,
|
||||
unsigned flipX) noexcept {
|
||||
oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow");
|
||||
GbaSpriteAttrUpdate oa;
|
||||
//oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow");
|
||||
teagba::GbaSpriteAttrUpdate oa;
|
||||
oa.attr0 = static_cast<uint16_t>(y & ox::onMask<uint8_t>(0b111'1111))
|
||||
| (static_cast<uint16_t>(1) << 10) // enable alpha
|
||||
| (static_cast<uint16_t>(spriteShape) << 14);
|
||||
@@ -270,21 +233,7 @@ void setSprite(Context*,
|
||||
| (static_cast<uint16_t>(spriteSize) << 14);
|
||||
oa.attr2 = static_cast<uint16_t>(tileIdx & ox::onMask<uint16_t>(8));
|
||||
oa.idx = idx;
|
||||
// block until g_spriteUpdates is less than buffer len
|
||||
if (g_spriteUpdates >= config::GbaSpriteBufferLen) [[unlikely]] {
|
||||
nostalgia_core_vblankintrwait();
|
||||
}
|
||||
if constexpr(config::GbaEventLoopTimerBased) {
|
||||
REG_IE = REG_IE & ~Int_vblank; // disable vblank interrupt handler
|
||||
g_spriteBuffer[g_spriteUpdates] = oa;
|
||||
REG_IE = REG_IE | Int_vblank; // enable vblank interrupt handler
|
||||
} else {
|
||||
const auto ie = REG_IE; // disable vblank interrupt handler
|
||||
REG_IE = REG_IE & static_cast<uint16_t>(~Int_vblank); // disable vblank interrupt handler
|
||||
g_spriteBuffer[g_spriteUpdates] = oa;
|
||||
REG_IE = ie; // enable vblank interrupt handler
|
||||
}
|
||||
g_spriteUpdates = g_spriteUpdates + 1;
|
||||
teagba::addSpriteUpdate(oa);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user