-
Notifications
You must be signed in to change notification settings - Fork 6
/
_link_common.hpp
297 lines (240 loc) · 7.42 KB
/
_link_common.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
#ifndef LINK_COMMON_H
#define LINK_COMMON_H
#ifndef LINK_DEVELOPMENT
#pragma GCC system_header
#endif
/**
* @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 and gba-hpp.
*/
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_JOYCNT = *reinterpret_cast<vu16*>(_REG_BASE + 0x0140);
inline vu16& _REG_JOY_RECV_L = *reinterpret_cast<vu16*>(_REG_BASE + 0x0150);
inline vu16& _REG_JOY_RECV_H = *reinterpret_cast<vu16*>(_REG_BASE + 0x0152);
inline vu16& _REG_JOY_TRANS_L = *reinterpret_cast<vu16*>(_REG_BASE + 0x0154);
inline vu16& _REG_JOY_TRANS_H = *reinterpret_cast<vu16*>(_REG_BASE + 0x0156);
inline vu16& _REG_JOYSTAT = *reinterpret_cast<vu16*>(_REG_BASE + 0x0158);
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 vu16& _REG_IME = *reinterpret_cast<vu16*>(_REG_BASE + 0x0208);
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(
bool clearCurrent,
u32 flags) noexcept {
register auto r0 asm("r0") = clearCurrent;
register auto r1 asm("r1") = flags;
asm volatile inline("swi 0x4 << ((1f - . == 4) * -16); 1:"
: "+r"(r0), "+r"(r1)::"r3");
}
static inline auto _MultiBoot(const _MultiBootParam* param,
u32 mbmode) noexcept {
register union {
const _MultiBootParam* ptr;
int res;
} r0 asm("r0") = {param};
register auto r1 asm("r1") = mbmode;
asm volatile inline("swi 0x25 << ((1f - . == 4) * -16); 1:"
: "+r"(r0), "+r"(r1)::"r3");
return r0.res;
}
// 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 = count + 1;
}
T pop() {
if (isEmpty())
return T{};
auto x = arr[front];
front = (front + 1) % Size;
count = count - 1;
return x;
}
T peek() {
if (isEmpty())
return T{};
return arr[front];
}
T* peekRef() {
if (isEmpty())
return nullptr;
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 = 0;
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;
}
}
T syncPop() {
_isReading = true;
asm volatile("" ::: "memory");
auto value = pop();
asm volatile("" ::: "memory");
_isReading = false;
asm volatile("" ::: "memory");
return value;
}
void syncClear() {
if (_isReading)
return; // (it will be cleared later anyway)
if (!_isWriting)
clear();
else
_needsClear = true;
}
u32 size() { return count; }
bool isEmpty() { return count == 0; }
bool isFull() { return count == 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;
};
// Packets per frame -> Timer interval
static inline u16 perFrame(u16 packets) {
return (1667 * 1024) / (packets * 6104);
}
// 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