/* * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include #include #include #include namespace teagba { volatile uint16_t g_spriteUpdates = 0; ox::Array g_spriteBuffer; void addSpriteUpdate(const GbaSpriteAttrUpdate &upd) noexcept { // block until g_spriteUpdates is less than buffer len if (g_spriteUpdates >= g_spriteBuffer.size()) [[unlikely]] { teagba_vblankintrwait(); } const auto ie = REG_IE; // disable vblank interrupt handler REG_IE = REG_IE & static_cast(~teagba::Int_vblank); // disable vblank interrupt handler const auto updateCnt = g_spriteUpdates; g_spriteBuffer[updateCnt] = upd; g_spriteUpdates = updateCnt + 1; REG_IE = ie; // enable vblank interrupt handler } void applySpriteUpdates() noexcept { // 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 unsigned updates = g_spriteUpdates; for (unsigned i = 0; i < updates; ++i) { const auto &oa = g_spriteBuffer[i]; MEM_OAM[oa.idx] = *reinterpret_cast(&oa); } } }