90 lines
2.4 KiB
C++

/*
* Copyright 2016 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <teagba/addresses.hpp>
#include <teagba/irq.hpp>
#include <keel/keel.hpp>
#include <turbine/context.hpp>
#include <turbine/gfx.hpp>
#include <turbine/turbine.hpp>
#include "context.hpp"
#include "turbine.hpp"
extern "C" void turbine_isr();
namespace turbine {
// Timer Consts
constexpr int NanoSecond = 1'000'000'000;
constexpr int MilliSecond = 1000;
constexpr int TicksMs59ns =
65535 - static_cast<uint16_t>(static_cast<double>(NanoSecond / MilliSecond) / 59.59);
extern volatile gba_timer_t g_timerMs;
static void initIrq() noexcept {
REG_ISR = turbine_isr;
REG_IME = 1; // enable interrupts
}
static void initTimer() noexcept {
// make timer0 a ~1 millisecond timer
REG_TIMER0 = TicksMs59ns;
REG_TIMER0CTL = 0b11000000;
// enable interrupt for timer0
REG_IE = REG_IE | teagba::Int_timer0;
}
[[maybe_unused]]
static ox::Result<std::size_t> findPreloadSection() noexcept {
// put the header in the wrong order to prevent mistaking this code for the
// media section
constexpr auto headerP2 = "DER_____________";
constexpr auto headerP1 = "KEEL_PRELOAD_HEA";
constexpr auto headerP1Len = ox::strlen(headerP2);
constexpr auto headerP2Len = ox::strlen(headerP1);
constexpr auto headerLen = headerP1Len + headerP2Len;
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
for (auto current = MEM_ROM; current < reinterpret_cast<char*>(0x0a000000); current += headerLen) {
if (memcmp(current, headerP1, headerP1Len) == 0 &&
memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) {
return reinterpret_cast<std::size_t>(current + headerLen);
}
}
OX_ALLOW_UNSAFE_BUFFERS_END
return OxError(1);
}
ox::Result<ContextUPtr> init(
ox::UPtr<ox::FileSystem> &&fs, ox::StringViewCR appName) noexcept {
auto ctx = ox::make_unique<Context>();
oxReturnError(keel::init(ctx->keelCtx, std::move(fs), appName));
#ifdef OX_BARE_METAL
oxReturnError(findPreloadSection().moveTo(ctx->keelCtx.preloadSectionOffset));
#endif
oxReturnError(initGfx(*ctx));
initTimer();
initIrq();
return ox::UPtr<turbine::Context, ContextDeleter>(std::move(ctx));
}
void shutdown(Context&) noexcept {
}
TimeMs ticksMs(Context const&) noexcept {
return g_timerMs;
}
bool buttonDown(Context const&, Key k) noexcept {
return k <= Key::GamePad_L && !(REG_GAMEPAD & (1 << static_cast<int>(k)));
}
void requestShutdown(Context &ctx) noexcept {
ctx.running = false;
}
}