diff options
author | Bad Diode <bd@badd10de.dev> | 2021-04-26 11:11:36 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-04-26 11:11:36 +0200 |
commit | 24887f28c3eb6d67a7c1a0803520b3fb728ee4f3 (patch) | |
tree | 3816d8f7f4e1d3d6c4bb6caf084cbb06d93e941c /src/main.c | |
parent | 0f16e5bfb1738330a33b300067f86d363fd250bf (diff) | |
download | gba-experiments-24887f28c3eb6d67a7c1a0803520b3fb728ee4f3.tar.gz gba-experiments-24887f28c3eb6d67a7c1a0803520b3fb728ee4f3.zip |
Move code to relevant files for organization
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 1184 |
1 files changed, 8 insertions, 1176 deletions
@@ -1,677 +1,17 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | 2 | ||
3 | #include "shorthand.h" | 3 | #include "common.h" |
4 | #include "bd-font.c" | ||
5 | #include "gba-buttons.c" | 4 | #include "gba-buttons.c" |
6 | #include "background-tiles.c" | 5 | #include "background-tiles.c" |
7 | 6 | #include "sprites.h" | |
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 | // Registers to control of BG layers. | ||
52 | #define BG_CTRL_0 *((vu16*)(0x04000008 + 0x0002 * 0)) | ||
53 | #define BG_CTRL_1 *((vu16*)(0x04000008 + 0x0002 * 1)) | ||
54 | #define BG_CTRL_2 *((vu16*)(0x04000008 + 0x0002 * 2)) | ||
55 | #define BG_CTRL_3 *((vu16*)(0x04000008 + 0x0002 * 3)) | ||
56 | |||
57 | // Bits to control the background. | ||
58 | #define BG_PRIORITY_0 0x0 | ||
59 | #define BG_PRIORITY_1 0x1 | ||
60 | #define BG_PRIORITY_2 0x2 | ||
61 | #define BG_PRIORITY_3 0x3 | ||
62 | #define BG_CHARBLOCK(N) ((N) << 2) | ||
63 | #define BG_MOSAIC (1 << 6) | ||
64 | #define BG_HIGH_COLOR (1 << 7) | ||
65 | #define BG_SCREENBLOCK(N) ((N) << 8) | ||
66 | #define BG_AFFINE (1 << 0xD) | ||
67 | #define BG_SIZE(N) ((N) << 0xE) | ||
68 | |||
69 | // BG registers for horizontal displacement. | ||
70 | #define BG_H_SCROLL_0 *((vu16*)(0x04000010 + 0x0004 * 0)) | ||
71 | #define BG_H_SCROLL_1 *((vu16*)(0x04000010 + 0x0004 * 1)) | ||
72 | #define BG_H_SCROLL_2 *((vu16*)(0x04000010 + 0x0004 * 2)) | ||
73 | #define BG_H_SCROLL_3 *((vu16*)(0x04000010 + 0x0004 * 3)) | ||
74 | |||
75 | // BG registers for vertical displacement. | ||
76 | #define BG_V_SCROLL_0 *((vu16*)(0x04000012 + 0x0004 * 0)) | ||
77 | #define BG_V_SCROLL_1 *((vu16*)(0x04000012 + 0x0004 * 1)) | ||
78 | #define BG_V_SCROLL_2 *((vu16*)(0x04000012 + 0x0004 * 2)) | ||
79 | #define BG_V_SCROLL_3 *((vu16*)(0x04000012 + 0x0004 * 3)) | ||
80 | |||
81 | // Screen settings. | ||
82 | #define SCREEN_WIDTH 240 | ||
83 | #define SCREEN_HEIGHT 160 | ||
84 | |||
85 | // The GBA in mode 3 expects rbg15 colors in the VRAM, where each component | ||
86 | // (RGB) have a 0--31 range. For example, pure red would be rgb15(31, 0, 0). | ||
87 | typedef u16 Color; | ||
88 | |||
89 | // | ||
90 | // Tile memory access. | ||
91 | // | ||
92 | |||
93 | // NOTE: Only defining 4bpp tiles for now. | ||
94 | typedef struct Tile { | ||
95 | u32 data[8]; | ||
96 | } Tile; | ||
97 | |||
98 | typedef Tile TileBlock[512]; | ||
99 | #define TILE_MEM ((TileBlock*) MEM_VRAM) | ||
100 | |||
101 | typedef u16 ScreenBlock[1024]; | ||
102 | #define SCREENBLOCK_MEM ((ScreenBlock*)MEM_VRAM) | ||
103 | |||
104 | // We can treat the screen as a HxW matrix. With the following macro we can | ||
105 | // write a pixel to the screen at the (x, y) position using: | ||
106 | // | ||
107 | // FRAMEBUFFER[y][x] = color; | ||
108 | // | ||
109 | typedef Color Scanline[SCREEN_WIDTH]; | ||
110 | #define FRAMEBUFFER ((Scanline*)MEM_VRAM) | ||
111 | #define SCREEN_BUFFER ((u16*) MEM_VRAM) | ||
112 | #define PAL_BUFFER_BG ((u16*) MEM_PAL) | ||
113 | #define PAL_BUFFER_SPRITES ((u16*) 0x05000200) | ||
114 | |||
115 | // | ||
116 | // Colors. | ||
117 | // | ||
118 | |||
119 | static inline Color | ||
120 | rgb15(u32 red, u32 green, u32 blue ) { | ||
121 | return (blue << 10) | (green << 5) | red; | ||
122 | } | ||
123 | |||
124 | #define COLOR_RED rgb15(31, 0, 12) | ||
125 | #define COLOR_BLUE rgb15(2, 15, 30) | ||
126 | #define COLOR_CYAN rgb15(0, 30, 30) | ||
127 | #define COLOR_GREY rgb15(4, 4, 4) | ||
128 | #define COLOR_BLACK rgb15(0, 0, 0) | ||
129 | #define COLOR_WHITE rgb15(28, 28, 28) | ||
130 | |||
131 | // | ||
132 | // Sprites. | ||
133 | // | ||
134 | |||
135 | // Using macros instead of aligned structs for setting up OBJ attributes and | ||
136 | // affine parameters. | ||
137 | // TODO: Benchmark if this would be slower or the same that TONC's | ||
138 | // implementation. | ||
139 | #define OBJ_ATTR_0(N) *((vu16*)(MEM_OAM + 0 + 8 * (N))) | ||
140 | #define OBJ_ATTR_1(N) *((vu16*)(MEM_OAM + 2 + 8 * (N))) | ||
141 | #define OBJ_ATTR_2(N) *((vu16*)(MEM_OAM + 4 + 8 * (N))) | ||
142 | #define OBJ_AFFINE_PA(N) *((vs16*)(MEM_OAM + 6 + 8 * 0 + 8 * 4 * (N))) | ||
143 | #define OBJ_AFFINE_PB(N) *((vs16*)(MEM_OAM + 6 + 8 * 1 + 8 * 4 * (N))) | ||
144 | #define OBJ_AFFINE_PC(N) *((vs16*)(MEM_OAM + 6 + 8 * 2 + 8 * 4 * (N))) | ||
145 | #define OBJ_AFFINE_PD(N) *((vs16*)(MEM_OAM + 6 + 8 * 3 + 8 * 4 * (N))) | ||
146 | |||
147 | // OBJ_ATTR_0 parameters | ||
148 | #define OBJ_Y_COORD(N) ((N) & 0xFF) | ||
149 | #define OBJ_NORMAL (0x00 << 0x8) | ||
150 | #define OBJ_AFFINE (0x01 << 0x8) | ||
151 | #define OBJ_HIDDEN (0x02 << 0x8) | ||
152 | #define OBJ_AFFINE_2X (0x03 << 0x8) | ||
153 | #define OBJ_ALPHA_BLEND (0x01 << 0xA) | ||
154 | #define OBJ_WINDOW (0x02 << 0xA) | ||
155 | #define OBJ_SHAPE_SQUARE (0x00 << 0xE) | ||
156 | #define OBJ_SHAPE_WIDE (0x01 << 0xE) | ||
157 | #define OBJ_SHAPE_TALL (0x02 << 0xE) | ||
158 | |||
159 | // OBJ_ATTR_1 parameters | ||
160 | #define OBJ_X_COORD(N) ((N) & 0x1FF) | ||
161 | #define OBJ_AFFINE_IDX(N) ((N) << 0x9) | ||
162 | #define OBJ_H_FLIP (0x01 << 0xC) | ||
163 | #define OBJ_V_FLIP (0x01 << 0xD) | ||
164 | #define OBJ_SIZE_SMALL (0x00 << 0xE) | ||
165 | #define OBJ_SIZE_MID (0x01 << 0xE) | ||
166 | #define OBJ_SIZE_BIG (0x02 << 0xE) | ||
167 | #define OBJ_SIZE_HUGE (0x03 << 0xE) | ||
168 | |||
169 | // OBJ_ATTR_2 parameters | ||
170 | #define OBJ_TILE_INDEX(N) ((N) & 0x3FF) | ||
171 | #define OBJ_PRIORITY(N) ((N) << 0xA) | ||
172 | #define OBJ_PAL_BANK(N) ((N) << 0xC) | ||
173 | |||
174 | // Using bd-font, an 8x8 bitmap font. | ||
175 | static void | ||
176 | put_char(int x, int y, Color clr, u8 chr) { | ||
177 | for (size_t i = 0; i < 8; ++i) { | ||
178 | for (size_t j = 0; j < 8; ++j) { | ||
179 | if ((font[chr][i] >> (7 - j)) & 0x1) { | ||
180 | FRAMEBUFFER[y + i][x + j] = clr; | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | |||
186 | static void | ||
187 | put_text(int x, int y, Color clr, char *msg) { | ||
188 | int count = 0; | ||
189 | while (*msg) { | ||
190 | put_char(x + count, y, clr, *msg++); | ||
191 | count += 8; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | // Draws a line with the given color between (x0,y0) and (x1,y1) using the | ||
196 | // Bresenham's line drawing algorithm using exclusively integer arithmetic. | ||
197 | static void | ||
198 | draw_line(int x0, int y0, int x1, int y1, Color clr) { | ||
199 | // Pointer to the initial position of the screen buffer where we will start | ||
200 | // writing our data. | ||
201 | vu16 *destination = (u16*)(SCREEN_BUFFER + y0 * SCREEN_WIDTH + x0); | ||
202 | |||
203 | // Adjust the step direction and calculate deltas. | ||
204 | int x_step; | ||
205 | int y_step; | ||
206 | int dx; | ||
207 | int dy; | ||
208 | if (x0 > x1) { | ||
209 | x_step = -1; | ||
210 | dx = x0 - x1; | ||
211 | } else { | ||
212 | x_step = 1; | ||
213 | dx = x1 - x0; | ||
214 | } | ||
215 | if (y0 > y1) { | ||
216 | y_step = -SCREEN_WIDTH; | ||
217 | dy = y0 - y1; | ||
218 | } else { | ||
219 | y_step = +SCREEN_WIDTH; | ||
220 | dy = y1 - y0; | ||
221 | } | ||
222 | |||
223 | if(dy == 0) { | ||
224 | // Horizontal line. | ||
225 | for(int i = 0; i <= dx; i++) { | ||
226 | destination[i * x_step] = clr; | ||
227 | } | ||
228 | } else if(dx == 0) { | ||
229 | // Vertical line. | ||
230 | for(int i = 0; i <= dy; i++) { | ||
231 | destination[i * y_step] = clr; | ||
232 | } | ||
233 | } else if (dx >= dy){ | ||
234 | // Positive slope. | ||
235 | int diff = 2 * dy - dx; | ||
236 | for (int i = 0; i <= dx; ++i) { | ||
237 | *destination = clr; | ||
238 | if (diff >= 0) { | ||
239 | destination += y_step; | ||
240 | diff -= 2 * dx; | ||
241 | } | ||
242 | destination += x_step; | ||
243 | diff += 2 * dy; | ||
244 | } | ||
245 | } else { | ||
246 | // Negative slope. | ||
247 | int diff = 2 * dx - dy; | ||
248 | for (int i = 0; i <= dy; ++i) { | ||
249 | *destination = clr; | ||
250 | if (diff >= 0) { | ||
251 | destination += x_step; | ||
252 | diff -= 2 * dy; | ||
253 | } | ||
254 | destination += y_step; | ||
255 | diff += 2 * dx; | ||
256 | } | ||
257 | } | ||
258 | } | ||
259 | |||
260 | static inline void | ||
261 | draw_rect(int x0, int y0, int x1, int y1, Color clr) { | ||
262 | if (x0 > x1) { | ||
263 | int tmp = x0; | ||
264 | x0 = x1; | ||
265 | x1 = tmp; | ||
266 | } | ||
267 | if (y0 > y1) { | ||
268 | int tmp = y0; | ||
269 | y0 = y1; | ||
270 | y1 = tmp; | ||
271 | } | ||
272 | int dx = x1 - x0; | ||
273 | int dy = y1 - y0; | ||
274 | for (int i = 0; i <= dx; ++i) { | ||
275 | int x = x0 + i; | ||
276 | FRAMEBUFFER[y0][x] = clr; | ||
277 | FRAMEBUFFER[y1][x] = clr; | ||
278 | } | ||
279 | for (int j = 0; j <= dy; ++j) { | ||
280 | int y = y0 + j; | ||
281 | FRAMEBUFFER[y][x0] = clr; | ||
282 | FRAMEBUFFER[y][x1] = clr; | ||
283 | } | ||
284 | } | ||
285 | |||
286 | static inline void | ||
287 | draw_fill_rect(int x0, int y0, int x1, int y1, Color clr) { | ||
288 | if (x0 > x1) { | ||
289 | int tmp = x0; | ||
290 | x0 = x1; | ||
291 | x1 = tmp; | ||
292 | } | ||
293 | if (y0 > y1) { | ||
294 | int tmp = y0; | ||
295 | y0 = y1; | ||
296 | y1 = tmp; | ||
297 | } | ||
298 | int dx = x1 - x0; | ||
299 | int dy = y1 - y0; | ||
300 | for (int i = 0; i <= dx; ++i) { | ||
301 | for (int j = 0; j <= dy; ++j) { | ||
302 | int x = x0 + i; | ||
303 | int y = y0 + j; | ||
304 | FRAMEBUFFER[y][x] = clr; | ||
305 | } | ||
306 | } | ||
307 | } | ||
308 | |||
309 | static inline void | ||
310 | wait_vsync(void) { | ||
311 | while(DISP_VCOUNT >= 160); | ||
312 | while(DISP_VCOUNT < 160); | ||
313 | } | ||
314 | 7 | ||
315 | // | 8 | // |
316 | // Main functions. | 9 | // Main functions. |
317 | // | 10 | // |
318 | 11 | ||
319 | // In Mode4 the buffer is of 8 bytes per pixel instead of 16. We can't write the | 12 | // TODO: Cleanup OBJ/OAM memory copying and access. |
320 | // color directly, instead the color is stored in the palette memory at | ||
321 | // `MEM_PAL`. Note that in this mode MEM_PAL[0] is the background color. This | ||
322 | // plotter takes an index to a color stored in MEM_PAL[col_index]. Because the | ||
323 | // GBA needs to meet memory alignment requirements, we can't write a u8 into | ||
324 | // memory, instead we need to read a u16 word, mask and or the corresponding | ||
325 | // bits and wave the updated u16. | ||
326 | static void | ||
327 | put_pixel_m4(int x, int y, u8 col_index, vu16 *buffer) { | ||
328 | int buffer_index = (y * SCREEN_WIDTH + x) / 2; | ||
329 | vu16 *destination = &buffer[buffer_index]; | ||
330 | // Odd pixels will go to the top 8 bits of the destination. Even pixels to | ||
331 | // the lower 8 bits. | ||
332 | int odd = x & 0x1; | ||
333 | if(odd) { | ||
334 | *destination= (*destination & 0xFF) | (col_index << 8); | ||
335 | } else { | ||
336 | *destination= (*destination & ~0xFF) | col_index; | ||
337 | } | ||
338 | } | ||
339 | |||
340 | static void | ||
341 | draw_fill_rect_m4(int x0, int y0, int x1, int y1, u8 col_index, vu16 *buffer) { | ||
342 | int ix, iy; | ||
343 | for(iy = y0; iy < y1; iy++) { | ||
344 | for(ix = x0; ix < x1; ix++) { | ||
345 | put_pixel_m4(ix, iy, col_index, buffer); | ||
346 | } | ||
347 | } | ||
348 | } | ||
349 | |||
350 | static inline void | ||
351 | flip_page(void) { | ||
352 | DISP_CTRL ^= DISP_PAGE; | ||
353 | } | ||
354 | |||
355 | #define SCREEN_PAGE_1 ((vu16*) MEM_VRAM) | ||
356 | #define SCREEN_PAGE_2 ((vu16*) (MEM_VRAM + 0xa000)) | ||
357 | |||
358 | // | ||
359 | // Profiling. | ||
360 | // | ||
361 | |||
362 | #define TIMER_DATA_0 *((vu16*) (0x04000100 + 0x04 * 0)) | ||
363 | #define TIMER_DATA_1 *((vu16*) (0x04000100 + 0x04 * 1)) | ||
364 | #define TIMER_DATA_2 *((vu16*) (0x04000100 + 0x04 * 2)) | ||
365 | #define TIMER_DATA_3 *((vu16*) (0x04000100 + 0x04 * 3)) | ||
366 | #define TIMER_CTRL_0 *((vu16*) (0x04000102 + 0x04 * 0)) | ||
367 | #define TIMER_CTRL_1 *((vu16*) (0x04000102 + 0x04 * 1)) | ||
368 | #define TIMER_CTRL_2 *((vu16*) (0x04000102 + 0x04 * 2)) | ||
369 | #define TIMER_CTRL_3 *((vu16*) (0x04000102 + 0x04 * 3)) | ||
370 | |||
371 | // Timer control bits. | ||
372 | #define TIMER_CTRL_FREQ_0 0 | ||
373 | #define TIMER_CTRL_FREQ_1 1 | ||
374 | #define TIMER_CTRL_FREQ_2 2 | ||
375 | #define TIMER_CTRL_FREQ_3 3 | ||
376 | #define TIMER_CTRL_CASCADE (1 << 2) | ||
377 | #define TIMER_CTRL_IRQ (1 << 6) | ||
378 | #define TIMER_CTRL_ENABLE (1 << 7) | ||
379 | |||
380 | // We use timers 2 and 3 to count the number of cycles since the profile_start | ||
381 | // functions is called. Don't use if the code we are trying to profile make use | ||
382 | // of these timers. | ||
383 | static inline | ||
384 | void profile_start(void) { | ||
385 | TIMER_DATA_2 = 0; | ||
386 | TIMER_DATA_3 = 0; | ||
387 | TIMER_CTRL_2 = 0; | ||
388 | TIMER_CTRL_3 = 0; | ||
389 | TIMER_CTRL_3 = TIMER_CTRL_ENABLE | TIMER_CTRL_CASCADE; | ||
390 | TIMER_CTRL_2 = TIMER_CTRL_ENABLE; | ||
391 | } | ||
392 | |||
393 | static inline | ||
394 | u32 profile_stop(void) { | ||
395 | TIMER_CTRL_2 = 0; | ||
396 | return (TIMER_DATA_3 << 16) | TIMER_DATA_2; | ||
397 | } | ||
398 | |||
399 | // | ||
400 | // Input handling. | ||
401 | // | ||
402 | |||
403 | // Memory address for key input register | ||
404 | #define KEY_INPUTS *((vu16*) 0x04000130) | ||
405 | |||
406 | // Alias for key pressing bits. | ||
407 | #define KEY_A (1 << 0) | ||
408 | #define KEY_B (1 << 1) | ||
409 | #define KEY_SELECT (1 << 2) | ||
410 | #define KEY_START (1 << 3) | ||
411 | #define KEY_RIGHT (1 << 4) | ||
412 | #define KEY_LEFT (1 << 5) | ||
413 | #define KEY_UP (1 << 6) | ||
414 | #define KEY_DOWN (1 << 7) | ||
415 | #define KEY_R (1 << 8) | ||
416 | #define KEY_L (1 << 9) | ||
417 | |||
418 | #define KEY_MASK 0x03FF | ||
419 | |||
420 | // Saving the previous and current key states as globals for now. | ||
421 | static u16 key_curr = 0; | ||
422 | static u16 key_prev = 0; | ||
423 | |||
424 | static inline void | ||
425 | poll_keys(void) { | ||
426 | key_prev = key_curr; | ||
427 | key_curr = ~KEY_INPUTS & KEY_MASK; | ||
428 | } | ||
429 | |||
430 | // Returns true if the given key has been pressed at time of calling and was not | ||
431 | // pressed since the previous call. For example, if a key is being held, this | ||
432 | // function will return `true` only on the frame where the key initially | ||
433 | // activated. | ||
434 | static inline u32 | ||
435 | key_pressed(u32 key) { | ||
436 | return (key_curr & key) & ~(key_prev & key); | ||
437 | } | ||
438 | |||
439 | // Check if the given key is pressed and has been since at least one frame. | ||
440 | static inline u32 | ||
441 | key_hold(u32 key) { | ||
442 | return (key_curr & key) & key_prev & key; | ||
443 | } | ||
444 | |||
445 | // Check if the given key/button is currently pressed. | ||
446 | #define KEY_PRESSED(key) (~(KEY_INPUTS) & key) | ||
447 | |||
448 | void | ||
449 | draw_logo(void) { | ||
450 | int side = 60; | ||
451 | int line = 35; | ||
452 | int height = side * 0.5; | ||
453 | int x = SCREEN_WIDTH / 2 - height / 2; | ||
454 | int y = SCREEN_HEIGHT / 2; | ||
455 | |||
456 | // Draw red triangle. | ||
457 | draw_line(x + height - 1, y - side / 2, x, y - 1, COLOR_RED); | ||
458 | draw_line(x + height - 1, y + side / 2, x, y + 1, COLOR_RED); | ||
459 | draw_line(x + height - 1, y - side / 2 + 1, x, y, COLOR_RED); | ||
460 | draw_line(x + height - 1, y + side / 2 - 1, x, y, COLOR_RED); | ||
461 | |||
462 | // Draw white triangle. | ||
463 | draw_line(x, y - side / 2, x, y + side / 2, COLOR_WHITE); | ||
464 | draw_line(x + 1, y - side / 2, x + height, y - 1, COLOR_WHITE); | ||
465 | draw_line(x + 1, y + side / 2, x + height, y + 1, COLOR_WHITE); | ||
466 | |||
467 | // Draw white line at triangle tip. | ||
468 | draw_line(x + height, y - side / 2, x + height, y + side / 2, COLOR_WHITE); | ||
469 | draw_line(x + height + 1, y - side / 2, x + height + 1, y + side / 2, COLOR_WHITE); | ||
470 | |||
471 | // Double triangle line. | ||
472 | draw_line(x - 1, y - side / 2, x - 1, y + side / 2, COLOR_WHITE); | ||
473 | draw_line(x + 1, y - side / 2 + 1, x + height, y, COLOR_WHITE); | ||
474 | draw_line(x + 1, y + side / 2 - 1, x + height, y, COLOR_WHITE); | ||
475 | |||
476 | // Draw white lines. | ||
477 | draw_line(x - line, y, x, y, COLOR_WHITE); | ||
478 | draw_line(x + height, y, x + height + line, y, COLOR_WHITE); | ||
479 | draw_line(x - line, y + 1, x, y + 1, COLOR_WHITE); | ||
480 | draw_line(x + height, y + 1, x + height + line, y + 1, COLOR_WHITE); | ||
481 | } | ||
482 | |||
483 | void | ||
484 | copy_font_to_tile_memory(Tile *tile) { | ||
485 | // Hex to bits translation table. | ||
486 | const u32 conversion_u32[16] = { | ||
487 | 0x00000000, 0x00001000, 0x00000100, 0x00001100, | ||
488 | 0x00000010, 0x00001010, 0x00000110, 0x00001110, | ||
489 | 0x00000001, 0x00001001, 0x00000101, 0x00001101, | ||
490 | 0x00000011, 0x00001011, 0x00000111, 0x00001111, | ||
491 | }; | ||
492 | for (size_t i = 0; i < 250; ++i) { | ||
493 | for (size_t j = 0; j < 8; ++j) { | ||
494 | u8 row = font[i][j]; | ||
495 | u32 tile_idx = 0x00000000; | ||
496 | tile_idx = conversion_u32[row & 0xF] << 16; | ||
497 | tile_idx |= conversion_u32[(row >> 4) & 0xF]; | ||
498 | (tile + i)->data[j] = tile_idx; | ||
499 | } | ||
500 | } | ||
501 | } | ||
502 | |||
503 | |||
504 | u32 | ||
505 | unpack_1bb(u8 hex) { | ||
506 | const u32 conversion_u32[16] = { | ||
507 | 0x00000000, 0x00000001, 0x00000010, 0x00000011, | ||
508 | 0x00000100, 0x00000101, 0x00000110, 0x00000111, | ||
509 | 0x00001000, 0x00001001, 0x00001010, 0x00001011, | ||
510 | 0x00001100, 0x00001101, 0x00001110, 0x00001111, | ||
511 | }; | ||
512 | u8 low = hex & 0xF; | ||
513 | u8 high = (hex >> 4) & 0xF; | ||
514 | return (conversion_u32[high] << 16) | conversion_u32[low]; | ||
515 | } | ||
516 | |||
517 | typedef struct Sprite { | ||
518 | // A unique sprite identifier. | ||
519 | size_t id; | ||
520 | // The number of tiles for a single sprite frame. | ||
521 | size_t n_tiles; | ||
522 | // The starting tile of this sprite. | ||
523 | size_t tile_start; | ||
524 | // The associated palette bank for this sprite. | ||
525 | size_t pal_bank; | ||
526 | } Sprite; | ||
527 | |||
528 | typedef struct ButtonSprite { | ||
529 | int id; | ||
530 | int x; | ||
531 | int y; | ||
532 | int frame; | ||
533 | BtnState state; | ||
534 | } ButtonSprite; | ||
535 | |||
536 | typedef struct MultiSprite { | ||
537 | ObjState *sprites; | ||
538 | AnimationEntry **animations; | ||
539 | size_t frame; | ||
540 | size_t n_obj; | ||
541 | size_t n_frames; | ||
542 | BtnState state; | ||
543 | } MultiSprite; | ||
544 | |||
545 | #define NUM_SPRITES 128 | ||
546 | |||
547 | Sprite sprites[NUM_SPRITES]; | ||
548 | |||
549 | // Keeping track of unique sprites and current sprite memory pointer using | ||
550 | // global singletons. | ||
551 | size_t sprite_counter = 0; | ||
552 | size_t sprite_tile_counter = 0; | ||
553 | u32 *sprite_memory = NULL; | ||
554 | |||
555 | // Loads the sprite data into video memory and initialize the Sprite structure. | ||
556 | size_t | ||
557 | load_sprite_data(u32 *sprite_data, size_t n_tiles, size_t n_frames) { | ||
558 | memcpy(sprite_memory, sprite_data, 8 * n_tiles * n_frames * sizeof(u32)); | ||
559 | sprite_memory += 8 * n_tiles * n_frames; | ||
560 | Sprite sprite = { | ||
561 | .id = sprite_counter, | ||
562 | .n_tiles = n_tiles, | ||
563 | .tile_start = sprite_tile_counter, | ||
564 | }; | ||
565 | sprite_tile_counter += n_tiles * n_frames; | ||
566 | sprites[sprite_counter] = sprite; | ||
567 | return sprite_counter++; | ||
568 | } | ||
569 | |||
570 | size_t | ||
571 | load_packed_sprite_data(u32 *sprite_data, size_t n_tiles, size_t n_frames) { | ||
572 | size_t counter = 0; | ||
573 | for (size_t i = 0; i < 8 * n_tiles * n_frames / 4; ++i) { | ||
574 | u32 hex = sprite_data[i]; | ||
575 | sprite_memory[counter++] = unpack_1bb((hex >> 24) & 0xFF); | ||
576 | sprite_memory[counter++] = unpack_1bb((hex >> 16) & 0xFF); | ||
577 | sprite_memory[counter++] = unpack_1bb((hex >> 8) & 0xFF); | ||
578 | sprite_memory[counter++] = unpack_1bb((hex) & 0xFF); | ||
579 | } | ||
580 | sprite_memory += 8 * n_tiles * n_frames; | ||
581 | Sprite sprite = { | ||
582 | .id = sprite_counter, | ||
583 | .n_tiles = n_tiles, | ||
584 | .tile_start = sprite_tile_counter, | ||
585 | }; | ||
586 | sprite_tile_counter += n_tiles * n_frames; | ||
587 | sprites[sprite_counter] = sprite; | ||
588 | return sprite_counter++; | ||
589 | } | ||
590 | |||
591 | void | ||
592 | init_button_sprite(MultiSprite *btn) { | ||
593 | for (size_t i = 0; i < btn->n_obj; ++i) { | ||
594 | btn->sprites[i].id = load_packed_sprite_data( | ||
595 | btn->sprites[i].data, | ||
596 | btn->sprites[i].n_tiles, | ||
597 | btn->sprites[i].frames); | ||
598 | btn->sprites[i].base_tile = sprites[btn->sprites[i].id].tile_start; | ||
599 | } | ||
600 | } | ||
601 | |||
602 | void | ||
603 | button_tick(MultiSprite *btn) { | ||
604 | // Nothing to do here. | ||
605 | if (btn->state == BTN_STATE_IDLE) { | ||
606 | return; | ||
607 | } | ||
608 | |||
609 | // Reset animation state. | ||
610 | if (btn->state == BTN_STATE_PRESSED && btn->frame != 0) { | ||
611 | btn->frame = 0; | ||
612 | } | ||
613 | |||
614 | // Continue the animation. | ||
615 | if (btn->state == BTN_STATE_HOLD || btn->state == BTN_STATE_PRESSED ) { | ||
616 | if(btn->frame < btn->n_frames - 1) { | ||
617 | btn->frame++; | ||
618 | } | ||
619 | } | ||
620 | |||
621 | // Finish the animation and return to idle. | ||
622 | if (btn->state == BTN_STATE_RELEASED) { | ||
623 | if (btn->frame > 0 && btn->frame < btn->n_frames - 1) { | ||
624 | btn->frame++; | ||
625 | } else { | ||
626 | btn->frame = 0; | ||
627 | btn->state = BTN_STATE_IDLE; | ||
628 | } | ||
629 | } | ||
630 | for (size_t i = 0; i < btn->n_obj; ++i) { | ||
631 | AnimationEntry anim_frame = btn->animations[i][btn->frame]; | ||
632 | int x = btn->sprites[i].x + anim_frame.x_offset; | ||
633 | int y = btn->sprites[i].y + anim_frame.y_offset; | ||
634 | int base_tile = btn->sprites[i].base_tile + anim_frame.tile_offset; | ||
635 | |||
636 | // Clear the previous x/y coordinate and base tiles. | ||
637 | btn->sprites[i].obj_attr_0 &= ~0xFF; | ||
638 | btn->sprites[i].obj_attr_1 &= ~0x1FF; | ||
639 | btn->sprites[i].obj_attr_2 &= ~0x3FF; | ||
640 | |||
641 | // Update x/y/tile and hidden state from the animations. | ||
642 | btn->sprites[i].obj_attr_0 |= OBJ_Y_COORD(y); | ||
643 | btn->sprites[i].obj_attr_1 |= OBJ_X_COORD(x); | ||
644 | btn->sprites[i].obj_attr_2 |= base_tile; | ||
645 | if (anim_frame.hidden) { | ||
646 | btn->sprites[i].obj_attr_0 |= OBJ_HIDDEN; | ||
647 | } else { | ||
648 | btn->sprites[i].obj_attr_0 &= ~OBJ_HIDDEN; | ||
649 | } | ||
650 | |||
651 | // Update OBJ attributes. | ||
652 | OBJ_ATTR_0(btn->sprites[i].id) = btn->sprites[i].obj_attr_0; | ||
653 | OBJ_ATTR_1(btn->sprites[i].id) = btn->sprites[i].obj_attr_1; | ||
654 | OBJ_ATTR_2(btn->sprites[i].id) = btn->sprites[i].obj_attr_2; | ||
655 | } | ||
656 | } | ||
657 | 13 | ||
658 | int main(void) { | 14 | int main(void) { |
659 | |||
660 | // Add colors to the sprite color palette. Tiles with color number 0 are | ||
661 | // treated as transparent. | ||
662 | for (size_t i = 0; i < 16; ++i) { | ||
663 | PAL_BUFFER_SPRITES[i] = COLOR_WHITE; | ||
664 | } | ||
665 | |||
666 | // Initialize all attributes by disabling rendering. If we don't do this, | ||
667 | // glitches may appear. | ||
668 | for (size_t i = 0; i < 128; ++i) { | ||
669 | OBJ_ATTR_0(i) = (1 << 9); | ||
670 | } | ||
671 | |||
672 | sprite_tile_counter = 0; | ||
673 | sprite_memory = &TILE_MEM[4][sprite_tile_counter]; | ||
674 | |||
675 | // Load background palette. | 15 | // Load background palette. |
676 | memcpy(&PAL_BUFFER_BG[0], &bg_palette, 512); | 16 | memcpy(&PAL_BUFFER_BG[0], &bg_palette, 512); |
677 | memcpy(&TILE_MEM[0][0], bg_data, 3168); | 17 | memcpy(&TILE_MEM[0][0], bg_data, 3168); |
@@ -687,445 +27,10 @@ int main(void) { | |||
687 | // sequential. | 27 | // sequential. |
688 | DISP_CTRL = DISP_ENABLE_SPRITES | DISP_MODE_0 | DISP_BG_0; | 28 | DISP_CTRL = DISP_ENABLE_SPRITES | DISP_MODE_0 | DISP_BG_0; |
689 | 29 | ||
690 | // Initialize the A/B button sprites. | 30 | // Initialize sprite button overlay. |
691 | int buttons_x = SCREEN_WIDTH / 2; | 31 | init_sprite_pal(0, COLOR_WHITE); |
692 | int buttons_y = SCREEN_HEIGHT / 2; | 32 | init_sprites(0); |
693 | 33 | init_button_sprites(); | |
694 | MultiSprite buttons[] = { | ||
695 | // DOWN. | ||
696 | { | ||
697 | .frame = 0, | ||
698 | .n_obj = 3, | ||
699 | .n_frames = 8, | ||
700 | .state = BTN_STATE_RELEASED, | ||
701 | .animations = &btn_animation, | ||
702 | .sprites = &(ObjState[]){ | ||
703 | { | ||
704 | .id = 0, | ||
705 | .x = buttons_x - 64 - 16, | ||
706 | .y = buttons_y + 29, | ||
707 | .data = &gba_btn_updown_data, | ||
708 | .n_tiles = 4, | ||
709 | .frames = 1, | ||
710 | .obj_attr_0 = 0, | ||
711 | .obj_attr_1 = OBJ_V_FLIP | OBJ_SIZE_MID, | ||
712 | .obj_attr_2 = 0 | ||
713 | }, | ||
714 | { | ||
715 | .id = 0, | ||
716 | .x = buttons_x - 64 - 16, | ||
717 | .y = buttons_y + 29 + 11, | ||
718 | .data = &gba_btn_down_shadow_data, | ||
719 | .n_tiles = 2, | ||
720 | .frames = 1, | ||
721 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
722 | .obj_attr_1 = OBJ_SIZE_SMALL, | ||
723 | .obj_attr_2 = 0 | ||
724 | }, | ||
725 | { | ||
726 | .id = 0, | ||
727 | .x = buttons_x - 64 - 16 - 8, | ||
728 | .y = buttons_y + 29 + 17, | ||
729 | .data = &gba_btn_fx_downup, | ||
730 | .n_tiles = 4, | ||
731 | .frames = 4, | ||
732 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
733 | .obj_attr_1 = OBJ_SIZE_MID, | ||
734 | .obj_attr_2 = 0 | ||
735 | }, | ||
736 | }, | ||
737 | }, | ||
738 | // UP. | ||
739 | { | ||
740 | .frame = 0, | ||
741 | .n_obj = 3, | ||
742 | .n_frames = 8, | ||
743 | .state = BTN_STATE_RELEASED, | ||
744 | .animations = &btn_animation, | ||
745 | .sprites = &(ObjState[]){ | ||
746 | { | ||
747 | .id = 0, | ||
748 | .x = buttons_x - 64 - 16, | ||
749 | .y = buttons_y + 32 - 18, | ||
750 | .data = &gba_btn_updown_data, | ||
751 | .n_tiles = 4, | ||
752 | .frames = 1, | ||
753 | .obj_attr_0 = 0, | ||
754 | .obj_attr_1 = OBJ_SIZE_MID, | ||
755 | .obj_attr_2 = 0 | ||
756 | }, | ||
757 | { | ||
758 | .id = 0, | ||
759 | .x = buttons_x - 64 - 16, | ||
760 | .y = buttons_y + 32 - 18 + 7, | ||
761 | .data = &gba_btn_up_shadow_data, | ||
762 | .n_tiles = 2, | ||
763 | .frames = 1, | ||
764 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
765 | .obj_attr_1 = OBJ_SIZE_SMALL, | ||
766 | .obj_attr_2 = 0 | ||
767 | }, | ||
768 | { | ||
769 | .id = 0, | ||
770 | .x = buttons_x - 64 - 16 - 8, | ||
771 | .y = buttons_y + 32 - 18 - 7, | ||
772 | .data = &gba_btn_fx_downup, | ||
773 | .n_tiles = 4, | ||
774 | .frames = 4, | ||
775 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
776 | .obj_attr_1 = OBJ_V_FLIP | OBJ_SIZE_MID, | ||
777 | .obj_attr_2 = 0 | ||
778 | }, | ||
779 | }, | ||
780 | }, | ||
781 | // LEFT | ||
782 | { | ||
783 | .frame = 0, | ||
784 | .n_obj = 3, | ||
785 | .n_frames = 8, | ||
786 | .state = BTN_STATE_RELEASED, | ||
787 | .animations = &btn_animation, | ||
788 | .sprites = &(ObjState[]){ | ||
789 | { | ||
790 | .id = 0, | ||
791 | .x = buttons_x - 64 - 16 - 10, | ||
792 | .y = buttons_y + 32 - 10, | ||
793 | .data = &gba_btn_leftright_data, | ||
794 | .n_tiles = 4, | ||
795 | .frames = 1, | ||
796 | .obj_attr_0 = 0, | ||
797 | .obj_attr_1 = OBJ_SIZE_MID, | ||
798 | .obj_attr_2 = 0 | ||
799 | }, | ||
800 | { | ||
801 | .id = 0, | ||
802 | .x = buttons_x - 64 - 16 - 10, | ||
803 | .y = buttons_y + 32 - 10 + 6, | ||
804 | .data = &gba_btn_leftright_shadow_data, | ||
805 | .n_tiles = 2, | ||
806 | .frames = 1, | ||
807 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
808 | .obj_attr_1 = OBJ_SIZE_SMALL, | ||
809 | .obj_attr_2 = 0 | ||
810 | }, | ||
811 | { | ||
812 | .id = 0, | ||
813 | .x = buttons_x - 64 - 16 - 10 - 6, | ||
814 | .y = buttons_y + 32 - 10 - 8, | ||
815 | .data = &gba_btn_fx_leftright, | ||
816 | .n_tiles = 4, | ||
817 | .frames = 4, | ||
818 | .obj_attr_0 = OBJ_SHAPE_TALL, | ||
819 | .obj_attr_1 = OBJ_H_FLIP | OBJ_SIZE_MID, | ||
820 | .obj_attr_2 = 0 | ||
821 | }, | ||
822 | }, | ||
823 | }, | ||
824 | // RIGHT. | ||
825 | { | ||
826 | .frame = 0, | ||
827 | .n_obj = 3, | ||
828 | .n_frames = 8, | ||
829 | .state = BTN_STATE_RELEASED, | ||
830 | .animations = &btn_animation, | ||
831 | .sprites = &(ObjState[]){ | ||
832 | { | ||
833 | .id = 0, | ||
834 | .x = buttons_x - 64 - 16 + 11, | ||
835 | .y = buttons_y + 32 - 10, | ||
836 | .data = &gba_btn_leftright_data, | ||
837 | .n_tiles = 4, | ||
838 | .frames = 1, | ||
839 | .obj_attr_0 = 0, | ||
840 | .obj_attr_1 = OBJ_H_FLIP | OBJ_SIZE_MID, | ||
841 | .obj_attr_2 = 0 | ||
842 | }, | ||
843 | { | ||
844 | .id = 0, | ||
845 | .x = buttons_x - 64 - 16 + 11, | ||
846 | .y = buttons_y + 32 - 10 + 6, | ||
847 | .data = &gba_btn_leftright_shadow_data, | ||
848 | .n_tiles = 2, | ||
849 | .frames = 1, | ||
850 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
851 | .obj_attr_1 = OBJ_H_FLIP | OBJ_SIZE_SMALL, | ||
852 | .obj_attr_2 = 0 | ||
853 | }, | ||
854 | { | ||
855 | .id = 0, | ||
856 | .x = buttons_x - 64 - 16 + 11 + 14, | ||
857 | .y = buttons_y + 32 - 10 - 8, | ||
858 | .data = &gba_btn_fx_leftright, | ||
859 | .n_tiles = 4, | ||
860 | .frames = 4, | ||
861 | .obj_attr_0 = OBJ_SHAPE_TALL, | ||
862 | .obj_attr_1 = OBJ_SIZE_MID, | ||
863 | .obj_attr_2 = 0 | ||
864 | }, | ||
865 | }, | ||
866 | }, | ||
867 | // L. | ||
868 | { | ||
869 | .frame = 0, | ||
870 | .n_obj = 3, | ||
871 | .n_frames = 8, | ||
872 | .state = BTN_STATE_RELEASED, | ||
873 | .animations = &btn_animation, | ||
874 | .sprites = &(ObjState[]){ | ||
875 | { | ||
876 | .id = 0, | ||
877 | .x = buttons_x - 64 - 28, | ||
878 | .y = buttons_y - 32 - 20, | ||
879 | .data = &gba_btn_l_data, | ||
880 | .n_tiles = 2, | ||
881 | .frames = 1, | ||
882 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
883 | .obj_attr_1 = OBJ_SIZE_SMALL, | ||
884 | .obj_attr_2 = 0 | ||
885 | }, | ||
886 | { | ||
887 | .id = 0, | ||
888 | .x = buttons_x - 64 - 28, | ||
889 | .y = buttons_y - 32 - 20 + 2, | ||
890 | .data = &gba_btn_lr_shadow_data, | ||
891 | .n_tiles = 2, | ||
892 | .frames = 1, | ||
893 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
894 | .obj_attr_1 = OBJ_SIZE_SMALL, | ||
895 | .obj_attr_2 = 0 | ||
896 | }, | ||
897 | { | ||
898 | .id = 0, | ||
899 | .x = buttons_x - 64 - 28 - 12, | ||
900 | .y = buttons_y - 32 - 20 - 12, | ||
901 | .data = &gba_btn_fx_lr, | ||
902 | .n_tiles = 4, | ||
903 | .frames = 4, | ||
904 | .obj_attr_0 = OBJ_SHAPE_SQUARE, | ||
905 | .obj_attr_1 = OBJ_SIZE_MID, | ||
906 | .obj_attr_2 = 0 | ||
907 | }, | ||
908 | }, | ||
909 | }, | ||
910 | // R. | ||
911 | { | ||
912 | .frame = 0, | ||
913 | .n_obj = 3, | ||
914 | .n_frames = 8, | ||
915 | .state = BTN_STATE_RELEASED, | ||
916 | .animations = &btn_animation, | ||
917 | .sprites = &(ObjState[]){ | ||
918 | { | ||
919 | .id = 0, | ||
920 | .x = buttons_x + 32 + 20 + 24, | ||
921 | .y = buttons_y - 32 - 20, | ||
922 | .data = &gba_btn_r_data, | ||
923 | .n_tiles = 2, | ||
924 | .frames = 1, | ||
925 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
926 | .obj_attr_1 = OBJ_SIZE_SMALL, | ||
927 | .obj_attr_2 = 0 | ||
928 | }, | ||
929 | { | ||
930 | .id = 0, | ||
931 | .x = buttons_x + 32 + 20 + 24, | ||
932 | .y = buttons_y - 32 - 20 + 2, | ||
933 | .data = &gba_btn_lr_shadow_data, | ||
934 | .n_tiles = 2, | ||
935 | .frames = 1, | ||
936 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
937 | .obj_attr_1 = OBJ_SIZE_SMALL, | ||
938 | .obj_attr_2 = 0 | ||
939 | }, | ||
940 | { | ||
941 | .id = 0, | ||
942 | .x = buttons_x + 32 + 20 + 12 + 24, | ||
943 | .y = buttons_y - 32 - 20 - 12, | ||
944 | .data = &gba_btn_fx_lr, | ||
945 | .n_tiles = 4, | ||
946 | .frames = 4, | ||
947 | .obj_attr_0 = OBJ_SHAPE_SQUARE, | ||
948 | .obj_attr_1 = OBJ_H_FLIP | OBJ_SIZE_MID, | ||
949 | .obj_attr_2 = 0 | ||
950 | }, | ||
951 | }, | ||
952 | }, | ||
953 | // A. | ||
954 | { | ||
955 | .frame = 0, | ||
956 | .n_obj = 3, | ||
957 | .n_frames = 8, | ||
958 | .state = BTN_STATE_RELEASED, | ||
959 | .animations = &btn_animation_ab, | ||
960 | .sprites = &(ObjState[]){ | ||
961 | { | ||
962 | .id = 0, | ||
963 | .x = buttons_x + 32 + 20 + 20, | ||
964 | .y = buttons_y + 32 - 16, | ||
965 | .data = &gba_btn_a_data, | ||
966 | .n_tiles = 4, | ||
967 | .frames = 1, | ||
968 | .obj_attr_0 = OBJ_SHAPE_SQUARE, | ||
969 | .obj_attr_1 = OBJ_SIZE_MID, | ||
970 | .obj_attr_2 = 0 | ||
971 | }, | ||
972 | { | ||
973 | .id = 0, | ||
974 | .x = buttons_x + 32 + 20 + 20, | ||
975 | .y = buttons_y + 32 - 16 + 8, | ||
976 | .data = &gba_btn_ab_shadow_data, | ||
977 | .n_tiles = 2, | ||
978 | .frames = 1, | ||
979 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
980 | .obj_attr_1 = OBJ_SIZE_SMALL, | ||
981 | .obj_attr_2 = 0 | ||
982 | }, | ||
983 | { | ||
984 | .id = 0, | ||
985 | .x = buttons_x + 32 + 19 - 7 + 20, | ||
986 | .y = buttons_y + 32 - 23, | ||
987 | .data = &gba_btn_fx_ab, | ||
988 | .n_tiles = 16, | ||
989 | .frames = 3, | ||
990 | .obj_attr_0 = OBJ_SHAPE_SQUARE, | ||
991 | .obj_attr_1 = OBJ_SIZE_BIG, | ||
992 | .obj_attr_2 = 0 | ||
993 | }, | ||
994 | }, | ||
995 | }, | ||
996 | // B. | ||
997 | { | ||
998 | .frame = 0, | ||
999 | .n_obj = 3, | ||
1000 | .n_frames = 8, | ||
1001 | .state = BTN_STATE_RELEASED, | ||
1002 | .animations = &btn_animation_ab, | ||
1003 | .sprites = &(ObjState[]){ | ||
1004 | { | ||
1005 | .id = 0, | ||
1006 | .x = buttons_x + 32 + 20, | ||
1007 | .y = buttons_y + 32, | ||
1008 | .data = &gba_btn_b_data, | ||
1009 | .n_tiles = 4, | ||
1010 | .frames = 1, | ||
1011 | .obj_attr_0 = OBJ_SHAPE_SQUARE, | ||
1012 | .obj_attr_1 = OBJ_SIZE_MID, | ||
1013 | .obj_attr_2 = 0 | ||
1014 | }, | ||
1015 | { | ||
1016 | .id = 0, | ||
1017 | .x = buttons_x + 32 + 20, | ||
1018 | .y = buttons_y + 32 + 8, | ||
1019 | .data = &gba_btn_ab_shadow_data, | ||
1020 | .n_tiles = 2, | ||
1021 | .frames = 1, | ||
1022 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
1023 | .obj_attr_1 = OBJ_SIZE_SMALL, | ||
1024 | .obj_attr_2 = 0 | ||
1025 | }, | ||
1026 | { | ||
1027 | .id = 0, | ||
1028 | .x = buttons_x + 32 - 8 + 20, | ||
1029 | .y = buttons_y + 33 + 8 - 16, | ||
1030 | .data = &gba_btn_fx_ab, | ||
1031 | .n_tiles = 16, | ||
1032 | .frames = 3, | ||
1033 | .obj_attr_0 = OBJ_SHAPE_SQUARE, | ||
1034 | .obj_attr_1 = OBJ_SIZE_BIG, | ||
1035 | .obj_attr_2 = 0 | ||
1036 | }, | ||
1037 | }, | ||
1038 | }, | ||
1039 | // START. | ||
1040 | { | ||
1041 | .frame = 0, | ||
1042 | .n_obj = 3, | ||
1043 | .n_frames = 8, | ||
1044 | .state = BTN_STATE_RELEASED, | ||
1045 | .animations = &btn_animation_startselect, | ||
1046 | .sprites = &(ObjState[]){ | ||
1047 | { | ||
1048 | .id = 0, | ||
1049 | .x = buttons_x - 10 - 14, | ||
1050 | .y = buttons_y + 40 + 10, | ||
1051 | .data = &gba_btn_startselect_data, | ||
1052 | .n_tiles = 2, | ||
1053 | .frames = 2, | ||
1054 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
1055 | .obj_attr_1 = OBJ_SIZE_SMALL, | ||
1056 | .obj_attr_2 = 0 | ||
1057 | }, | ||
1058 | { | ||
1059 | .id = 0, | ||
1060 | .x = buttons_x - 18 - 14, | ||
1061 | .y = buttons_y + 46 + 10, | ||
1062 | .data = &gba_btn_start_text, | ||
1063 | .n_tiles = 4, | ||
1064 | .frames = 1, | ||
1065 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
1066 | .obj_attr_1 = OBJ_SIZE_MID, | ||
1067 | .obj_attr_2 = 0 | ||
1068 | }, | ||
1069 | { | ||
1070 | .id = 0, | ||
1071 | .x = buttons_x - 19 - 14, | ||
1072 | .y = buttons_y + 37 + 10, | ||
1073 | .data = &gba_btn_fx_startselect, | ||
1074 | .n_tiles = 4, | ||
1075 | .frames = 4, | ||
1076 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
1077 | .obj_attr_1 = OBJ_SIZE_MID, | ||
1078 | .obj_attr_2 = 0 | ||
1079 | }, | ||
1080 | }, | ||
1081 | }, | ||
1082 | // SELECT. | ||
1083 | { | ||
1084 | .frame = 0, | ||
1085 | .n_obj = 3, | ||
1086 | .n_frames = 8, | ||
1087 | .state = BTN_STATE_RELEASED, | ||
1088 | .animations = &btn_animation_startselect, | ||
1089 | .sprites = &(ObjState[]){ | ||
1090 | { | ||
1091 | .id = 0, | ||
1092 | .x = buttons_x - 10 + 26 - 14, | ||
1093 | .y = buttons_y + 40 + 10, | ||
1094 | .data = &gba_btn_startselect_data, | ||
1095 | .n_tiles = 2, | ||
1096 | .frames = 2, | ||
1097 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
1098 | .obj_attr_1 = OBJ_SIZE_SMALL, | ||
1099 | .obj_attr_2 = 0 | ||
1100 | }, | ||
1101 | { | ||
1102 | .id = 0, | ||
1103 | .x = buttons_x - 18 + 26 - 14, | ||
1104 | .y = buttons_y + 46 + 10, | ||
1105 | .data = &gba_btn_select_text, | ||
1106 | .n_tiles = 4, | ||
1107 | .frames = 1, | ||
1108 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
1109 | .obj_attr_1 = OBJ_SIZE_MID, | ||
1110 | .obj_attr_2 = 0 | ||
1111 | }, | ||
1112 | { | ||
1113 | .id = 0, | ||
1114 | .x = buttons_x - 19 + 26 - 14, | ||
1115 | .y = buttons_y + 37 + 10, | ||
1116 | .data = &gba_btn_fx_startselect, | ||
1117 | .n_tiles = 4, | ||
1118 | .frames = 4, | ||
1119 | .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
1120 | .obj_attr_1 = OBJ_SIZE_MID, | ||
1121 | .obj_attr_2 = 0 | ||
1122 | }, | ||
1123 | }, | ||
1124 | }, | ||
1125 | }; | ||
1126 | for (size_t i = 0; i < sizeof(buttons) / sizeof(MultiSprite); ++i) { | ||
1127 | init_button_sprite(&buttons[i]); | ||
1128 | } | ||
1129 | 34 | ||
1130 | int frame_counter = 0; | 35 | int frame_counter = 0; |
1131 | int x = 0; | 36 | int x = 0; |
@@ -1134,77 +39,6 @@ int main(void) { | |||
1134 | wait_vsync(); | 39 | wait_vsync(); |
1135 | poll_keys(); | 40 | poll_keys(); |
1136 | 41 | ||
1137 | if (key_pressed(KEY_DOWN)) { | ||
1138 | buttons[0].state = BTN_STATE_PRESSED; | ||
1139 | } else if (key_hold(KEY_DOWN)) { | ||
1140 | buttons[0].state = BTN_STATE_HOLD; | ||
1141 | } else { | ||
1142 | buttons[0].state = BTN_STATE_RELEASED; | ||
1143 | } | ||
1144 | if (key_pressed(KEY_UP)) { | ||
1145 | buttons[1].state = BTN_STATE_PRESSED; | ||
1146 | } else if (key_hold(KEY_UP)) { | ||
1147 | buttons[1].state = BTN_STATE_HOLD; | ||
1148 | } else { | ||
1149 | buttons[1].state = BTN_STATE_RELEASED; | ||
1150 | } | ||
1151 | if (key_pressed(KEY_LEFT)) { | ||
1152 | buttons[2].state = BTN_STATE_PRESSED; | ||
1153 | } else if (key_hold(KEY_LEFT)) { | ||
1154 | buttons[2].state = BTN_STATE_HOLD; | ||
1155 | } else { | ||
1156 | buttons[2].state = BTN_STATE_RELEASED; | ||
1157 | } | ||
1158 | if (key_pressed(KEY_RIGHT)) { | ||
1159 | buttons[3].state = BTN_STATE_PRESSED; | ||
1160 | } else if (key_hold(KEY_RIGHT)) { | ||
1161 | buttons[3].state = BTN_STATE_HOLD; | ||
1162 | } else { | ||
1163 | buttons[3].state = BTN_STATE_RELEASED; | ||
1164 | } | ||
1165 | if (key_pressed(KEY_L)) { | ||
1166 | buttons[4].state = BTN_STATE_PRESSED; | ||
1167 | } else if (key_hold(KEY_L)) { | ||
1168 | buttons[4].state = BTN_STATE_HOLD; | ||
1169 | } else { | ||
1170 | buttons[4].state = BTN_STATE_RELEASED; | ||
1171 | } | ||
1172 | if (key_pressed(KEY_R)) { | ||
1173 | buttons[5].state = BTN_STATE_PRESSED; | ||
1174 | } else if (key_hold(KEY_R)) { | ||
1175 | buttons[5].state = BTN_STATE_HOLD; | ||
1176 | } else { | ||
1177 | buttons[5].state = BTN_STATE_RELEASED; | ||
1178 | } | ||
1179 | if (key_pressed(KEY_A)) { | ||
1180 | buttons[6].state = BTN_STATE_PRESSED; | ||
1181 | } else if (key_hold(KEY_A)) { | ||
1182 | buttons[6].state = BTN_STATE_HOLD; | ||
1183 | } else { | ||
1184 | buttons[6].state = BTN_STATE_RELEASED; | ||
1185 | } | ||
1186 | if (key_pressed(KEY_B)) { | ||
1187 | buttons[7].state = BTN_STATE_PRESSED; | ||
1188 | } else if (key_hold(KEY_B)) { | ||
1189 | buttons[7].state = BTN_STATE_HOLD; | ||
1190 | } else { | ||
1191 | buttons[7].state = BTN_STATE_RELEASED; | ||
1192 | } | ||
1193 | if (key_pressed(KEY_START)) { | ||
1194 | buttons[8].state = BTN_STATE_PRESSED; | ||
1195 | } else if (key_hold(KEY_START)) { | ||
1196 | buttons[8].state = BTN_STATE_HOLD; | ||
1197 | } else { | ||
1198 | buttons[8].state = BTN_STATE_RELEASED; | ||
1199 | } | ||
1200 | if (key_pressed(KEY_SELECT)) { | ||
1201 | buttons[9].state = BTN_STATE_PRESSED; | ||
1202 | } else if (key_hold(KEY_SELECT)) { | ||
1203 | buttons[9].state = BTN_STATE_HOLD; | ||
1204 | } else { | ||
1205 | buttons[9].state = BTN_STATE_RELEASED; | ||
1206 | } | ||
1207 | |||
1208 | if (key_hold(KEY_DOWN)) { | 42 | if (key_hold(KEY_DOWN)) { |
1209 | y += 3; | 43 | y += 3; |
1210 | } | 44 | } |
@@ -1222,9 +56,7 @@ int main(void) { | |||
1222 | BG_H_SCROLL_0 = x; | 56 | BG_H_SCROLL_0 = x; |
1223 | BG_V_SCROLL_0 = y; | 57 | BG_V_SCROLL_0 = y; |
1224 | 58 | ||
1225 | for (size_t i = 0; i < sizeof(buttons) / sizeof(MultiSprite); ++i) { | 59 | update_button_sprites(); |
1226 | button_tick(&buttons[i]); | ||
1227 | } | ||
1228 | }; | 60 | }; |
1229 | 61 | ||
1230 | return 0; | 62 | return 0; |