diff options
author | Bad Diode <bd@badd10de.dev> | 2021-05-18 16:40:24 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-05-18 16:40:24 +0200 |
commit | 0c7265cf0de9d4ec95d28c5e103c00a63f4a1697 (patch) | |
tree | 4a1145e849e078395430a8d718c4bd69a06fb29f /src/common.h | |
download | uxngba-0c7265cf0de9d4ec95d28c5e103c00a63f4a1697.tar.gz uxngba-0c7265cf0de9d4ec95d28c5e103c00a63f4a1697.zip |
Proof of concept of UXN on the GBA
Diffstat (limited to 'src/common.h')
-rw-r--r-- | src/common.h | 696 |
1 files changed, 696 insertions, 0 deletions
diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..aeea1a6 --- /dev/null +++ b/src/common.h | |||
@@ -0,0 +1,696 @@ | |||
1 | #ifndef GBAEXP_COMMON_H | ||
2 | #define GBAEXP_COMMON_H | ||
3 | |||
4 | #include "shorthand.h" | ||
5 | |||
6 | // | ||
7 | // Memory sections. | ||
8 | // | ||
9 | |||
10 | // Defines for the different memory sections in the GBA. | ||
11 | #define MEM_SROM 0x00000000 | ||
12 | #define MEM_EW 0x02000000 | ||
13 | #define MEM_IW 0x03000000 | ||
14 | #define MEM_IO 0x04000000 | ||
15 | #define MEM_PAL 0x05000000 | ||
16 | #define MEM_VRAM 0x06000000 | ||
17 | #define MEM_OAM 0x07000000 | ||
18 | #define MEM_PAK 0x08000000 | ||
19 | #define MEM_CART 0x0E000000 | ||
20 | |||
21 | // | ||
22 | // Display modes. | ||
23 | // | ||
24 | |||
25 | // Display registers. | ||
26 | #define DISP_CTRL *((vu32*)(MEM_IO + 0x0000)) | ||
27 | #define DISP_STATUS *((vu16*)(MEM_IO + 0x0004)) | ||
28 | #define DISP_VCOUNT *((vu16*)(MEM_IO + 0x0006)) | ||
29 | |||
30 | // The first three bits in the DISP_CTRL are used to control the video mode. | ||
31 | #define DISP_MODE_0 0x0000 | ||
32 | #define DISP_MODE_1 0x0001 | ||
33 | #define DISP_MODE_2 0x0002 | ||
34 | #define DISP_MODE_3 0x0003 | ||
35 | #define DISP_MODE_4 0x0004 | ||
36 | #define DISP_MODE_5 0x0005 | ||
37 | #define DISP_GB (1 << 3) | ||
38 | #define DISP_PAGE (1 << 4) | ||
39 | #define DISP_OAM_HBLANK (1 << 5) | ||
40 | #define DISP_OBJ_1D (1 << 6) | ||
41 | #define DISP_BLANK (1 << 7) | ||
42 | #define DISP_BG_0 (1 << 8) | ||
43 | #define DISP_BG_1 (1 << 9) | ||
44 | #define DISP_BG_2 (1 << 10) | ||
45 | #define DISP_BG_3 (1 << 11) | ||
46 | #define DISP_OBJ (1 << 12) | ||
47 | #define DISP_ENABLE_SPRITES DISP_OBJ | DISP_OBJ_1D | ||
48 | |||
49 | // These bits are used to control the DISP_STATUS register. | ||
50 | #define DISP_VBLANK_STATUS (1 << 0x0) | ||
51 | #define DISP_HBLANK_STATUS (1 << 0x1) | ||
52 | #define DISP_VCOUNT_STATUS (1 << 0x2) | ||
53 | #define DISP_VBLANK_IRQ (1 << 0x3) | ||
54 | #define DISP_HBLANK_IRQ (1 << 0x4) | ||
55 | #define DISP_VCOUNT_IRQ (1 << 0x5) | ||
56 | #define DISP_VCOUNT_TRIGGER(N) ((N) << 0x8) | ||
57 | |||
58 | // Registers to control of BG layers. | ||
59 | #define BG_CTRL(N) *((vu16*)(0x04000008 + 0x0002 * (N))) | ||
60 | |||
61 | // Bits to control the background. | ||
62 | #define BG_PRIORITY(N) ((N) & 0x3) | ||
63 | #define BG_CHARBLOCK(N) ((N) << 2) | ||
64 | #define BG_MOSAIC (1 << 6) | ||
65 | #define BG_HIGH_COLOR (1 << 7) | ||
66 | #define BG_SCREENBLOCK(N) ((N) << 8) | ||
67 | #define BG_AFFINE (1 << 0xD) | ||
68 | #define BG_SIZE(N) ((N) << 0xE) | ||
69 | |||
70 | // BG registers for horizontal displacement. | ||
71 | #define BG_H_SCROLL_0 *((vu16*)(0x04000010 + 0x0004 * 0)) | ||
72 | #define BG_H_SCROLL_1 *((vu16*)(0x04000010 + 0x0004 * 1)) | ||
73 | #define BG_H_SCROLL_2 *((vu16*)(0x04000010 + 0x0004 * 2)) | ||
74 | #define BG_H_SCROLL_3 *((vu16*)(0x04000010 + 0x0004 * 3)) | ||
75 | |||
76 | // BG registers for vertical displacement. | ||
77 | #define BG_V_SCROLL_0 *((vu16*)(0x04000012 + 0x0004 * 0)) | ||
78 | #define BG_V_SCROLL_1 *((vu16*)(0x04000012 + 0x0004 * 1)) | ||
79 | #define BG_V_SCROLL_2 *((vu16*)(0x04000012 + 0x0004 * 2)) | ||
80 | #define BG_V_SCROLL_3 *((vu16*)(0x04000012 + 0x0004 * 3)) | ||
81 | |||
82 | // Screen settings. | ||
83 | #define SCREEN_WIDTH 240 | ||
84 | #define SCREEN_HEIGHT 160 | ||
85 | |||
86 | // | ||
87 | // Colors. | ||
88 | // | ||
89 | |||
90 | // The GBA in mode 3 expects rbg15 colors in the VRAM, where each component | ||
91 | // (RGB) have a 0--31 range. For example, pure red would be rgb15(31, 0, 0). | ||
92 | typedef u16 Color; | ||
93 | |||
94 | // A palette is composed of 16 colors, with color at index 0 being transparent | ||
95 | // for sprites. | ||
96 | typedef Color Palette[16]; | ||
97 | |||
98 | // Inline function to calculate the 15 bit color value. | ||
99 | static inline Color | ||
100 | rgb15(u32 red, u32 green, u32 blue ) { | ||
101 | return (blue << 10) | (green << 5) | red; | ||
102 | } | ||
103 | |||
104 | // Some nice default colors. | ||
105 | #define COLOR_RED rgb15(31, 0, 12) | ||
106 | #define COLOR_BLUE rgb15(2, 15, 30) | ||
107 | #define COLOR_CYAN rgb15(0, 30, 30) | ||
108 | #define COLOR_GREY rgb15(12, 12, 12) | ||
109 | #define COLOR_BLACK rgb15(0, 0, 0) | ||
110 | #define COLOR_WHITE rgb15(28, 28, 28) | ||
111 | |||
112 | // | ||
113 | // Tile memory access. | ||
114 | // | ||
115 | |||
116 | // NOTE: Only defining 4bpp tiles for now. | ||
117 | typedef struct Tile { | ||
118 | u32 row[8]; | ||
119 | } Tile; | ||
120 | |||
121 | // Screenblocks and charblocks (tile blocks). | ||
122 | typedef Tile TileBlock[512]; | ||
123 | #define TILE_MEM ((TileBlock*) MEM_VRAM) | ||
124 | typedef u16 ScreenBlock[1024]; | ||
125 | #define SCREENBLOCK_MEM ((ScreenBlock*)MEM_VRAM) | ||
126 | |||
127 | // Screenblock entry bits. | ||
128 | #define SCREENBLOCK_ENTRY_H_FLIP (1 << 0xA) | ||
129 | #define SCREENBLOCK_ENTRY_V_FLIP (1 << 0xB) | ||
130 | #define SCREENBLOCK_ENTRY_PAL(N) ((N) << 0xC) | ||
131 | |||
132 | size_t | ||
133 | se_index(size_t tile_x, size_t tile_y, size_t map_width) { | ||
134 | size_t sbb = ((tile_x >> 5) + (tile_y >> 5) * (map_width >> 5)); | ||
135 | return sbb * 1024 + ((tile_x & 31) + (tile_y & 31) * 32); | ||
136 | } | ||
137 | |||
138 | // We can treat the screen as a HxW matrix. With the following macro we can | ||
139 | // write a pixel to the screen at the (x, y) position using: | ||
140 | // | ||
141 | // FRAMEBUFFER[y][x] = color; | ||
142 | // | ||
143 | typedef Color Scanline[SCREEN_WIDTH]; | ||
144 | #define FRAMEBUFFER ((Scanline*) MEM_VRAM) | ||
145 | #define SCREEN_BUFFER ((u16*) MEM_VRAM) | ||
146 | #define PAL_BUFFER_BG ((u16*) MEM_PAL) | ||
147 | #define PAL_BUFFER_SPRITES ((u16*)(MEM_PAL + 0x200)) | ||
148 | #define PAL_BANK_BG ((Palette*) MEM_PAL) | ||
149 | #define PAL_BANK_SPRITES ((Palette*)(MEM_PAL + 0x200)) | ||
150 | static vu16 *backbuffer = ((vu16*)(MEM_VRAM + 0x0A000)); | ||
151 | |||
152 | // | ||
153 | // Sprites. | ||
154 | // | ||
155 | |||
156 | // Using macros instead of aligned structs for setting up OBJ attributes and | ||
157 | // affine parameters. | ||
158 | // TODO: Benchmark if this would be slower or the same that TONC's | ||
159 | // implementation. | ||
160 | // TODO: Cleanup OBJ/OAM memory copying and access. | ||
161 | #define OBJ_ATTR_0(N) *((vu16*)(MEM_OAM + 0 + 8 * (N))) | ||
162 | #define OBJ_ATTR_1(N) *((vu16*)(MEM_OAM + 2 + 8 * (N))) | ||
163 | #define OBJ_ATTR_2(N) *((vu16*)(MEM_OAM + 4 + 8 * (N))) | ||
164 | #define OBJ_AFFINE_PA(N) *((vs16*)(MEM_OAM + 6 + 8 * 0 + 8 * 4 * (N))) | ||
165 | #define OBJ_AFFINE_PB(N) *((vs16*)(MEM_OAM + 6 + 8 * 1 + 8 * 4 * (N))) | ||
166 | #define OBJ_AFFINE_PC(N) *((vs16*)(MEM_OAM + 6 + 8 * 2 + 8 * 4 * (N))) | ||
167 | #define OBJ_AFFINE_PD(N) *((vs16*)(MEM_OAM + 6 + 8 * 3 + 8 * 4 * (N))) | ||
168 | |||
169 | // OBJ_ATTR_0 parameters | ||
170 | #define OBJ_Y_COORD(N) ((N) & 0xFF) | ||
171 | #define OBJ_NORMAL (0x00 << 0x8) | ||
172 | #define OBJ_AFFINE (0x01 << 0x8) | ||
173 | #define OBJ_HIDDEN (0x02 << 0x8) | ||
174 | #define OBJ_AFFINE_2X (0x03 << 0x8) | ||
175 | #define OBJ_ALPHA_BLEND (0x01 << 0xA) | ||
176 | #define OBJ_WINDOW (0x02 << 0xA) | ||
177 | #define OBJ_SHAPE_SQUARE (0x00 << 0xE) | ||
178 | #define OBJ_SHAPE_WIDE (0x01 << 0xE) | ||
179 | #define OBJ_SHAPE_TALL (0x02 << 0xE) | ||
180 | |||
181 | // OBJ_ATTR_1 parameters | ||
182 | #define OBJ_X_COORD(N) ((N) & 0x1FF) | ||
183 | #define OBJ_AFFINE_IDX(N) ((N) << 0x9) | ||
184 | #define OBJ_H_FLIP (0x01 << 0xC) | ||
185 | #define OBJ_V_FLIP (0x01 << 0xD) | ||
186 | #define OBJ_SIZE_SMALL (0x00 << 0xE) | ||
187 | #define OBJ_SIZE_MID (0x01 << 0xE) | ||
188 | #define OBJ_SIZE_BIG (0x02 << 0xE) | ||
189 | #define OBJ_SIZE_HUGE (0x03 << 0xE) | ||
190 | |||
191 | // OBJ_ATTR_2 parameters | ||
192 | #define OBJ_TILE_INDEX(N) ((N) & 0x3FF) | ||
193 | #define OBJ_PRIORITY(N) ((N) << 0xA) | ||
194 | #define OBJ_PAL_BANK(N) ((N) << 0xC) | ||
195 | |||
196 | // | ||
197 | // Mode 4 page flipping | ||
198 | // | ||
199 | |||
200 | static inline void | ||
201 | flip_page(void) { | ||
202 | backbuffer = (u16*)((u32)backbuffer ^ 0x0A000); | ||
203 | DISP_CTRL ^= DISP_PAGE; | ||
204 | } | ||
205 | |||
206 | #define SCREEN_PAGE_1 ((vu16*) MEM_VRAM) | ||
207 | #define SCREEN_PAGE_2 ((vu16*) (MEM_VRAM + 0xa000)) | ||
208 | |||
209 | // | ||
210 | // Profiling. | ||
211 | // | ||
212 | |||
213 | #define TIMER_DATA_0 *((vu16*) (0x04000100 + 0x04 * 0)) | ||
214 | #define TIMER_DATA_1 *((vu16*) (0x04000100 + 0x04 * 1)) | ||
215 | #define TIMER_DATA_2 *((vu16*) (0x04000100 + 0x04 * 2)) | ||
216 | #define TIMER_DATA_3 *((vu16*) (0x04000100 + 0x04 * 3)) | ||
217 | #define TIMER_CTRL_0 *((vu16*) (0x04000102 + 0x04 * 0)) | ||
218 | #define TIMER_CTRL_1 *((vu16*) (0x04000102 + 0x04 * 1)) | ||
219 | #define TIMER_CTRL_2 *((vu16*) (0x04000102 + 0x04 * 2)) | ||
220 | #define TIMER_CTRL_3 *((vu16*) (0x04000102 + 0x04 * 3)) | ||
221 | |||
222 | // Timer control bits. | ||
223 | #define TIMER_CTRL_FREQ_0 0 | ||
224 | #define TIMER_CTRL_FREQ_1 1 | ||
225 | #define TIMER_CTRL_FREQ_2 2 | ||
226 | #define TIMER_CTRL_FREQ_3 3 | ||
227 | #define TIMER_CTRL_CASCADE (1 << 2) | ||
228 | #define TIMER_CTRL_IRQ (1 << 6) | ||
229 | #define TIMER_CTRL_ENABLE (1 << 7) | ||
230 | #define TIMER_CTRL_DISABLE (0 << 7) | ||
231 | |||
232 | // We use timers 2 and 3 to count the number of cycles since the profile_start | ||
233 | // functions is called. Don't use if the code we are trying to profile make use | ||
234 | // of these timers. | ||
235 | static inline | ||
236 | void profile_start(void) { | ||
237 | TIMER_DATA_2 = 0; | ||
238 | TIMER_DATA_3 = 0; | ||
239 | TIMER_CTRL_2 = 0; | ||
240 | TIMER_CTRL_3 = 0; | ||
241 | TIMER_CTRL_3 = TIMER_CTRL_ENABLE | TIMER_CTRL_CASCADE; | ||
242 | TIMER_CTRL_2 = TIMER_CTRL_ENABLE; | ||
243 | } | ||
244 | |||
245 | static inline | ||
246 | u32 profile_stop(void) { | ||
247 | TIMER_CTRL_2 = 0; | ||
248 | return (TIMER_DATA_3 << 16) | TIMER_DATA_2; | ||
249 | } | ||
250 | |||
251 | // | ||
252 | // Input handling. | ||
253 | // | ||
254 | |||
255 | // Memory address for key input and control register | ||
256 | #define KEY_INPUTS *((vu16*) 0x04000130) | ||
257 | #define KEY_CTRL *((vu16*) 0x04000132) | ||
258 | |||
259 | // Key control register bits. | ||
260 | #define KEY_IRQ_KEY(N) (N) | ||
261 | #define KEY_IRQ (1 << 0xE) | ||
262 | #define KEY_IRQ_IF_SET (1 << 0xF) | ||
263 | |||
264 | // Alias for key pressing bits. | ||
265 | #define KEY_A (1 << 0) | ||
266 | #define KEY_B (1 << 1) | ||
267 | #define KEY_SELECT (1 << 2) | ||
268 | #define KEY_START (1 << 3) | ||
269 | #define KEY_RIGHT (1 << 4) | ||
270 | #define KEY_LEFT (1 << 5) | ||
271 | #define KEY_UP (1 << 6) | ||
272 | #define KEY_DOWN (1 << 7) | ||
273 | #define KEY_R (1 << 8) | ||
274 | #define KEY_L (1 << 9) | ||
275 | |||
276 | #define KEY_MASK 0x03FF | ||
277 | |||
278 | // Saving the previous and current key states as globals for now. | ||
279 | static u16 key_curr = 0; | ||
280 | static u16 key_prev = 0; | ||
281 | |||
282 | static inline void | ||
283 | poll_keys(void) { | ||
284 | key_prev = key_curr; | ||
285 | key_curr = ~KEY_INPUTS & KEY_MASK; | ||
286 | } | ||
287 | |||
288 | // Returns true if the given key has been pressed at time of calling and was not | ||
289 | // pressed since the previous call. For example, if a key is being held, this | ||
290 | // function will return `true` only on the frame where the key initially | ||
291 | // activated. | ||
292 | static inline u32 | ||
293 | key_pressed(u32 key) { | ||
294 | return (key_curr & key) & ~(key_prev & key); | ||
295 | } | ||
296 | |||
297 | // Check if the given key is pressed and has been since at least one frame. | ||
298 | static inline u32 | ||
299 | key_hold(u32 key) { | ||
300 | return (key_curr & key) & key_prev & key; | ||
301 | } | ||
302 | |||
303 | // Check if the given key/button is currently pressed. | ||
304 | #define KEY_PRESSED(key) (~(KEY_INPUTS) & key) | ||
305 | |||
306 | // Back/unpack bits. | ||
307 | static inline u32 | ||
308 | unpack_1bb(u8 hex) { | ||
309 | const u32 conversion_u32[16] = { | ||
310 | 0x00000000, 0x00000001, 0x00000010, 0x00000011, | ||
311 | 0x00000100, 0x00000101, 0x00000110, 0x00000111, | ||
312 | 0x00001000, 0x00001001, 0x00001010, 0x00001011, | ||
313 | 0x00001100, 0x00001101, 0x00001110, 0x00001111, | ||
314 | }; | ||
315 | u8 low = hex & 0xF; | ||
316 | u8 high = (hex >> 4) & 0xF; | ||
317 | return (conversion_u32[high] << 16) | conversion_u32[low]; | ||
318 | } | ||
319 | |||
320 | // Unpack N tiles packed at 1bpp. | ||
321 | static inline void | ||
322 | unpack_tiles(u32 *src, u32 *dst, size_t n_tiles) { | ||
323 | u32 *target_src = src + n_tiles * 2; | ||
324 | while (src != target_src) { | ||
325 | *dst++ = unpack_1bb((*src >> 24) & 0xFF); | ||
326 | *dst++ = unpack_1bb((*src >> 16) & 0xFF); | ||
327 | *dst++ = unpack_1bb((*src >> 8) & 0xFF); | ||
328 | *dst++ = unpack_1bb(*src & 0xFF); | ||
329 | src++; | ||
330 | } | ||
331 | } | ||
332 | |||
333 | // | ||
334 | // Direct Memory Access (DMA) | ||
335 | // | ||
336 | |||
337 | |||
338 | // Source, destination, and control registers. | ||
339 | #define DMA_SRC(N) *((vu32*) 0x040000B0 + (N) * 12) | ||
340 | #define DMA_DST(N) *((vu32*) 0x040000B4 + (N) * 12) | ||
341 | #define DMA_CTRL(N) *((vu32*) 0x040000B8 + (N) * 12) | ||
342 | |||
343 | // DMA control bits. | ||
344 | #define DMA_DST_INC (0 << 0x15) | ||
345 | #define DMA_DST_DEC (1 << 0x15) | ||
346 | #define DMA_DST_FIXED (2 << 0x15) | ||
347 | #define DMA_DST_RELOAD (3 << 0x15) | ||
348 | #define DMA_SRC_INC (0 << 0x17) | ||
349 | #define DMA_SRC_DEC (1 << 0x17) | ||
350 | #define DMA_SRC_FIXED (2 << 0x17) | ||
351 | #define DMA_REPEAT (1 << 0x19) | ||
352 | #define DMA_CHUNK_16 (0 << 0x1A) | ||
353 | #define DMA_CHUNK_32 (1 << 0x1A) | ||
354 | #define DMA_NOW (0 << 0x1C) | ||
355 | #define DMA_VBLANK (1 << 0x1C) | ||
356 | #define DMA_HBLANK (2 << 0x1C) | ||
357 | #define DMA_REFRESH (3 << 0x1C) | ||
358 | #define DMA_IRQ (1 << 0x1E) | ||
359 | #define DMA_ENABLE (1 << 0x1F) | ||
360 | |||
361 | // Custom struct for cleaner DMA transfer functions. | ||
362 | typedef struct DmaStr { | ||
363 | const void *src; | ||
364 | void *dst; | ||
365 | u32 ctrl; | ||
366 | } DmaStr; | ||
367 | |||
368 | #define DMA_TRANSFER ((volatile DmaStr*) 0x040000B0) | ||
369 | |||
370 | // Transfer `count` number of chunks from src to dst using a DMA channel. Note | ||
371 | // that chunks are not bytes, but instead configured based on bits set by | ||
372 | // DMA_CTRL. | ||
373 | inline void | ||
374 | dma_transfer_copy(void *dst, const void *src, u32 count, int channel, u32 options) { | ||
375 | DMA_TRANSFER[channel].ctrl = 0; | ||
376 | DMA_TRANSFER[channel].src = src; | ||
377 | DMA_TRANSFER[channel].dst = dst; | ||
378 | DMA_TRANSFER[channel].ctrl = count | options; | ||
379 | } | ||
380 | |||
381 | inline void | ||
382 | dma_transfer_fill(void *dst, volatile u32 src, u32 count, int channel, u32 options) { | ||
383 | DMA_TRANSFER[channel].ctrl = 0; | ||
384 | DMA_TRANSFER[channel].src = (const void *)&src; | ||
385 | DMA_TRANSFER[channel].dst = dst; | ||
386 | DMA_TRANSFER[channel].ctrl = count | options | DMA_SRC_FIXED; | ||
387 | } | ||
388 | |||
389 | // Copy N number of bytes using a DMA channel. | ||
390 | inline void | ||
391 | dma_copy(void *dst, const void *src, u32 size, int channel) { | ||
392 | dma_transfer_copy(dst, src, size / 4, channel, DMA_CHUNK_32 | DMA_ENABLE); | ||
393 | } | ||
394 | |||
395 | // Fill the dst location with the word set at src. | ||
396 | inline void | ||
397 | dma_fill(void *dst, const void *src, u32 size, int channel) { | ||
398 | dma_transfer_fill(dst, (volatile u32)src, size / 4, channel, DMA_CHUNK_32 | DMA_ENABLE); | ||
399 | } | ||
400 | |||
401 | // | ||
402 | // Interrupts. | ||
403 | // | ||
404 | |||
405 | #define IRQ_ENABLE *((vu16*) 0x04000200) | ||
406 | #define IRQ_ACK *((vu16*) 0x04000202) | ||
407 | #define IRQ_CTRL *((vu16*) 0x04000208) | ||
408 | #define IRQ_ACK_BIOS *((vu16*) 0x03007FF8) | ||
409 | |||
410 | typedef enum { | ||
411 | IRQ_VBLANK, | ||
412 | IRQ_HBLANK, | ||
413 | IRQ_VCOUNT, | ||
414 | IRQ_TIMER_0, | ||
415 | IRQ_TIMER_1, | ||
416 | IRQ_TIMER_2, | ||
417 | IRQ_TIMER_3, | ||
418 | IRQ_SERIAL, | ||
419 | IRQ_DMA_0, | ||
420 | IRQ_DMA_1, | ||
421 | IRQ_DMA_2, | ||
422 | IRQ_DMA_3, | ||
423 | IRQ_KEYPAD, | ||
424 | IRQ_GAMEPAK, | ||
425 | } IrqIndex; | ||
426 | |||
427 | typedef void (*IrsFunc)(void); | ||
428 | |||
429 | IrsFunc irs_table[] = { | ||
430 | [IRQ_VBLANK ] = NULL, | ||
431 | [IRQ_HBLANK ] = NULL, | ||
432 | [IRQ_VCOUNT ] = NULL, | ||
433 | [IRQ_TIMER_0] = NULL, | ||
434 | [IRQ_TIMER_1] = NULL, | ||
435 | [IRQ_TIMER_2] = NULL, | ||
436 | [IRQ_TIMER_3] = NULL, | ||
437 | [IRQ_SERIAL ] = NULL, | ||
438 | [IRQ_DMA_0 ] = NULL, | ||
439 | [IRQ_DMA_1 ] = NULL, | ||
440 | [IRQ_DMA_2 ] = NULL, | ||
441 | [IRQ_DMA_3 ] = NULL, | ||
442 | [IRQ_KEYPAD ] = NULL, | ||
443 | [IRQ_GAMEPAK] = NULL, | ||
444 | }; | ||
445 | |||
446 | void | ||
447 | irq_enable(IrqIndex idx) { | ||
448 | switch (idx) { | ||
449 | case IRQ_VBLANK: { DISP_STATUS |= DISP_VBLANK_IRQ; } break; | ||
450 | case IRQ_HBLANK: { DISP_STATUS |= DISP_HBLANK_IRQ; } break; | ||
451 | case IRQ_VCOUNT: { DISP_STATUS |= DISP_VCOUNT_IRQ; } break; | ||
452 | case IRQ_TIMER_0: { TIMER_CTRL_0 |= TIMER_CTRL_IRQ; } break; | ||
453 | case IRQ_TIMER_1: { TIMER_CTRL_1 |= TIMER_CTRL_IRQ; } break; | ||
454 | case IRQ_TIMER_2: { TIMER_CTRL_2 |= TIMER_CTRL_IRQ; } break; | ||
455 | case IRQ_TIMER_3: { TIMER_CTRL_3 |= TIMER_CTRL_IRQ; } break; | ||
456 | case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; | ||
457 | case IRQ_DMA_0: { DMA_CTRL(0) |= DMA_IRQ; } break; | ||
458 | case IRQ_DMA_1: { DMA_CTRL(1) |= DMA_IRQ; } break; | ||
459 | case IRQ_DMA_2: { DMA_CTRL(2) |= DMA_IRQ; } break; | ||
460 | case IRQ_DMA_3: { DMA_CTRL(3) |= DMA_IRQ; } break; | ||
461 | case IRQ_KEYPAD: { KEY_CTRL |= KEY_IRQ; } break; | ||
462 | case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; | ||
463 | } | ||
464 | IRQ_ENABLE |= (1 << idx); | ||
465 | } | ||
466 | |||
467 | void | ||
468 | irq_disable(IrqIndex idx) { | ||
469 | switch (idx) { | ||
470 | case IRQ_VBLANK: { DISP_STATUS &= ~DISP_VBLANK_IRQ; } break; | ||
471 | case IRQ_HBLANK: { DISP_STATUS &= ~DISP_HBLANK_IRQ; } break; | ||
472 | case IRQ_VCOUNT: { DISP_STATUS &= ~DISP_VCOUNT_IRQ; } break; | ||
473 | case IRQ_TIMER_0: { TIMER_CTRL_0 &= ~TIMER_CTRL_IRQ; } break; | ||
474 | case IRQ_TIMER_1: { TIMER_CTRL_1 &= ~TIMER_CTRL_IRQ; } break; | ||
475 | case IRQ_TIMER_2: { TIMER_CTRL_2 &= ~TIMER_CTRL_IRQ; } break; | ||
476 | case IRQ_TIMER_3: { TIMER_CTRL_3 &= ~TIMER_CTRL_IRQ; } break; | ||
477 | case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; | ||
478 | case IRQ_DMA_0: { DMA_CTRL(0) &= ~DMA_IRQ; } break; | ||
479 | case IRQ_DMA_1: { DMA_CTRL(1) &= ~DMA_IRQ; } break; | ||
480 | case IRQ_DMA_2: { DMA_CTRL(2) &= ~DMA_IRQ; } break; | ||
481 | case IRQ_DMA_3: { DMA_CTRL(3) &= ~DMA_IRQ; } break; | ||
482 | case IRQ_KEYPAD: { KEY_CTRL &= ~KEY_IRQ; } break; | ||
483 | case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; | ||
484 | } | ||
485 | IRQ_ENABLE &= ~(1 << idx); | ||
486 | } | ||
487 | |||
488 | void | ||
489 | irs_set(IrqIndex idx, IrsFunc func) { | ||
490 | // Store IRQ_CTRL status and disable interrupts for now. | ||
491 | u16 irq_ctrl = IRQ_CTRL; | ||
492 | IRQ_CTRL = 0; | ||
493 | |||
494 | // Update the IRS table and enable/disable the given IRQ. | ||
495 | irs_table[idx] = func; | ||
496 | if (func == NULL) { | ||
497 | irq_disable(idx); | ||
498 | } else { | ||
499 | irq_enable(idx); | ||
500 | } | ||
501 | |||
502 | // Restore previous irq_ctrl. | ||
503 | IRQ_CTRL = irq_ctrl; | ||
504 | } | ||
505 | |||
506 | // External irs_main function, has to be written in ARM assembly. | ||
507 | void irs_main(void); | ||
508 | |||
509 | // Initialize the function pointer for the main IRS routine written in ARM | ||
510 | // assembly and enable interrupts. | ||
511 | #define IRS_MAIN *(IrsFunc*)(0x03007FFC) | ||
512 | void | ||
513 | irq_init() { | ||
514 | IRS_MAIN = irs_main; | ||
515 | IRQ_CTRL = 1; | ||
516 | } | ||
517 | |||
518 | // Stub function pointer needed for when we want to enable interrupts that don't | ||
519 | // require a custom function, such as for the BIOS VSync. | ||
520 | void | ||
521 | irs_stub() {} | ||
522 | |||
523 | // | ||
524 | // BIOS function declarations. | ||
525 | // | ||
526 | |||
527 | // These functions declarations can be used to call the BIOS functions from the | ||
528 | // asm code. | ||
529 | int bios_vblank_wait(); | ||
530 | int bios_div(int num, int denom); | ||
531 | |||
532 | // | ||
533 | // Sound. | ||
534 | // | ||
535 | |||
536 | // Sound registers. | ||
537 | #define SOUND_SQUARE1_SWEEP *((vu16*)(MEM_IO + 0x60)) | ||
538 | #define SOUND_SQUARE1_CTRL *((vu16*)(MEM_IO + 0x62)) | ||
539 | #define SOUND_SQUARE1_FREQ *((vu16*)(MEM_IO + 0x64)) | ||
540 | #define SOUND_SQUARE2_CTRL *((vu16*)(MEM_IO + 0x68)) | ||
541 | #define SOUND_SQUARE2_FREQ *((vu16*)(MEM_IO + 0x6C)) | ||
542 | #define SOUND_WAVE_MODE *((vu16*)(MEM_IO + 0x70)) | ||
543 | #define SOUND_WAVE_CTRL *((vu16*)(MEM_IO + 0x72)) | ||
544 | #define SOUND_WAVE_FREQ *((vu16*)(MEM_IO + 0x74)) | ||
545 | #define SOUND_NOISE_CTRL *((vu16*)(MEM_IO + 0x78)) | ||
546 | #define SOUND_NOISE_FREQ *((vu16*)(MEM_IO + 0x7C)) | ||
547 | #define SOUND_DMG_MASTER *((vu16*)(MEM_IO + 0x80)) | ||
548 | #define SOUND_DSOUND_MASTER *((vu16*)(MEM_IO + 0x82)) | ||
549 | #define SOUND_STATUS *((vu16*)(MEM_IO + 0x84)) | ||
550 | #define SOUND_BIAS *((vu16*)(MEM_IO + 0x88)) | ||
551 | |||
552 | // Sound DMG master bits. | ||
553 | #define SOUND_VOLUME_LEFT(N) (N) | ||
554 | #define SOUND_VOLUME_RIGHT(N) ((N) << 4) | ||
555 | #define SOUND_ENABLE_SQUARE1_LEFT (1 << 0x8) | ||
556 | #define SOUND_ENABLE_SQUARE2_LEFT (1 << 0x9) | ||
557 | #define SOUND_ENABLE_WAVE_LEFT (1 << 0xA) | ||
558 | #define SOUND_ENABLE_NOISE_LEFT (1 << 0xB) | ||
559 | #define SOUND_ENABLE_SQUARE1_RIGHT (1 << 0xC) | ||
560 | #define SOUND_ENABLE_SQUARE2_RIGHT (1 << 0xD) | ||
561 | #define SOUND_ENABLE_WAVE_RIGHT (1 << 0xE) | ||
562 | #define SOUND_ENABLE_NOISE_RIGHT (1 << 0xF) | ||
563 | |||
564 | typedef enum { | ||
565 | SOUND_SQUARE1 = (0x1 << 0), | ||
566 | SOUND_SQUARE2 = (0x1 << 1), | ||
567 | SOUND_WAVE = (0x1 << 2), | ||
568 | SOUND_NOISE = (0x1 << 3), | ||
569 | } SoundChannel; | ||
570 | |||
571 | u16 | ||
572 | sound_volume(SoundChannel channels, u8 volume) { | ||
573 | volume = volume & 0x7; | ||
574 | channels = channels & 0xF; | ||
575 | return volume | (volume << 0x4) | (channels << 0x8) | (channels << 0xC); | ||
576 | } | ||
577 | |||
578 | // Sound Direct Sound master bits. | ||
579 | #define SOUND_DMG25 0x0 | ||
580 | #define SOUND_DMG50 0x1 | ||
581 | #define SOUND_DMG100 0x2 | ||
582 | #define SOUND_DSOUND_RATIO_A (1 << 0x2) | ||
583 | #define SOUND_DSOUND_RATIO_B (1 << 0x3) | ||
584 | #define SOUND_DSOUND_LEFT_A (1 << 0x8) | ||
585 | #define SOUND_DSOUND_RIGHT_A (1 << 0x9) | ||
586 | #define SOUND_DSOUND_TIMER_A (1 << 0xA) | ||
587 | #define SOUND_DSOUND_RESET_A (1 << 0xB) | ||
588 | #define SOUND_DSOUND_LEFT_B (1 << 0xC) | ||
589 | #define SOUND_DSOUND_RIGHT_B (1 << 0xD) | ||
590 | #define SOUND_DSOUND_TIMER_B (1 << 0xE) | ||
591 | #define SOUND_DSOUND_RESET_B (1 << 0xF) | ||
592 | |||
593 | // Sound status bits. | ||
594 | #define SOUND_ENABLE (1 << 0x7) | ||
595 | |||
596 | // DMG square control bits. | ||
597 | #define SOUND_SQUARE_LEN(N) (N) | ||
598 | #define SOUND_SQUARE_DUTY(N) ((N) << 0x6) | ||
599 | #define SOUND_SQUARE_ENV_TIME(N) ((N) << 0x8) | ||
600 | #define SOUND_SQUARE_ENV_DIR(N) ((N) << 0xB) | ||
601 | #define SOUND_SQUARE_ENV_VOL(N) ((N) << 0xC) | ||
602 | |||
603 | // DMG square 1 sweep control bits. | ||
604 | #define SOUND_SWEEP_NUMBER(N) (N) | ||
605 | #define SOUND_SWEEP_DIR(N) ((N) << 0x3) | ||
606 | #define SOUND_SWEEP_TIME(N) ((N) << 0x4) | ||
607 | |||
608 | // DMG frequency bits (Square/Wave). | ||
609 | #define SOUND_FREQ_TIMED (1 << 0xE) | ||
610 | #define SOUND_FREQ_RESET (1 << 0xF) | ||
611 | |||
612 | // DMG wave ram. | ||
613 | #define SOUND_WAVE_RAM_0 *((vu32*)(MEM_IO + 0x90)) | ||
614 | #define SOUND_WAVE_RAM_1 *((vu32*)(MEM_IO + 0x94)) | ||
615 | #define SOUND_WAVE_RAM_2 *((vu32*)(MEM_IO + 0x98)) | ||
616 | #define SOUND_WAVE_RAM_3 *((vu32*)(MEM_IO + 0x9C)) | ||
617 | |||
618 | // DMG wave control bits. | ||
619 | #define SOUND_WAVE_LENGTH(N) (N) | ||
620 | #define SOUND_WAVE_MUTE 0x0 | ||
621 | #define SOUND_WAVE_VOL_100 (0x1 << 0xD) | ||
622 | #define SOUND_WAVE_VOL_75 (0x4 << 0xD) | ||
623 | #define SOUND_WAVE_VOL_50 (0x2 << 0xD) | ||
624 | #define SOUND_WAVE_VOL_25 (0x3 << 0xD) | ||
625 | |||
626 | // DMG wave mode bits. | ||
627 | #define SOUND_WAVE_BANK_MODE(N) ((N) << 0x5) | ||
628 | #define SOUND_WAVE_BANK_SELECT(N) ((N) << 0x6) | ||
629 | #define SOUND_WAVE_ENABLE (1 << 0x7) | ||
630 | |||
631 | typedef u8 WaveBank[32]; | ||
632 | |||
633 | // typedef u32 WaveBank[4]; | ||
634 | #define SOUND_WAVE_RAM ((WaveBank*)(MEM_IO + 0x90)) | ||
635 | |||
636 | typedef enum { | ||
637 | NOTE_C_2 , NOTE_C_SHARP_2 , NOTE_D_2 , NOTE_D_SHARP_2 , | ||
638 | NOTE_E_2 , NOTE_F_2 , NOTE_F_SHARP_2 , NOTE_G_2 , | ||
639 | NOTE_G_SHARP_2 , NOTE_A_2 , NOTE_A_SHARP_2 , NOTE_B_2 , | ||
640 | NOTE_C_3 , NOTE_C_SHARP_3 , NOTE_D_3 , NOTE_D_SHARP_3 , | ||
641 | NOTE_E_3 , NOTE_F_3 , NOTE_F_SHARP_3 , NOTE_G_3 , | ||
642 | NOTE_G_SHARP_3 , NOTE_A_3 , NOTE_A_SHARP_3 , NOTE_B_3 , | ||
643 | NOTE_C_4 , NOTE_C_SHARP_4 , NOTE_D_4 , NOTE_D_SHARP_4 , | ||
644 | NOTE_E_4 , NOTE_F_4 , NOTE_F_SHARP_4 , NOTE_G_4 , | ||
645 | NOTE_G_SHARP_4 , NOTE_A_4 , NOTE_A_SHARP_4 , NOTE_B_4 , | ||
646 | NOTE_C_5 , NOTE_C_SHARP_5 , NOTE_D_5 , NOTE_D_SHARP_5 , | ||
647 | NOTE_E_5 , NOTE_F_5 , NOTE_F_SHARP_5 , NOTE_G_5 , | ||
648 | NOTE_G_SHARP_5 , NOTE_A_5 , NOTE_A_SHARP_5 , NOTE_B_5 , | ||
649 | NOTE_C_6 , NOTE_C_SHARP_6 , NOTE_D_6 , NOTE_D_SHARP_6 , | ||
650 | NOTE_E_6 , NOTE_F_6 , NOTE_F_SHARP_6 , NOTE_G_6 , | ||
651 | NOTE_G_SHARP_6 , NOTE_A_6 , NOTE_A_SHARP_6 , NOTE_B_6 , | ||
652 | NOTE_C_7 , NOTE_C_SHARP_7 , NOTE_D_7 , NOTE_D_SHARP_7 , | ||
653 | NOTE_E_7 , NOTE_F_7 , NOTE_F_SHARP_7 , NOTE_G_7 , | ||
654 | NOTE_G_SHARP_7 , NOTE_A_7 , NOTE_A_SHARP_7 , NOTE_B_7 , | ||
655 | NOTE_C_8 | ||
656 | } Note; | ||
657 | |||
658 | const char * note_names[] = { | ||
659 | "C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2", | ||
660 | "C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", "A3", "A#3", "B3", | ||
661 | "C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4", | ||
662 | "C5", "C#5", "D5", "D#5", "E5", "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5", | ||
663 | "C6", "C#6", "D6", "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6", | ||
664 | "C7", "C#7", "D7", "D#7", "E7", "F7", "F#7", "G7", "G#7", "A7", "A#7", "B7", | ||
665 | "C8" | ||
666 | }; | ||
667 | |||
668 | const u32 sound_rates[] = { | ||
669 | 44 , 156 , 262 , 363 , 457 , 547 , 631 , 710 , 785 , 856 , 923 , 986 , | ||
670 | 1046, 1102, 1155, 1205, 1252, 1297, 1339, 1379, 1416, 1452, 1485, 1517, | ||
671 | 1547, 1575, 1601, 1626, 1650, 1672, 1693, 1713, 1732, 1750, 1766, 1782, | ||
672 | 1797, 1811, 1824, 1837, 1849, 1860, 1870, 1880, 1890, 1899, 1907, 1915, | ||
673 | 1922, 1929, 1936, 1942, 1948, 1954, 1959, 1964, 1969, 1973, 1977, 1981, | ||
674 | 1985, 1988, 1992, 1995, 1998, 2001, 2003, 2006, 2008, 2010, 2012, 2014, | ||
675 | 2016, | ||
676 | }; | ||
677 | |||
678 | // | ||
679 | // Misc. | ||
680 | // | ||
681 | |||
682 | // Custom VSync option. This will waste a lot of battery power, since the | ||
683 | // machine is not clocked down. Prefer using `bios_vblank_wait()` if interrupts | ||
684 | // are enabled. | ||
685 | static inline void | ||
686 | wait_vsync(void) { | ||
687 | while(DISP_VCOUNT >= 160); | ||
688 | while(DISP_VCOUNT < 160); | ||
689 | } | ||
690 | |||
691 | // General utility macros. | ||
692 | #define MIN(A, B) ((A) <= (B) ? (A) : (B)) | ||
693 | #define MAX(A, B) ((A) >= (B) ? (A) : (B)) | ||
694 | #define CLAMP(X, MIN, MAX) ((X) <= (MIN) ? (MIN) : (X) > (MAX) ? (MAX): (X)) | ||
695 | |||
696 | #endif // GBAEXP_COMMON_H | ||