/* * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include #include #include #include #include #include #include #include #include #include namespace nostalgia::core { struct BgCbbData { unsigned bpp = 4; }; static ox::Array g_cbbData; constexpr auto GbaTileColumns = 32; constexpr auto GbaTileRows = 32; struct GbaPaletteTarget { static constexpr auto TypeName = Palette::TypeName; static constexpr auto TypeVersion = Palette::TypeVersion; volatile uint16_t *palette = nullptr; }; struct GbaTileMapTarget { static constexpr auto TypeName = CompactTileSheet::TypeName; static constexpr auto TypeVersion = CompactTileSheet::TypeVersion; BgCbbData *cbbData = nullptr; ox::FileAddress defaultPalette; GbaPaletteTarget pal; volatile uint16_t *tileMap = nullptr; }; constexpr ox::Error model(auto *io, ox::CommonPtrWith auto *t) noexcept { io->template setTypeInfo(); const auto colorHandler = [t](std::size_t i, const Color16 *c) { t->palette[i] = *c; return OxError(0); }; return io->template field("colors", colorHandler); } constexpr ox::Error model(auto *io, ox::CommonPtrWith auto *t) noexcept { io->template setTypeInfo(); oxReturnError(io->field("bpp", &t->cbbData->bpp)); oxReturnError(io->field("defaultPalette", &t->defaultPalette)); uint16_t intermediate = 0; const auto handleTileMap = [t, &intermediate](std::size_t i, const uint8_t *tile) { if (i & 1) { // i is odd intermediate |= static_cast(*tile) << 8; t->tileMap[i / 2] = intermediate; } else { // i is even intermediate = *tile & 0x00ff; } return OxError(0); }; return io->template field("tileMap", handleTileMap); } ox::Error initGfx(Context*, const InitParams&) noexcept { for (auto bgCtl = ®_BG0CTL; bgCtl <= ®_BG3CTL; bgCtl += 2) { teagba::bgSetSbb(bgCtl, 28); } return {}; } void shutdownGfx(Context*) noexcept { } uint8_t bgStatus(Context*) noexcept { return (REG_DISPCTL >> 8) & 0b1111; } void setBgStatus(Context*, uint32_t status) noexcept { constexpr auto BgStatus = 8; REG_DISPCTL = (REG_DISPCTL & ~0b111100000000u) | status << BgStatus; } bool bgStatus(Context*, unsigned bg) noexcept { return (REG_DISPCTL >> (8 + bg)) & 1; } void setBgStatus(Context*, unsigned bg, bool status) noexcept { constexpr auto Bg0Status = 8; const auto mask = static_cast(status) << (Bg0Status + bg); REG_DISPCTL = REG_DISPCTL | ((REG_DISPCTL & ~mask) | mask); } // Do NOT rely on Context in the GBA version of this function. ox::Error initConsole(Context *ctx) noexcept { constexpr ox::FileAddress TilesheetAddr("/TileSheets/Charset.ng"); constexpr ox::FileAddress PaletteAddr("/Palettes/Charset.npal"); setBgStatus(ctx, 0b0001); if (!ctx) { 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); oxRequireM(tctx, turbine::init(ox::UPtr(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 {}; } } void setBgCbb(Context*, unsigned bgIdx, unsigned cbb) noexcept { auto &bgCtl = regBgCtl(bgIdx); const auto &cbbData = g_cbbData[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(&ctx->rom())->directAccess(tilesheetAddr)); GbaTileMapTarget target; target.pal.palette = MEM_BG_PALETTE; target.cbbData = &g_cbbData[cbb]; target.tileMap = MEM_BG_TILES[cbb].data(); oxReturnError(ox::readMC(ts, tsStat.size, &target)); // load external palette if available if (paletteAddr) { oxRequire(palStat, ctx->rom().stat(paletteAddr)); oxRequire(pal, static_cast(&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; teagba::iterateBgCtl([bpp, cbb](auto bgCtl) { if (teagba::bgCbb(bgCtl) == cbb) { teagba::bgSetBpp(bgCtl, bpp); } }); return {}; } ox::Error loadSpriteTileSheet(Context *ctx, const ox::FileAddress &tilesheetAddr, const ox::FileAddress &paletteAddr) noexcept { oxRequire(tsStat, ctx->rom().stat(tilesheetAddr)); oxRequire(ts, static_cast(&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(&ctx->rom())->directAccess(paletteAddr)); oxReturnError(ox::readMC(pal, palStat.size, &target.pal)); } return {}; } 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(&ctx->rom())->directAccess(paletteAddr)); oxReturnError(ox::readMC(pal, palStat.size, &target)); return {}; } 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(&ctx->rom())->directAccess(paletteAddr)); oxReturnError(ox::readMC(pal, palStat.size, &target)); return {}; } void puts(Context *ctx, int column, int row, ox::CRStringView str) noexcept { const auto col = static_cast(column); for (auto i = 0u; i < str.bytes(); i++) { const auto c = charMap[static_cast(str[i])]; setTile(ctx, 0, static_cast(col + i), row, static_cast(c)); } } void setTile(Context*, unsigned bgIdx, int column, int row, uint8_t tile) noexcept { const auto tileIdx = static_cast(row * GbaTileColumns + column); MEM_BG_MAP[bgIdx][tileIdx] = tile; } // Do NOT use Context in the GBA version of this function. void clearTileLayer(Context*, unsigned bgIdx) noexcept { memset(MEM_BG_MAP[bgIdx].data(), 0, GbaTileRows * GbaTileColumns); } [[maybe_unused]] void hideSprite(Context*, unsigned idx) noexcept { //oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow"); teagba::GbaSpriteAttrUpdate oa; oa.attr0 = 2 << 8; oa.idx = idx; teagba::addSpriteUpdate(oa); } void setSprite(Context*, unsigned idx, int x, int y, unsigned tileIdx, unsigned spriteShape, unsigned spriteSize, unsigned flipX) noexcept { //oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow"); teagba::GbaSpriteAttrUpdate oa; oa.attr0 = static_cast(y & ox::onMask(0b111'1111)) | (static_cast(1) << 10) // enable alpha | (static_cast(spriteShape) << 14); oa.attr1 = (static_cast(x) & ox::onMask(8)) | (static_cast(flipX) << 12) | (static_cast(spriteSize) << 14); oa.attr2 = static_cast(tileIdx & ox::onMask(8)); oa.idx = idx; teagba::addSpriteUpdate(oa); } }