diff options
author | Bad Diode <bd@badd10de.dev> | 2021-06-02 20:20:23 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-06-02 20:20:23 +0200 |
commit | 215465df74a6065f4b0fdf199b8b04454520a398 (patch) | |
tree | f979791df1ae25bf7da9644ce4a0d55d3b8f8fb4 /src/gba | |
parent | f6686f1e86927f038086023362251ebe78ce5ad6 (diff) | |
download | stepper-215465df74a6065f4b0fdf199b8b04454520a398.tar.gz stepper-215465df74a6065f4b0fdf199b8b04454520a398.zip |
Update the renderer to support a text layer
Diffstat (limited to 'src/gba')
-rw-r--r-- | src/gba/bios_calls.s | 337 | ||||
-rw-r--r-- | src/gba/gba.h | 667 | ||||
-rw-r--r-- | src/gba/interrupts.c | 91 | ||||
-rw-r--r-- | src/gba/interrupts.s | 89 |
4 files changed, 1184 insertions, 0 deletions
diff --git a/src/gba/bios_calls.s b/src/gba/bios_calls.s new file mode 100644 index 0000000..740fa02 --- /dev/null +++ b/src/gba/bios_calls.s | |||
@@ -0,0 +1,337 @@ | |||
1 | @ | ||
2 | @ Arithmetic functions. | ||
3 | @ | ||
4 | |||
5 | @ Division. | ||
6 | .text | ||
7 | .code 16 | ||
8 | .align 2 | ||
9 | .global bios_div | ||
10 | .thumb_func | ||
11 | bios_div: | ||
12 | swi 0x06 | ||
13 | bx lr | ||
14 | |||
15 | @ Square root. | ||
16 | .text | ||
17 | .code 16 | ||
18 | .align 2 | ||
19 | .global bios_sqrt | ||
20 | .thumb_func | ||
21 | bios_sqrt: | ||
22 | swi 0x08 | ||
23 | bx lr | ||
24 | |||
25 | @ Arc-tangent. | ||
26 | .text | ||
27 | .code 16 | ||
28 | .align 2 | ||
29 | .global bios_atan | ||
30 | .thumb_func | ||
31 | bios_atan: | ||
32 | swi 0x09 | ||
33 | bx lr | ||
34 | |||
35 | @ Arc-tangent2. | ||
36 | .text | ||
37 | .code 16 | ||
38 | .align 2 | ||
39 | .global bios_atan2 | ||
40 | .thumb_func | ||
41 | bios_atan2: | ||
42 | swi 0x0a | ||
43 | bx lr | ||
44 | |||
45 | @ | ||
46 | @ Rotation/Scaling. | ||
47 | @ | ||
48 | |||
49 | @ BG Affine Set. | ||
50 | .text | ||
51 | .code 16 | ||
52 | .align 2 | ||
53 | .global bios_bg_affine_set | ||
54 | .thumb_func | ||
55 | bios_bg_affine_set: | ||
56 | swi 0x0e | ||
57 | bx lr | ||
58 | |||
59 | @ OBJ Affine Set. | ||
60 | .text | ||
61 | .code 16 | ||
62 | .align 2 | ||
63 | .global bios_obj_affine_set | ||
64 | .thumb_func | ||
65 | bios_obj_affine_set: | ||
66 | swi 0x0f | ||
67 | bx lr | ||
68 | |||
69 | @ | ||
70 | @ (De)compression. | ||
71 | @ | ||
72 | |||
73 | @ Bit unpacking. | ||
74 | .text | ||
75 | .code 16 | ||
76 | .align 2 | ||
77 | .global bios_bit_unpack | ||
78 | .thumb_func | ||
79 | bios_bit_unpack: | ||
80 | swi 0x10 | ||
81 | bx lr | ||
82 | |||
83 | @ Huffman decompression. | ||
84 | .text | ||
85 | .code 16 | ||
86 | .align 2 | ||
87 | .global bios_huff_expand | ||
88 | .thumb_func | ||
89 | bios_huff_expand: | ||
90 | swi 0x13 | ||
91 | bx lr | ||
92 | |||
93 | @ LZ77 decompression (Fast, WRAM, 8 bit writes). | ||
94 | .text | ||
95 | .code 16 | ||
96 | .align 2 | ||
97 | .global bios_lz77_expand_wram | ||
98 | .thumb_func | ||
99 | bios_lz77_expand_wram: | ||
100 | swi 0x11 | ||
101 | bx lr | ||
102 | |||
103 | @ LZ77 decompression (Slow, VRAM, 16 bit writes). | ||
104 | .text | ||
105 | .code 16 | ||
106 | .align 2 | ||
107 | .global bios_lz77_expand_vram | ||
108 | .thumb_func | ||
109 | bios_lz77_expand_vram: | ||
110 | swi 0x12 | ||
111 | bx lr | ||
112 | |||
113 | @ Run length encoding decompression (Fast, WRAM, 8 bit writes). | ||
114 | .text | ||
115 | .code 16 | ||
116 | .align 2 | ||
117 | .global bios_rle_expand_wram | ||
118 | .thumb_func | ||
119 | bios_rle_expand_wram: | ||
120 | swi 0x14 | ||
121 | bx lr | ||
122 | |||
123 | @ Run length encoding decompression (Slow, VRAM, 16 bit writes). | ||
124 | .text | ||
125 | .code 16 | ||
126 | .align 2 | ||
127 | .global bios_rle_expand_vram | ||
128 | .thumb_func | ||
129 | bios_rle_expand_vram: | ||
130 | swi 0x15 | ||
131 | bx lr | ||
132 | |||
133 | @ | ||
134 | @ Memory copy. | ||
135 | @ | ||
136 | |||
137 | @ Memcopy/memfill in 32 byte units (Fast). | ||
138 | .text | ||
139 | .code 16 | ||
140 | .align 2 | ||
141 | .global bios_memcopy_32 | ||
142 | .thumb_func | ||
143 | bios_memcopy_32: | ||
144 | swi 0x0C | ||
145 | bx lr | ||
146 | |||
147 | @ Memcopy/memfill in 4 / 2 byte units (Slow). | ||
148 | .text | ||
149 | .code 16 | ||
150 | .align 2 | ||
151 | .global bios_memcopy_8 | ||
152 | .thumb_func | ||
153 | bios_memcopy_8: | ||
154 | swi 0x0B | ||
155 | bx lr | ||
156 | |||
157 | @ | ||
158 | @ Sound functions. | ||
159 | @ | ||
160 | |||
161 | @ MIDI key to frequency. | ||
162 | .text | ||
163 | .code 16 | ||
164 | .align 2 | ||
165 | .global bios_midi2freq | ||
166 | .thumb_func | ||
167 | bios_midi2freq: | ||
168 | swi 0x1f | ||
169 | bx lr | ||
170 | |||
171 | @ Sound bias. | ||
172 | .text | ||
173 | .code 16 | ||
174 | .align 2 | ||
175 | .global bios_sound_bias | ||
176 | .thumb_func | ||
177 | bios_sound_bias: | ||
178 | swi 0x19 | ||
179 | bx lr | ||
180 | |||
181 | @ Sound channels clear. | ||
182 | .text | ||
183 | .code 16 | ||
184 | .align 2 | ||
185 | .global bios_sound_clear | ||
186 | .thumb_func | ||
187 | bios_sound_clear: | ||
188 | swi 0x1e | ||
189 | bx lr | ||
190 | |||
191 | @ Sound driver initialization. | ||
192 | .text | ||
193 | .code 16 | ||
194 | .align 2 | ||
195 | .global bios_sound_init | ||
196 | .thumb_func | ||
197 | bios_sound_init: | ||
198 | swi 0x1a | ||
199 | bx lr | ||
200 | |||
201 | @ Sound main function. To be called each 1/60 of a second after the sound VSync. | ||
202 | .text | ||
203 | .code 16 | ||
204 | .align 2 | ||
205 | .global bios_sound_main | ||
206 | .thumb_func | ||
207 | bios_sound_main: | ||
208 | swi 0x1c | ||
209 | bx lr | ||
210 | |||
211 | @ Sound driver operation mode. | ||
212 | .text | ||
213 | .code 16 | ||
214 | .align 2 | ||
215 | .global bios_sound_mode | ||
216 | .thumb_func | ||
217 | bios_sound_mode: | ||
218 | swi 0x1b | ||
219 | bx lr | ||
220 | |||
221 | @ Sound VSync. Called just after the VBlank interrupt each 1/60 of a second. | ||
222 | .text | ||
223 | .code 16 | ||
224 | .align 2 | ||
225 | .global bios_sound_vsync | ||
226 | .thumb_func | ||
227 | bios_sound_vsync: | ||
228 | swi 0x1d | ||
229 | bx lr | ||
230 | |||
231 | @ Sound VSync off In case of issues manually call this to stop the sound. | ||
232 | .text | ||
233 | .code 16 | ||
234 | .align 2 | ||
235 | .global bios_sound_vsync_off | ||
236 | .thumb_func | ||
237 | bios_sound_vsync_off: | ||
238 | swi 0x28 | ||
239 | bx lr | ||
240 | |||
241 | @ | ||
242 | @ Halt/Reset functions. | ||
243 | @ | ||
244 | |||
245 | @ Halt until interrupt request. | ||
246 | .text | ||
247 | .code 16 | ||
248 | .align 2 | ||
249 | .global bios_halt | ||
250 | .thumb_func | ||
251 | bios_halt: | ||
252 | swi 0x02 | ||
253 | bx lr | ||
254 | |||
255 | @ Halt until one of the specified interrupts occur. | ||
256 | .text | ||
257 | .code 16 | ||
258 | .align 2 | ||
259 | .global bios_interrupt_wait | ||
260 | .thumb_func | ||
261 | bios_interrupt_wait: | ||
262 | swi 0x04 | ||
263 | bx lr | ||
264 | |||
265 | @ Halt until the VBlank interrupt occurs. | ||
266 | .text | ||
267 | .code 16 | ||
268 | .align 2 | ||
269 | .global bios_vblank_wait | ||
270 | .thumb_func | ||
271 | bios_vblank_wait: | ||
272 | swi 0x05 | ||
273 | bx lr | ||
274 | |||
275 | @ Stop. Switches GBA to low power mode. | ||
276 | .text | ||
277 | .code 16 | ||
278 | .align 2 | ||
279 | .global bios_stop | ||
280 | .thumb_func | ||
281 | bios_stop: | ||
282 | swi 0x03 | ||
283 | bx lr | ||
284 | |||
285 | @ Soft reset. | ||
286 | .text | ||
287 | .code 16 | ||
288 | .align 2 | ||
289 | .global bios_soft_reset | ||
290 | .thumb_func | ||
291 | bios_soft_reset: | ||
292 | swi 0x00 | ||
293 | bx lr | ||
294 | |||
295 | @ Register RAM reset. | ||
296 | .text | ||
297 | .code 16 | ||
298 | .align 2 | ||
299 | .global bios_regram_reset | ||
300 | .thumb_func | ||
301 | bios_regram_reset: | ||
302 | swi 0x01 | ||
303 | bx lr | ||
304 | |||
305 | @ Hard reset. | ||
306 | .text | ||
307 | .code 16 | ||
308 | .align 2 | ||
309 | .global bios_hard_reset | ||
310 | .thumb_func | ||
311 | bios_hard_reset: | ||
312 | swi 0x26 | ||
313 | bx lr | ||
314 | |||
315 | @ | ||
316 | @ Misc functions. | ||
317 | @ | ||
318 | |||
319 | @ BIOS checksum. | ||
320 | .text | ||
321 | .code 16 | ||
322 | .align 2 | ||
323 | .global bios_bios_checksum | ||
324 | .thumb_func | ||
325 | bios_bios_checksum: | ||
326 | swi 0x0d | ||
327 | bx lr | ||
328 | |||
329 | @ MultiBoot. | ||
330 | .text | ||
331 | .code 16 | ||
332 | .align 2 | ||
333 | .global bios_multiboot | ||
334 | .thumb_func | ||
335 | bios_multiboot: | ||
336 | swi 0x25 | ||
337 | bx lr | ||
diff --git a/src/gba/gba.h b/src/gba/gba.h new file mode 100644 index 0000000..56c4876 --- /dev/null +++ b/src/gba/gba.h | |||
@@ -0,0 +1,667 @@ | |||
1 | #ifndef GBA_H | ||
2 | #define GBA_H | ||
3 | |||
4 | #include "shorthand.h" | ||
5 | |||
6 | #define CPU_FREQUENCY (2 << 23) | ||
7 | |||
8 | // | ||
9 | // Memory sections. | ||
10 | // | ||
11 | |||
12 | // Defines for the different memory sections in the GBA. | ||
13 | #define MEM_SROM 0x00000000 | ||
14 | #define MEM_EW 0x02000000 | ||
15 | #define MEM_IW 0x03000000 | ||
16 | #define MEM_IO 0x04000000 | ||
17 | #define MEM_PAL 0x05000000 | ||
18 | #define MEM_VRAM 0x06000000 | ||
19 | #define MEM_OAM 0x07000000 | ||
20 | #define MEM_PAK 0x08000000 | ||
21 | #define MEM_CART 0x0E000000 | ||
22 | |||
23 | // | ||
24 | // Display modes. | ||
25 | // | ||
26 | |||
27 | // Display registers. | ||
28 | #define DISP_CTRL *((vu32*)(MEM_IO + 0x0000)) | ||
29 | #define DISP_STATUS *((vu16*)(MEM_IO + 0x0004)) | ||
30 | #define DISP_VCOUNT *((vu16*)(MEM_IO + 0x0006)) | ||
31 | |||
32 | // The first three bits in the DISP_CTRL are used to control the video mode. | ||
33 | #define DISP_MODE_0 0x0000 | ||
34 | #define DISP_MODE_1 0x0001 | ||
35 | #define DISP_MODE_2 0x0002 | ||
36 | #define DISP_MODE_3 0x0003 | ||
37 | #define DISP_MODE_4 0x0004 | ||
38 | #define DISP_MODE_5 0x0005 | ||
39 | #define DISP_GB (1 << 3) | ||
40 | #define DISP_PAGE (1 << 4) | ||
41 | #define DISP_OAM_HBLANK (1 << 5) | ||
42 | #define DISP_OBJ_1D (1 << 6) | ||
43 | #define DISP_BLANK (1 << 7) | ||
44 | #define DISP_BG_0 (1 << 8) | ||
45 | #define DISP_BG_1 (1 << 9) | ||
46 | #define DISP_BG_2 (1 << 10) | ||
47 | #define DISP_BG_3 (1 << 11) | ||
48 | #define DISP_OBJ (1 << 12) | ||
49 | #define DISP_ENABLE_SPRITES DISP_OBJ | DISP_OBJ_1D | ||
50 | |||
51 | // These bits are used to control the DISP_STATUS register. | ||
52 | #define DISP_VBLANK_STATUS (1 << 0x0) | ||
53 | #define DISP_HBLANK_STATUS (1 << 0x1) | ||
54 | #define DISP_VCOUNT_STATUS (1 << 0x2) | ||
55 | #define DISP_VBLANK_IRQ (1 << 0x3) | ||
56 | #define DISP_HBLANK_IRQ (1 << 0x4) | ||
57 | #define DISP_VCOUNT_IRQ (1 << 0x5) | ||
58 | #define DISP_VCOUNT_TRIGGER(N) ((N) << 0x8) | ||
59 | |||
60 | // Registers to control of BG layers. | ||
61 | #define BG_CTRL(N) *((vu16*)(0x04000008 + 0x0002 * (N))) | ||
62 | |||
63 | // Bits to control the background. | ||
64 | #define BG_PRIORITY(N) ((N) & 0x3) | ||
65 | #define BG_CHARBLOCK(N) ((N) << 2) | ||
66 | #define BG_MOSAIC (1 << 6) | ||
67 | #define BG_HIGH_COLOR (1 << 7) | ||
68 | #define BG_SCREENBLOCK(N) ((N) << 8) | ||
69 | #define BG_AFFINE (1 << 0xD) | ||
70 | #define BG_SIZE(N) ((N) << 0xE) | ||
71 | |||
72 | // BG registers for horizontal displacement. | ||
73 | #define BG_H_SCROLL_0 *((vu16*)(0x04000010 + 0x0004 * 0)) | ||
74 | #define BG_H_SCROLL_1 *((vu16*)(0x04000010 + 0x0004 * 1)) | ||
75 | #define BG_H_SCROLL_2 *((vu16*)(0x04000010 + 0x0004 * 2)) | ||
76 | #define BG_H_SCROLL_3 *((vu16*)(0x04000010 + 0x0004 * 3)) | ||
77 | |||
78 | // BG registers for vertical displacement. | ||
79 | #define BG_V_SCROLL_0 *((vu16*)(0x04000012 + 0x0004 * 0)) | ||
80 | #define BG_V_SCROLL_1 *((vu16*)(0x04000012 + 0x0004 * 1)) | ||
81 | #define BG_V_SCROLL_2 *((vu16*)(0x04000012 + 0x0004 * 2)) | ||
82 | #define BG_V_SCROLL_3 *((vu16*)(0x04000012 + 0x0004 * 3)) | ||
83 | |||
84 | // Screen settings. | ||
85 | #define SCREEN_WIDTH 240 | ||
86 | #define SCREEN_HEIGHT 160 | ||
87 | |||
88 | // | ||
89 | // Colors. | ||
90 | // | ||
91 | |||
92 | // The GBA in mode 3 expects rbg15 colors in the VRAM, where each component | ||
93 | // (RGB) have a 0--31 range. For example, pure red would be rgb15(31, 0, 0). | ||
94 | typedef u16 Color; | ||
95 | |||
96 | // A palette is composed of 16 colors, with color at index 0 being transparent | ||
97 | // for sprites. | ||
98 | typedef Color Palette[16]; | ||
99 | |||
100 | // Inline function to calculate the 15 bit color value. | ||
101 | #define RGB15(R,G,B) (u16)(((B) << 10) | ((G) << 5) | (R)); | ||
102 | |||
103 | // Some nice default colors. | ||
104 | #define COLOR_RED RGB15(31, 0,12) | ||
105 | #define COLOR_BLUE RGB15(2, 15,30) | ||
106 | #define COLOR_CYAN RGB15(0, 30,30) | ||
107 | #define COLOR_GREY RGB15(12,12,12) | ||
108 | #define COLOR_BLACK RGB15(0, 0, 0) | ||
109 | #define COLOR_WHITE RGB15(28,28,28) | ||
110 | |||
111 | // | ||
112 | // Tile memory access. | ||
113 | // | ||
114 | |||
115 | // NOTE: Only defining 4bpp tiles for now. | ||
116 | // TODO: typedef u32 Tile[8]; | ||
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 | inline 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 | |||
151 | // | ||
152 | // Sprites. | ||
153 | // | ||
154 | |||
155 | // Using macros instead of aligned structs for setting up OBJ attributes and | ||
156 | // affine parameters. | ||
157 | // TODO: Benchmark if this would be slower or the same that TONC's | ||
158 | // implementation. | ||
159 | // TODO: Cleanup OBJ/OAM memory copying and access. | ||
160 | #define OBJ_ATTR_0(N) *((vu16*)(MEM_OAM + 0 + 8 * (N))) | ||
161 | #define OBJ_ATTR_1(N) *((vu16*)(MEM_OAM + 2 + 8 * (N))) | ||
162 | #define OBJ_ATTR_2(N) *((vu16*)(MEM_OAM + 4 + 8 * (N))) | ||
163 | #define OBJ_AFFINE_PA(N) *((vs16*)(MEM_OAM + 6 + 8 * 0 + 8 * 4 * (N))) | ||
164 | #define OBJ_AFFINE_PB(N) *((vs16*)(MEM_OAM + 6 + 8 * 1 + 8 * 4 * (N))) | ||
165 | #define OBJ_AFFINE_PC(N) *((vs16*)(MEM_OAM + 6 + 8 * 2 + 8 * 4 * (N))) | ||
166 | #define OBJ_AFFINE_PD(N) *((vs16*)(MEM_OAM + 6 + 8 * 3 + 8 * 4 * (N))) | ||
167 | |||
168 | // OBJ_ATTR_0 parameters | ||
169 | #define OBJ_Y_COORD(N) ((N) & 0xFF) | ||
170 | #define OBJ_NORMAL (0x00 << 0x8) | ||
171 | #define OBJ_AFFINE (0x01 << 0x8) | ||
172 | #define OBJ_HIDDEN (0x02 << 0x8) | ||
173 | #define OBJ_AFFINE_2X (0x03 << 0x8) | ||
174 | #define OBJ_ALPHA_BLEND (0x01 << 0xA) | ||
175 | #define OBJ_WINDOW (0x02 << 0xA) | ||
176 | #define OBJ_SHAPE_SQUARE (0x00 << 0xE) | ||
177 | #define OBJ_SHAPE_WIDE (0x01 << 0xE) | ||
178 | #define OBJ_SHAPE_TALL (0x02 << 0xE) | ||
179 | |||
180 | // OBJ_ATTR_1 parameters | ||
181 | #define OBJ_X_COORD(N) ((N) & 0x1FF) | ||
182 | #define OBJ_AFFINE_IDX(N) ((N) << 0x9) | ||
183 | #define OBJ_H_FLIP (0x01 << 0xC) | ||
184 | #define OBJ_V_FLIP (0x01 << 0xD) | ||
185 | #define OBJ_SIZE_SMALL (0x00 << 0xE) | ||
186 | #define OBJ_SIZE_MID (0x01 << 0xE) | ||
187 | #define OBJ_SIZE_BIG (0x02 << 0xE) | ||
188 | #define OBJ_SIZE_HUGE (0x03 << 0xE) | ||
189 | |||
190 | // OBJ_ATTR_2 parameters | ||
191 | #define OBJ_TILE_INDEX(N) ((N) & 0x3FF) | ||
192 | #define OBJ_PRIORITY(N) ((N) << 0xA) | ||
193 | #define OBJ_PAL_BANK(N) ((N) << 0xC) | ||
194 | |||
195 | // | ||
196 | // Mode 4 page flipping | ||
197 | // | ||
198 | |||
199 | static inline | ||
200 | void | ||
201 | flip_page(vu16 *backbuffer) { | ||
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 | ||
237 | profile_start(void) { | ||
238 | TIMER_DATA_2 = 0; | ||
239 | TIMER_DATA_3 = 0; | ||
240 | TIMER_CTRL_2 = 0; | ||
241 | TIMER_CTRL_3 = 0; | ||
242 | TIMER_CTRL_3 = TIMER_CTRL_ENABLE | TIMER_CTRL_CASCADE; | ||
243 | TIMER_CTRL_2 = TIMER_CTRL_ENABLE; | ||
244 | } | ||
245 | |||
246 | static inline | ||
247 | u32 | ||
248 | profile_stop(void) { | ||
249 | TIMER_CTRL_2 = 0; | ||
250 | return (TIMER_DATA_3 << 16) | TIMER_DATA_2; | ||
251 | } | ||
252 | |||
253 | static inline | ||
254 | u32 | ||
255 | profile_measure(void) { | ||
256 | return (TIMER_DATA_3 << 16) | TIMER_DATA_2; | ||
257 | } | ||
258 | |||
259 | // | ||
260 | // Input handling. | ||
261 | // | ||
262 | |||
263 | // Memory address for key input and control register | ||
264 | #define KEY_INPUTS *((vu16*) 0x04000130) | ||
265 | #define KEY_CTRL *((vu16*) 0x04000132) | ||
266 | |||
267 | // Key control register bits. | ||
268 | #define KEY_IRQ_KEY(N) (N) | ||
269 | #define KEY_IRQ (1 << 0xE) | ||
270 | #define KEY_IRQ_IF_SET (1 << 0xF) | ||
271 | |||
272 | // Alias for key pressing bits. | ||
273 | #define KEY_A (1 << 0) | ||
274 | #define KEY_B (1 << 1) | ||
275 | #define KEY_SELECT (1 << 2) | ||
276 | #define KEY_START (1 << 3) | ||
277 | #define KEY_RIGHT (1 << 4) | ||
278 | #define KEY_LEFT (1 << 5) | ||
279 | #define KEY_UP (1 << 6) | ||
280 | #define KEY_DOWN (1 << 7) | ||
281 | #define KEY_R (1 << 8) | ||
282 | #define KEY_L (1 << 9) | ||
283 | |||
284 | #define KEY_MASK 0x03FF | ||
285 | |||
286 | // Saving the previous and current key states as globals for now. | ||
287 | static u16 key_curr = 0; | ||
288 | static u16 key_prev = 0; | ||
289 | |||
290 | static inline | ||
291 | void | ||
292 | poll_keys(void) { | ||
293 | key_prev = key_curr; | ||
294 | key_curr = ~KEY_INPUTS & KEY_MASK; | ||
295 | } | ||
296 | |||
297 | // Returns true if the given key has been pressed at time of calling and was not | ||
298 | // pressed since the previous call. For example, if a key is being held, this | ||
299 | // function will return `true` only on the frame where the key initially | ||
300 | // activated. | ||
301 | static inline | ||
302 | u32 | ||
303 | key_tap(u32 key) { | ||
304 | return (key_curr & key) & ~(key_prev & key); | ||
305 | } | ||
306 | |||
307 | // Check if a given key is currently pressed. | ||
308 | static inline | ||
309 | u32 | ||
310 | key_pressed(u32 key) { | ||
311 | return (key_curr & key); | ||
312 | } | ||
313 | |||
314 | // Check if a given key was just released. | ||
315 | static inline | ||
316 | u32 | ||
317 | key_released(u32 key) { | ||
318 | return ~(key_curr & key) & (key_prev & key); | ||
319 | } | ||
320 | |||
321 | // Check if the given key is pressed and has been since at least one frame. | ||
322 | static inline | ||
323 | u32 | ||
324 | key_hold(u32 key) { | ||
325 | return key_curr & key_prev & key; | ||
326 | } | ||
327 | |||
328 | // Check if the given key/button is currently pressed. | ||
329 | #define KEY_PRESSED(key) (~(KEY_INPUTS) & key) | ||
330 | |||
331 | // Back/unpack bits. | ||
332 | static inline | ||
333 | u32 | ||
334 | unpack_1bb(u8 hex) { | ||
335 | const u32 conversion_u32[16] = { | ||
336 | 0x00000000, 0x00000001, 0x00000010, 0x00000011, | ||
337 | 0x00000100, 0x00000101, 0x00000110, 0x00000111, | ||
338 | 0x00001000, 0x00001001, 0x00001010, 0x00001011, | ||
339 | 0x00001100, 0x00001101, 0x00001110, 0x00001111, | ||
340 | }; | ||
341 | u8 low = hex & 0xF; | ||
342 | u8 high = (hex >> 4) & 0xF; | ||
343 | return (conversion_u32[high] << 16) | conversion_u32[low]; | ||
344 | } | ||
345 | |||
346 | // Unpack N tiles packed at 1bpp. | ||
347 | static inline | ||
348 | void | ||
349 | unpack_tiles(u32 *src, u32 *dst, size_t n_tiles) { | ||
350 | u32 *target_src = src + n_tiles * 2; | ||
351 | while (src != target_src) { | ||
352 | *dst++ = unpack_1bb((*src >> 24) & 0xFF); | ||
353 | *dst++ = unpack_1bb((*src >> 16) & 0xFF); | ||
354 | *dst++ = unpack_1bb((*src >> 8) & 0xFF); | ||
355 | *dst++ = unpack_1bb(*src & 0xFF); | ||
356 | src++; | ||
357 | } | ||
358 | } | ||
359 | |||
360 | // | ||
361 | // Direct Memory Access (DMA) | ||
362 | // | ||
363 | |||
364 | |||
365 | // Source, destination, and control registers. | ||
366 | #define DMA_SRC(N) *((vu32*) 0x040000B0 + (N) * 12) | ||
367 | #define DMA_DST(N) *((vu32*) 0x040000B4 + (N) * 12) | ||
368 | #define DMA_CTRL(N) *((vu32*) 0x040000B8 + (N) * 12) | ||
369 | |||
370 | // DMA control bits. | ||
371 | #define DMA_DST_INC (0 << 0x15) | ||
372 | #define DMA_DST_DEC (1 << 0x15) | ||
373 | #define DMA_DST_FIXED (2 << 0x15) | ||
374 | #define DMA_DST_RELOAD (3 << 0x15) | ||
375 | #define DMA_SRC_INC (0 << 0x17) | ||
376 | #define DMA_SRC_DEC (1 << 0x17) | ||
377 | #define DMA_SRC_FIXED (2 << 0x17) | ||
378 | #define DMA_REPEAT (1 << 0x19) | ||
379 | #define DMA_CHUNK_16 (0 << 0x1A) | ||
380 | #define DMA_CHUNK_32 (1 << 0x1A) | ||
381 | #define DMA_NOW (0 << 0x1C) | ||
382 | #define DMA_VBLANK (1 << 0x1C) | ||
383 | #define DMA_HBLANK (2 << 0x1C) | ||
384 | #define DMA_REFRESH (3 << 0x1C) | ||
385 | #define DMA_IRQ (1 << 0x1E) | ||
386 | #define DMA_ENABLE (1 << 0x1F) | ||
387 | |||
388 | // Custom struct for cleaner DMA transfer functions. | ||
389 | typedef struct DmaStr { | ||
390 | const void *src; | ||
391 | void *dst; | ||
392 | u32 ctrl; | ||
393 | } DmaStr; | ||
394 | |||
395 | #define DMA_TRANSFER ((volatile DmaStr*) 0x040000B0) | ||
396 | |||
397 | // Transfer `count` number of chunks from src to dst using a DMA channel. Note | ||
398 | // that chunks are not bytes, but instead configured based on bits set by | ||
399 | // DMA_CTRL. | ||
400 | inline | ||
401 | void | ||
402 | dma_transfer_copy(void *dst, const void *src, u32 count, int channel, u32 options) { | ||
403 | DMA_TRANSFER[channel].ctrl = 0; | ||
404 | DMA_TRANSFER[channel].src = src; | ||
405 | DMA_TRANSFER[channel].dst = dst; | ||
406 | DMA_TRANSFER[channel].ctrl = count | options; | ||
407 | } | ||
408 | |||
409 | inline | ||
410 | void | ||
411 | dma_transfer_fill(void *dst, volatile u32 src, u32 count, int channel, u32 options) { | ||
412 | DMA_TRANSFER[channel].ctrl = 0; | ||
413 | DMA_TRANSFER[channel].src = (const void *)&src; | ||
414 | DMA_TRANSFER[channel].dst = dst; | ||
415 | DMA_TRANSFER[channel].ctrl = count | options | DMA_SRC_FIXED; | ||
416 | } | ||
417 | |||
418 | // Copy N number of bytes using a DMA channel. | ||
419 | inline | ||
420 | void | ||
421 | dma_copy(void *dst, const void *src, u32 size, int channel) { | ||
422 | dma_transfer_copy(dst, src, size / 4, channel, DMA_CHUNK_32 | DMA_ENABLE); | ||
423 | } | ||
424 | |||
425 | // Fill the dst location with the word set at src. | ||
426 | inline | ||
427 | void | ||
428 | dma_fill(void *dst, vu32 src, u32 size, int channel) { | ||
429 | dma_transfer_fill(dst, src, size / 4, channel, DMA_CHUNK_32 | DMA_ENABLE); | ||
430 | } | ||
431 | |||
432 | // | ||
433 | // Interrupts. | ||
434 | // | ||
435 | |||
436 | #define IRQ_ENABLE *((vu16*) 0x04000200) | ||
437 | #define IRQ_ACK *((vu16*) 0x04000202) | ||
438 | #define IRQ_CTRL *((vu16*) 0x04000208) | ||
439 | #define IRQ_ACK_BIOS *((vu16*) 0x03007FF8) | ||
440 | |||
441 | typedef enum { | ||
442 | IRQ_VBLANK, | ||
443 | IRQ_HBLANK, | ||
444 | IRQ_VCOUNT, | ||
445 | IRQ_TIMER_0, | ||
446 | IRQ_TIMER_1, | ||
447 | IRQ_TIMER_2, | ||
448 | IRQ_TIMER_3, | ||
449 | IRQ_SERIAL, | ||
450 | IRQ_DMA_0, | ||
451 | IRQ_DMA_1, | ||
452 | IRQ_DMA_2, | ||
453 | IRQ_DMA_3, | ||
454 | IRQ_KEYPAD, | ||
455 | IRQ_GAMEPAK, | ||
456 | } IrqIndex; | ||
457 | |||
458 | typedef void (*IrsFunc)(void); | ||
459 | |||
460 | // Stub function pointer needed for when we want to enable interrupts that don't | ||
461 | // require a custom function, such as for the BIOS VSync. | ||
462 | void irs_stub(void); | ||
463 | |||
464 | // Set and enable a given function in the interrupt table. If func is NULL, the | ||
465 | // interrupt will be disabled instead. | ||
466 | void irs_set(IrqIndex idx, IrsFunc func); | ||
467 | |||
468 | // Initialize the function pointer for the main IRS routine written in ARM | ||
469 | // assembly and enable interrupts. | ||
470 | void irq_init(void); | ||
471 | |||
472 | // | ||
473 | // BIOS function declarations. | ||
474 | // | ||
475 | |||
476 | // These functions declarations can be used to call the BIOS functions from the | ||
477 | // asm code. | ||
478 | int bios_vblank_wait(); | ||
479 | int bios_div(int num, int denom); | ||
480 | |||
481 | // | ||
482 | // Sound. | ||
483 | // | ||
484 | |||
485 | // Sound registers. | ||
486 | #define SOUND_SQUARE1_SWEEP *((vu16*)(MEM_IO + 0x60)) | ||
487 | #define SOUND_SQUARE1_CTRL *((vu16*)(MEM_IO + 0x62)) | ||
488 | #define SOUND_SQUARE1_FREQ *((vu16*)(MEM_IO + 0x64)) | ||
489 | #define SOUND_SQUARE2_CTRL *((vu16*)(MEM_IO + 0x68)) | ||
490 | #define SOUND_SQUARE2_FREQ *((vu16*)(MEM_IO + 0x6C)) | ||
491 | #define SOUND_WAVE_MODE *((vu16*)(MEM_IO + 0x70)) | ||
492 | #define SOUND_WAVE_CTRL *((vu16*)(MEM_IO + 0x72)) | ||
493 | #define SOUND_WAVE_FREQ *((vu16*)(MEM_IO + 0x74)) | ||
494 | #define SOUND_NOISE_CTRL *((vu16*)(MEM_IO + 0x78)) | ||
495 | #define SOUND_NOISE_FREQ *((vu16*)(MEM_IO + 0x7C)) | ||
496 | #define SOUND_DMG_MASTER *((vu16*)(MEM_IO + 0x80)) | ||
497 | #define SOUND_DSOUND_MASTER *((vu16*)(MEM_IO + 0x82)) | ||
498 | #define SOUND_STATUS *((vu16*)(MEM_IO + 0x84)) | ||
499 | #define SOUND_BIAS *((vu16*)(MEM_IO + 0x88)) | ||
500 | |||
501 | // Sound DMG master bits. | ||
502 | #define SOUND_VOLUME_LEFT(N) (N) | ||
503 | #define SOUND_VOLUME_RIGHT(N) ((N) << 4) | ||
504 | #define SOUND_ENABLE_SQUARE1_LEFT (1 << 0x8) | ||
505 | #define SOUND_ENABLE_SQUARE2_LEFT (1 << 0x9) | ||
506 | #define SOUND_ENABLE_WAVE_LEFT (1 << 0xA) | ||
507 | #define SOUND_ENABLE_NOISE_LEFT (1 << 0xB) | ||
508 | #define SOUND_ENABLE_SQUARE1_RIGHT (1 << 0xC) | ||
509 | #define SOUND_ENABLE_SQUARE2_RIGHT (1 << 0xD) | ||
510 | #define SOUND_ENABLE_WAVE_RIGHT (1 << 0xE) | ||
511 | #define SOUND_ENABLE_NOISE_RIGHT (1 << 0xF) | ||
512 | |||
513 | typedef enum { | ||
514 | SOUND_DSOUND = (0x0 << 0), | ||
515 | SOUND_SQUARE1 = (0x1 << 0), | ||
516 | SOUND_SQUARE2 = (0x1 << 1), | ||
517 | SOUND_WAVE = (0x1 << 2), | ||
518 | SOUND_NOISE = (0x1 << 3), | ||
519 | } SoundChannel; | ||
520 | |||
521 | inline u16 | ||
522 | sound_volume(SoundChannel channels, u8 volume) { | ||
523 | volume = volume & 0x7; | ||
524 | channels = channels & 0xF; | ||
525 | return volume | (volume << 0x4) | (channels << 0x8) | (channels << 0xC); | ||
526 | } | ||
527 | |||
528 | // Sound Direct Sound master bits. | ||
529 | #define SOUND_DMG25 0x0 | ||
530 | #define SOUND_DMG50 0x1 | ||
531 | #define SOUND_DMG100 0x2 | ||
532 | #define SOUND_DSOUND_RATIO_A (1 << 0x2) | ||
533 | #define SOUND_DSOUND_RATIO_B (1 << 0x3) | ||
534 | #define SOUND_DSOUND_LEFT_A (1 << 0x8) | ||
535 | #define SOUND_DSOUND_RIGHT_A (1 << 0x9) | ||
536 | #define SOUND_DSOUND_TIMER_A (1 << 0xA) | ||
537 | #define SOUND_DSOUND_RESET_A (1 << 0xB) | ||
538 | #define SOUND_DSOUND_LEFT_B (1 << 0xC) | ||
539 | #define SOUND_DSOUND_RIGHT_B (1 << 0xD) | ||
540 | #define SOUND_DSOUND_TIMER_B (1 << 0xE) | ||
541 | #define SOUND_DSOUND_RESET_B (1 << 0xF) | ||
542 | |||
543 | // Direct sound FIFO queues. | ||
544 | #define SOUND_FIFO_A ((u16*)(MEM_IO + 0xA0)) | ||
545 | #define SOUND_FIFO_B ((u16*)(MEM_IO + 0xA4)) | ||
546 | |||
547 | // Sound status bits. | ||
548 | #define SOUND_ENABLE (1 << 0x7) | ||
549 | |||
550 | // DMG square control bits. | ||
551 | #define SOUND_SQUARE_LEN(N) (N) | ||
552 | #define SOUND_SQUARE_DUTY(N) ((N) << 0x6) | ||
553 | #define SOUND_SQUARE_ENV_TIME(N) ((N) << 0x8) | ||
554 | #define SOUND_SQUARE_ENV_DIR(N) ((N) << 0xB) | ||
555 | #define SOUND_SQUARE_ENV_VOL(N) ((N) << 0xC) | ||
556 | |||
557 | // DMG square 1 sweep control bits. | ||
558 | #define SOUND_SWEEP_NUMBER(N) (N) | ||
559 | #define SOUND_SWEEP_DIR(N) ((N) << 0x3) | ||
560 | #define SOUND_SWEEP_TIME(N) ((N) << 0x4) | ||
561 | |||
562 | // DMG frequency bits (Square/Wave). | ||
563 | #define SOUND_FREQ_TIMED (1 << 0xE) | ||
564 | #define SOUND_FREQ_RESET (1 << 0xF) | ||
565 | |||
566 | // DMG wave ram. | ||
567 | #define SOUND_WAVE_RAM_0 *((vu32*)(MEM_IO + 0x90)) | ||
568 | #define SOUND_WAVE_RAM_1 *((vu32*)(MEM_IO + 0x94)) | ||
569 | #define SOUND_WAVE_RAM_2 *((vu32*)(MEM_IO + 0x98)) | ||
570 | #define SOUND_WAVE_RAM_3 *((vu32*)(MEM_IO + 0x9C)) | ||
571 | |||
572 | // DMG wave control bits. | ||
573 | #define SOUND_WAVE_LENGTH(N) (N) | ||
574 | #define SOUND_WAVE_MUTE 0x0 | ||
575 | #define SOUND_WAVE_VOL_100 (0x1 << 0xD) | ||
576 | #define SOUND_WAVE_VOL_75 (0x4 << 0xD) | ||
577 | #define SOUND_WAVE_VOL_50 (0x2 << 0xD) | ||
578 | #define SOUND_WAVE_VOL_25 (0x3 << 0xD) | ||
579 | |||
580 | // DMG wave mode bits. | ||
581 | #define SOUND_WAVE_BANK_MODE(N) ((N) << 0x5) | ||
582 | #define SOUND_WAVE_BANK_SELECT(N) ((N) << 0x6) | ||
583 | #define SOUND_WAVE_ENABLE (1 << 0x7) | ||
584 | |||
585 | typedef u8 WaveBank[32]; | ||
586 | |||
587 | #define SOUND_WAVE_RAM ((WaveBank*)(MEM_IO + 0x90)) | ||
588 | |||
589 | typedef enum { | ||
590 | NOTE_C_2 , NOTE_C_SHARP_2 , NOTE_D_2 , NOTE_D_SHARP_2 , | ||
591 | NOTE_E_2 , NOTE_F_2 , NOTE_F_SHARP_2 , NOTE_G_2 , | ||
592 | NOTE_G_SHARP_2 , NOTE_A_2 , NOTE_A_SHARP_2 , NOTE_B_2 , | ||
593 | NOTE_C_3 , NOTE_C_SHARP_3 , NOTE_D_3 , NOTE_D_SHARP_3 , | ||
594 | NOTE_E_3 , NOTE_F_3 , NOTE_F_SHARP_3 , NOTE_G_3 , | ||
595 | NOTE_G_SHARP_3 , NOTE_A_3 , NOTE_A_SHARP_3 , NOTE_B_3 , | ||
596 | NOTE_C_4 , NOTE_C_SHARP_4 , NOTE_D_4 , NOTE_D_SHARP_4 , | ||
597 | NOTE_E_4 , NOTE_F_4 , NOTE_F_SHARP_4 , NOTE_G_4 , | ||
598 | NOTE_G_SHARP_4 , NOTE_A_4 , NOTE_A_SHARP_4 , NOTE_B_4 , | ||
599 | NOTE_C_5 , NOTE_C_SHARP_5 , NOTE_D_5 , NOTE_D_SHARP_5 , | ||
600 | NOTE_E_5 , NOTE_F_5 , NOTE_F_SHARP_5 , NOTE_G_5 , | ||
601 | NOTE_G_SHARP_5 , NOTE_A_5 , NOTE_A_SHARP_5 , NOTE_B_5 , | ||
602 | NOTE_C_6 , NOTE_C_SHARP_6 , NOTE_D_6 , NOTE_D_SHARP_6 , | ||
603 | NOTE_E_6 , NOTE_F_6 , NOTE_F_SHARP_6 , NOTE_G_6 , | ||
604 | NOTE_G_SHARP_6 , NOTE_A_6 , NOTE_A_SHARP_6 , NOTE_B_6 , | ||
605 | NOTE_C_7 , NOTE_C_SHARP_7 , NOTE_D_7 , NOTE_D_SHARP_7 , | ||
606 | NOTE_E_7 , NOTE_F_7 , NOTE_F_SHARP_7 , NOTE_G_7 , | ||
607 | NOTE_G_SHARP_7 , NOTE_A_7 , NOTE_A_SHARP_7 , NOTE_B_7 , | ||
608 | NOTE_C_8 | ||
609 | } Note; | ||
610 | |||
611 | static const u32 sound_rates[] = { | ||
612 | 44 , 156 , 262 , 363 , 457 , 547 , 631 , 710 , 785 , 856 , 923 , 986 , | ||
613 | 1046, 1102, 1155, 1205, 1252, 1297, 1339, 1379, 1416, 1452, 1485, 1517, | ||
614 | 1547, 1575, 1601, 1626, 1650, 1672, 1693, 1713, 1732, 1750, 1766, 1782, | ||
615 | 1797, 1811, 1824, 1837, 1849, 1860, 1870, 1880, 1890, 1899, 1907, 1915, | ||
616 | 1922, 1929, 1936, 1942, 1948, 1954, 1959, 1964, 1969, 1973, 1977, 1981, | ||
617 | 1985, 1988, 1992, 1995, 1998, 2001, 2003, 2006, 2008, 2010, 2012, 2014, | ||
618 | 2016, | ||
619 | }; | ||
620 | |||
621 | // | ||
622 | // System control. | ||
623 | // | ||
624 | |||
625 | // Used to configure gamepak access timings. | ||
626 | #define SYSTEM_WAIT *((vu16*)(MEM_IO + 0x0204)) | ||
627 | |||
628 | // This register defaults to 0, but manufacture cartridges use the values | ||
629 | // provided below. | ||
630 | #define SYSTEM_WAIT_DEFAULT 0 | ||
631 | #define SYSTEM_WAIT_CARTRIDGE 0x4317 | ||
632 | |||
633 | // | ||
634 | // Misc. | ||
635 | // | ||
636 | |||
637 | // Custom VSync option. This will waste a lot of battery power, since the | ||
638 | // machine is not clocked down. Prefer using `bios_vblank_wait()` if interrupts | ||
639 | // are enabled. | ||
640 | static inline void | ||
641 | wait_vsync(void) { | ||
642 | while(DISP_VCOUNT >= 160); | ||
643 | while(DISP_VCOUNT < 160); | ||
644 | } | ||
645 | |||
646 | // General utility macros. | ||
647 | #define MIN(A, B) ((A) <= (B) ? (A) : (B)) | ||
648 | #define MAX(A, B) ((A) >= (B) ? (A) : (B)) | ||
649 | #define CLAMP(X, MIN, MAX) ((X) <= (MIN) ? (MIN) : (X) > (MAX) ? (MAX): (X)) | ||
650 | #define LEN(ARR) (sizeof(ARR) / sizeof((ARR)[0])) | ||
651 | |||
652 | // Fixed-point arithmetic for (i.P) numbers. | ||
653 | #define FP_MUL(A,B,P) (((A) * (B)) >> (P)) | ||
654 | #define FP_DIV(A,B,P) (((A) << (P)) / (B)) | ||
655 | #define FP_LERP(Y0,Y1,X,P) ((Y0) + FP_MUL((X), ((Y1) - (Y0)), P)) | ||
656 | |||
657 | // | ||
658 | // Memory section macros for devkitARM. | ||
659 | // | ||
660 | |||
661 | #define IWRAM_DATA __attribute__((section(".iwram"))) | ||
662 | #define IWRAM_CODE __attribute__((section(".iwram"), long_call, target("arm"))) | ||
663 | #define EWRAM_DATA __attribute__((section(".ewram"))) | ||
664 | #define EWRAM_CODE __attribute__((section(".ewram"), long_call)) | ||
665 | #define EWRAM_BSS __attribute__((section(".sbss"))) | ||
666 | |||
667 | #endif // GBA_H | ||
diff --git a/src/gba/interrupts.c b/src/gba/interrupts.c new file mode 100644 index 0000000..3b11335 --- /dev/null +++ b/src/gba/interrupts.c | |||
@@ -0,0 +1,91 @@ | |||
1 | #include "gba.h" | ||
2 | |||
3 | IrsFunc irs_table[] = { | ||
4 | [IRQ_VBLANK ] = NULL, | ||
5 | [IRQ_HBLANK ] = NULL, | ||
6 | [IRQ_VCOUNT ] = NULL, | ||
7 | [IRQ_TIMER_0] = NULL, | ||
8 | [IRQ_TIMER_1] = NULL, | ||
9 | [IRQ_TIMER_2] = NULL, | ||
10 | [IRQ_TIMER_3] = NULL, | ||
11 | [IRQ_SERIAL ] = NULL, | ||
12 | [IRQ_DMA_0 ] = NULL, | ||
13 | [IRQ_DMA_1 ] = NULL, | ||
14 | [IRQ_DMA_2 ] = NULL, | ||
15 | [IRQ_DMA_3 ] = NULL, | ||
16 | [IRQ_KEYPAD ] = NULL, | ||
17 | [IRQ_GAMEPAK] = NULL, | ||
18 | }; | ||
19 | |||
20 | // External irs_main function, has to be written in ARM assembly. | ||
21 | void irs_main(void); | ||
22 | #define IRS_MAIN *(IrsFunc*)(0x03007FFC) | ||
23 | |||
24 | void | ||
25 | irq_enable(IrqIndex idx) { | ||
26 | switch (idx) { | ||
27 | case IRQ_VBLANK: { DISP_STATUS |= DISP_VBLANK_IRQ; } break; | ||
28 | case IRQ_HBLANK: { DISP_STATUS |= DISP_HBLANK_IRQ; } break; | ||
29 | case IRQ_VCOUNT: { DISP_STATUS |= DISP_VCOUNT_IRQ; } break; | ||
30 | case IRQ_TIMER_0: { TIMER_CTRL_0 |= TIMER_CTRL_IRQ; } break; | ||
31 | case IRQ_TIMER_1: { TIMER_CTRL_1 |= TIMER_CTRL_IRQ; } break; | ||
32 | case IRQ_TIMER_2: { TIMER_CTRL_2 |= TIMER_CTRL_IRQ; } break; | ||
33 | case IRQ_TIMER_3: { TIMER_CTRL_3 |= TIMER_CTRL_IRQ; } break; | ||
34 | case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; | ||
35 | case IRQ_DMA_0: { DMA_CTRL(0) |= DMA_IRQ; } break; | ||
36 | case IRQ_DMA_1: { DMA_CTRL(1) |= DMA_IRQ; } break; | ||
37 | case IRQ_DMA_2: { DMA_CTRL(2) |= DMA_IRQ; } break; | ||
38 | case IRQ_DMA_3: { DMA_CTRL(3) |= DMA_IRQ; } break; | ||
39 | case IRQ_KEYPAD: { KEY_CTRL |= KEY_IRQ; } break; | ||
40 | case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; | ||
41 | } | ||
42 | IRQ_ENABLE |= (1 << idx); | ||
43 | } | ||
44 | |||
45 | void | ||
46 | irq_disable(IrqIndex idx) { | ||
47 | switch (idx) { | ||
48 | case IRQ_VBLANK: { DISP_STATUS &= ~DISP_VBLANK_IRQ; } break; | ||
49 | case IRQ_HBLANK: { DISP_STATUS &= ~DISP_HBLANK_IRQ; } break; | ||
50 | case IRQ_VCOUNT: { DISP_STATUS &= ~DISP_VCOUNT_IRQ; } break; | ||
51 | case IRQ_TIMER_0: { TIMER_CTRL_0 &= ~TIMER_CTRL_IRQ; } break; | ||
52 | case IRQ_TIMER_1: { TIMER_CTRL_1 &= ~TIMER_CTRL_IRQ; } break; | ||
53 | case IRQ_TIMER_2: { TIMER_CTRL_2 &= ~TIMER_CTRL_IRQ; } break; | ||
54 | case IRQ_TIMER_3: { TIMER_CTRL_3 &= ~TIMER_CTRL_IRQ; } break; | ||
55 | case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; | ||
56 | case IRQ_DMA_0: { DMA_CTRL(0) &= ~DMA_IRQ; } break; | ||
57 | case IRQ_DMA_1: { DMA_CTRL(1) &= ~DMA_IRQ; } break; | ||
58 | case IRQ_DMA_2: { DMA_CTRL(2) &= ~DMA_IRQ; } break; | ||
59 | case IRQ_DMA_3: { DMA_CTRL(3) &= ~DMA_IRQ; } break; | ||
60 | case IRQ_KEYPAD: { KEY_CTRL &= ~KEY_IRQ; } break; | ||
61 | case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; | ||
62 | } | ||
63 | IRQ_ENABLE &= ~(1 << idx); | ||
64 | } | ||
65 | |||
66 | void | ||
67 | irs_set(IrqIndex idx, IrsFunc func) { | ||
68 | // Store IRQ_CTRL status and disable interrupts for now. | ||
69 | u16 irq_ctrl = IRQ_CTRL; | ||
70 | IRQ_CTRL = 0; | ||
71 | |||
72 | // Update the IRS table and enable/disable the given IRQ. | ||
73 | irs_table[idx] = func; | ||
74 | if (func == NULL) { | ||
75 | irq_disable(idx); | ||
76 | } else { | ||
77 | irq_enable(idx); | ||
78 | } | ||
79 | |||
80 | // Restore previous irq_ctrl. | ||
81 | IRQ_CTRL = irq_ctrl; | ||
82 | } | ||
83 | |||
84 | void | ||
85 | irq_init(void) { | ||
86 | IRS_MAIN = irs_main; | ||
87 | IRQ_CTRL = 1; | ||
88 | } | ||
89 | |||
90 | void | ||
91 | irs_stub(void) {} | ||
diff --git a/src/gba/interrupts.s b/src/gba/interrupts.s new file mode 100644 index 0000000..67b9fe9 --- /dev/null +++ b/src/gba/interrupts.s | |||
@@ -0,0 +1,89 @@ | |||
1 | .file "interrupts.s" | ||
2 | .extern irs_table; | ||
3 | .section .iwram, "ax", %progbits | ||
4 | .arm | ||
5 | .align | ||
6 | .global irs_main | ||
7 | |||
8 | irs_main: | ||
9 | @ Get the contents of IRQ_ENABLE, IRQ_ACK, and IRQ_CTRL | ||
10 | ldr ip, mem_irq_base_reg @ ip = (IRQ_ENABLE << 16) | IRQ_ACK | ||
11 | ldr r0, [ip] @ r0 = irq_enable | ||
12 | and r1, r0, r0, lsr #16 @ r1 = irq_enable & irq_ack | ||
13 | |||
14 | @ Disable IRQ_CTRL for now. | ||
15 | mov r3, #0 @ r3 = 0 | ||
16 | strh r3, [ip, #8] @ *(ip + 0x8) = r3 | ||
17 | |||
18 | @ r0 = irs_table address pointer | ||
19 | @ r2 = tmp | ||
20 | @ r3 = 0 (for r3; r3 < 14; r3++) | ||
21 | ldr r0, = irs_table | ||
22 | irs_main_fp_search: | ||
23 | @ Check that the current index is an active IRQ. | ||
24 | mov r2, #1 | ||
25 | and r2, r1, r2, lsl r3 | ||
26 | cmp r2, #0 | ||
27 | beq irs_main_fp_search_not_enabled | ||
28 | |||
29 | @ Extract the function pointer for this IRS if available. | ||
30 | ldr r2, [r0] | ||
31 | cmp r2, #0 | ||
32 | bne irs_main_handle_irs | ||
33 | |||
34 | irs_main_fp_search_not_enabled: | ||
35 | add r0, r0, #4 | ||
36 | add r3, #1 | ||
37 | cmp r3, #14 | ||
38 | bne irs_main_fp_search | ||
39 | b irs_main_exit | ||
40 | |||
41 | irs_main_handle_irs: | ||
42 | @ r2: Contains IRQ function pointer. | ||
43 | @ r3: Stores the IRQ index. | ||
44 | |||
45 | @ Acknowledge that we are handling this interrupt writing to IRQ_ACK and | ||
46 | @ IRQ_ACK_BIOS. | ||
47 | mov r0, #1 | ||
48 | lsl r0, r0, r3 | ||
49 | strh r0, [ip, #2] | ||
50 | ldr r1, mem_irq_ack_bios @ r1 = IRQ_ACK_BIOS | ||
51 | str r0, [r1] | ||
52 | |||
53 | @ Store the SPSR in one of the free registers and save it along with the | ||
54 | @ return pointer. | ||
55 | mrs r3, spsr | ||
56 | stmfd sp!, {r3, lr} | ||
57 | |||
58 | @ Set CPU to system mode | ||
59 | mrs r3, cpsr | ||
60 | bic r3, r3, #0xDF | ||
61 | orr r3, r3, #0x1F | ||
62 | msr cpsr, r3 | ||
63 | |||
64 | @ Call isr function pointer | ||
65 | stmfd sp!, {lr} | ||
66 | mov lr, pc | ||
67 | bx r2 | ||
68 | ldmfd sp!, {lr} | ||
69 | |||
70 | @ Set CPU to irq mode | ||
71 | mrs r3, cpsr | ||
72 | bic r3, r3, #0xDF | ||
73 | orr r3, r3, #0x92 | ||
74 | msr cpsr, r3 | ||
75 | |||
76 | @ Restore the SPSR and the registers. | ||
77 | ldmfd sp!, {r3, lr} | ||
78 | msr spsr, r3 | ||
79 | |||
80 | irs_main_exit: | ||
81 | @ Restore IRQ_CTRL. | ||
82 | mov r3, #1 @ r3 = 0 | ||
83 | strh r3, [ip, #8] @ *(ip + 0x8) = r3 | ||
84 | bx lr | ||
85 | |||
86 | mem_irq_base_reg: | ||
87 | .word 0x04000200 @ IRQ_ENABLE | ||
88 | mem_irq_ack_bios: | ||
89 | .word 0x03007FF8 @ IRQ_ACK_BIOS | ||