mirror of
https://github.com/afska/gba-link-connection.git
synced 2026-04-19 23:47:34 -05:00
259 lines
6.3 KiB
C++
259 lines
6.3 KiB
C++
#ifndef LINK_COMMON_H
|
|
#define LINK_COMMON_H
|
|
|
|
/**
|
|
* @brief Enable mGBA debug logging.
|
|
*/
|
|
#ifndef LINK_ENABLE_DEBUG_LOGS
|
|
#define LINK_ENABLE_DEBUG_LOGS 0
|
|
#endif
|
|
|
|
#if LINK_ENABLE_DEBUG_LOGS != 0
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
/**
|
|
* @brief This namespace contains shared code between all libraries.
|
|
* \warning Most of these things are borrowed from libtonc.
|
|
*/
|
|
namespace Link {
|
|
|
|
// Types
|
|
|
|
using u32 = unsigned int;
|
|
using u16 = unsigned short;
|
|
using u8 = unsigned char;
|
|
|
|
using s16 = signed short;
|
|
using s8 = signed char;
|
|
|
|
using vu32 = volatile unsigned int;
|
|
using vs32 = volatile signed int;
|
|
using vu16 = volatile unsigned short;
|
|
using vs16 = volatile signed short;
|
|
|
|
// Structs
|
|
|
|
struct _TMR_REC {
|
|
union {
|
|
u16 start;
|
|
u16 count;
|
|
} __attribute__((packed));
|
|
|
|
u16 cnt;
|
|
} __attribute__((aligned(4)));
|
|
|
|
typedef struct {
|
|
u32 reserved1[5];
|
|
u8 handshake_data;
|
|
u8 padding;
|
|
u16 handshake_timeout;
|
|
u8 probe_count;
|
|
u8 client_data[3];
|
|
u8 palette_data;
|
|
u8 response_bit;
|
|
u8 client_bit;
|
|
u8 reserved2;
|
|
u8* boot_srcp;
|
|
u8* boot_endp;
|
|
u8* masterp;
|
|
u8* reserved3[3];
|
|
u32 system_work2[4];
|
|
u8 sendflag;
|
|
u8 probe_target_bit;
|
|
u8 check_wait;
|
|
u8 server_type;
|
|
} _MultiBootParam;
|
|
|
|
// I/O Registers
|
|
|
|
constexpr u32 _REG_BASE = 0x04000000;
|
|
|
|
inline vu16& _REG_RCNT = *reinterpret_cast<vu16*>(_REG_BASE + 0x0134);
|
|
inline vu16& _REG_SIOCNT = *reinterpret_cast<vu16*>(_REG_BASE + 0x0128);
|
|
inline vu32& _REG_SIODATA32 = *reinterpret_cast<vu32*>(_REG_BASE + 0x0120);
|
|
inline vu16& _REG_SIODATA8 = *reinterpret_cast<vu16*>(_REG_BASE + 0x012A);
|
|
inline vu16& _REG_SIOMLT_SEND = *reinterpret_cast<vu16*>(_REG_BASE + 0x012A);
|
|
inline vu16* const _REG_SIOMULTI = reinterpret_cast<vu16*>(_REG_BASE + 0x0120);
|
|
inline vu16& _REG_VCOUNT = *reinterpret_cast<vu16*>(_REG_BASE + 0x0006);
|
|
inline vu16& _REG_KEYS = *reinterpret_cast<vu16*>(_REG_BASE + 0x0130);
|
|
inline vu16& _REG_TM1CNT_L = *reinterpret_cast<vu16*>(_REG_BASE + 0x0104);
|
|
inline vu16& _REG_TM1CNT_H = *reinterpret_cast<vu16*>(_REG_BASE + 0x0106);
|
|
inline vu16& _REG_TM2CNT_L = *reinterpret_cast<vu16*>(_REG_BASE + 0x0108);
|
|
inline vu16& _REG_TM2CNT_H = *reinterpret_cast<vu16*>(_REG_BASE + 0x010a);
|
|
|
|
inline volatile _TMR_REC* const _REG_TM =
|
|
reinterpret_cast<volatile _TMR_REC*>(_REG_BASE + 0x0100);
|
|
|
|
static constexpr u16 _KEY_ANY = 0x03FF; //!< Here's the Any key :)
|
|
static constexpr u16 _TM_FREQ_1 = 0; //!< 1 cycle/tick (16.7 MHz)
|
|
static constexpr u16 _TM_FREQ_64 = 0x0001; //!< 64 cycles/tick (262 kHz)
|
|
static constexpr u16 _TM_FREQ_256 = 0x0002; //!< 256 cycles/tick (66 kHz)
|
|
static constexpr u16 _TM_FREQ_1024 = 0x0003; //!< 1024 cycles/tick (16 kHz)
|
|
static constexpr u16 _TM_CASCADE =
|
|
0x0004; //!< Increment when preceding timer overflows
|
|
static constexpr u16 _TM_IRQ = 0x0040; //!< Enable timer irq
|
|
static constexpr u16 _TM_ENABLE = 0x0080; //!< Enable timer
|
|
|
|
static constexpr u16 _IRQ_VBLANK = 0x0001; //!< Catch VBlank irq
|
|
static constexpr u16 _IRQ_TIMER0 = 0x0008; //!< Catch timer 0 irq
|
|
static constexpr u16 _IRQ_TIMER1 = 0x0010; //!< Catch timer 1 irq
|
|
static constexpr u16 _IRQ_TIMER2 = 0x0020; //!< Catch timer 2 irq
|
|
static constexpr u16 _IRQ_TIMER3 = 0x0040; //!< Catch timer 3 irq
|
|
static constexpr u16 _IRQ_SERIAL = 0x0080; //!< Catch serial comm irq
|
|
static constexpr u16 _TIMER_IRQ_IDS[] = {_IRQ_TIMER0, _IRQ_TIMER1, _IRQ_TIMER2,
|
|
_IRQ_TIMER3};
|
|
|
|
// SWI
|
|
|
|
static inline __attribute__((always_inline)) void _IntrWait(u32 flagClear,
|
|
u32 irq) {
|
|
register u32 r0 asm("r0") = flagClear;
|
|
register u32 r1 asm("r1") = irq;
|
|
|
|
asm volatile("swi 0x04\n" : : "r"(r0), "r"(r1));
|
|
}
|
|
|
|
static inline __attribute__((always_inline)) int _MultiBoot(_MultiBootParam* mb,
|
|
u32 mode) {
|
|
register _MultiBootParam* r0 asm("r0") = mb;
|
|
register u32 r1 asm("r1") = mode;
|
|
|
|
asm volatile("swi 0x25\n" : "+r"(r0) : "r"(r1));
|
|
|
|
return (int)r0;
|
|
}
|
|
|
|
// Helpers
|
|
|
|
static inline int _max(int a, int b) {
|
|
return (a > b) ? (a) : (b);
|
|
}
|
|
|
|
static inline int _min(int a, int b) {
|
|
return (a < b) ? (a) : (b);
|
|
}
|
|
|
|
// Queue
|
|
|
|
template <typename T, u32 Size, bool Overwrite = true>
|
|
class Queue {
|
|
public:
|
|
void push(T item) {
|
|
if (isFull()) {
|
|
if constexpr (Overwrite) {
|
|
pop();
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
rear = (rear + 1) % Size;
|
|
arr[rear] = item;
|
|
count++;
|
|
}
|
|
|
|
T pop() {
|
|
if (isEmpty())
|
|
return T{};
|
|
|
|
auto x = arr[front];
|
|
front = (front + 1) % Size;
|
|
count--;
|
|
|
|
return x;
|
|
}
|
|
|
|
T peek() {
|
|
if (isEmpty())
|
|
return T{};
|
|
return arr[front];
|
|
}
|
|
|
|
template <typename F>
|
|
void forEach(F action) {
|
|
vs32 currentFront = front;
|
|
|
|
for (u32 i = 0; i < count; i++) {
|
|
if (!action(arr[currentFront]))
|
|
return;
|
|
currentFront = (currentFront + 1) % Size;
|
|
}
|
|
}
|
|
|
|
void clear() {
|
|
front = count = 0;
|
|
rear = -1;
|
|
}
|
|
|
|
void startReading() { _isReading = true; }
|
|
void stopReading() { _isReading = false; }
|
|
|
|
void syncPush(T item) {
|
|
_isWriting = true;
|
|
asm volatile("" ::: "memory");
|
|
|
|
push(item);
|
|
|
|
asm volatile("" ::: "memory");
|
|
_isWriting = false;
|
|
asm volatile("" ::: "memory");
|
|
|
|
if (_needsClear) {
|
|
clear();
|
|
_needsClear = false;
|
|
}
|
|
}
|
|
|
|
void syncClear() {
|
|
if (_isReading)
|
|
return; // (it will be cleared later anyway)
|
|
|
|
if (!_isWriting)
|
|
clear();
|
|
else
|
|
_needsClear = true;
|
|
}
|
|
|
|
u32 size() { return count; }
|
|
bool isEmpty() { return size() == 0; }
|
|
bool isFull() { return size() == Size; }
|
|
bool isReading() { return _isReading; }
|
|
bool isWriting() { return _isWriting; }
|
|
bool canMutate() { return !_isReading && !_isWriting; }
|
|
|
|
private:
|
|
T arr[Size];
|
|
vs32 front = 0;
|
|
vs32 rear = -1;
|
|
vu32 count = 0;
|
|
volatile bool _isReading = false;
|
|
volatile bool _isWriting = false;
|
|
volatile bool _needsClear = false;
|
|
};
|
|
|
|
// mGBA Logging
|
|
|
|
#if LINK_ENABLE_DEBUG_LOGS != 0
|
|
inline vu16& _REG_LOG_ENABLE = *reinterpret_cast<vu16*>(0x4FFF780);
|
|
inline vu16& _REG_LOG_LEVEL = *reinterpret_cast<vu16*>(0x4FFF700);
|
|
|
|
static inline void log(const char* fmt, ...) {
|
|
_REG_LOG_ENABLE = 0xC0DE;
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
|
|
char* const log = (char*)0x4FFF600;
|
|
vsnprintf(log, 0x100, fmt, args);
|
|
_REG_LOG_LEVEL = 0x102; // Level: WARN
|
|
|
|
va_end(args);
|
|
}
|
|
#endif
|
|
|
|
} // namespace Link
|
|
|
|
#endif // LINK_COMMON_H
|