[nostalgia/core] Add ability to set a BG's CBB
This commit is contained in:
		| @@ -38,6 +38,10 @@ class ClipboardObject: public BaseClipboardObject { | ||||
|  | ||||
| class Drawer; | ||||
|  | ||||
| struct BgCbbData { | ||||
| 	unsigned bpp = 4; | ||||
| }; | ||||
|  | ||||
| // User Input Output | ||||
| class Context { | ||||
| 	friend constexpr void setApplicationData(Context *ctx, void *applicationData) noexcept; | ||||
| @@ -51,7 +55,7 @@ class Context { | ||||
| 	friend int getScreenWidth(Context *ctx) noexcept; | ||||
| 	friend ox::Error initGfx(Context *ctx) noexcept; | ||||
| 	friend ox::Error loadBgTileSheet(Context *ctx, | ||||
| 	                                 int section, | ||||
| 	                                 unsigned cbb, | ||||
| 	                                 const ox::FileAddress &tilesheetPath, | ||||
| 	                                 const ox::FileAddress &palettePath) noexcept; | ||||
| 	friend ox::Error run(Context *ctx) noexcept; | ||||
| @@ -60,16 +64,17 @@ class Context { | ||||
| 	friend ox::String getClipboardText(Context *ctx) noexcept; | ||||
| 	friend uint64_t ticksMs(Context *ctx) noexcept; | ||||
| 	friend uint8_t bgStatus(Context *ctx) noexcept; | ||||
| 	friend void clearTileLayer(Context *ctx, int layer) noexcept; | ||||
| 	friend void clearTileLayer(Context *ctx, unsigned bgIdx) noexcept; | ||||
| 	friend void draw(Context *ctx) noexcept; | ||||
| 	friend void focusWindow(Context *ctx) noexcept; | ||||
| 	friend void setBgCbb(Context *ctx, unsigned bgIdx, unsigned cbb) noexcept; | ||||
| 	friend void setBgStatus(Context *ctx, uint32_t status) noexcept; | ||||
| 	friend void setBgStatus(Context *ctx, unsigned bg, bool status) noexcept; | ||||
| 	friend void setClipboardText(Context *ctx, const ox::String &text) noexcept; | ||||
| 	friend void setUpdateHandler(Context *ctx, UpdateHandler h) noexcept; | ||||
| 	friend constexpr void setKeyEventHandler(Context *ctx, KeyEventHandler h) noexcept; | ||||
| 	friend constexpr KeyEventHandler keyEventHandler(Context *ctx) noexcept; | ||||
| 	friend void setTile(Context *ctx, int layer, int column, int row, uint8_t tile) noexcept; | ||||
| 	friend void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept; | ||||
| 	friend void setWindowTitle(Context *ctx, const char *title) noexcept; | ||||
|  | ||||
| 	public: | ||||
|   | ||||
| @@ -4,87 +4,104 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <ox/std/array.hpp> | ||||
| #include <ox/std/types.hpp> | ||||
|  | ||||
| ///////////////////////////////////////////////////////////////// | ||||
| // Interrupt Handler | ||||
|  | ||||
| using interrupt_handler = void (*)(); | ||||
| #define REG_ISR *reinterpret_cast<interrupt_handler*>(0x03007FFC) | ||||
| #define REG_IE  *reinterpret_cast<volatile uint16_t*>(0x04000200) | ||||
| #define REG_IF  *reinterpret_cast<volatile uint16_t*>(0x04000202) | ||||
| #define REG_IME *reinterpret_cast<volatile uint16_t*>(0x04000208) | ||||
| #define REG_ISR *reinterpret_cast<interrupt_handler*>(0x0300'7FFC) | ||||
| #define REG_IE  *reinterpret_cast<volatile uint16_t*>(0x0400'0200) | ||||
| #define REG_IF  *reinterpret_cast<volatile uint16_t*>(0x0400'0202) | ||||
| #define REG_IME *reinterpret_cast<volatile uint16_t*>(0x0400'0208) | ||||
|  | ||||
| ///////////////////////////////////////////////////////////////// | ||||
| // Display Registers | ||||
|  | ||||
| #define REG_DISPCTL  *reinterpret_cast<volatile uint32_t*>(0x04000000) | ||||
| #define REG_DISPSTAT *reinterpret_cast<volatile uint32_t*>(0x04000004) | ||||
| #define REG_VCOUNT   *reinterpret_cast<volatile uint32_t*>(0x04000006) | ||||
| #define REG_DISPCTL  *reinterpret_cast<volatile uint32_t*>(0x0400'0000) | ||||
| #define REG_DISPSTAT *reinterpret_cast<volatile uint32_t*>(0x0400'0004) | ||||
| #define REG_VCOUNT   *reinterpret_cast<volatile uint32_t*>(0x0400'0006) | ||||
|  | ||||
| ///////////////////////////////////////////////////////////////// | ||||
| // Timers | ||||
|  | ||||
| #define REG_TIMER0    *reinterpret_cast<volatile uint16_t*>(0x04000100) | ||||
| #define REG_TIMER0CTL *reinterpret_cast<volatile uint16_t*>(0x04000102) | ||||
| #define REG_TIMER0    *reinterpret_cast<volatile uint16_t*>(0x0400'0100) | ||||
| #define REG_TIMER0CTL *reinterpret_cast<volatile uint16_t*>(0x0400'0102) | ||||
|  | ||||
| #define REG_TIMER1    *reinterpret_cast<volatile uint16_t*>(0x04000104) | ||||
| #define REG_TIMER1CTL *reinterpret_cast<volatile uint16_t*>(0x04000106) | ||||
| #define REG_TIMER1    *reinterpret_cast<volatile uint16_t*>(0x0400'0104) | ||||
| #define REG_TIMER1CTL *reinterpret_cast<volatile uint16_t*>(0x0400'0106) | ||||
|  | ||||
| #define REG_TIMER2    *reinterpret_cast<volatile uint16_t*>(0x04000108) | ||||
| #define REG_TIMER2CTL *reinterpret_cast<volatile uint16_t*>(0x0400010a) | ||||
| #define REG_TIMER2    *reinterpret_cast<volatile uint16_t*>(0x0400'0108) | ||||
| #define REG_TIMER2CTL *reinterpret_cast<volatile uint16_t*>(0x0400'010a) | ||||
|  | ||||
| #define REG_TIMER3    *reinterpret_cast<volatile uint16_t*>(0x0400010c) | ||||
| #define REG_TIMER3CTL *reinterpret_cast<volatile uint16_t*>(0x0400010e) | ||||
| #define REG_TIMER3    *reinterpret_cast<volatile uint16_t*>(0x0400'010c) | ||||
| #define REG_TIMER3CTL *reinterpret_cast<volatile uint16_t*>(0x0400'010e) | ||||
|  | ||||
| ///////////////////////////////////////////////////////////////// | ||||
| // background registers | ||||
|  | ||||
| // background control registers | ||||
| #define REG_BG0CTL *reinterpret_cast<volatile uint16_t*>(0x04000008) | ||||
| #define REG_BG1CTL *reinterpret_cast<volatile uint16_t*>(0x0400000a) | ||||
| #define REG_BG2CTL *reinterpret_cast<volatile uint16_t*>(0x0400000c) | ||||
| #define REG_BG3CTL *reinterpret_cast<volatile uint16_t*>(0x0400000e) | ||||
| using BgCtl = uint16_t; | ||||
| #define REG_BG0CTL *reinterpret_cast<volatile BgCtl*>(0x0400'0008) | ||||
| #define REG_BG1CTL *reinterpret_cast<volatile BgCtl*>(0x0400'000a) | ||||
| #define REG_BG2CTL *reinterpret_cast<volatile BgCtl*>(0x0400'000c) | ||||
| #define REG_BG3CTL *reinterpret_cast<volatile BgCtl*>(0x0400'000e) | ||||
|  | ||||
| [[nodiscard]] | ||||
| inline auto ®BgCtl(auto bgIdx) noexcept { | ||||
| 	return *reinterpret_cast<volatile BgCtl*>(0x0400'0008 + 2 * bgIdx); | ||||
| } | ||||
|  | ||||
| // background horizontal scrolling registers | ||||
| #define REG_BG0HOFS *reinterpret_cast<volatile uint32_t*>(0x04000010) | ||||
| #define REG_BG1HOFS *reinterpret_cast<volatile uint32_t*>(0x04000014) | ||||
| #define REG_BG2HOFS *reinterpret_cast<volatile uint32_t*>(0x04000018) | ||||
| #define REG_BG3HOFS *reinterpret_cast<volatile uint32_t*>(0x0400001c) | ||||
| #define REG_BG0HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0010) | ||||
| #define REG_BG1HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0014) | ||||
| #define REG_BG2HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0018) | ||||
| #define REG_BG3HOFS *reinterpret_cast<volatile uint32_t*>(0x0400'001c) | ||||
|  | ||||
| [[nodiscard]] | ||||
| inline volatile auto ®BgHofs(auto bgIdx) noexcept { | ||||
| 	return *reinterpret_cast<volatile uint32_t*>(0x0400'0010 + 4 * bgIdx); | ||||
| } | ||||
|  | ||||
| // background vertical scrolling registers | ||||
| #define REG_BG0VOFS *reinterpret_cast<volatile uint32_t*>(0x04000012) | ||||
| #define REG_BG1VOFS *reinterpret_cast<volatile uint32_t*>(0x04000016) | ||||
| #define REG_BG2VOFS *reinterpret_cast<volatile uint32_t*>(0x0400001a) | ||||
| #define REG_BG3VOFS *reinterpret_cast<volatile uint32_t*>(0x0400001e) | ||||
| #define REG_BG0VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0012) | ||||
| #define REG_BG1VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'0016) | ||||
| #define REG_BG2VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'001a) | ||||
| #define REG_BG3VOFS *reinterpret_cast<volatile uint32_t*>(0x0400'001e) | ||||
|  | ||||
| [[nodiscard]] | ||||
| inline volatile auto ®BgVofs(auto bgIdx) noexcept { | ||||
| 	return *reinterpret_cast<volatile uint32_t*>(0x0400'0012 + 4 * bgIdx); | ||||
| } | ||||
|  | ||||
| ///////////////////////////////////////////////////////////////// | ||||
| // User Input | ||||
|  | ||||
| #define REG_GAMEPAD *reinterpret_cast<volatile uint16_t*>(0x04000130) | ||||
| #define REG_GAMEPAD *reinterpret_cast<volatile uint16_t*>(0x0400'0130) | ||||
|  | ||||
| ///////////////////////////////////////////////////////////////// | ||||
| // Memory Addresses | ||||
|  | ||||
| #define MEM_EWRAM_BEGIN reinterpret_cast<uint8_t*>(0x02000000) | ||||
| #define MEM_EWRAM_END   reinterpret_cast<uint8_t*>(0x0203FFFF) | ||||
| #define MEM_EWRAM_BEGIN reinterpret_cast<uint8_t*>(0x0200'0000) | ||||
| #define MEM_EWRAM_END   reinterpret_cast<uint8_t*>(0x0203'FFFF) | ||||
|  | ||||
| #define MEM_IWRAM_BEGIN reinterpret_cast<uint8_t*>(0x03000000) | ||||
| #define MEM_IWRAM_END   reinterpret_cast<uint8_t*>(0x03007FFF) | ||||
| #define MEM_IWRAM_BEGIN reinterpret_cast<uint8_t*>(0x0300'0000) | ||||
| #define MEM_IWRAM_END   reinterpret_cast<uint8_t*>(0x0300'7FFF) | ||||
|  | ||||
| #define REG_BLNDCTL *reinterpret_cast<uint16_t*>(0x04000050) | ||||
| #define REG_BLNDCTL *reinterpret_cast<uint16_t*>(0x0400'0050) | ||||
|  | ||||
| #define MEM_BG_PALETTE reinterpret_cast<uint16_t*>(0x05000000) | ||||
| #define MEM_SPRITE_PALETTE reinterpret_cast<uint16_t*>(0x05000200) | ||||
| #define MEM_BG_PALETTE reinterpret_cast<uint16_t*>(0x0500'0000) | ||||
| #define MEM_SPRITE_PALETTE reinterpret_cast<uint16_t*>(0x0500'0200) | ||||
|  | ||||
| typedef uint16_t BgMapTile[1024]; | ||||
| #define MEM_BG_TILES reinterpret_cast<BgMapTile*>(0x06000000) | ||||
| #define MEM_BG_MAP reinterpret_cast<BgMapTile*>(0x0600e000) | ||||
| using BgMapTile = ox::Array<uint16_t, 8192>; | ||||
| #define MEM_BG_TILES reinterpret_cast<BgMapTile*>(0x0600'0000) | ||||
| #define MEM_BG_MAP reinterpret_cast<BgMapTile*>(0x0600'e000) | ||||
|  | ||||
| #define MEM_SPRITE_TILES reinterpret_cast<uint16_t*>(0x06010000) | ||||
| #define MEM_OAM reinterpret_cast<uint64_t*>(0x07000000) | ||||
| #define MEM_SPRITE_TILES reinterpret_cast<uint16_t*>(0x0601'0000) | ||||
| #define MEM_OAM reinterpret_cast<uint64_t*>(0x0700'0000) | ||||
|  | ||||
| #define MEM_ROM reinterpret_cast<char*>(0x08000000) | ||||
| #define MEM_ROM reinterpret_cast<char*>(0x0800'0000) | ||||
|  | ||||
| #define MEM_SRAM reinterpret_cast<char*>(0x0e000000) | ||||
| #define MEM_SRAM reinterpret_cast<char*>(0x0e00'0000) | ||||
| #define MEM_SRAM_SIZE 65535 | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
|  | ||||
| #include <ox/fs/fs.hpp> | ||||
| #include <ox/mc/mc.hpp> | ||||
| #include <ox/std/array.hpp> | ||||
|  | ||||
| #include <nostalgia/core/media.hpp> | ||||
| #include <nostalgia/core/gfx.hpp> | ||||
| @@ -12,9 +13,12 @@ | ||||
| #include "bios.hpp" | ||||
| #include "gfx.hpp" | ||||
| #include "irq.hpp" | ||||
| #include "registers.hpp" | ||||
|  | ||||
| namespace nostalgia::core { | ||||
|  | ||||
| ox::Array<BgCbbData, 4> g_cbbData; | ||||
|  | ||||
| constexpr auto GbaTileColumns = 32; | ||||
| constexpr auto GbaTileRows = 32; | ||||
|  | ||||
| @@ -31,7 +35,7 @@ struct GbaPaletteTarget { | ||||
| struct GbaTileMapTarget { | ||||
| 	static constexpr auto TypeName = CompactTileSheet::TypeName; | ||||
| 	static constexpr auto TypeVersion = CompactTileSheet::TypeVersion; | ||||
| 	volatile uint16_t *bgCtl = nullptr; | ||||
| 	BgCbbData *cbbData = nullptr; | ||||
| 	ox::FileAddress defaultPalette; | ||||
| 	GbaPaletteTarget pal; | ||||
| 	volatile uint16_t *tileMap = nullptr; | ||||
| @@ -39,7 +43,7 @@ struct GbaTileMapTarget { | ||||
|  | ||||
| constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaPaletteTarget> auto *t) noexcept { | ||||
| 	io->template setTypeInfo<Palette>(); | ||||
| 	const auto colorHandler = [t](std::size_t i, Color16 *c) { | ||||
| 	const auto colorHandler = [t](std::size_t i, const Color16 *c) { | ||||
| 		t->palette[i] = *c; | ||||
| 		return OxError(0); | ||||
| 	}; | ||||
| @@ -48,20 +52,10 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaPaletteTarget> auto *t) | ||||
|  | ||||
| constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaTileMapTarget> auto *t) noexcept { | ||||
| 	io->template setTypeInfo<CompactTileSheet>(); | ||||
| 	uint8_t bpp; | ||||
| 	oxReturnError(io->field("bpp", &bpp)); | ||||
| 	constexpr auto Bpp8 = 1 << 7; | ||||
| 	if (t->bgCtl) { | ||||
| 		*t->bgCtl = (28 << 8) | 1; | ||||
| 		if (bpp == 4) { | ||||
| 			*t->bgCtl = *t->bgCtl | ((*t->bgCtl | Bpp8) ^ Bpp8); // set to use 4 bits per pixel | ||||
| 		} else { | ||||
| 			*t->bgCtl = *t->bgCtl | Bpp8; // set to use 8 bits per pixel | ||||
| 		} | ||||
| 	} | ||||
| 	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, uint8_t *tile) { | ||||
| 	const auto handleTileMap = [t, &intermediate](std::size_t i, const uint8_t *tile) { | ||||
| 		if (i & 1) { // i is odd | ||||
| 			intermediate |= static_cast<uint16_t>(*tile) << 8; | ||||
| 			t->tileMap[i / 2] = intermediate; | ||||
| @@ -81,11 +75,14 @@ ox::Error initGfx(Context*) noexcept { | ||||
| 	REG_DISPSTAT = REG_DISPSTAT | DispStat_irq_vblank; | ||||
| 	// enable vblank interrupt | ||||
| 	REG_IE = REG_IE | Int_vblank; | ||||
| 	return OxError(0); | ||||
| 	for (auto bgCtl = ®_BG0CTL; bgCtl <= ®_BG3CTL; bgCtl += 2) { | ||||
| 		bgSetSbb(bgCtl, 28); | ||||
| 	} | ||||
| 	return {}; | ||||
| } | ||||
|  | ||||
| ox::Error shutdownGfx(Context*) noexcept { | ||||
| 	return OxError(0); | ||||
| 	return {}; | ||||
| } | ||||
|  | ||||
| void setWindowTitle(Context*, const char*) noexcept { | ||||
| @@ -111,8 +108,8 @@ uint8_t bgStatus(Context*) noexcept { | ||||
| } | ||||
|  | ||||
| void setBgStatus(Context*, uint32_t status) noexcept { | ||||
| 	constexpr auto Bg0Status = 8; | ||||
| 	REG_DISPCTL = (REG_DISPCTL & ~0b111100000000u) | status << Bg0Status; | ||||
| 	constexpr auto BgStatus = 8; | ||||
| 	REG_DISPCTL = (REG_DISPCTL & ~0b111100000000u) | status << BgStatus; | ||||
| } | ||||
|  | ||||
| bool bgStatus(Context*, unsigned bg) noexcept { | ||||
| @@ -125,23 +122,6 @@ void setBgStatus(Context*, unsigned bg, bool status) noexcept { | ||||
| 	REG_DISPCTL = REG_DISPCTL | ((REG_DISPCTL & ~mask) | mask); | ||||
| } | ||||
|  | ||||
| [[nodiscard]] | ||||
| constexpr volatile uint16_t &bgCtl(int bg) noexcept { | ||||
| 	switch (bg) { | ||||
| 		case 0: | ||||
| 			return REG_BG0CTL; | ||||
| 		case 1: | ||||
| 			return REG_BG1CTL; | ||||
| 		case 2: | ||||
| 			return REG_BG2CTL; | ||||
| 		case 3: | ||||
| 			return REG_BG3CTL; | ||||
| 		default: | ||||
| 			oxPanic(OxError(1), "Looking up non-existent register"); | ||||
| 			return REG_BG0CTL; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Do NOT rely on Context in the GBA version of this function. | ||||
| ox::Error initConsole(Context *ctx) noexcept { | ||||
| 	constexpr auto TilesheetAddr = "/TileSheets/Charset.ng"; | ||||
| @@ -154,19 +134,28 @@ ox::Error initConsole(Context *ctx) noexcept { | ||||
| 		auto romFs = new (ox_alloca(sizeof(ox::FileSystem32))) ox::FileSystem32(fs); | ||||
| 		new (&ctx->rom) ox::UniquePtr<ox::FileSystem>(romFs); | ||||
| 	} | ||||
| 	return loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr); | ||||
| 	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); | ||||
| } | ||||
|  | ||||
| ox::Error loadBgTileSheet(Context *ctx, | ||||
|                           int section, | ||||
|                           unsigned cbb, | ||||
|                           const ox::FileAddress &tilesheetAddr, | ||||
|                           const ox::FileAddress &paletteAddr) noexcept { | ||||
| 	oxRequire(tsStat, ctx->rom->stat(tilesheetAddr)); | ||||
| 	oxRequire(ts, ctx->rom->directAccess(tilesheetAddr)); | ||||
| 	GbaTileMapTarget target; | ||||
| 	target.pal.palette = &MEM_BG_PALETTE[section]; | ||||
| 	target.bgCtl = &bgCtl(section); | ||||
| 	target.tileMap = &reinterpret_cast<uint16_t*>(MEM_BG_TILES)[section * 512]; | ||||
| 	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) { | ||||
| @@ -174,20 +163,25 @@ ox::Error loadBgTileSheet(Context *ctx, | ||||
| 		oxRequire(pal, ctx->rom->directAccess(paletteAddr)); | ||||
| 		oxReturnError(ox::readMC(pal, palStat.size, &target.pal)); | ||||
| 	} | ||||
| 	return OxError(0); | ||||
| 	// 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); | ||||
| 		} | ||||
| 	}); | ||||
| 	return {}; | ||||
| } | ||||
|  | ||||
| ox::Error loadSpriteTileSheet(Context *ctx, | ||||
|                               int section, | ||||
|                               unsigned cbb, | ||||
|                               const ox::FileAddress &tilesheetAddr, | ||||
|                               const ox::FileAddress &paletteAddr) noexcept { | ||||
| 	oxRequire(tsStat, ctx->rom->stat(tilesheetAddr)); | ||||
| 	oxRequire(ts, ctx->rom->directAccess(tilesheetAddr)); | ||||
| 	GbaTileMapTarget target; | ||||
| 	target.pal.palette = &MEM_SPRITE_PALETTE[section]; | ||||
| 	// Is this needed? Should this be written to an equivalent sprite value? | ||||
| 	// target.bgCtl = &bgCtl(section); | ||||
| 	target.tileMap = &reinterpret_cast<uint16_t*>(MEM_SPRITE_TILES)[section * 512]; | ||||
| 	target.pal.palette = &MEM_SPRITE_PALETTE[cbb]; | ||||
| 	target.tileMap = &reinterpret_cast<uint16_t*>(MEM_SPRITE_TILES)[cbb * 512]; | ||||
| 	oxReturnError(ox::readMC(ts, tsStat.size, &target)); | ||||
| 	// load external palette if available | ||||
| 	if (paletteAddr) { | ||||
| @@ -195,25 +189,25 @@ ox::Error loadSpriteTileSheet(Context *ctx, | ||||
| 		oxRequire(pal, ctx->rom->directAccess(paletteAddr)); | ||||
| 		oxReturnError(ox::readMC(pal, palStat.size, &target.pal)); | ||||
| 	} | ||||
| 	return OxError(0); | ||||
| 	return {}; | ||||
| } | ||||
|  | ||||
| ox::Error loadBgPalette(Context *ctx, int section, const ox::FileAddress &paletteAddr) noexcept { | ||||
| ox::Error loadBgPalette(Context *ctx, unsigned, const ox::FileAddress &paletteAddr) noexcept { | ||||
| 	GbaPaletteTarget target; | ||||
| 	target.palette = &MEM_BG_PALETTE[section]; | ||||
| 	target.palette = MEM_BG_PALETTE; | ||||
| 	oxRequire(palStat, ctx->rom->stat(paletteAddr)); | ||||
| 	oxRequire(pal, ctx->rom->directAccess(paletteAddr)); | ||||
| 	oxReturnError(ox::readMC(pal, palStat.size, &target)); | ||||
| 	return OxError(0); | ||||
| 	return {}; | ||||
| } | ||||
|  | ||||
| ox::Error loadSpritePalette(Context *ctx, int section, const ox::FileAddress &paletteAddr) noexcept { | ||||
| ox::Error loadSpritePalette(Context *ctx, unsigned cbb, const ox::FileAddress &paletteAddr) noexcept { | ||||
| 	GbaPaletteTarget target; | ||||
| 	target.palette = &MEM_SPRITE_PALETTE[section]; | ||||
| 	target.palette = &MEM_SPRITE_PALETTE[cbb]; | ||||
| 	oxRequire(palStat, ctx->rom->stat(paletteAddr)); | ||||
| 	oxRequire(pal, ctx->rom->directAccess(paletteAddr)); | ||||
| 	oxReturnError(ox::readMC(pal, palStat.size, &target)); | ||||
| 	return OxError(0); | ||||
| 	return {}; | ||||
| } | ||||
|  | ||||
| // Do NOT use Context in the GBA version of this function. | ||||
| @@ -223,13 +217,14 @@ void puts(Context *ctx, int column, int row, const char *str) noexcept { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void setTile(Context*, int layer, int column, int row, uint8_t tile) noexcept { | ||||
| 	MEM_BG_MAP[layer][row * GbaTileColumns + column] = tile; | ||||
| void setTile(Context*, unsigned bgIdx, int column, int row, uint8_t tile) noexcept { | ||||
| 	const auto tileIdx = static_cast<std::size_t>(row * GbaTileColumns + column); | ||||
| 	MEM_BG_MAP[bgIdx][tileIdx] = tile; | ||||
| } | ||||
|  | ||||
| // Do NOT use Context in the GBA version of this function. | ||||
| void clearTileLayer(Context*, int layer) noexcept { | ||||
| 	memset(&MEM_BG_MAP[layer], 0, GbaTileRows * GbaTileColumns); | ||||
| void clearTileLayer(Context*, unsigned bgIdx) noexcept { | ||||
| 	memset(MEM_BG_MAP[bgIdx].data(), 0, GbaTileRows * GbaTileColumns); | ||||
| } | ||||
|  | ||||
| [[maybe_unused]] | ||||
|   | ||||
| @@ -38,6 +38,6 @@ struct OX_ALIGN8 GbaSpriteAttrUpdate { | ||||
| }; | ||||
|  | ||||
| extern volatile uint16_t g_spriteUpdates; | ||||
| extern GbaSpriteAttrUpdate g_spriteBuffer[config::GbaSpriteBufferLen]; | ||||
| extern ox::Array<GbaSpriteAttrUpdate, config::GbaSpriteBufferLen> g_spriteBuffer; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -5,8 +5,6 @@ | ||||
| // NOTE: this file is compiled as ARM and not THUMB, so don't but too much in | ||||
| // here | ||||
|  | ||||
| #include <ox/std/bit.hpp> | ||||
|  | ||||
| #include "addresses.hpp" | ||||
| #include "core.hpp" | ||||
| #include "gfx.hpp" | ||||
| @@ -15,7 +13,7 @@ | ||||
| namespace nostalgia::core { | ||||
|  | ||||
| volatile uint16_t g_spriteUpdates = 0; | ||||
| GbaSpriteAttrUpdate g_spriteBuffer[config::GbaSpriteBufferLen]; | ||||
| ox::Array<GbaSpriteAttrUpdate, config::GbaSpriteBufferLen> g_spriteBuffer; | ||||
|  | ||||
| volatile gba_timer_t g_timerMs = 0; | ||||
|  | ||||
|   | ||||
							
								
								
									
										70
									
								
								src/nostalgia/core/gba/registers.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/nostalgia/core/gba/registers.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| /* | ||||
|  * Copyright 2016 - 2022 Gary Talent (gary@drinkingtea.net). All rights reserved. | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "addresses.hpp" | ||||
|  | ||||
| namespace nostalgia::core { | ||||
|  | ||||
| inline auto bgSetSbb(volatile BgCtl *bgCtl, unsigned sbb) noexcept { | ||||
| 	*bgCtl = (*bgCtl & ~0b11111'0000'0000u) | (sbb << 8); | ||||
| } | ||||
|  | ||||
| [[nodiscard]] | ||||
| constexpr unsigned bgPri(BgCtl bgCtl) noexcept { | ||||
| 	return bgCtl & 1; | ||||
| } | ||||
|  | ||||
| [[nodiscard]] | ||||
| inline auto bgPri(const volatile BgCtl *bgCtl) noexcept { | ||||
| 	return bgPri(*bgCtl); | ||||
| } | ||||
|  | ||||
| inline auto bgSetPri(volatile BgCtl *bgCtl, unsigned pri) noexcept { | ||||
| 	pri = pri & 0b1; | ||||
| 	*bgCtl = (*bgCtl & ~0b1u) | (pri << 0); | ||||
| } | ||||
|  | ||||
| [[nodiscard]] | ||||
| constexpr unsigned bgBpp(BgCtl bgCtl) noexcept { | ||||
| 	return ((bgCtl >> 7) & 1) ? 8 : 4; | ||||
| } | ||||
|  | ||||
| [[nodiscard]] | ||||
| inline auto bgBpp(const volatile BgCtl *bgCtl) noexcept { | ||||
| 	return bgBpp(*bgCtl); | ||||
| } | ||||
|  | ||||
| inline auto bgSetBpp(volatile BgCtl *bgCtl, unsigned bpp) noexcept { | ||||
| 	constexpr auto Bpp8 = 1 << 7; | ||||
| 	if (bpp == 4) { | ||||
| 		*bgCtl = *bgCtl | ((*bgCtl | Bpp8) ^ Bpp8); // set to use 4 bits per pixel | ||||
| 	} else { | ||||
| 		*bgCtl = *bgCtl | Bpp8; // set to use 8 bits per pixel | ||||
| 	} | ||||
| } | ||||
|  | ||||
| [[nodiscard]] | ||||
| constexpr auto bgCbb(BgCtl bgCtl) noexcept { | ||||
| 	return (bgCtl >> 2) & 0b11u; | ||||
| } | ||||
|  | ||||
| [[nodiscard]] | ||||
| inline auto bgCbb(const volatile BgCtl *bgCtl) noexcept { | ||||
| 	return bgCbb(*bgCtl); | ||||
| } | ||||
|  | ||||
| inline auto bgSetCbb(volatile BgCtl *bgCtl, unsigned cbb) noexcept { | ||||
| 	cbb = cbb & 0b11; | ||||
| 	*bgCtl = (*bgCtl & ~0b1100u) | (cbb << 2); | ||||
| } | ||||
|  | ||||
| constexpr void iterateBgCtl(auto cb) noexcept { | ||||
| 	for (auto bgCtl = ®_BG0CTL; bgCtl <= ®_BG3CTL; bgCtl += 2) { | ||||
| 		cb(bgCtl); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| } | ||||
| @@ -497,21 +497,23 @@ void setBgStatus(Context *ctx, unsigned bg, bool status) noexcept; | ||||
|  | ||||
| ox::Error initConsole(Context *ctx) noexcept; | ||||
|  | ||||
| void setBgCbb(Context *ctx, unsigned bgIdx, unsigned cbb) noexcept; | ||||
|  | ||||
| /** | ||||
|  * @param section describes which section of the selected TileSheetSpace to use (e.g. MEM_PALLETE_BG[section]) | ||||
|  */ | ||||
| ox::Error loadBgTileSheet(Context *ctx, int section, const ox::FileAddress &tilesheet, const ox::FileAddress &palette = nullptr) noexcept; | ||||
| ox::Error loadBgTileSheet(Context *ctx, unsigned cbb, const ox::FileAddress &tilesheet, const ox::FileAddress &palette = nullptr) noexcept; | ||||
|  | ||||
| ox::Error loadSpriteTileSheet(Context *ctx, | ||||
|                               int section, | ||||
|                               unsigned section, | ||||
|                               const ox::FileAddress &tilesheetAddr, | ||||
|                               const ox::FileAddress &paletteAddr) noexcept; | ||||
|  | ||||
| void puts(Context *ctx, int column, int row, const char *str) noexcept; | ||||
|  | ||||
| void setTile(Context *ctx, int layer, int column, int row, uint8_t tile) noexcept; | ||||
| void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept; | ||||
|  | ||||
| void clearTileLayer(Context *ctx, int layer) noexcept; | ||||
| void clearTileLayer(Context *ctx, unsigned bgIdx) noexcept; | ||||
|  | ||||
| void hideSprite(Context *ctx, unsigned) noexcept; | ||||
|  | ||||
|   | ||||
| @@ -13,18 +13,19 @@ ox::Error initConsole(Context *ctx) noexcept { | ||||
| 	constexpr auto TilesheetAddr = "/TileSheets/Charset.ng"; | ||||
| 	constexpr auto PaletteAddr = "/Palettes/Charset.npal"; | ||||
| 	setBgStatus(ctx, 0b0001); | ||||
| 	setBgCbb(ctx, 0, 0); | ||||
| 	return loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr); | ||||
| } | ||||
|  | ||||
| ox::Error loadSpriteTileSheet(Context*, | ||||
|                               int, | ||||
|                               unsigned, | ||||
|                               const ox::FileAddress&, | ||||
|                               const ox::FileAddress&) noexcept { | ||||
| 	return OxError(0); | ||||
| } | ||||
|  | ||||
| ox::Error loadBgTileSheet(Context *ctx, | ||||
|                           int section, | ||||
|                           unsigned cbb, | ||||
|                           const ox::FileAddress &tilesheetPath, | ||||
|                           const ox::FileAddress &palettePath) noexcept { | ||||
| 	oxRequire(tilesheet, readObj<CompactTileSheet>(ctx, tilesheetPath)); | ||||
| @@ -46,7 +47,7 @@ ox::Error loadBgTileSheet(Context *ctx, | ||||
| 			pixels[i * 2 + 1] = toColor32(palette->colors[tilesheet->pixels[i] >> 4]); | ||||
| 		} | ||||
| 	} | ||||
| 	renderer::loadBgTexture(ctx->rendererData<void>(), section, pixels.data(), width, height); | ||||
| 	renderer::loadBgTexture(ctx->rendererData<void>(), cbb, pixels.data(), width, height); | ||||
| 	return OxError(0); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,6 @@ ox::Error init(Context *ctx, void **rendererData) noexcept; | ||||
|  | ||||
| void shutdown(Context *ctx, void *rendererData) noexcept; | ||||
|  | ||||
| void loadBgTexture(void *rendererData, int section, void *pixels, int w, int h) noexcept; | ||||
| void loadBgTexture(void *rendererData, unsigned cbb, void *pixels, int w, int h) noexcept; | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -22,25 +22,30 @@ namespace renderer { | ||||
| constexpr auto TileRows = 128; | ||||
| constexpr auto TileColumns = 128; | ||||
| constexpr auto TileCount = TileRows * TileColumns; | ||||
| constexpr auto BgVertexVboRows = 4; | ||||
| constexpr auto BgVertexVboRowLength = 4; | ||||
| constexpr auto BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength; | ||||
| constexpr auto BgVertexEboLength = 6; | ||||
| constexpr unsigned BgVertexVboRows = 4; | ||||
| constexpr unsigned BgVertexVboRowLength = 4; | ||||
| constexpr unsigned BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength; | ||||
| constexpr unsigned BgVertexEboLength = 6; | ||||
|  | ||||
| struct Background: public glutils::BufferSet { | ||||
| 	bool enabled = false; | ||||
| struct CBB: public glutils::BufferSet { | ||||
| 	bool updated = false; | ||||
|  | ||||
| 	 Background() noexcept { | ||||
| 	CBB() noexcept { | ||||
| 		vertices.resize(TileCount * BgVertexVboLength); | ||||
| 		elements.resize(TileCount * BgVertexEboLength); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct Background { | ||||
| 	bool enabled = false; | ||||
| 	unsigned cbbIdx = 0; | ||||
| }; | ||||
|  | ||||
| struct GlImplData { | ||||
| 	glutils::GLProgram bgShader; | ||||
| 	int64_t prevFpsCheckTime = 0; | ||||
| 	uint64_t draws = 0; | ||||
| 	ox::Array<CBB, 4> cbbs; | ||||
| 	ox::Array<Background, 4> backgrounds; | ||||
| }; | ||||
|  | ||||
| @@ -80,18 +85,18 @@ setTileBufferObject(Context *ctx, unsigned vi, float x, float y, int textureRow, | ||||
| 	y *= -ymod; | ||||
| 	x -= 1.0f; | ||||
| 	y += 1.0f - ymod; | ||||
| 	const float vertices[BgVertexVboLength] = { | ||||
| 	const ox::Array<float, BgVertexVboLength> vertices { | ||||
| 		       x,        y, 0, static_cast<float>(textureRow + 1), // bottom left | ||||
| 		x + xmod,        y, 1, static_cast<float>(textureRow + 1), // bottom right | ||||
| 		x + xmod, y + ymod, 1, static_cast<float>(textureRow + 0), // top right | ||||
| 		       x, y + ymod, 0, static_cast<float>(textureRow + 0), // top left | ||||
| 	}; | ||||
| 	memcpy(vbo, vertices, sizeof(vertices)); | ||||
| 	const GLuint elms[BgVertexEboLength] = { | ||||
| 	memcpy(vbo, vertices.data(), sizeof(vertices)); | ||||
| 	const ox::Array<GLuint, BgVertexEboLength> elms { | ||||
| 		vi + 0, vi + 1, vi + 2, | ||||
| 		vi + 2, vi + 3, vi + 0, | ||||
| 	}; | ||||
| 	memcpy(ebo, elms, sizeof(elms)); | ||||
| 	memcpy(ebo, elms.data(), sizeof(elms)); | ||||
| } | ||||
|  | ||||
| static void initBackgroundBufferObjects(Context *ctx, glutils::BufferSet *bg) noexcept { | ||||
| @@ -157,16 +162,14 @@ static void tickFps(GlImplData *id) noexcept { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void drawBackground(Background *bg) noexcept { | ||||
| 	if (bg->enabled) { | ||||
| 		glBindVertexArray(bg->vao); | ||||
| 		if (bg->updated) { | ||||
| 			bg->updated = false; | ||||
| 			glutils::sendVbo(*bg); | ||||
| 		} | ||||
| 		glBindTexture(GL_TEXTURE_2D, bg->tex); | ||||
| 		glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(bg->elements.size()), GL_UNSIGNED_INT, nullptr); | ||||
| static void drawBackground(CBB *cbb) noexcept { | ||||
| 	glBindVertexArray(cbb->vao); | ||||
| 	if (cbb->updated) { | ||||
| 		cbb->updated = false; | ||||
| 		glutils::sendVbo(*cbb); | ||||
| 	} | ||||
| 	glBindTexture(GL_TEXTURE_2D, cbb->tex); | ||||
| 	glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(cbb->elements.size()), GL_UNSIGNED_INT, nullptr); | ||||
| } | ||||
|  | ||||
| static void drawBackgrounds(GlImplData *id) noexcept { | ||||
| @@ -174,8 +177,11 @@ static void drawBackgrounds(GlImplData *id) noexcept { | ||||
| 	glUseProgram(id->bgShader); | ||||
| 	const auto uniformTileHeight = static_cast<GLint>(glGetUniformLocation(id->bgShader, "vTileHeight")); | ||||
| 	for (auto &bg : id->backgrounds) { | ||||
| 		glUniform1f(uniformTileHeight, 1.0f / static_cast<float>(bg.tex.height / 8)); | ||||
| 		drawBackground(&bg); | ||||
| 		if (bg.enabled) { | ||||
| 			auto &cbb = id->cbbs[bg.cbbIdx]; | ||||
| 			glUniform1f(uniformTileHeight, 1.0f / static_cast<float>(cbb.tex.height / 8)); | ||||
| 			drawBackground(&cbb); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -186,7 +192,7 @@ ox::Error init(Context *ctx, void **rendererData) noexcept { | ||||
| 	const auto id = new GlImplData; | ||||
| 	*rendererData = id; | ||||
| 	id->bgShader = std::move(bgShader); | ||||
| 	for (auto &bg : id->backgrounds) { | ||||
| 	for (auto &bg : id->cbbs) { | ||||
| 		initBackgroundBufferset(ctx, id->bgShader, &bg); | ||||
| 	} | ||||
| 	ImGui_ImplOpenGL3_Init(glutils::GlslVersion); | ||||
| @@ -198,19 +204,25 @@ void shutdown(Context*, void *rendererData) noexcept { | ||||
| 	ox::safeDelete(id); | ||||
| } | ||||
|  | ||||
| void loadBgTexture(void *rendererData, int section, void *pixels, int w, int h) noexcept { | ||||
| 	oxTracef("nostalgia::core::gfx::gl", "loadBgTexture: { section: {}, w: {}, h: {} }", section, w, h); | ||||
| void loadBgTexture(void *rendererData, unsigned cbbIdx, void *pixels, int w, int h) noexcept { | ||||
| 	oxTracef("nostalgia::core::gfx::gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbbIdx, w, h); | ||||
| 	const auto id = static_cast<GlImplData*>(rendererData); | ||||
| 	auto &tex = id->backgrounds[static_cast<std::size_t>(section)].tex; | ||||
| 	auto &tex = id->cbbs[cbbIdx].tex; | ||||
| 	tex = loadTexture(w, h, pixels); | ||||
| } | ||||
|  | ||||
| } | ||||
|  | ||||
| void setBgCbb(Context *ctx, unsigned bgIdx, unsigned cbbIdx) noexcept { | ||||
| 	const auto id = ctx->rendererData<renderer::GlImplData>(); | ||||
| 	auto &bg = id->backgrounds[bgIdx]; | ||||
| 	bg.cbbIdx = cbbIdx; | ||||
| } | ||||
|  | ||||
| uint8_t bgStatus(Context *ctx) noexcept { | ||||
| 	const auto id = ctx->rendererData<renderer::GlImplData>(); | ||||
| 	uint8_t out = 0; | ||||
| 	for (unsigned i = 0; i < id->backgrounds.size(); ++i) { | ||||
| 	for (unsigned i = 0; i < id->cbbs.size(); ++i) { | ||||
| 		out |= id->backgrounds[i].enabled << i; | ||||
| 	} | ||||
| 	return out; | ||||
| @@ -218,7 +230,7 @@ uint8_t bgStatus(Context *ctx) noexcept { | ||||
|  | ||||
| void setBgStatus(Context *ctx, uint32_t status) noexcept { | ||||
| 	const auto id = ctx->rendererData<renderer::GlImplData>(); | ||||
| 	for (unsigned i = 0; i < id->backgrounds.size(); ++i) { | ||||
| 	for (unsigned i = 0; i < id->cbbs.size(); ++i) { | ||||
| 		id->backgrounds[i].enabled = (status >> i) & 1; | ||||
| 	} | ||||
| } | ||||
| @@ -256,9 +268,9 @@ void draw(Context *ctx) noexcept { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void clearTileLayer(Context *ctx, int layer) noexcept { | ||||
| void clearTileLayer(Context *ctx, unsigned bgIdx) noexcept { | ||||
| 	const auto id = ctx->rendererData<renderer::GlImplData>(); | ||||
| 	auto &bg = id->backgrounds[static_cast<std::size_t>(layer)]; | ||||
| 	auto &bg = id->cbbs[static_cast<std::size_t>(bgIdx)]; | ||||
| 	initBackgroundBufferObjects(ctx, &bg); | ||||
| 	bg.updated = true; | ||||
| } | ||||
| @@ -276,13 +288,13 @@ void setSprite(Context*, | ||||
|                unsigned) noexcept { | ||||
| } | ||||
|  | ||||
| void setTile(Context *ctx, int layer, int column, int row, uint8_t tile) noexcept { | ||||
| void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept { | ||||
| 	const auto id = ctx->rendererData<renderer::GlImplData>(); | ||||
| 	const auto z = static_cast<unsigned>(layer); | ||||
| 	const auto z = static_cast<unsigned>(bgIdx); | ||||
| 	const auto y = static_cast<unsigned>(row); | ||||
| 	const auto x = static_cast<unsigned>(column); | ||||
| 	const auto i = renderer::bgVertexRow(x, y); | ||||
| 	auto &bg = id->backgrounds[z]; | ||||
| 	auto &bg = id->cbbs[z]; | ||||
| 	auto vbo = &bg.vertices[i * renderer::BgVertexVboLength]; | ||||
| 	auto ebo = &bg.elements[i * renderer::BgVertexEboLength]; | ||||
| 	renderer::setTileBufferObject(ctx, i * renderer::BgVertexVboRows, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user