aboutsummaryrefslogtreecommitdiffstats
path: root/src/gba
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-06-02 20:20:23 +0200
committerBad Diode <bd@badd10de.dev>2021-06-02 20:20:23 +0200
commit215465df74a6065f4b0fdf199b8b04454520a398 (patch)
treef979791df1ae25bf7da9644ce4a0d55d3b8f8fb4 /src/gba
parentf6686f1e86927f038086023362251ebe78ce5ad6 (diff)
downloadstepper-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.s337
-rw-r--r--src/gba/gba.h667
-rw-r--r--src/gba/interrupts.c91
-rw-r--r--src/gba/interrupts.s89
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
11bios_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
21bios_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
31bios_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
41bios_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
55bios_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
65bios_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
79bios_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
89bios_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
99bios_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
109bios_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
119bios_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
129bios_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
143bios_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
153bios_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
167bios_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
177bios_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
187bios_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
197bios_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
207bios_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
217bios_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
227bios_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
237bios_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
251bios_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
261bios_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
271bios_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
281bios_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
291bios_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
301bios_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
311bios_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
325bios_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
335bios_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).
94typedef u16 Color;
95
96// A palette is composed of 16 colors, with color at index 0 being transparent
97// for sprites.
98typedef 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];
117typedef struct Tile {
118 u32 row[8];
119} Tile;
120
121// Screenblocks and charblocks (tile blocks).
122typedef Tile TileBlock[512];
123#define TILE_MEM ((TileBlock*) MEM_VRAM)
124typedef 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
132inline size_t
133se_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//
143typedef 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
199static inline
200void
201flip_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.
235static inline
236void
237profile_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
246static inline
247u32
248profile_stop(void) {
249 TIMER_CTRL_2 = 0;
250 return (TIMER_DATA_3 << 16) | TIMER_DATA_2;
251}
252
253static inline
254u32
255profile_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.
287static u16 key_curr = 0;
288static u16 key_prev = 0;
289
290static inline
291void
292poll_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.
301static inline
302u32
303key_tap(u32 key) {
304 return (key_curr & key) & ~(key_prev & key);
305}
306
307// Check if a given key is currently pressed.
308static inline
309u32
310key_pressed(u32 key) {
311 return (key_curr & key);
312}
313
314// Check if a given key was just released.
315static inline
316u32
317key_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.
322static inline
323u32
324key_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.
332static inline
333u32
334unpack_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.
347static inline
348void
349unpack_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.
389typedef 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.
400inline
401void
402dma_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
409inline
410void
411dma_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.
419inline
420void
421dma_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.
426inline
427void
428dma_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
441typedef 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
458typedef 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.
462void 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.
466void 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.
470void 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.
478int bios_vblank_wait();
479int 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
513typedef 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
521inline u16
522sound_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
585typedef u8 WaveBank[32];
586
587#define SOUND_WAVE_RAM ((WaveBank*)(MEM_IO + 0x90))
588
589typedef 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
611static 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.
640static inline void
641wait_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
3IrsFunc 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.
21void irs_main(void);
22#define IRS_MAIN *(IrsFunc*)(0x03007FFC)
23
24void
25irq_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
45void
46irq_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
66void
67irs_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
84void
85irq_init(void) {
86 IRS_MAIN = irs_main;
87 IRQ_CTRL = 1;
88}
89
90void
91irs_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
8irs_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
22irs_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
34irs_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
41irs_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
80irs_main_exit:
81 @ Restore IRQ_CTRL.
82 mov r3, #1 @ r3 = 0
83 strh r3, [ip, #8] @ *(ip + 0x8) = r3
84 bx lr
85
86mem_irq_base_reg:
87 .word 0x04000200 @ IRQ_ENABLE
88mem_irq_ack_bios:
89 .word 0x03007FF8 @ IRQ_ACK_BIOS