summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-04-28 19:30:20 +0200
committerBad Diode <bd@badd10de.dev>2021-04-28 19:30:20 +0200
commit3e0bb6c9d1f788b51d26ed55acd32291df721129 (patch)
tree654ef0692811128bff1dc1bd62aefcc38d4293f5
parentd576a07fbafb48449474651d384e37d34c111cbd (diff)
downloadgba-experiments-3e0bb6c9d1f788b51d26ed55acd32291df721129.tar.gz
gba-experiments-3e0bb6c9d1f788b51d26ed55acd32291df721129.zip
Add support for IRQ handlingirq-demo
-rw-r--r--Makefile6
-rw-r--r--src/common.h133
-rw-r--r--src/gba-buttons.c7
-rw-r--r--src/irs.s88
-rw-r--r--src/main.c38
5 files changed, 248 insertions, 24 deletions
diff --git a/Makefile b/Makefile
index 7202bb9..741cbfd 100644
--- a/Makefile
+++ b/Makefile
@@ -30,13 +30,13 @@ CC := arm-none-eabi-gcc
30OBJCOPY := arm-none-eabi-objcopy 30OBJCOPY := arm-none-eabi-objcopy
31ARCH := -mthumb -mthumb-interwork 31ARCH := -mthumb -mthumb-interwork
32SPECS := -specs=gba.specs 32SPECS := -specs=gba.specs
33CFLAGS := -g -Wall -Wextra -pedantic -fno-strict-aliasing -Wno-incompatible-pointer-types 33CFLAGS := -Wall -Wextra -pedantic -fno-strict-aliasing -Wno-incompatible-pointer-types
34CFLAGS += -mcpu=arm7tdmi -mtune=arm7tdmi $(ARCH) 34CFLAGS += -mcpu=arm7tdmi -mtune=arm7tdmi $(ARCH)
35CFLAGS += -I$(LIBGBA_SRC) 35CFLAGS += -I$(LIBGBA_SRC)
36LDFLAGS := $(ARCH) $(SPECS) 36LDFLAGS := $(ARCH) $(SPECS)
37LDLIBS := $(LIBGBA) 37LDLIBS := $(LIBGBA)
38RELEASE_CFLAGS := -DNDEBUG -O2 38RELEASE_CFLAGS := -DNDEBUG -O2 -g
39DEBUG_CFLAGS := -DDEBUG -O2 39DEBUG_CFLAGS := -DDEBUG -O2 -g
40 40
41.PHONY: clean run 41.PHONY: clean run
42 42
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
412typedef 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
429typedef void (*IrsFunc)(void);
430
431IrsFunc 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
448void
449irq_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
469void
470irq_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
490void
491irs_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.
511void irs_main(void);
512
513void
514irq_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
139typedef enum {BTN_STATE_IDLE, BTN_STATE_PRESSED, BTN_STATE_RELEASED, BTN_STATE_HOLD} BtnState; 139typedef enum {
140 BTN_STATE_IDLE = 0,
141 BTN_STATE_PRESSED,
142 BTN_STATE_RELEASED,
143 BTN_STATE_HOLD,
144} BtnState;
140 145
141typedef struct AnimationEntry { 146typedef 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
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 @ 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
79irs_main_exit:
80 @ Restore IRQ_CTRL.
81 mov r3, #1 @ r3 = 0
82 strh r3, [ip, #8] @ *(ip + 0x8) = r3
83 bx lr
84
85mem_irq_base_reg:
86 .word 0x04000200 @ IRQ_ENABLE
87 .word 0x04000202 @ IRQ_ACK
88 .word 0x04000208 @ IRQ_CTRL
diff --git a/src/main.c b/src/main.c
index decd40d..6073a6a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -17,11 +17,17 @@
17// BIOS calls 17// BIOS calls
18// 18//
19 19
20int bios_vblank_wait();
20int bios_div(int num, int denom); 21int bios_div(int num, int denom);
21 22
22int normal_div(int num, int denom) { 23int hblank_counter = 0;
23 return num / denom; 24
24}; 25void
26irs_hblank_func() {
27 u16 clr = (DISP_VCOUNT / 8);
28 PAL_BUFFER_BG[0] = rgb15(clr, 0, 31 - clr);
29 hblank_counter++;
30}
25 31
26int main(void) { 32int 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 };