/* * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. */ #include #include #include #include #include "context.hpp" #include "turbine.hpp" extern "C" void turbine_isr(); namespace turbine { // Timer Consts constexpr int NanoSecond = 1000000000; constexpr int MilliSecond = 1000; constexpr int TicksMs59ns = 65535 - (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; } static ox::Result 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; for (auto current = MEM_ROM; current < reinterpret_cast(0x0a000000); current += headerLen) { if (ox_memcmp(current, headerP1, headerP1Len) == 0 && ox_memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) { return reinterpret_cast(current + headerLen); } } return OxError(1); } ox::Result> init(ox::UPtr fs, ox::CRStringView appName) noexcept { oxRequireM(ctx, keel::init(std::move(fs), appName)); oxReturnError(findPreloadSection().moveTo(&ctx->preloadSectionOffset)); oxReturnError(initGfx(*ctx)); initTimer(); initIrq(); return ox::UPtr(std::move(ctx)); } void shutdown(Context&) noexcept { } uint64_t ticksMs(Context&) noexcept { return g_timerMs; } bool buttonDown(Context&, Key k) noexcept { return k <= Key::GamePad_L && !(REG_GAMEPAD & (1 << static_cast(k))); } void requestShutdown(Context &ctx) noexcept { const auto gbaCtx = static_cast(&ctx); gbaCtx->running = false; } }