summaryrefslogtreecommitdiffstats
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c1184
1 files changed, 8 insertions, 1176 deletions
diff --git a/src/main.c b/src/main.c
index 3002f7e..b8f2334 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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).
87typedef u16 Color;
88
89//
90// Tile memory access.
91//
92
93// NOTE: Only defining 4bpp tiles for now.
94typedef struct Tile {
95 u32 data[8];
96} Tile;
97
98typedef Tile TileBlock[512];
99#define TILE_MEM ((TileBlock*) MEM_VRAM)
100
101typedef 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//
109typedef 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
119static inline Color
120rgb15(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.
175static void
176put_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
186static void
187put_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.
197static void
198draw_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
260static inline void
261draw_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
286static inline void
287draw_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
309static inline void
310wait_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.
326static void
327put_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
340static void
341draw_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
350static inline void
351flip_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.
383static inline
384void 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
393static inline
394u32 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.
421static u16 key_curr = 0;
422static u16 key_prev = 0;
423
424static inline void
425poll_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.
434static inline u32
435key_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.
440static inline u32
441key_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
448void
449draw_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
483void
484copy_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
504u32
505unpack_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
517typedef 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
528typedef struct ButtonSprite {
529 int id;
530 int x;
531 int y;
532 int frame;
533 BtnState state;
534} ButtonSprite;
535
536typedef 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
547Sprite sprites[NUM_SPRITES];
548
549// Keeping track of unique sprites and current sprite memory pointer using
550// global singletons.
551size_t sprite_counter = 0;
552size_t sprite_tile_counter = 0;
553u32 *sprite_memory = NULL;
554
555// Loads the sprite data into video memory and initialize the Sprite structure.
556size_t
557load_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
570size_t
571load_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
591void
592init_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
602void
603button_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
658int main(void) { 14int 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;