[nostalgia] Break part of core out into Turbine and TeaGBA libraries

This commit is contained in:
2023-06-01 23:22:31 -05:00
parent 07284ac595
commit 8c43baedea
119 changed files with 1918 additions and 1873 deletions

View File

@@ -0,0 +1,16 @@
enable_language(CXX ASM)
set_source_files_properties(turbine.arm.cpp irq.arm.cpp PROPERTIES COMPILE_FLAGS -marm)
target_sources(
Turbine PRIVATE
clipboard.cpp
gfx.cpp
irq.arm.cpp
irq.s
turbine.arm.cpp
turbine.cpp
)
target_link_libraries(
Turbine PUBLIC
TeaGBA
)

View File

@@ -0,0 +1,19 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <ox/std/string.hpp>
#include <turbine/context.hpp>
namespace turbine {
ox::String getClipboardText(Context&) noexcept {
return {};
}
void setClipboardText(Context&, ox::CRStringView) noexcept {
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/fs/fs.hpp>
#include <ox/model/desctypes.hpp>
#include <ox/std/buffer.hpp>
#include <ox/std/size.hpp>
#include <keel/context.hpp>
#include "../context.hpp"
namespace turbine::gba {
class Context: public turbine::Context {
public:
bool running = true;
Context() noexcept = default;
Context(Context &other) noexcept = delete;
Context(const Context &other) noexcept = delete;
Context(const Context &&other) noexcept = delete;
};
}

42
src/turbine/gba/gfx.cpp Normal file
View File

@@ -0,0 +1,42 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <ox/std/size.hpp>
#include <ox/std/stringview.hpp>
#include <teagba/addresses.hpp>
#include <teagba/gfx.hpp>
#include <teagba/irq.hpp>
#include <turbine/context.hpp>
namespace turbine {
ox::Error initGfx(Context&) noexcept {
REG_DISPCTL = teagba::DispCtl_Mode0
| teagba::DispCtl_SpriteMap1D
| teagba::DispCtl_Obj;
// tell display to trigger vblank interrupts
REG_DISPSTAT = REG_DISPSTAT | teagba::DispStat_irq_vblank;
// enable vblank interrupt
REG_IE = REG_IE | teagba::Int_vblank;
return {};
}
void setWindowTitle(Context&, ox::CRStringView) noexcept {
}
int getScreenWidth(Context&) noexcept {
return 240;
}
int getScreenHeight(Context&) noexcept {
return 160;
}
ox::Size getScreenSize(Context&) noexcept {
return {240, 160};
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
// NOTE: this file is compiled as ARM and not THUMB, so don't but too much in
// here
#include <teagba/addresses.hpp>
#include <teagba/gfx.hpp>
#include <teagba/irq.hpp>
#include "turbine.hpp"
namespace turbine {
volatile gba_timer_t g_timerMs = 0;
}
using namespace turbine;
extern "C" {
void turbine_isr_vblank() {
teagba::applySpriteUpdates();
if constexpr(config::GbaEventLoopTimerBased) {
// disable vblank interrupt until it is needed again
REG_IE = REG_IE & ~teagba::Int_vblank;
}
}
void turbine_isr_timer0() {
g_timerMs = g_timerMs + 1;
}
}

110
src/turbine/gba/irq.s Normal file
View File

@@ -0,0 +1,110 @@
//
// Copyright 2016 - 2023 gary@drinkingtea.net
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
.section .iwram, "ax", %progbits
.arm
.align
.extern turbine_isr_vblank
.extern turbine_isr_timer0
.equ REG_IFBIOS, 0x03007ff8
.equ REG_IE, 0x04000200
.equ REG_IF, 0x04000202
.equ REG_IME, 0x04000208
.equ Int_vblank, 1
.equ Int_hblank, 2
.equ Int_vcount, 4
.equ Int_timer0, 8
.equ Int_timer1, 16
.equ Int_timer2, 32
.equ Int_timer3, 64
.equ Int_serial, 128 // link cable
.equ Int_dma0, 256
.equ Int_dma1, 512
.equ Int_dma2, 1024
.equ Int_dma3, 2048
.equ Int_dma4, 4096
.equ Int_dma5, 8192
.equ Int_input, 16384 // gamepad
.equ Int_cart, 32768 // cartridge removed
.global isr
.type isr, %function
isr:
// read IE
ldr r0, =#REG_IE
ldrh r1, [r0] // r1 becomes IE value
ldrh r2, [r0, #2] // r2 becomes IF value
and r1, r1, r2 // r1 becomes IE & IF
// done with r2 as IF value
// Acknowledge IRQ in REG_IF
strh r1, [r0, #2]
ldr r0, =#REG_IFBIOS
// Acknowledge IRQ in REG_IFBIOS
ldr r2, [r0] // r2 becomes REG_IFBIOS value
orr r2, r2, r1
str r2, [r0]
// done with r2 as IFBIOS value
// done with r0 as REG_IFBIOS
////////////////////////////////////////////////////
// Interrupt Table Begin //
////////////////////////////////////////////////////
cmp r1, #Int_timer0
ldreq r0, =turbine_isr_timer0
beq isr_call_handler
cmp r1, #Int_vblank
ldreq r0, =turbine_isr_vblank
beq isr_call_handler
////////////////////////////////////////////////////
// Interrupt Table End //
////////////////////////////////////////////////////
bx lr
isr_call_handler:
// clear IME to disable interrupts
ldr r2, =#REG_IME
mov r1, #0
str r1, [r2]
// enter system mode
mrs r1, cpsr
bic r1, r1, #0xDF
orr r1, r1, #0x1F
msr cpsr, r1
push {lr}
ldr lr, =isr_restore
bx r0
isr_restore:
pop {lr}
// re-enter irq mode
mrs r0, cpsr
bic r0, r0, #0xDF
orr r0, r0, #0x92
msr cpsr, r0
// set IME to re-enable interrupts
ldr r2, =#REG_IME
mov r0, #1
str r0, [r2]
bx lr
// vim: ft=armv4

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <teagba/addresses.hpp>
#include <teagba/bios.hpp>
#include <teagba/irq.hpp>
#include "context.hpp"
#include "turbine.hpp"
namespace turbine {
extern volatile gba_timer_t g_timerMs;
gba_timer_t g_wakeupTime;
ox::Error run(Context &ctx) noexcept {
g_wakeupTime = 0;
const auto gbaCtx = static_cast<gba::Context*>(&ctx);
while (gbaCtx->running) {
if (g_wakeupTime <= g_timerMs && ctx.updateHandler) {
auto sleepTime = ctx.updateHandler(ctx);
if (sleepTime >= 0) {
g_wakeupTime = g_timerMs + static_cast<unsigned>(sleepTime);
} else {
g_wakeupTime = ~gba_timer_t(0);
}
}
if constexpr(config::GbaEventLoopTimerBased) {
// wait for timer interrupt
teagba_intrwait(
0,
teagba::Int_timer0 | teagba::Int_timer1 | teagba::Int_timer2 | teagba::Int_timer3);
} else {
teagba_vblankintrwait();
}
}
return {};
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#include <teagba/addresses.hpp>
#include <teagba/irq.hpp>
#include <keel/keel.hpp>
#include <turbine/gfx.hpp>
#include "context.hpp"
#include "turbine.hpp"
extern "C" void 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 = 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<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;
for (auto current = MEM_ROM; current < reinterpret_cast<char*>(0x0a000000); current += headerLen) {
if (ox_memcmp(current, headerP1, headerP1Len) == 0 &&
ox_memcmp(current + headerP1Len, headerP2, headerP2Len) == 0) {
return reinterpret_cast<std::size_t>(current + headerLen);
}
}
return OxError(1);
}
ox::Result<ox::UniquePtr<turbine::Context>> init(ox::UPtr<ox::FileSystem> fs, ox::CRStringView appName) noexcept {
oxRequireM(ctx, keel::init<gba::Context>(std::move(fs), appName));
oxReturnError(findPreloadSection().moveTo(&ctx->preloadSectionOffset));
oxReturnError(initGfx(*ctx));
initTimer();
initIrq();
return ox::UPtr<turbine::Context>(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<int>(k)));
}
void requestShutdown(Context &ctx) noexcept {
const auto gbaCtx = static_cast<gba::Context*>(&ctx);
gbaCtx->running = false;
}
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#pragma once
#include <ox/std/types.hpp>
#include "../config.hpp"
namespace turbine {
using gba_timer_t = ox::Uint<config::GbaTimerBits>;
}