diff options
author | Bad Diode <bd@badd10de.dev> | 2021-04-28 19:30:20 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-04-28 19:30:20 +0200 |
commit | 3e0bb6c9d1f788b51d26ed55acd32291df721129 (patch) | |
tree | 654ef0692811128bff1dc1bd62aefcc38d4293f5 /src | |
parent | d576a07fbafb48449474651d384e37d34c111cbd (diff) | |
download | gba-experiments-irq-demo.tar.gz gba-experiments-irq-demo.zip |
Add support for IRQ handlingirq-demo
Diffstat (limited to 'src')
-rw-r--r-- | src/common.h | 133 | ||||
-rw-r--r-- | src/gba-buttons.c | 7 | ||||
-rw-r--r-- | src/irs.s | 88 | ||||
-rw-r--r-- | src/main.c | 38 |
4 files changed, 245 insertions, 21 deletions
diff --git a/src/common.h b/src/common.h index e3405be..dac48ad 100644 --- a/src/common.h +++ b/src/common.h | |||
@@ -46,6 +46,15 @@ | |||
46 | #define DISP_OBJ (1 << 12) | 46 | #define DISP_OBJ (1 << 12) |
47 | #define DISP_ENABLE_SPRITES DISP_OBJ | DISP_OBJ_1D | 47 | #define DISP_ENABLE_SPRITES DISP_OBJ | DISP_OBJ_1D |
48 | 48 | ||
49 | // These bits are used to control the DISP_STATUS register. | ||
50 | #define DISP_VBLANK_STATUS (1 << 0x0) | ||
51 | #define DISP_HBLANK_STATUS (1 << 0x1) | ||
52 | #define DISP_VCOUNT_STATUS (1 << 0x2) | ||
53 | #define DISP_VBLANK_IRQ (1 << 0x3) | ||
54 | #define DISP_HBLANK_IRQ (1 << 0x4) | ||
55 | #define DISP_VCOUNT_IRQ (1 << 0x5) | ||
56 | #define DISP_VCOUNT_TRIGGER(N) ((N) << 0x8) | ||
57 | |||
49 | // Registers to control of BG layers. | 58 | // Registers to control of BG layers. |
50 | #define BG_CTRL(N) *((vu16*)(0x04000008 + 0x0002 * (N))) | 59 | #define BG_CTRL(N) *((vu16*)(0x04000008 + 0x0002 * (N))) |
51 | 60 | ||
@@ -245,8 +254,14 @@ u32 profile_stop(void) { | |||
245 | // Input handling. | 254 | // Input handling. |
246 | // | 255 | // |
247 | 256 | ||
248 | // Memory address for key input register | 257 | // Memory address for key input and control register |
249 | #define KEY_INPUTS *((vu16*) 0x04000130) | 258 | #define KEY_INPUTS *((vu16*) 0x04000130) |
259 | #define KEY_CTRL *((vu16*) 0x04000132) | ||
260 | |||
261 | // Key control register bits. | ||
262 | #define KEY_IRQ_KEY(N) (N) | ||
263 | #define KEY_IRQ (1 << 0xE) | ||
264 | #define KEY_IRQ_IF_SET (1 << 0xF) | ||
250 | 265 | ||
251 | // Alias for key pressing bits. | 266 | // Alias for key pressing bits. |
252 | #define KEY_A (1 << 0) | 267 | #define KEY_A (1 << 0) |
@@ -385,4 +400,120 @@ dma_fill(void *dst, const void *src, u32 size, int channel) { | |||
385 | dma_transfer_fill(dst, src, size / 4, channel, DMA_CHUNK_32 | DMA_ENABLE); | 400 | dma_transfer_fill(dst, src, size / 4, channel, DMA_CHUNK_32 | DMA_ENABLE); |
386 | } | 401 | } |
387 | 402 | ||
403 | // | ||
404 | // Interrupts. | ||
405 | // | ||
406 | |||
407 | #define IRQ_ENABLE *((vu16*) 0x04000200) | ||
408 | #define IRQ_ACK *((vu16*) 0x04000202) | ||
409 | #define IRQ_CTRL *((vu16*) 0x04000208) | ||
410 | #define IRQ_ACK_BIOS *((vu16*) 0x03007FF8) | ||
411 | |||
412 | typedef enum { | ||
413 | IRQ_VBLANK, | ||
414 | IRQ_HBLANK, | ||
415 | IRQ_VCOUNT, | ||
416 | IRQ_TIMER_0, | ||
417 | IRQ_TIMER_1, | ||
418 | IRQ_TIMER_2, | ||
419 | IRQ_TIMER_3, | ||
420 | IRQ_SERIAL, | ||
421 | IRQ_DMA_0, | ||
422 | IRQ_DMA_1, | ||
423 | IRQ_DMA_2, | ||
424 | IRQ_DMA_3, | ||
425 | IRQ_KEYPAD, | ||
426 | IRQ_GAMEPAK, | ||
427 | } IrqIndex; | ||
428 | |||
429 | typedef void (*IrsFunc)(void); | ||
430 | |||
431 | IrsFunc irs_table[] = { | ||
432 | [IRQ_VBLANK ] = NULL, | ||
433 | [IRQ_HBLANK ] = NULL, | ||
434 | [IRQ_VCOUNT ] = NULL, | ||
435 | [IRQ_TIMER_0] = NULL, | ||
436 | [IRQ_TIMER_1] = NULL, | ||
437 | [IRQ_TIMER_2] = NULL, | ||
438 | [IRQ_TIMER_3] = NULL, | ||
439 | [IRQ_SERIAL ] = NULL, | ||
440 | [IRQ_DMA_0 ] = NULL, | ||
441 | [IRQ_DMA_1 ] = NULL, | ||
442 | [IRQ_DMA_2 ] = NULL, | ||
443 | [IRQ_DMA_3 ] = NULL, | ||
444 | [IRQ_KEYPAD ] = NULL, | ||
445 | [IRQ_GAMEPAK] = NULL, | ||
446 | }; | ||
447 | |||
448 | void | ||
449 | irq_enable(IrqIndex idx) { | ||
450 | switch (idx) { | ||
451 | case IRQ_VBLANK: { DISP_STATUS |= DISP_VBLANK_IRQ; } break; | ||
452 | case IRQ_HBLANK: { DISP_STATUS |= DISP_HBLANK_IRQ; } break; | ||
453 | case IRQ_VCOUNT: { DISP_STATUS |= DISP_VCOUNT_IRQ; } break; | ||
454 | case IRQ_TIMER_0: { TIMER_CTRL_0 |= TIMER_CTRL_IRQ; } break; | ||
455 | case IRQ_TIMER_1: { TIMER_CTRL_1 |= TIMER_CTRL_IRQ; } break; | ||
456 | case IRQ_TIMER_2: { TIMER_CTRL_2 |= TIMER_CTRL_IRQ; } break; | ||
457 | case IRQ_TIMER_3: { TIMER_CTRL_3 |= TIMER_CTRL_IRQ; } break; | ||
458 | case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; | ||
459 | case IRQ_DMA_0: { DMA_CTRL(0) |= DMA_IRQ; } break; | ||
460 | case IRQ_DMA_1: { DMA_CTRL(1) |= DMA_IRQ; } break; | ||
461 | case IRQ_DMA_2: { DMA_CTRL(2) |= DMA_IRQ; } break; | ||
462 | case IRQ_DMA_3: { DMA_CTRL(3) |= DMA_IRQ; } break; | ||
463 | case IRQ_KEYPAD: { KEY_CTRL |= KEY_IRQ; } break; | ||
464 | case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; | ||
465 | } | ||
466 | IRQ_ENABLE |= (1 << idx); | ||
467 | } | ||
468 | |||
469 | void | ||
470 | irq_disable(IrqIndex idx) { | ||
471 | switch (idx) { | ||
472 | case IRQ_VBLANK: { DISP_STATUS &= ~DISP_VBLANK_IRQ; } break; | ||
473 | case IRQ_HBLANK: { DISP_STATUS &= ~DISP_HBLANK_IRQ; } break; | ||
474 | case IRQ_VCOUNT: { DISP_STATUS &= ~DISP_VCOUNT_IRQ; } break; | ||
475 | case IRQ_TIMER_0: { TIMER_CTRL_0 &= ~TIMER_CTRL_IRQ; } break; | ||
476 | case IRQ_TIMER_1: { TIMER_CTRL_1 &= ~TIMER_CTRL_IRQ; } break; | ||
477 | case IRQ_TIMER_2: { TIMER_CTRL_2 &= ~TIMER_CTRL_IRQ; } break; | ||
478 | case IRQ_TIMER_3: { TIMER_CTRL_3 &= ~TIMER_CTRL_IRQ; } break; | ||
479 | case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; | ||
480 | case IRQ_DMA_0: { DMA_CTRL(0) &= ~DMA_IRQ; } break; | ||
481 | case IRQ_DMA_1: { DMA_CTRL(1) &= ~DMA_IRQ; } break; | ||
482 | case IRQ_DMA_2: { DMA_CTRL(2) &= ~DMA_IRQ; } break; | ||
483 | case IRQ_DMA_3: { DMA_CTRL(3) &= ~DMA_IRQ; } break; | ||
484 | case IRQ_KEYPAD: { KEY_CTRL &= ~KEY_IRQ; } break; | ||
485 | case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; | ||
486 | } | ||
487 | IRQ_ENABLE &= ~(1 << idx); | ||
488 | } | ||
489 | |||
490 | void | ||
491 | irs_set(IrqIndex idx, IrsFunc func) { | ||
492 | // Store IRQ_CTRL status and disable interrupts for now. | ||
493 | u16 irq_ctrl = IRQ_CTRL; | ||
494 | IRQ_CTRL = 0; | ||
495 | |||
496 | // Update the IRS table and enable/disable the given IRQ. | ||
497 | irs_table[idx] = func; | ||
498 | if (func == NULL) { | ||
499 | irq_disable(idx); | ||
500 | } else { | ||
501 | irq_enable(idx); | ||
502 | } | ||
503 | |||
504 | // Restore previous irq_ctrl. | ||
505 | IRQ_CTRL = irq_ctrl; | ||
506 | } | ||
507 | |||
508 | #define IRS_MAIN *(IrsFunc*)(0x03007FFC) | ||
509 | |||
510 | // External irs_main function, has to be written in ARM assembly. | ||
511 | void irs_main(void); | ||
512 | |||
513 | void | ||
514 | irq_init() { | ||
515 | IRS_MAIN = irs_main; | ||
516 | IRQ_CTRL = 1; | ||
517 | } | ||
518 | |||
388 | #endif // GBAEXP_COMMON_H | 519 | #endif // GBAEXP_COMMON_H |
diff --git a/src/gba-buttons.c b/src/gba-buttons.c index dd06351..d49383e 100644 --- a/src/gba-buttons.c +++ b/src/gba-buttons.c | |||
@@ -136,7 +136,12 @@ u32 gba_btn_fx_startselect[] = { | |||
136 | 0x01800000, 0x00000000, 0x00000000, 0x00000004, | 136 | 0x01800000, 0x00000000, 0x00000000, 0x00000004, |
137 | }; | 137 | }; |
138 | 138 | ||
139 | typedef enum {BTN_STATE_IDLE, BTN_STATE_PRESSED, BTN_STATE_RELEASED, BTN_STATE_HOLD} BtnState; | 139 | typedef enum { |
140 | BTN_STATE_IDLE = 0, | ||
141 | BTN_STATE_PRESSED, | ||
142 | BTN_STATE_RELEASED, | ||
143 | BTN_STATE_HOLD, | ||
144 | } BtnState; | ||
140 | 145 | ||
141 | typedef struct AnimationEntry { | 146 | typedef struct AnimationEntry { |
142 | int x_offset; | 147 | int x_offset; |
diff --git a/src/irs.s b/src/irs.s new file mode 100644 index 0000000..924441e --- /dev/null +++ b/src/irs.s | |||
@@ -0,0 +1,88 @@ | |||
1 | .file "irs.s" | ||
2 | .extern irs_table; | ||
3 | .section .iwram, "ax", %progbits | ||
4 | .arm | ||
5 | .align | ||
6 | .global irs_main | ||
7 | |||
8 | irs_main: | ||
9 | @ Get the contents of IRQ_ENABLE, IRQ_ACK, and IRQ_CTRL | ||
10 | ldr ip, mem_irq_base_reg @ ip = (IRQ_ENABLE << 16) | IRQ_ACK | ||
11 | ldr r0, [ip] @ r0 = irq_enable | ||
12 | and r1, r0, r0, lsr #16 @ r1 = irq_enable & irq_ack | ||
13 | |||
14 | @ Disable IRQ_CTRL for now. | ||
15 | mov r3, #0 @ r3 = 0 | ||
16 | strh r3, [ip, #8] @ *(ip + 0x8) = r3 | ||
17 | |||
18 | @ r0 = irs_table address pointer | ||
19 | @ r2 = tmp | ||
20 | @ r3 = 0 (for r3; r3 < 14; r3++) | ||
21 | ldr r0, = irs_table | ||
22 | irs_main_fp_search: | ||
23 | @ Check that the current index is an active IRQ. | ||
24 | mov r2, #1 | ||
25 | and r2, r1, r2, lsl r3 | ||
26 | cmp r2, #0 | ||
27 | beq irs_main_fp_search_not_enabled | ||
28 | |||
29 | @ Extract the function pointer for this IRS if available. | ||
30 | ldr r2, [r0] | ||
31 | cmp r2, #0 | ||
32 | bne irs_main_handle_irs | ||
33 | |||
34 | irs_main_fp_search_not_enabled: | ||
35 | add r0, r0, #4 | ||
36 | add r3, #1 | ||
37 | cmp r3, #14 | ||
38 | bne irs_main_fp_search | ||
39 | b irs_main_exit | ||
40 | |||
41 | irs_main_handle_irs: | ||
42 | @ r2: Contains IRQ function pointer. | ||
43 | @ r3: Stores the IRQ index. | ||
44 | |||
45 | @ Acknowledge that we are handling this interrupt writing to IRQ_ACK and | ||
46 | @ IRQ_ACK_BIOS. | ||
47 | mov r0, #1 | ||
48 | lsl r0, r0, r3 | ||
49 | strh r0, [ip, #2] | ||
50 | @ TODO: IRQ_ACK_BIOS | ||
51 | |||
52 | @ Store the SPSR in one of the free registers and save it along with the | ||
53 | @ return pointer. | ||
54 | mrs r3, spsr | ||
55 | stmfd sp!, {r3, lr} | ||
56 | |||
57 | @ Set CPU to system mode | ||
58 | mrs r3, cpsr | ||
59 | bic r3, r3, #0xDF | ||
60 | orr r3, r3, #0x1F | ||
61 | msr cpsr, r3 | ||
62 | |||
63 | @ Call isr function pointer | ||
64 | stmfd sp!, {lr} | ||
65 | mov lr, pc | ||
66 | bx r2 | ||
67 | ldmfd sp!, {lr} | ||
68 | |||
69 | @ Set CPU to irq mode | ||
70 | mrs r3, cpsr | ||
71 | bic r3, r3, #0xDF | ||
72 | orr r3, r3, #0x92 | ||
73 | msr cpsr, r3 | ||
74 | |||
75 | @ Restore the SPSR and the registers. | ||
76 | ldmfd sp!, {r3, lr} | ||
77 | msr spsr, r3 | ||
78 | |||
79 | irs_main_exit: | ||
80 | @ Restore IRQ_CTRL. | ||
81 | mov r3, #1 @ r3 = 0 | ||
82 | strh r3, [ip, #8] @ *(ip + 0x8) = r3 | ||
83 | bx lr | ||
84 | |||
85 | mem_irq_base_reg: | ||
86 | .word 0x04000200 @ IRQ_ENABLE | ||
87 | .word 0x04000202 @ IRQ_ACK | ||
88 | .word 0x04000208 @ IRQ_CTRL | ||
@@ -17,11 +17,17 @@ | |||
17 | // BIOS calls | 17 | // BIOS calls |
18 | // | 18 | // |
19 | 19 | ||
20 | int bios_vblank_wait(); | ||
20 | int bios_div(int num, int denom); | 21 | int bios_div(int num, int denom); |
21 | 22 | ||
22 | int normal_div(int num, int denom) { | 23 | int hblank_counter = 0; |
23 | return num / denom; | 24 | |
24 | }; | 25 | void |
26 | irs_hblank_func() { | ||
27 | u16 clr = (DISP_VCOUNT / 8); | ||
28 | PAL_BUFFER_BG[0] = rgb15(clr, 0, 31 - clr); | ||
29 | hblank_counter++; | ||
30 | } | ||
25 | 31 | ||
26 | int main(void) { | 32 | int main(void) { |
27 | // Configure the display in mode 0 to show OBJs, where tile memory is | 33 | // Configure the display in mode 0 to show OBJs, where tile memory is |
@@ -36,28 +42,22 @@ int main(void) { | |||
36 | // Initialize text engine. | 42 | // Initialize text engine. |
37 | txt_init(0, COLOR_RED, 0); | 43 | txt_init(0, COLOR_RED, 0); |
38 | 44 | ||
39 | size_t n_iter = 1000; | 45 | // Register interrupts. |
40 | profile_start(); | 46 | irq_init(); |
41 | int test = 0; | 47 | irs_set(IRQ_HBLANK, irs_hblank_func); |
42 | for (size_t i = 0; i < n_iter; ++i) { | ||
43 | txt_printf("Bios div: %d\n", bios_div(5 * i, 5)); | ||
44 | } | ||
45 | u32 bios_div_perf = profile_stop(); | ||
46 | |||
47 | profile_start(); | ||
48 | for (size_t i = 0; i < n_iter; ++i) { | ||
49 | txt_printf("Code div: %d\n", normal_div(5 * i, 5)); | ||
50 | } | ||
51 | u32 code_perf = profile_stop(); | ||
52 | |||
53 | txt_printf("C code div perf: %d\n", code_perf); | ||
54 | txt_printf("BIOS div perf: %d\n", bios_div_perf); | ||
55 | 48 | ||
56 | int frame_counter = 0; | 49 | int frame_counter = 0; |
57 | while(true) { | 50 | while(true) { |
51 | // bios_vblank_wait(); | ||
58 | wait_vsync(); | 52 | wait_vsync(); |
59 | poll_keys(); | 53 | poll_keys(); |
60 | 54 | ||
55 | txt_position(0, 1); | ||
56 | txt_clear_line(); | ||
57 | txt_printf(" HBlank counter: %d\n", hblank_counter); | ||
58 | txt_clear_line(); | ||
59 | txt_printf(" Frame counter: %d", frame_counter); | ||
60 | |||
61 | frame_counter++; | 61 | frame_counter++; |
62 | // update_button_sprites(); | 62 | // update_button_sprites(); |
63 | }; | 63 | }; |