From 215465df74a6065f4b0fdf199b8b04454520a398 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Wed, 2 Jun 2021 20:20:23 +0200 Subject: Update the renderer to support a text layer --- src/bd-font.c | 141 +++++++++++ src/bios_calls.s | 337 -------------------------- src/common.h | 667 --------------------------------------------------- src/filesystem.h | 2 +- src/gba/bios_calls.s | 337 ++++++++++++++++++++++++++ src/gba/gba.h | 667 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/gba/interrupts.c | 91 +++++++ src/gba/interrupts.s | 89 +++++++ src/interrupts.c | 91 ------- src/interrupts.s | 89 ------- src/main.c | 9 +- src/renderer.c | 71 ++++-- 12 files changed, 1380 insertions(+), 1211 deletions(-) create mode 100644 src/bd-font.c delete mode 100644 src/bios_calls.s delete mode 100644 src/common.h create mode 100644 src/gba/bios_calls.s create mode 100644 src/gba/gba.h create mode 100644 src/gba/interrupts.c create mode 100644 src/gba/interrupts.s delete mode 100644 src/interrupts.c delete mode 100644 src/interrupts.s (limited to 'src') diff --git a/src/bd-font.c b/src/bd-font.c new file mode 100644 index 0000000..5276735 --- /dev/null +++ b/src/bd-font.c @@ -0,0 +1,141 @@ +/* +Copyright (c) 2021 Bad Diode + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE. +*/ + +static const u32 bd_font[] = { + 0x00000000, 0x00000000, 0x00002400, 0x423c0000, + 0x00002400, 0x3c420000, 0x0000363e, 0x3e1c0800, + 0x00081c3e, 0x3e1c0800, 0x001c1c3e, 0x363e081c, + 0x00081c3e, 0x3e3e081c, 0x00000018, 0x18000000, + 0x7e7e7e66, 0x667e7e7e, 0x00001824, 0x24180000, + 0x7e7e665a, 0x5a667e7e, 0x00081c3e, 0x081c221c, + 0x001c221c, 0x08083e08, 0x00183828, 0x08080c0c, + 0x003c2424, 0x24343606, 0x00082208, 0x1c082208, + 0x040c1c3c, 0x1c0c0400, 0x2030383c, 0x38302000, + 0x081c3e08, 0x083e1c08, 0x00141414, 0x14140014, + 0x003c2a2a, 0x2c282828, 0x0038043c, 0x423c201e, + 0x00000000, 0x7e000000, 0x081c3e08, 0x3e1c083e, + 0x081c3e08, 0x08080800, 0x00080808, 0x083e1c08, + 0x00001030, 0x7e301000, 0x0000080c, 0x7e0c0800, + 0x00000000, 0x0002023e, 0x00002466, 0xff662400, + 0x0008081c, 0x1c3e3e00, 0x003e3e1c, 0x1c080800, + 0x00000000, 0x00000000, 0x00080808, 0x08080008, + 0x14141400, 0x00000000, 0x0000143e, 0x143e1400, + 0x00081c02, 0x1c201e08, 0x00002616, 0x08343200, + 0x00081408, 0x34122c00, 0x08080800, 0x00000000, + 0x08040404, 0x04040408, 0x08101010, 0x10101008, + 0x00001408, 0x3e081400, 0x00000808, 0x3e080800, + 0x00000000, 0x000c0804, 0x00000000, 0x3e000000, + 0x00000000, 0x000c0c00, 0x00203018, 0x0c060200, + 0x00001c22, 0x2a221c00, 0x00000c0a, 0x08083e00, + 0x00001c20, 0x1c023e00, 0x00001c22, 0x18221c00, + 0x00001018, 0x143e1000, 0x00003e02, 0x1e201e00, + 0x00001c02, 0x1e221c00, 0x00003e20, 0x10080400, + 0x00001c22, 0x1c221c00, 0x00001c22, 0x3c201c00, + 0x00000c0c, 0x000c0c00, 0x00000c0c, 0x000c0804, + 0x00100804, 0x04081000, 0x0000003e, 0x003e0000, + 0x00040810, 0x10080400, 0x001c2220, 0x10080008, + 0x001c322a, 0x1a021c00, 0x001c2222, 0x3e222200, + 0x001e221e, 0x22221e00, 0x001c2202, 0x02221c00, + 0x000e1222, 0x22120e00, 0x003e021e, 0x02023e00, + 0x003e021e, 0x02020200, 0x001c2202, 0x32221c00, + 0x00222222, 0x3e222200, 0x003e0808, 0x08083e00, + 0x00202020, 0x22221c00, 0x0022120a, 0x0e122200, + 0x00020202, 0x02023e00, 0x0022362a, 0x22222200, + 0x0022262a, 0x32222200, 0x001c2222, 0x22221c00, + 0x001e2222, 0x1e020200, 0x001c2222, 0x2a122c00, + 0x001e2222, 0x1e122200, 0x001c021c, 0x20221c00, + 0x003e0808, 0x08080800, 0x00222222, 0x22221c00, + 0x00222222, 0x22140800, 0x0022222a, 0x2a2a1400, + 0x00221408, 0x08142200, 0x00222214, 0x08080800, + 0x003e1008, 0x04023e00, 0x1c040404, 0x0404041c, + 0x0002060c, 0x18302000, 0x1c101010, 0x1010101c, + 0x08142200, 0x00000000, 0x00000000, 0x00003e00, + 0x00040810, 0x00000000, 0x00001c20, 0x3c223c00, + 0x0002021e, 0x22221e00, 0x00001c22, 0x02221c00, + 0x0020203c, 0x22223c00, 0x00001c22, 0x1e023c00, + 0x00003c02, 0x021e0202, 0x00003c22, 0x223c201c, + 0x0002021e, 0x22222200, 0x0008000c, 0x08083e00, + 0x00200020, 0x2020221c, 0x0002120a, 0x060a3200, + 0x000c0808, 0x08083e00, 0x0000162a, 0x2a2a2a00, + 0x00000e32, 0x22222200, 0x00001c22, 0x22221c00, + 0x00001e22, 0x221e0202, 0x00003c22, 0x223c2070, + 0x00001a26, 0x02020200, 0x00001c02, 0x1c201e00, + 0x00043e04, 0x04041800, 0x00002222, 0x22221c00, + 0x00002222, 0x22140800, 0x00002222, 0x2a2a1400, + 0x00002214, 0x08142200, 0x00002222, 0x223c201c, + 0x00003e10, 0x08043e00, 0x18040402, 0x02040418, + 0x00080808, 0x08080808, 0x0c101020, 0x2010100c, + 0x00002c1a, 0x00000000, 0x00000814, 0x22223e00, + 0x001c2202, 0x221c080e, 0x00140022, 0x2222221c, + 0x1008001c, 0x221e023c, 0x0814001c, 0x203c223c, + 0x0014001c, 0x203c223c, 0x0408001c, 0x203c223c, + 0x0814081c, 0x203c223c, 0x00001c02, 0x221c080e, + 0x0814001c, 0x221e023c, 0x0014001c, 0x221e023c, + 0x0408001c, 0x221e023c, 0x0014000c, 0x0808083e, + 0x0014000c, 0x0808083e, 0x0408000c, 0x0808083e, + 0x0014001c, 0x223e2222, 0x0814081c, 0x223e2222, + 0x1008003e, 0x021e023e, 0x00001628, 0x1c0a3400, + 0x003c0a1a, 0x0e0a3a00, 0x0814001c, 0x2222221c, + 0x0014001c, 0x2222221c, 0x0408001c, 0x2222221c, + 0x08140022, 0x2222221c, 0x04080022, 0x2222221c, + 0x00140022, 0x223c201c, 0x14001c22, 0x2222221c, + 0x14002222, 0x2222221c, 0x00081c22, 0x02221c08, + 0x0018240e, 0x04043e00, 0x00221408, 0x1c081c08, + 0x0038041e, 0x041e0438, 0x0030081c, 0x08080806, + 0x1008001c, 0x203c223c, 0x1008000c, 0x0808083e, + 0x1008001c, 0x2222221c, 0x10080022, 0x2222221c, + 0x2c1a000e, 0x32222222, 0x2c1a0022, 0x262a3222, + 0x001c122c, 0x001e0000, 0x000c120c, 0x001e0000, + 0x08000804, 0x02221c00, 0x0000003e, 0x02020000, + 0x0000003e, 0x20200000, 0x06241468, 0x4422f000, + 0x06241468, 0x54f24000, 0x08000808, 0x08080800, + 0x00482412, 0x24480000, 0x00122448, 0x24120000, + 0x00880022, 0x00880022, 0x55885522, 0x55885522, + 0x55aa55aa, 0x55aa55aa, 0x08080808, 0x08080808, + 0x0808080f, 0x08080808, 0x08080f08, 0x0f080808, + 0x14141417, 0x14141414, 0x0000001f, 0x14141414, + 0x00000f08, 0x0f080808, 0x14141710, 0x17141414, + 0x14141414, 0x14141414, 0x00001f10, 0x17141414, + 0x14141710, 0x1f000000, 0x1414141f, 0x00000000, + 0x08080f08, 0x0f000000, 0x0000000f, 0x08080808, + 0x080808f8, 0x00000000, 0x080808ff, 0x00000000, + 0x000000ff, 0x08080808, 0x080808f8, 0x08080808, + 0x000000ff, 0x00000000, 0x080808ff, 0x08080808, + 0x0808f808, 0xf8080808, 0x141414f4, 0x14141414, + 0x1414f404, 0xfc000000, 0x0000fc04, 0xf4141414, + 0x1414f700, 0xff000000, 0x0000ff00, 0xf7141414, + 0x1414f404, 0xf4141414, 0x0000ff00, 0xff000000, + 0x1414f700, 0xf7141414, 0x0808ff00, 0xff000000, + 0x141414ff, 0x00000000, 0x0000ff00, 0xff080808, + 0x000000ff, 0x14141414, 0x141414fc, 0x00000000, + 0x0808f808, 0xf8000000, 0x0000f808, 0xf8080808, + 0x000000fc, 0x14141414, 0x141414ff, 0x14141414, + 0x0808ff08, 0xff080808, 0x0808080f, 0x00000000, + 0x000000f8, 0x08080808, 0xffffffff, 0xffffffff, + 0x00000000, 0xffffffff, 0x0f0f0f0f, 0x0f0f0f0f, + 0xf0f0f0f0, 0xf0f0f0f0, 0xffffffff, 0x00000000, + 0x00002c12, 0x12122c00, 0x000c120a, 0x12223a02, + 0x003e2202, 0x02020200, 0x00003e14, 0x14141400, + 0x3e220408, 0x04223e00, 0x00003c12, 0x12120c00, + 0x00002222, 0x221e0202, 0x002c1a08, 0x08281000, + 0x1c081c22, 0x221c081c, 0x1c22223e, 0x22221c00, + 0x1c222222, 0x22143600, 0x1e041824, 0x22221c00, + 0x0000142a, 0x2a140000, 0x0020142a, 0x2a140200, + 0x00003c02, 0x1c023c00, 0x001c2222, 0x22222202, + 0x00003e00, 0x3e003e00, 0x0000081c, 0x08001c00, + 0x04081008, 0x04001c00, 0x10080408, 0x10001c00, + 0x30480808, 0x08080808, 0x08080808, 0x08080906, + 0x00000800, 0x3e000800, 0x00004c32, 0x004c3200, + 0x00182418, 0x00000000, 0x00001c1c, 0x1c000000, + 0x00000018, 0x18000000, 0x00f01010, 0x10121418, + 0x0c342424, 0x00000000, 0x1810083c, 0x00000000, + 0x00003c3c, 0x3c3c0000, 0x00000000, 0x00000000, +}; diff --git a/src/bios_calls.s b/src/bios_calls.s deleted file mode 100644 index 740fa02..0000000 --- a/src/bios_calls.s +++ /dev/null @@ -1,337 +0,0 @@ -@ -@ Arithmetic functions. -@ - -@ Division. - .text - .code 16 - .align 2 - .global bios_div - .thumb_func -bios_div: - swi 0x06 - bx lr - -@ Square root. - .text - .code 16 - .align 2 - .global bios_sqrt - .thumb_func -bios_sqrt: - swi 0x08 - bx lr - -@ Arc-tangent. - .text - .code 16 - .align 2 - .global bios_atan - .thumb_func -bios_atan: - swi 0x09 - bx lr - -@ Arc-tangent2. - .text - .code 16 - .align 2 - .global bios_atan2 - .thumb_func -bios_atan2: - swi 0x0a - bx lr - -@ -@ Rotation/Scaling. -@ - -@ BG Affine Set. - .text - .code 16 - .align 2 - .global bios_bg_affine_set - .thumb_func -bios_bg_affine_set: - swi 0x0e - bx lr - -@ OBJ Affine Set. - .text - .code 16 - .align 2 - .global bios_obj_affine_set - .thumb_func -bios_obj_affine_set: - swi 0x0f - bx lr - -@ -@ (De)compression. -@ - -@ Bit unpacking. - .text - .code 16 - .align 2 - .global bios_bit_unpack - .thumb_func -bios_bit_unpack: - swi 0x10 - bx lr - -@ Huffman decompression. - .text - .code 16 - .align 2 - .global bios_huff_expand - .thumb_func -bios_huff_expand: - swi 0x13 - bx lr - -@ LZ77 decompression (Fast, WRAM, 8 bit writes). - .text - .code 16 - .align 2 - .global bios_lz77_expand_wram - .thumb_func -bios_lz77_expand_wram: - swi 0x11 - bx lr - -@ LZ77 decompression (Slow, VRAM, 16 bit writes). - .text - .code 16 - .align 2 - .global bios_lz77_expand_vram - .thumb_func -bios_lz77_expand_vram: - swi 0x12 - bx lr - -@ Run length encoding decompression (Fast, WRAM, 8 bit writes). - .text - .code 16 - .align 2 - .global bios_rle_expand_wram - .thumb_func -bios_rle_expand_wram: - swi 0x14 - bx lr - -@ Run length encoding decompression (Slow, VRAM, 16 bit writes). - .text - .code 16 - .align 2 - .global bios_rle_expand_vram - .thumb_func -bios_rle_expand_vram: - swi 0x15 - bx lr - -@ -@ Memory copy. -@ - -@ Memcopy/memfill in 32 byte units (Fast). - .text - .code 16 - .align 2 - .global bios_memcopy_32 - .thumb_func -bios_memcopy_32: - swi 0x0C - bx lr - -@ Memcopy/memfill in 4 / 2 byte units (Slow). - .text - .code 16 - .align 2 - .global bios_memcopy_8 - .thumb_func -bios_memcopy_8: - swi 0x0B - bx lr - -@ -@ Sound functions. -@ - -@ MIDI key to frequency. - .text - .code 16 - .align 2 - .global bios_midi2freq - .thumb_func -bios_midi2freq: - swi 0x1f - bx lr - -@ Sound bias. - .text - .code 16 - .align 2 - .global bios_sound_bias - .thumb_func -bios_sound_bias: - swi 0x19 - bx lr - -@ Sound channels clear. - .text - .code 16 - .align 2 - .global bios_sound_clear - .thumb_func -bios_sound_clear: - swi 0x1e - bx lr - -@ Sound driver initialization. - .text - .code 16 - .align 2 - .global bios_sound_init - .thumb_func -bios_sound_init: - swi 0x1a - bx lr - -@ Sound main function. To be called each 1/60 of a second after the sound VSync. - .text - .code 16 - .align 2 - .global bios_sound_main - .thumb_func -bios_sound_main: - swi 0x1c - bx lr - -@ Sound driver operation mode. - .text - .code 16 - .align 2 - .global bios_sound_mode - .thumb_func -bios_sound_mode: - swi 0x1b - bx lr - -@ Sound VSync. Called just after the VBlank interrupt each 1/60 of a second. - .text - .code 16 - .align 2 - .global bios_sound_vsync - .thumb_func -bios_sound_vsync: - swi 0x1d - bx lr - -@ Sound VSync off In case of issues manually call this to stop the sound. - .text - .code 16 - .align 2 - .global bios_sound_vsync_off - .thumb_func -bios_sound_vsync_off: - swi 0x28 - bx lr - -@ -@ Halt/Reset functions. -@ - -@ Halt until interrupt request. - .text - .code 16 - .align 2 - .global bios_halt - .thumb_func -bios_halt: - swi 0x02 - bx lr - -@ Halt until one of the specified interrupts occur. - .text - .code 16 - .align 2 - .global bios_interrupt_wait - .thumb_func -bios_interrupt_wait: - swi 0x04 - bx lr - -@ Halt until the VBlank interrupt occurs. - .text - .code 16 - .align 2 - .global bios_vblank_wait - .thumb_func -bios_vblank_wait: - swi 0x05 - bx lr - -@ Stop. Switches GBA to low power mode. - .text - .code 16 - .align 2 - .global bios_stop - .thumb_func -bios_stop: - swi 0x03 - bx lr - -@ Soft reset. - .text - .code 16 - .align 2 - .global bios_soft_reset - .thumb_func -bios_soft_reset: - swi 0x00 - bx lr - -@ Register RAM reset. - .text - .code 16 - .align 2 - .global bios_regram_reset - .thumb_func -bios_regram_reset: - swi 0x01 - bx lr - -@ Hard reset. - .text - .code 16 - .align 2 - .global bios_hard_reset - .thumb_func -bios_hard_reset: - swi 0x26 - bx lr - -@ -@ Misc functions. -@ - -@ BIOS checksum. - .text - .code 16 - .align 2 - .global bios_bios_checksum - .thumb_func -bios_bios_checksum: - swi 0x0d - bx lr - -@ MultiBoot. - .text - .code 16 - .align 2 - .global bios_multiboot - .thumb_func -bios_multiboot: - swi 0x25 - bx lr diff --git a/src/common.h b/src/common.h deleted file mode 100644 index fbe01a4..0000000 --- a/src/common.h +++ /dev/null @@ -1,667 +0,0 @@ -#ifndef COMMON_H -#define COMMON_H - -#include "shorthand.h" - -#define CPU_FREQUENCY (2 << 23) - -// -// Memory sections. -// - -// Defines for the different memory sections in the GBA. -#define MEM_SROM 0x00000000 -#define MEM_EW 0x02000000 -#define MEM_IW 0x03000000 -#define MEM_IO 0x04000000 -#define MEM_PAL 0x05000000 -#define MEM_VRAM 0x06000000 -#define MEM_OAM 0x07000000 -#define MEM_PAK 0x08000000 -#define MEM_CART 0x0E000000 - -// -// Display modes. -// - -// Display registers. -#define DISP_CTRL *((vu32*)(MEM_IO + 0x0000)) -#define DISP_STATUS *((vu16*)(MEM_IO + 0x0004)) -#define DISP_VCOUNT *((vu16*)(MEM_IO + 0x0006)) - -// The first three bits in the DISP_CTRL are used to control the video mode. -#define DISP_MODE_0 0x0000 -#define DISP_MODE_1 0x0001 -#define DISP_MODE_2 0x0002 -#define DISP_MODE_3 0x0003 -#define DISP_MODE_4 0x0004 -#define DISP_MODE_5 0x0005 -#define DISP_GB (1 << 3) -#define DISP_PAGE (1 << 4) -#define DISP_OAM_HBLANK (1 << 5) -#define DISP_OBJ_1D (1 << 6) -#define DISP_BLANK (1 << 7) -#define DISP_BG_0 (1 << 8) -#define DISP_BG_1 (1 << 9) -#define DISP_BG_2 (1 << 10) -#define DISP_BG_3 (1 << 11) -#define DISP_OBJ (1 << 12) -#define DISP_ENABLE_SPRITES DISP_OBJ | DISP_OBJ_1D - -// These bits are used to control the DISP_STATUS register. -#define DISP_VBLANK_STATUS (1 << 0x0) -#define DISP_HBLANK_STATUS (1 << 0x1) -#define DISP_VCOUNT_STATUS (1 << 0x2) -#define DISP_VBLANK_IRQ (1 << 0x3) -#define DISP_HBLANK_IRQ (1 << 0x4) -#define DISP_VCOUNT_IRQ (1 << 0x5) -#define DISP_VCOUNT_TRIGGER(N) ((N) << 0x8) - -// Registers to control of BG layers. -#define BG_CTRL(N) *((vu16*)(0x04000008 + 0x0002 * (N))) - -// Bits to control the background. -#define BG_PRIORITY(N) ((N) & 0x3) -#define BG_CHARBLOCK(N) ((N) << 2) -#define BG_MOSAIC (1 << 6) -#define BG_HIGH_COLOR (1 << 7) -#define BG_SCREENBLOCK(N) ((N) << 8) -#define BG_AFFINE (1 << 0xD) -#define BG_SIZE(N) ((N) << 0xE) - -// BG registers for horizontal displacement. -#define BG_H_SCROLL_0 *((vu16*)(0x04000010 + 0x0004 * 0)) -#define BG_H_SCROLL_1 *((vu16*)(0x04000010 + 0x0004 * 1)) -#define BG_H_SCROLL_2 *((vu16*)(0x04000010 + 0x0004 * 2)) -#define BG_H_SCROLL_3 *((vu16*)(0x04000010 + 0x0004 * 3)) - -// BG registers for vertical displacement. -#define BG_V_SCROLL_0 *((vu16*)(0x04000012 + 0x0004 * 0)) -#define BG_V_SCROLL_1 *((vu16*)(0x04000012 + 0x0004 * 1)) -#define BG_V_SCROLL_2 *((vu16*)(0x04000012 + 0x0004 * 2)) -#define BG_V_SCROLL_3 *((vu16*)(0x04000012 + 0x0004 * 3)) - -// Screen settings. -#define SCREEN_WIDTH 240 -#define SCREEN_HEIGHT 160 - -// -// Colors. -// - -// The GBA in mode 3 expects rbg15 colors in the VRAM, where each component -// (RGB) have a 0--31 range. For example, pure red would be rgb15(31, 0, 0). -typedef u16 Color; - -// A palette is composed of 16 colors, with color at index 0 being transparent -// for sprites. -typedef Color Palette[16]; - -// Inline function to calculate the 15 bit color value. -#define RGB15(R,G,B) (u16)(((B) << 10) | ((G) << 5) | (R)); - -// Some nice default colors. -#define COLOR_RED RGB15(31, 0,12) -#define COLOR_BLUE RGB15(2, 15,30) -#define COLOR_CYAN RGB15(0, 30,30) -#define COLOR_GREY RGB15(12,12,12) -#define COLOR_BLACK RGB15(0, 0, 0) -#define COLOR_WHITE RGB15(28,28,28) - -// -// Tile memory access. -// - -// NOTE: Only defining 4bpp tiles for now. -// TODO: typedef u32 Tile[8]; -typedef struct Tile { - u32 row[8]; -} Tile; - -// Screenblocks and charblocks (tile blocks). -typedef Tile TileBlock[512]; -#define TILE_MEM ((TileBlock*) MEM_VRAM) -typedef u16 ScreenBlock[1024]; -#define SCREENBLOCK_MEM ((ScreenBlock*)MEM_VRAM) - -// Screenblock entry bits. -#define SCREENBLOCK_ENTRY_H_FLIP (1 << 0xA) -#define SCREENBLOCK_ENTRY_V_FLIP (1 << 0xB) -#define SCREENBLOCK_ENTRY_PAL(N) ((N) << 0xC) - -inline size_t -se_index(size_t tile_x, size_t tile_y, size_t map_width) { - size_t sbb = ((tile_x >> 5) + (tile_y >> 5) * (map_width >> 5)); - return sbb * 1024 + ((tile_x & 31) + (tile_y & 31) * 32); -} - -// We can treat the screen as a HxW matrix. With the following macro we can -// write a pixel to the screen at the (x, y) position using: -// -// FRAMEBUFFER[y][x] = color; -// -typedef Color Scanline[SCREEN_WIDTH]; -#define FRAMEBUFFER ((Scanline*) MEM_VRAM) -#define SCREEN_BUFFER ((u16*) MEM_VRAM) -#define PAL_BUFFER_BG ((u16*) MEM_PAL) -#define PAL_BUFFER_SPRITES ((u16*)(MEM_PAL + 0x200)) -#define PAL_BANK_BG ((Palette*) MEM_PAL) -#define PAL_BANK_SPRITES ((Palette*)(MEM_PAL + 0x200)) - -// -// Sprites. -// - -// Using macros instead of aligned structs for setting up OBJ attributes and -// affine parameters. -// TODO: Benchmark if this would be slower or the same that TONC's -// implementation. -// TODO: Cleanup OBJ/OAM memory copying and access. -#define OBJ_ATTR_0(N) *((vu16*)(MEM_OAM + 0 + 8 * (N))) -#define OBJ_ATTR_1(N) *((vu16*)(MEM_OAM + 2 + 8 * (N))) -#define OBJ_ATTR_2(N) *((vu16*)(MEM_OAM + 4 + 8 * (N))) -#define OBJ_AFFINE_PA(N) *((vs16*)(MEM_OAM + 6 + 8 * 0 + 8 * 4 * (N))) -#define OBJ_AFFINE_PB(N) *((vs16*)(MEM_OAM + 6 + 8 * 1 + 8 * 4 * (N))) -#define OBJ_AFFINE_PC(N) *((vs16*)(MEM_OAM + 6 + 8 * 2 + 8 * 4 * (N))) -#define OBJ_AFFINE_PD(N) *((vs16*)(MEM_OAM + 6 + 8 * 3 + 8 * 4 * (N))) - -// OBJ_ATTR_0 parameters -#define OBJ_Y_COORD(N) ((N) & 0xFF) -#define OBJ_NORMAL (0x00 << 0x8) -#define OBJ_AFFINE (0x01 << 0x8) -#define OBJ_HIDDEN (0x02 << 0x8) -#define OBJ_AFFINE_2X (0x03 << 0x8) -#define OBJ_ALPHA_BLEND (0x01 << 0xA) -#define OBJ_WINDOW (0x02 << 0xA) -#define OBJ_SHAPE_SQUARE (0x00 << 0xE) -#define OBJ_SHAPE_WIDE (0x01 << 0xE) -#define OBJ_SHAPE_TALL (0x02 << 0xE) - -// OBJ_ATTR_1 parameters -#define OBJ_X_COORD(N) ((N) & 0x1FF) -#define OBJ_AFFINE_IDX(N) ((N) << 0x9) -#define OBJ_H_FLIP (0x01 << 0xC) -#define OBJ_V_FLIP (0x01 << 0xD) -#define OBJ_SIZE_SMALL (0x00 << 0xE) -#define OBJ_SIZE_MID (0x01 << 0xE) -#define OBJ_SIZE_BIG (0x02 << 0xE) -#define OBJ_SIZE_HUGE (0x03 << 0xE) - -// OBJ_ATTR_2 parameters -#define OBJ_TILE_INDEX(N) ((N) & 0x3FF) -#define OBJ_PRIORITY(N) ((N) << 0xA) -#define OBJ_PAL_BANK(N) ((N) << 0xC) - -// -// Mode 4 page flipping -// - -static inline -void -flip_page(vu16 *backbuffer) { - backbuffer = (u16*)((u32)backbuffer ^ 0x0A000); - DISP_CTRL ^= DISP_PAGE; -} - -#define SCREEN_PAGE_1 ((vu16*) MEM_VRAM) -#define SCREEN_PAGE_2 ((vu16*) (MEM_VRAM + 0xa000)) - -// -// Profiling. -// - -#define TIMER_DATA_0 *((vu16*) (0x04000100 + 0x04 * 0)) -#define TIMER_DATA_1 *((vu16*) (0x04000100 + 0x04 * 1)) -#define TIMER_DATA_2 *((vu16*) (0x04000100 + 0x04 * 2)) -#define TIMER_DATA_3 *((vu16*) (0x04000100 + 0x04 * 3)) -#define TIMER_CTRL_0 *((vu16*) (0x04000102 + 0x04 * 0)) -#define TIMER_CTRL_1 *((vu16*) (0x04000102 + 0x04 * 1)) -#define TIMER_CTRL_2 *((vu16*) (0x04000102 + 0x04 * 2)) -#define TIMER_CTRL_3 *((vu16*) (0x04000102 + 0x04 * 3)) - -// Timer control bits. -#define TIMER_CTRL_FREQ_0 0 -#define TIMER_CTRL_FREQ_1 1 -#define TIMER_CTRL_FREQ_2 2 -#define TIMER_CTRL_FREQ_3 3 -#define TIMER_CTRL_CASCADE (1 << 2) -#define TIMER_CTRL_IRQ (1 << 6) -#define TIMER_CTRL_ENABLE (1 << 7) -#define TIMER_CTRL_DISABLE (0 << 7) - -// We use timers 2 and 3 to count the number of cycles since the profile_start -// functions is called. Don't use if the code we are trying to profile make use -// of these timers. -static inline -void -profile_start(void) { - TIMER_DATA_2 = 0; - TIMER_DATA_3 = 0; - TIMER_CTRL_2 = 0; - TIMER_CTRL_3 = 0; - TIMER_CTRL_3 = TIMER_CTRL_ENABLE | TIMER_CTRL_CASCADE; - TIMER_CTRL_2 = TIMER_CTRL_ENABLE; -} - -static inline -u32 -profile_stop(void) { - TIMER_CTRL_2 = 0; - return (TIMER_DATA_3 << 16) | TIMER_DATA_2; -} - -static inline -u32 -profile_measure(void) { - return (TIMER_DATA_3 << 16) | TIMER_DATA_2; -} - -// -// Input handling. -// - -// Memory address for key input and control register -#define KEY_INPUTS *((vu16*) 0x04000130) -#define KEY_CTRL *((vu16*) 0x04000132) - -// Key control register bits. -#define KEY_IRQ_KEY(N) (N) -#define KEY_IRQ (1 << 0xE) -#define KEY_IRQ_IF_SET (1 << 0xF) - -// Alias for key pressing bits. -#define KEY_A (1 << 0) -#define KEY_B (1 << 1) -#define KEY_SELECT (1 << 2) -#define KEY_START (1 << 3) -#define KEY_RIGHT (1 << 4) -#define KEY_LEFT (1 << 5) -#define KEY_UP (1 << 6) -#define KEY_DOWN (1 << 7) -#define KEY_R (1 << 8) -#define KEY_L (1 << 9) - -#define KEY_MASK 0x03FF - -// Saving the previous and current key states as globals for now. -static u16 key_curr = 0; -static u16 key_prev = 0; - -static inline -void -poll_keys(void) { - key_prev = key_curr; - key_curr = ~KEY_INPUTS & KEY_MASK; -} - -// Returns true if the given key has been pressed at time of calling and was not -// pressed since the previous call. For example, if a key is being held, this -// function will return `true` only on the frame where the key initially -// activated. -static inline -u32 -key_tap(u32 key) { - return (key_curr & key) & ~(key_prev & key); -} - -// Check if a given key is currently pressed. -static inline -u32 -key_pressed(u32 key) { - return (key_curr & key); -} - -// Check if a given key was just released. -static inline -u32 -key_released(u32 key) { - return ~(key_curr & key) & (key_prev & key); -} - -// Check if the given key is pressed and has been since at least one frame. -static inline -u32 -key_hold(u32 key) { - return key_curr & key_prev & key; -} - -// Check if the given key/button is currently pressed. -#define KEY_PRESSED(key) (~(KEY_INPUTS) & key) - -// Back/unpack bits. -static inline -u32 -unpack_1bb(u8 hex) { - const u32 conversion_u32[16] = { - 0x00000000, 0x00000001, 0x00000010, 0x00000011, - 0x00000100, 0x00000101, 0x00000110, 0x00000111, - 0x00001000, 0x00001001, 0x00001010, 0x00001011, - 0x00001100, 0x00001101, 0x00001110, 0x00001111, - }; - u8 low = hex & 0xF; - u8 high = (hex >> 4) & 0xF; - return (conversion_u32[high] << 16) | conversion_u32[low]; -} - -// Unpack N tiles packed at 1bpp. -static inline -void -unpack_tiles(u32 *src, u32 *dst, size_t n_tiles) { - u32 *target_src = src + n_tiles * 2; - while (src != target_src) { - *dst++ = unpack_1bb((*src >> 24) & 0xFF); - *dst++ = unpack_1bb((*src >> 16) & 0xFF); - *dst++ = unpack_1bb((*src >> 8) & 0xFF); - *dst++ = unpack_1bb(*src & 0xFF); - src++; - } -} - -// -// Direct Memory Access (DMA) -// - - -// Source, destination, and control registers. -#define DMA_SRC(N) *((vu32*) 0x040000B0 + (N) * 12) -#define DMA_DST(N) *((vu32*) 0x040000B4 + (N) * 12) -#define DMA_CTRL(N) *((vu32*) 0x040000B8 + (N) * 12) - -// DMA control bits. -#define DMA_DST_INC (0 << 0x15) -#define DMA_DST_DEC (1 << 0x15) -#define DMA_DST_FIXED (2 << 0x15) -#define DMA_DST_RELOAD (3 << 0x15) -#define DMA_SRC_INC (0 << 0x17) -#define DMA_SRC_DEC (1 << 0x17) -#define DMA_SRC_FIXED (2 << 0x17) -#define DMA_REPEAT (1 << 0x19) -#define DMA_CHUNK_16 (0 << 0x1A) -#define DMA_CHUNK_32 (1 << 0x1A) -#define DMA_NOW (0 << 0x1C) -#define DMA_VBLANK (1 << 0x1C) -#define DMA_HBLANK (2 << 0x1C) -#define DMA_REFRESH (3 << 0x1C) -#define DMA_IRQ (1 << 0x1E) -#define DMA_ENABLE (1 << 0x1F) - -// Custom struct for cleaner DMA transfer functions. -typedef struct DmaStr { - const void *src; - void *dst; - u32 ctrl; -} DmaStr; - -#define DMA_TRANSFER ((volatile DmaStr*) 0x040000B0) - -// Transfer `count` number of chunks from src to dst using a DMA channel. Note -// that chunks are not bytes, but instead configured based on bits set by -// DMA_CTRL. -inline -void -dma_transfer_copy(void *dst, const void *src, u32 count, int channel, u32 options) { - DMA_TRANSFER[channel].ctrl = 0; - DMA_TRANSFER[channel].src = src; - DMA_TRANSFER[channel].dst = dst; - DMA_TRANSFER[channel].ctrl = count | options; -} - -inline -void -dma_transfer_fill(void *dst, volatile u32 src, u32 count, int channel, u32 options) { - DMA_TRANSFER[channel].ctrl = 0; - DMA_TRANSFER[channel].src = (const void *)&src; - DMA_TRANSFER[channel].dst = dst; - DMA_TRANSFER[channel].ctrl = count | options | DMA_SRC_FIXED; -} - -// Copy N number of bytes using a DMA channel. -inline -void -dma_copy(void *dst, const void *src, u32 size, int channel) { - dma_transfer_copy(dst, src, size / 4, channel, DMA_CHUNK_32 | DMA_ENABLE); -} - -// Fill the dst location with the word set at src. -inline -void -dma_fill(void *dst, vu32 src, u32 size, int channel) { - dma_transfer_fill(dst, src, size / 4, channel, DMA_CHUNK_32 | DMA_ENABLE); -} - -// -// Interrupts. -// - -#define IRQ_ENABLE *((vu16*) 0x04000200) -#define IRQ_ACK *((vu16*) 0x04000202) -#define IRQ_CTRL *((vu16*) 0x04000208) -#define IRQ_ACK_BIOS *((vu16*) 0x03007FF8) - -typedef enum { - IRQ_VBLANK, - IRQ_HBLANK, - IRQ_VCOUNT, - IRQ_TIMER_0, - IRQ_TIMER_1, - IRQ_TIMER_2, - IRQ_TIMER_3, - IRQ_SERIAL, - IRQ_DMA_0, - IRQ_DMA_1, - IRQ_DMA_2, - IRQ_DMA_3, - IRQ_KEYPAD, - IRQ_GAMEPAK, -} IrqIndex; - -typedef void (*IrsFunc)(void); - -// Stub function pointer needed for when we want to enable interrupts that don't -// require a custom function, such as for the BIOS VSync. -void irs_stub(void); - -// Set and enable a given function in the interrupt table. If func is NULL, the -// interrupt will be disabled instead. -void irs_set(IrqIndex idx, IrsFunc func); - -// Initialize the function pointer for the main IRS routine written in ARM -// assembly and enable interrupts. -void irq_init(void); - -// -// BIOS function declarations. -// - -// These functions declarations can be used to call the BIOS functions from the -// asm code. -int bios_vblank_wait(); -int bios_div(int num, int denom); - -// -// Sound. -// - -// Sound registers. -#define SOUND_SQUARE1_SWEEP *((vu16*)(MEM_IO + 0x60)) -#define SOUND_SQUARE1_CTRL *((vu16*)(MEM_IO + 0x62)) -#define SOUND_SQUARE1_FREQ *((vu16*)(MEM_IO + 0x64)) -#define SOUND_SQUARE2_CTRL *((vu16*)(MEM_IO + 0x68)) -#define SOUND_SQUARE2_FREQ *((vu16*)(MEM_IO + 0x6C)) -#define SOUND_WAVE_MODE *((vu16*)(MEM_IO + 0x70)) -#define SOUND_WAVE_CTRL *((vu16*)(MEM_IO + 0x72)) -#define SOUND_WAVE_FREQ *((vu16*)(MEM_IO + 0x74)) -#define SOUND_NOISE_CTRL *((vu16*)(MEM_IO + 0x78)) -#define SOUND_NOISE_FREQ *((vu16*)(MEM_IO + 0x7C)) -#define SOUND_DMG_MASTER *((vu16*)(MEM_IO + 0x80)) -#define SOUND_DSOUND_MASTER *((vu16*)(MEM_IO + 0x82)) -#define SOUND_STATUS *((vu16*)(MEM_IO + 0x84)) -#define SOUND_BIAS *((vu16*)(MEM_IO + 0x88)) - -// Sound DMG master bits. -#define SOUND_VOLUME_LEFT(N) (N) -#define SOUND_VOLUME_RIGHT(N) ((N) << 4) -#define SOUND_ENABLE_SQUARE1_LEFT (1 << 0x8) -#define SOUND_ENABLE_SQUARE2_LEFT (1 << 0x9) -#define SOUND_ENABLE_WAVE_LEFT (1 << 0xA) -#define SOUND_ENABLE_NOISE_LEFT (1 << 0xB) -#define SOUND_ENABLE_SQUARE1_RIGHT (1 << 0xC) -#define SOUND_ENABLE_SQUARE2_RIGHT (1 << 0xD) -#define SOUND_ENABLE_WAVE_RIGHT (1 << 0xE) -#define SOUND_ENABLE_NOISE_RIGHT (1 << 0xF) - -typedef enum { - SOUND_DSOUND = (0x0 << 0), - SOUND_SQUARE1 = (0x1 << 0), - SOUND_SQUARE2 = (0x1 << 1), - SOUND_WAVE = (0x1 << 2), - SOUND_NOISE = (0x1 << 3), -} SoundChannel; - -inline u16 -sound_volume(SoundChannel channels, u8 volume) { - volume = volume & 0x7; - channels = channels & 0xF; - return volume | (volume << 0x4) | (channels << 0x8) | (channels << 0xC); -} - -// Sound Direct Sound master bits. -#define SOUND_DMG25 0x0 -#define SOUND_DMG50 0x1 -#define SOUND_DMG100 0x2 -#define SOUND_DSOUND_RATIO_A (1 << 0x2) -#define SOUND_DSOUND_RATIO_B (1 << 0x3) -#define SOUND_DSOUND_LEFT_A (1 << 0x8) -#define SOUND_DSOUND_RIGHT_A (1 << 0x9) -#define SOUND_DSOUND_TIMER_A (1 << 0xA) -#define SOUND_DSOUND_RESET_A (1 << 0xB) -#define SOUND_DSOUND_LEFT_B (1 << 0xC) -#define SOUND_DSOUND_RIGHT_B (1 << 0xD) -#define SOUND_DSOUND_TIMER_B (1 << 0xE) -#define SOUND_DSOUND_RESET_B (1 << 0xF) - -// Direct sound FIFO queues. -#define SOUND_FIFO_A ((u16*)(MEM_IO + 0xA0)) -#define SOUND_FIFO_B ((u16*)(MEM_IO + 0xA4)) - -// Sound status bits. -#define SOUND_ENABLE (1 << 0x7) - -// DMG square control bits. -#define SOUND_SQUARE_LEN(N) (N) -#define SOUND_SQUARE_DUTY(N) ((N) << 0x6) -#define SOUND_SQUARE_ENV_TIME(N) ((N) << 0x8) -#define SOUND_SQUARE_ENV_DIR(N) ((N) << 0xB) -#define SOUND_SQUARE_ENV_VOL(N) ((N) << 0xC) - -// DMG square 1 sweep control bits. -#define SOUND_SWEEP_NUMBER(N) (N) -#define SOUND_SWEEP_DIR(N) ((N) << 0x3) -#define SOUND_SWEEP_TIME(N) ((N) << 0x4) - -// DMG frequency bits (Square/Wave). -#define SOUND_FREQ_TIMED (1 << 0xE) -#define SOUND_FREQ_RESET (1 << 0xF) - -// DMG wave ram. -#define SOUND_WAVE_RAM_0 *((vu32*)(MEM_IO + 0x90)) -#define SOUND_WAVE_RAM_1 *((vu32*)(MEM_IO + 0x94)) -#define SOUND_WAVE_RAM_2 *((vu32*)(MEM_IO + 0x98)) -#define SOUND_WAVE_RAM_3 *((vu32*)(MEM_IO + 0x9C)) - -// DMG wave control bits. -#define SOUND_WAVE_LENGTH(N) (N) -#define SOUND_WAVE_MUTE 0x0 -#define SOUND_WAVE_VOL_100 (0x1 << 0xD) -#define SOUND_WAVE_VOL_75 (0x4 << 0xD) -#define SOUND_WAVE_VOL_50 (0x2 << 0xD) -#define SOUND_WAVE_VOL_25 (0x3 << 0xD) - -// DMG wave mode bits. -#define SOUND_WAVE_BANK_MODE(N) ((N) << 0x5) -#define SOUND_WAVE_BANK_SELECT(N) ((N) << 0x6) -#define SOUND_WAVE_ENABLE (1 << 0x7) - -typedef u8 WaveBank[32]; - -#define SOUND_WAVE_RAM ((WaveBank*)(MEM_IO + 0x90)) - -typedef enum { - NOTE_C_2 , NOTE_C_SHARP_2 , NOTE_D_2 , NOTE_D_SHARP_2 , - NOTE_E_2 , NOTE_F_2 , NOTE_F_SHARP_2 , NOTE_G_2 , - NOTE_G_SHARP_2 , NOTE_A_2 , NOTE_A_SHARP_2 , NOTE_B_2 , - NOTE_C_3 , NOTE_C_SHARP_3 , NOTE_D_3 , NOTE_D_SHARP_3 , - NOTE_E_3 , NOTE_F_3 , NOTE_F_SHARP_3 , NOTE_G_3 , - NOTE_G_SHARP_3 , NOTE_A_3 , NOTE_A_SHARP_3 , NOTE_B_3 , - NOTE_C_4 , NOTE_C_SHARP_4 , NOTE_D_4 , NOTE_D_SHARP_4 , - NOTE_E_4 , NOTE_F_4 , NOTE_F_SHARP_4 , NOTE_G_4 , - NOTE_G_SHARP_4 , NOTE_A_4 , NOTE_A_SHARP_4 , NOTE_B_4 , - NOTE_C_5 , NOTE_C_SHARP_5 , NOTE_D_5 , NOTE_D_SHARP_5 , - NOTE_E_5 , NOTE_F_5 , NOTE_F_SHARP_5 , NOTE_G_5 , - NOTE_G_SHARP_5 , NOTE_A_5 , NOTE_A_SHARP_5 , NOTE_B_5 , - NOTE_C_6 , NOTE_C_SHARP_6 , NOTE_D_6 , NOTE_D_SHARP_6 , - NOTE_E_6 , NOTE_F_6 , NOTE_F_SHARP_6 , NOTE_G_6 , - NOTE_G_SHARP_6 , NOTE_A_6 , NOTE_A_SHARP_6 , NOTE_B_6 , - NOTE_C_7 , NOTE_C_SHARP_7 , NOTE_D_7 , NOTE_D_SHARP_7 , - NOTE_E_7 , NOTE_F_7 , NOTE_F_SHARP_7 , NOTE_G_7 , - NOTE_G_SHARP_7 , NOTE_A_7 , NOTE_A_SHARP_7 , NOTE_B_7 , - NOTE_C_8 -} Note; - -static const u32 sound_rates[] = { - 44 , 156 , 262 , 363 , 457 , 547 , 631 , 710 , 785 , 856 , 923 , 986 , - 1046, 1102, 1155, 1205, 1252, 1297, 1339, 1379, 1416, 1452, 1485, 1517, - 1547, 1575, 1601, 1626, 1650, 1672, 1693, 1713, 1732, 1750, 1766, 1782, - 1797, 1811, 1824, 1837, 1849, 1860, 1870, 1880, 1890, 1899, 1907, 1915, - 1922, 1929, 1936, 1942, 1948, 1954, 1959, 1964, 1969, 1973, 1977, 1981, - 1985, 1988, 1992, 1995, 1998, 2001, 2003, 2006, 2008, 2010, 2012, 2014, - 2016, -}; - -// -// System control. -// - -// Used to configure gamepak access timings. -#define SYSTEM_WAIT *((vu16*)(MEM_IO + 0x0204)) - -// This register defaults to 0, but manufacture cartridges use the values -// provided below. -#define SYSTEM_WAIT_DEFAULT 0 -#define SYSTEM_WAIT_CARTRIDGE 0x4317 - -// -// Misc. -// - -// Custom VSync option. This will waste a lot of battery power, since the -// machine is not clocked down. Prefer using `bios_vblank_wait()` if interrupts -// are enabled. -static inline void -wait_vsync(void) { - while(DISP_VCOUNT >= 160); - while(DISP_VCOUNT < 160); -} - -// General utility macros. -#define MIN(A, B) ((A) <= (B) ? (A) : (B)) -#define MAX(A, B) ((A) >= (B) ? (A) : (B)) -#define CLAMP(X, MIN, MAX) ((X) <= (MIN) ? (MIN) : (X) > (MAX) ? (MAX): (X)) -#define LEN(ARR) (sizeof(ARR) / sizeof((ARR)[0])) - -// Fixed-point arithmetic for (i.P) numbers. -#define FP_MUL(A,B,P) (((A) * (B)) >> (P)) -#define FP_DIV(A,B,P) (((A) << (P)) / (B)) -#define FP_LERP(Y0,Y1,X,P) ((Y0) + FP_MUL((X), ((Y1) - (Y0)), P)) - -// -// Memory section macros for devkitARM. -// - -#define IWRAM_DATA __attribute__((section(".iwram"))) -#define IWRAM_CODE __attribute__((section(".iwram"), long_call, target("arm"))) -#define EWRAM_DATA __attribute__((section(".ewram"))) -#define EWRAM_CODE __attribute__((section(".ewram"), long_call)) -#define EWRAM_BSS __attribute__((section(".sbss"))) - -#endif // COMMON_H diff --git a/src/filesystem.h b/src/filesystem.h index d50e5e7..6439f83 100644 --- a/src/filesystem.h +++ b/src/filesystem.h @@ -1,7 +1,7 @@ #ifndef FILESYSTEM_H #define FILESYSTEM_H -#include "common.h" +#include "gba/gba.h" typedef enum { FS_OPEN_READ = (1 << 0), diff --git a/src/gba/bios_calls.s b/src/gba/bios_calls.s new file mode 100644 index 0000000..740fa02 --- /dev/null +++ b/src/gba/bios_calls.s @@ -0,0 +1,337 @@ +@ +@ Arithmetic functions. +@ + +@ Division. + .text + .code 16 + .align 2 + .global bios_div + .thumb_func +bios_div: + swi 0x06 + bx lr + +@ Square root. + .text + .code 16 + .align 2 + .global bios_sqrt + .thumb_func +bios_sqrt: + swi 0x08 + bx lr + +@ Arc-tangent. + .text + .code 16 + .align 2 + .global bios_atan + .thumb_func +bios_atan: + swi 0x09 + bx lr + +@ Arc-tangent2. + .text + .code 16 + .align 2 + .global bios_atan2 + .thumb_func +bios_atan2: + swi 0x0a + bx lr + +@ +@ Rotation/Scaling. +@ + +@ BG Affine Set. + .text + .code 16 + .align 2 + .global bios_bg_affine_set + .thumb_func +bios_bg_affine_set: + swi 0x0e + bx lr + +@ OBJ Affine Set. + .text + .code 16 + .align 2 + .global bios_obj_affine_set + .thumb_func +bios_obj_affine_set: + swi 0x0f + bx lr + +@ +@ (De)compression. +@ + +@ Bit unpacking. + .text + .code 16 + .align 2 + .global bios_bit_unpack + .thumb_func +bios_bit_unpack: + swi 0x10 + bx lr + +@ Huffman decompression. + .text + .code 16 + .align 2 + .global bios_huff_expand + .thumb_func +bios_huff_expand: + swi 0x13 + bx lr + +@ LZ77 decompression (Fast, WRAM, 8 bit writes). + .text + .code 16 + .align 2 + .global bios_lz77_expand_wram + .thumb_func +bios_lz77_expand_wram: + swi 0x11 + bx lr + +@ LZ77 decompression (Slow, VRAM, 16 bit writes). + .text + .code 16 + .align 2 + .global bios_lz77_expand_vram + .thumb_func +bios_lz77_expand_vram: + swi 0x12 + bx lr + +@ Run length encoding decompression (Fast, WRAM, 8 bit writes). + .text + .code 16 + .align 2 + .global bios_rle_expand_wram + .thumb_func +bios_rle_expand_wram: + swi 0x14 + bx lr + +@ Run length encoding decompression (Slow, VRAM, 16 bit writes). + .text + .code 16 + .align 2 + .global bios_rle_expand_vram + .thumb_func +bios_rle_expand_vram: + swi 0x15 + bx lr + +@ +@ Memory copy. +@ + +@ Memcopy/memfill in 32 byte units (Fast). + .text + .code 16 + .align 2 + .global bios_memcopy_32 + .thumb_func +bios_memcopy_32: + swi 0x0C + bx lr + +@ Memcopy/memfill in 4 / 2 byte units (Slow). + .text + .code 16 + .align 2 + .global bios_memcopy_8 + .thumb_func +bios_memcopy_8: + swi 0x0B + bx lr + +@ +@ Sound functions. +@ + +@ MIDI key to frequency. + .text + .code 16 + .align 2 + .global bios_midi2freq + .thumb_func +bios_midi2freq: + swi 0x1f + bx lr + +@ Sound bias. + .text + .code 16 + .align 2 + .global bios_sound_bias + .thumb_func +bios_sound_bias: + swi 0x19 + bx lr + +@ Sound channels clear. + .text + .code 16 + .align 2 + .global bios_sound_clear + .thumb_func +bios_sound_clear: + swi 0x1e + bx lr + +@ Sound driver initialization. + .text + .code 16 + .align 2 + .global bios_sound_init + .thumb_func +bios_sound_init: + swi 0x1a + bx lr + +@ Sound main function. To be called each 1/60 of a second after the sound VSync. + .text + .code 16 + .align 2 + .global bios_sound_main + .thumb_func +bios_sound_main: + swi 0x1c + bx lr + +@ Sound driver operation mode. + .text + .code 16 + .align 2 + .global bios_sound_mode + .thumb_func +bios_sound_mode: + swi 0x1b + bx lr + +@ Sound VSync. Called just after the VBlank interrupt each 1/60 of a second. + .text + .code 16 + .align 2 + .global bios_sound_vsync + .thumb_func +bios_sound_vsync: + swi 0x1d + bx lr + +@ Sound VSync off In case of issues manually call this to stop the sound. + .text + .code 16 + .align 2 + .global bios_sound_vsync_off + .thumb_func +bios_sound_vsync_off: + swi 0x28 + bx lr + +@ +@ Halt/Reset functions. +@ + +@ Halt until interrupt request. + .text + .code 16 + .align 2 + .global bios_halt + .thumb_func +bios_halt: + swi 0x02 + bx lr + +@ Halt until one of the specified interrupts occur. + .text + .code 16 + .align 2 + .global bios_interrupt_wait + .thumb_func +bios_interrupt_wait: + swi 0x04 + bx lr + +@ Halt until the VBlank interrupt occurs. + .text + .code 16 + .align 2 + .global bios_vblank_wait + .thumb_func +bios_vblank_wait: + swi 0x05 + bx lr + +@ Stop. Switches GBA to low power mode. + .text + .code 16 + .align 2 + .global bios_stop + .thumb_func +bios_stop: + swi 0x03 + bx lr + +@ Soft reset. + .text + .code 16 + .align 2 + .global bios_soft_reset + .thumb_func +bios_soft_reset: + swi 0x00 + bx lr + +@ Register RAM reset. + .text + .code 16 + .align 2 + .global bios_regram_reset + .thumb_func +bios_regram_reset: + swi 0x01 + bx lr + +@ Hard reset. + .text + .code 16 + .align 2 + .global bios_hard_reset + .thumb_func +bios_hard_reset: + swi 0x26 + bx lr + +@ +@ Misc functions. +@ + +@ BIOS checksum. + .text + .code 16 + .align 2 + .global bios_bios_checksum + .thumb_func +bios_bios_checksum: + swi 0x0d + bx lr + +@ MultiBoot. + .text + .code 16 + .align 2 + .global bios_multiboot + .thumb_func +bios_multiboot: + swi 0x25 + bx lr diff --git a/src/gba/gba.h b/src/gba/gba.h new file mode 100644 index 0000000..56c4876 --- /dev/null +++ b/src/gba/gba.h @@ -0,0 +1,667 @@ +#ifndef GBA_H +#define GBA_H + +#include "shorthand.h" + +#define CPU_FREQUENCY (2 << 23) + +// +// Memory sections. +// + +// Defines for the different memory sections in the GBA. +#define MEM_SROM 0x00000000 +#define MEM_EW 0x02000000 +#define MEM_IW 0x03000000 +#define MEM_IO 0x04000000 +#define MEM_PAL 0x05000000 +#define MEM_VRAM 0x06000000 +#define MEM_OAM 0x07000000 +#define MEM_PAK 0x08000000 +#define MEM_CART 0x0E000000 + +// +// Display modes. +// + +// Display registers. +#define DISP_CTRL *((vu32*)(MEM_IO + 0x0000)) +#define DISP_STATUS *((vu16*)(MEM_IO + 0x0004)) +#define DISP_VCOUNT *((vu16*)(MEM_IO + 0x0006)) + +// The first three bits in the DISP_CTRL are used to control the video mode. +#define DISP_MODE_0 0x0000 +#define DISP_MODE_1 0x0001 +#define DISP_MODE_2 0x0002 +#define DISP_MODE_3 0x0003 +#define DISP_MODE_4 0x0004 +#define DISP_MODE_5 0x0005 +#define DISP_GB (1 << 3) +#define DISP_PAGE (1 << 4) +#define DISP_OAM_HBLANK (1 << 5) +#define DISP_OBJ_1D (1 << 6) +#define DISP_BLANK (1 << 7) +#define DISP_BG_0 (1 << 8) +#define DISP_BG_1 (1 << 9) +#define DISP_BG_2 (1 << 10) +#define DISP_BG_3 (1 << 11) +#define DISP_OBJ (1 << 12) +#define DISP_ENABLE_SPRITES DISP_OBJ | DISP_OBJ_1D + +// These bits are used to control the DISP_STATUS register. +#define DISP_VBLANK_STATUS (1 << 0x0) +#define DISP_HBLANK_STATUS (1 << 0x1) +#define DISP_VCOUNT_STATUS (1 << 0x2) +#define DISP_VBLANK_IRQ (1 << 0x3) +#define DISP_HBLANK_IRQ (1 << 0x4) +#define DISP_VCOUNT_IRQ (1 << 0x5) +#define DISP_VCOUNT_TRIGGER(N) ((N) << 0x8) + +// Registers to control of BG layers. +#define BG_CTRL(N) *((vu16*)(0x04000008 + 0x0002 * (N))) + +// Bits to control the background. +#define BG_PRIORITY(N) ((N) & 0x3) +#define BG_CHARBLOCK(N) ((N) << 2) +#define BG_MOSAIC (1 << 6) +#define BG_HIGH_COLOR (1 << 7) +#define BG_SCREENBLOCK(N) ((N) << 8) +#define BG_AFFINE (1 << 0xD) +#define BG_SIZE(N) ((N) << 0xE) + +// BG registers for horizontal displacement. +#define BG_H_SCROLL_0 *((vu16*)(0x04000010 + 0x0004 * 0)) +#define BG_H_SCROLL_1 *((vu16*)(0x04000010 + 0x0004 * 1)) +#define BG_H_SCROLL_2 *((vu16*)(0x04000010 + 0x0004 * 2)) +#define BG_H_SCROLL_3 *((vu16*)(0x04000010 + 0x0004 * 3)) + +// BG registers for vertical displacement. +#define BG_V_SCROLL_0 *((vu16*)(0x04000012 + 0x0004 * 0)) +#define BG_V_SCROLL_1 *((vu16*)(0x04000012 + 0x0004 * 1)) +#define BG_V_SCROLL_2 *((vu16*)(0x04000012 + 0x0004 * 2)) +#define BG_V_SCROLL_3 *((vu16*)(0x04000012 + 0x0004 * 3)) + +// Screen settings. +#define SCREEN_WIDTH 240 +#define SCREEN_HEIGHT 160 + +// +// Colors. +// + +// The GBA in mode 3 expects rbg15 colors in the VRAM, where each component +// (RGB) have a 0--31 range. For example, pure red would be rgb15(31, 0, 0). +typedef u16 Color; + +// A palette is composed of 16 colors, with color at index 0 being transparent +// for sprites. +typedef Color Palette[16]; + +// Inline function to calculate the 15 bit color value. +#define RGB15(R,G,B) (u16)(((B) << 10) | ((G) << 5) | (R)); + +// Some nice default colors. +#define COLOR_RED RGB15(31, 0,12) +#define COLOR_BLUE RGB15(2, 15,30) +#define COLOR_CYAN RGB15(0, 30,30) +#define COLOR_GREY RGB15(12,12,12) +#define COLOR_BLACK RGB15(0, 0, 0) +#define COLOR_WHITE RGB15(28,28,28) + +// +// Tile memory access. +// + +// NOTE: Only defining 4bpp tiles for now. +// TODO: typedef u32 Tile[8]; +typedef struct Tile { + u32 row[8]; +} Tile; + +// Screenblocks and charblocks (tile blocks). +typedef Tile TileBlock[512]; +#define TILE_MEM ((TileBlock*) MEM_VRAM) +typedef u16 ScreenBlock[1024]; +#define SCREENBLOCK_MEM ((ScreenBlock*)MEM_VRAM) + +// Screenblock entry bits. +#define SCREENBLOCK_ENTRY_H_FLIP (1 << 0xA) +#define SCREENBLOCK_ENTRY_V_FLIP (1 << 0xB) +#define SCREENBLOCK_ENTRY_PAL(N) ((N) << 0xC) + +inline size_t +se_index(size_t tile_x, size_t tile_y, size_t map_width) { + size_t sbb = ((tile_x >> 5) + (tile_y >> 5) * (map_width >> 5)); + return sbb * 1024 + ((tile_x & 31) + (tile_y & 31) * 32); +} + +// We can treat the screen as a HxW matrix. With the following macro we can +// write a pixel to the screen at the (x, y) position using: +// +// FRAMEBUFFER[y][x] = color; +// +typedef Color Scanline[SCREEN_WIDTH]; +#define FRAMEBUFFER ((Scanline*) MEM_VRAM) +#define SCREEN_BUFFER ((u16*) MEM_VRAM) +#define PAL_BUFFER_BG ((u16*) MEM_PAL) +#define PAL_BUFFER_SPRITES ((u16*)(MEM_PAL + 0x200)) +#define PAL_BANK_BG ((Palette*) MEM_PAL) +#define PAL_BANK_SPRITES ((Palette*)(MEM_PAL + 0x200)) + +// +// Sprites. +// + +// Using macros instead of aligned structs for setting up OBJ attributes and +// affine parameters. +// TODO: Benchmark if this would be slower or the same that TONC's +// implementation. +// TODO: Cleanup OBJ/OAM memory copying and access. +#define OBJ_ATTR_0(N) *((vu16*)(MEM_OAM + 0 + 8 * (N))) +#define OBJ_ATTR_1(N) *((vu16*)(MEM_OAM + 2 + 8 * (N))) +#define OBJ_ATTR_2(N) *((vu16*)(MEM_OAM + 4 + 8 * (N))) +#define OBJ_AFFINE_PA(N) *((vs16*)(MEM_OAM + 6 + 8 * 0 + 8 * 4 * (N))) +#define OBJ_AFFINE_PB(N) *((vs16*)(MEM_OAM + 6 + 8 * 1 + 8 * 4 * (N))) +#define OBJ_AFFINE_PC(N) *((vs16*)(MEM_OAM + 6 + 8 * 2 + 8 * 4 * (N))) +#define OBJ_AFFINE_PD(N) *((vs16*)(MEM_OAM + 6 + 8 * 3 + 8 * 4 * (N))) + +// OBJ_ATTR_0 parameters +#define OBJ_Y_COORD(N) ((N) & 0xFF) +#define OBJ_NORMAL (0x00 << 0x8) +#define OBJ_AFFINE (0x01 << 0x8) +#define OBJ_HIDDEN (0x02 << 0x8) +#define OBJ_AFFINE_2X (0x03 << 0x8) +#define OBJ_ALPHA_BLEND (0x01 << 0xA) +#define OBJ_WINDOW (0x02 << 0xA) +#define OBJ_SHAPE_SQUARE (0x00 << 0xE) +#define OBJ_SHAPE_WIDE (0x01 << 0xE) +#define OBJ_SHAPE_TALL (0x02 << 0xE) + +// OBJ_ATTR_1 parameters +#define OBJ_X_COORD(N) ((N) & 0x1FF) +#define OBJ_AFFINE_IDX(N) ((N) << 0x9) +#define OBJ_H_FLIP (0x01 << 0xC) +#define OBJ_V_FLIP (0x01 << 0xD) +#define OBJ_SIZE_SMALL (0x00 << 0xE) +#define OBJ_SIZE_MID (0x01 << 0xE) +#define OBJ_SIZE_BIG (0x02 << 0xE) +#define OBJ_SIZE_HUGE (0x03 << 0xE) + +// OBJ_ATTR_2 parameters +#define OBJ_TILE_INDEX(N) ((N) & 0x3FF) +#define OBJ_PRIORITY(N) ((N) << 0xA) +#define OBJ_PAL_BANK(N) ((N) << 0xC) + +// +// Mode 4 page flipping +// + +static inline +void +flip_page(vu16 *backbuffer) { + backbuffer = (u16*)((u32)backbuffer ^ 0x0A000); + DISP_CTRL ^= DISP_PAGE; +} + +#define SCREEN_PAGE_1 ((vu16*) MEM_VRAM) +#define SCREEN_PAGE_2 ((vu16*) (MEM_VRAM + 0xa000)) + +// +// Profiling. +// + +#define TIMER_DATA_0 *((vu16*) (0x04000100 + 0x04 * 0)) +#define TIMER_DATA_1 *((vu16*) (0x04000100 + 0x04 * 1)) +#define TIMER_DATA_2 *((vu16*) (0x04000100 + 0x04 * 2)) +#define TIMER_DATA_3 *((vu16*) (0x04000100 + 0x04 * 3)) +#define TIMER_CTRL_0 *((vu16*) (0x04000102 + 0x04 * 0)) +#define TIMER_CTRL_1 *((vu16*) (0x04000102 + 0x04 * 1)) +#define TIMER_CTRL_2 *((vu16*) (0x04000102 + 0x04 * 2)) +#define TIMER_CTRL_3 *((vu16*) (0x04000102 + 0x04 * 3)) + +// Timer control bits. +#define TIMER_CTRL_FREQ_0 0 +#define TIMER_CTRL_FREQ_1 1 +#define TIMER_CTRL_FREQ_2 2 +#define TIMER_CTRL_FREQ_3 3 +#define TIMER_CTRL_CASCADE (1 << 2) +#define TIMER_CTRL_IRQ (1 << 6) +#define TIMER_CTRL_ENABLE (1 << 7) +#define TIMER_CTRL_DISABLE (0 << 7) + +// We use timers 2 and 3 to count the number of cycles since the profile_start +// functions is called. Don't use if the code we are trying to profile make use +// of these timers. +static inline +void +profile_start(void) { + TIMER_DATA_2 = 0; + TIMER_DATA_3 = 0; + TIMER_CTRL_2 = 0; + TIMER_CTRL_3 = 0; + TIMER_CTRL_3 = TIMER_CTRL_ENABLE | TIMER_CTRL_CASCADE; + TIMER_CTRL_2 = TIMER_CTRL_ENABLE; +} + +static inline +u32 +profile_stop(void) { + TIMER_CTRL_2 = 0; + return (TIMER_DATA_3 << 16) | TIMER_DATA_2; +} + +static inline +u32 +profile_measure(void) { + return (TIMER_DATA_3 << 16) | TIMER_DATA_2; +} + +// +// Input handling. +// + +// Memory address for key input and control register +#define KEY_INPUTS *((vu16*) 0x04000130) +#define KEY_CTRL *((vu16*) 0x04000132) + +// Key control register bits. +#define KEY_IRQ_KEY(N) (N) +#define KEY_IRQ (1 << 0xE) +#define KEY_IRQ_IF_SET (1 << 0xF) + +// Alias for key pressing bits. +#define KEY_A (1 << 0) +#define KEY_B (1 << 1) +#define KEY_SELECT (1 << 2) +#define KEY_START (1 << 3) +#define KEY_RIGHT (1 << 4) +#define KEY_LEFT (1 << 5) +#define KEY_UP (1 << 6) +#define KEY_DOWN (1 << 7) +#define KEY_R (1 << 8) +#define KEY_L (1 << 9) + +#define KEY_MASK 0x03FF + +// Saving the previous and current key states as globals for now. +static u16 key_curr = 0; +static u16 key_prev = 0; + +static inline +void +poll_keys(void) { + key_prev = key_curr; + key_curr = ~KEY_INPUTS & KEY_MASK; +} + +// Returns true if the given key has been pressed at time of calling and was not +// pressed since the previous call. For example, if a key is being held, this +// function will return `true` only on the frame where the key initially +// activated. +static inline +u32 +key_tap(u32 key) { + return (key_curr & key) & ~(key_prev & key); +} + +// Check if a given key is currently pressed. +static inline +u32 +key_pressed(u32 key) { + return (key_curr & key); +} + +// Check if a given key was just released. +static inline +u32 +key_released(u32 key) { + return ~(key_curr & key) & (key_prev & key); +} + +// Check if the given key is pressed and has been since at least one frame. +static inline +u32 +key_hold(u32 key) { + return key_curr & key_prev & key; +} + +// Check if the given key/button is currently pressed. +#define KEY_PRESSED(key) (~(KEY_INPUTS) & key) + +// Back/unpack bits. +static inline +u32 +unpack_1bb(u8 hex) { + const u32 conversion_u32[16] = { + 0x00000000, 0x00000001, 0x00000010, 0x00000011, + 0x00000100, 0x00000101, 0x00000110, 0x00000111, + 0x00001000, 0x00001001, 0x00001010, 0x00001011, + 0x00001100, 0x00001101, 0x00001110, 0x00001111, + }; + u8 low = hex & 0xF; + u8 high = (hex >> 4) & 0xF; + return (conversion_u32[high] << 16) | conversion_u32[low]; +} + +// Unpack N tiles packed at 1bpp. +static inline +void +unpack_tiles(u32 *src, u32 *dst, size_t n_tiles) { + u32 *target_src = src + n_tiles * 2; + while (src != target_src) { + *dst++ = unpack_1bb((*src >> 24) & 0xFF); + *dst++ = unpack_1bb((*src >> 16) & 0xFF); + *dst++ = unpack_1bb((*src >> 8) & 0xFF); + *dst++ = unpack_1bb(*src & 0xFF); + src++; + } +} + +// +// Direct Memory Access (DMA) +// + + +// Source, destination, and control registers. +#define DMA_SRC(N) *((vu32*) 0x040000B0 + (N) * 12) +#define DMA_DST(N) *((vu32*) 0x040000B4 + (N) * 12) +#define DMA_CTRL(N) *((vu32*) 0x040000B8 + (N) * 12) + +// DMA control bits. +#define DMA_DST_INC (0 << 0x15) +#define DMA_DST_DEC (1 << 0x15) +#define DMA_DST_FIXED (2 << 0x15) +#define DMA_DST_RELOAD (3 << 0x15) +#define DMA_SRC_INC (0 << 0x17) +#define DMA_SRC_DEC (1 << 0x17) +#define DMA_SRC_FIXED (2 << 0x17) +#define DMA_REPEAT (1 << 0x19) +#define DMA_CHUNK_16 (0 << 0x1A) +#define DMA_CHUNK_32 (1 << 0x1A) +#define DMA_NOW (0 << 0x1C) +#define DMA_VBLANK (1 << 0x1C) +#define DMA_HBLANK (2 << 0x1C) +#define DMA_REFRESH (3 << 0x1C) +#define DMA_IRQ (1 << 0x1E) +#define DMA_ENABLE (1 << 0x1F) + +// Custom struct for cleaner DMA transfer functions. +typedef struct DmaStr { + const void *src; + void *dst; + u32 ctrl; +} DmaStr; + +#define DMA_TRANSFER ((volatile DmaStr*) 0x040000B0) + +// Transfer `count` number of chunks from src to dst using a DMA channel. Note +// that chunks are not bytes, but instead configured based on bits set by +// DMA_CTRL. +inline +void +dma_transfer_copy(void *dst, const void *src, u32 count, int channel, u32 options) { + DMA_TRANSFER[channel].ctrl = 0; + DMA_TRANSFER[channel].src = src; + DMA_TRANSFER[channel].dst = dst; + DMA_TRANSFER[channel].ctrl = count | options; +} + +inline +void +dma_transfer_fill(void *dst, volatile u32 src, u32 count, int channel, u32 options) { + DMA_TRANSFER[channel].ctrl = 0; + DMA_TRANSFER[channel].src = (const void *)&src; + DMA_TRANSFER[channel].dst = dst; + DMA_TRANSFER[channel].ctrl = count | options | DMA_SRC_FIXED; +} + +// Copy N number of bytes using a DMA channel. +inline +void +dma_copy(void *dst, const void *src, u32 size, int channel) { + dma_transfer_copy(dst, src, size / 4, channel, DMA_CHUNK_32 | DMA_ENABLE); +} + +// Fill the dst location with the word set at src. +inline +void +dma_fill(void *dst, vu32 src, u32 size, int channel) { + dma_transfer_fill(dst, src, size / 4, channel, DMA_CHUNK_32 | DMA_ENABLE); +} + +// +// Interrupts. +// + +#define IRQ_ENABLE *((vu16*) 0x04000200) +#define IRQ_ACK *((vu16*) 0x04000202) +#define IRQ_CTRL *((vu16*) 0x04000208) +#define IRQ_ACK_BIOS *((vu16*) 0x03007FF8) + +typedef enum { + IRQ_VBLANK, + IRQ_HBLANK, + IRQ_VCOUNT, + IRQ_TIMER_0, + IRQ_TIMER_1, + IRQ_TIMER_2, + IRQ_TIMER_3, + IRQ_SERIAL, + IRQ_DMA_0, + IRQ_DMA_1, + IRQ_DMA_2, + IRQ_DMA_3, + IRQ_KEYPAD, + IRQ_GAMEPAK, +} IrqIndex; + +typedef void (*IrsFunc)(void); + +// Stub function pointer needed for when we want to enable interrupts that don't +// require a custom function, such as for the BIOS VSync. +void irs_stub(void); + +// Set and enable a given function in the interrupt table. If func is NULL, the +// interrupt will be disabled instead. +void irs_set(IrqIndex idx, IrsFunc func); + +// Initialize the function pointer for the main IRS routine written in ARM +// assembly and enable interrupts. +void irq_init(void); + +// +// BIOS function declarations. +// + +// These functions declarations can be used to call the BIOS functions from the +// asm code. +int bios_vblank_wait(); +int bios_div(int num, int denom); + +// +// Sound. +// + +// Sound registers. +#define SOUND_SQUARE1_SWEEP *((vu16*)(MEM_IO + 0x60)) +#define SOUND_SQUARE1_CTRL *((vu16*)(MEM_IO + 0x62)) +#define SOUND_SQUARE1_FREQ *((vu16*)(MEM_IO + 0x64)) +#define SOUND_SQUARE2_CTRL *((vu16*)(MEM_IO + 0x68)) +#define SOUND_SQUARE2_FREQ *((vu16*)(MEM_IO + 0x6C)) +#define SOUND_WAVE_MODE *((vu16*)(MEM_IO + 0x70)) +#define SOUND_WAVE_CTRL *((vu16*)(MEM_IO + 0x72)) +#define SOUND_WAVE_FREQ *((vu16*)(MEM_IO + 0x74)) +#define SOUND_NOISE_CTRL *((vu16*)(MEM_IO + 0x78)) +#define SOUND_NOISE_FREQ *((vu16*)(MEM_IO + 0x7C)) +#define SOUND_DMG_MASTER *((vu16*)(MEM_IO + 0x80)) +#define SOUND_DSOUND_MASTER *((vu16*)(MEM_IO + 0x82)) +#define SOUND_STATUS *((vu16*)(MEM_IO + 0x84)) +#define SOUND_BIAS *((vu16*)(MEM_IO + 0x88)) + +// Sound DMG master bits. +#define SOUND_VOLUME_LEFT(N) (N) +#define SOUND_VOLUME_RIGHT(N) ((N) << 4) +#define SOUND_ENABLE_SQUARE1_LEFT (1 << 0x8) +#define SOUND_ENABLE_SQUARE2_LEFT (1 << 0x9) +#define SOUND_ENABLE_WAVE_LEFT (1 << 0xA) +#define SOUND_ENABLE_NOISE_LEFT (1 << 0xB) +#define SOUND_ENABLE_SQUARE1_RIGHT (1 << 0xC) +#define SOUND_ENABLE_SQUARE2_RIGHT (1 << 0xD) +#define SOUND_ENABLE_WAVE_RIGHT (1 << 0xE) +#define SOUND_ENABLE_NOISE_RIGHT (1 << 0xF) + +typedef enum { + SOUND_DSOUND = (0x0 << 0), + SOUND_SQUARE1 = (0x1 << 0), + SOUND_SQUARE2 = (0x1 << 1), + SOUND_WAVE = (0x1 << 2), + SOUND_NOISE = (0x1 << 3), +} SoundChannel; + +inline u16 +sound_volume(SoundChannel channels, u8 volume) { + volume = volume & 0x7; + channels = channels & 0xF; + return volume | (volume << 0x4) | (channels << 0x8) | (channels << 0xC); +} + +// Sound Direct Sound master bits. +#define SOUND_DMG25 0x0 +#define SOUND_DMG50 0x1 +#define SOUND_DMG100 0x2 +#define SOUND_DSOUND_RATIO_A (1 << 0x2) +#define SOUND_DSOUND_RATIO_B (1 << 0x3) +#define SOUND_DSOUND_LEFT_A (1 << 0x8) +#define SOUND_DSOUND_RIGHT_A (1 << 0x9) +#define SOUND_DSOUND_TIMER_A (1 << 0xA) +#define SOUND_DSOUND_RESET_A (1 << 0xB) +#define SOUND_DSOUND_LEFT_B (1 << 0xC) +#define SOUND_DSOUND_RIGHT_B (1 << 0xD) +#define SOUND_DSOUND_TIMER_B (1 << 0xE) +#define SOUND_DSOUND_RESET_B (1 << 0xF) + +// Direct sound FIFO queues. +#define SOUND_FIFO_A ((u16*)(MEM_IO + 0xA0)) +#define SOUND_FIFO_B ((u16*)(MEM_IO + 0xA4)) + +// Sound status bits. +#define SOUND_ENABLE (1 << 0x7) + +// DMG square control bits. +#define SOUND_SQUARE_LEN(N) (N) +#define SOUND_SQUARE_DUTY(N) ((N) << 0x6) +#define SOUND_SQUARE_ENV_TIME(N) ((N) << 0x8) +#define SOUND_SQUARE_ENV_DIR(N) ((N) << 0xB) +#define SOUND_SQUARE_ENV_VOL(N) ((N) << 0xC) + +// DMG square 1 sweep control bits. +#define SOUND_SWEEP_NUMBER(N) (N) +#define SOUND_SWEEP_DIR(N) ((N) << 0x3) +#define SOUND_SWEEP_TIME(N) ((N) << 0x4) + +// DMG frequency bits (Square/Wave). +#define SOUND_FREQ_TIMED (1 << 0xE) +#define SOUND_FREQ_RESET (1 << 0xF) + +// DMG wave ram. +#define SOUND_WAVE_RAM_0 *((vu32*)(MEM_IO + 0x90)) +#define SOUND_WAVE_RAM_1 *((vu32*)(MEM_IO + 0x94)) +#define SOUND_WAVE_RAM_2 *((vu32*)(MEM_IO + 0x98)) +#define SOUND_WAVE_RAM_3 *((vu32*)(MEM_IO + 0x9C)) + +// DMG wave control bits. +#define SOUND_WAVE_LENGTH(N) (N) +#define SOUND_WAVE_MUTE 0x0 +#define SOUND_WAVE_VOL_100 (0x1 << 0xD) +#define SOUND_WAVE_VOL_75 (0x4 << 0xD) +#define SOUND_WAVE_VOL_50 (0x2 << 0xD) +#define SOUND_WAVE_VOL_25 (0x3 << 0xD) + +// DMG wave mode bits. +#define SOUND_WAVE_BANK_MODE(N) ((N) << 0x5) +#define SOUND_WAVE_BANK_SELECT(N) ((N) << 0x6) +#define SOUND_WAVE_ENABLE (1 << 0x7) + +typedef u8 WaveBank[32]; + +#define SOUND_WAVE_RAM ((WaveBank*)(MEM_IO + 0x90)) + +typedef enum { + NOTE_C_2 , NOTE_C_SHARP_2 , NOTE_D_2 , NOTE_D_SHARP_2 , + NOTE_E_2 , NOTE_F_2 , NOTE_F_SHARP_2 , NOTE_G_2 , + NOTE_G_SHARP_2 , NOTE_A_2 , NOTE_A_SHARP_2 , NOTE_B_2 , + NOTE_C_3 , NOTE_C_SHARP_3 , NOTE_D_3 , NOTE_D_SHARP_3 , + NOTE_E_3 , NOTE_F_3 , NOTE_F_SHARP_3 , NOTE_G_3 , + NOTE_G_SHARP_3 , NOTE_A_3 , NOTE_A_SHARP_3 , NOTE_B_3 , + NOTE_C_4 , NOTE_C_SHARP_4 , NOTE_D_4 , NOTE_D_SHARP_4 , + NOTE_E_4 , NOTE_F_4 , NOTE_F_SHARP_4 , NOTE_G_4 , + NOTE_G_SHARP_4 , NOTE_A_4 , NOTE_A_SHARP_4 , NOTE_B_4 , + NOTE_C_5 , NOTE_C_SHARP_5 , NOTE_D_5 , NOTE_D_SHARP_5 , + NOTE_E_5 , NOTE_F_5 , NOTE_F_SHARP_5 , NOTE_G_5 , + NOTE_G_SHARP_5 , NOTE_A_5 , NOTE_A_SHARP_5 , NOTE_B_5 , + NOTE_C_6 , NOTE_C_SHARP_6 , NOTE_D_6 , NOTE_D_SHARP_6 , + NOTE_E_6 , NOTE_F_6 , NOTE_F_SHARP_6 , NOTE_G_6 , + NOTE_G_SHARP_6 , NOTE_A_6 , NOTE_A_SHARP_6 , NOTE_B_6 , + NOTE_C_7 , NOTE_C_SHARP_7 , NOTE_D_7 , NOTE_D_SHARP_7 , + NOTE_E_7 , NOTE_F_7 , NOTE_F_SHARP_7 , NOTE_G_7 , + NOTE_G_SHARP_7 , NOTE_A_7 , NOTE_A_SHARP_7 , NOTE_B_7 , + NOTE_C_8 +} Note; + +static const u32 sound_rates[] = { + 44 , 156 , 262 , 363 , 457 , 547 , 631 , 710 , 785 , 856 , 923 , 986 , + 1046, 1102, 1155, 1205, 1252, 1297, 1339, 1379, 1416, 1452, 1485, 1517, + 1547, 1575, 1601, 1626, 1650, 1672, 1693, 1713, 1732, 1750, 1766, 1782, + 1797, 1811, 1824, 1837, 1849, 1860, 1870, 1880, 1890, 1899, 1907, 1915, + 1922, 1929, 1936, 1942, 1948, 1954, 1959, 1964, 1969, 1973, 1977, 1981, + 1985, 1988, 1992, 1995, 1998, 2001, 2003, 2006, 2008, 2010, 2012, 2014, + 2016, +}; + +// +// System control. +// + +// Used to configure gamepak access timings. +#define SYSTEM_WAIT *((vu16*)(MEM_IO + 0x0204)) + +// This register defaults to 0, but manufacture cartridges use the values +// provided below. +#define SYSTEM_WAIT_DEFAULT 0 +#define SYSTEM_WAIT_CARTRIDGE 0x4317 + +// +// Misc. +// + +// Custom VSync option. This will waste a lot of battery power, since the +// machine is not clocked down. Prefer using `bios_vblank_wait()` if interrupts +// are enabled. +static inline void +wait_vsync(void) { + while(DISP_VCOUNT >= 160); + while(DISP_VCOUNT < 160); +} + +// General utility macros. +#define MIN(A, B) ((A) <= (B) ? (A) : (B)) +#define MAX(A, B) ((A) >= (B) ? (A) : (B)) +#define CLAMP(X, MIN, MAX) ((X) <= (MIN) ? (MIN) : (X) > (MAX) ? (MAX): (X)) +#define LEN(ARR) (sizeof(ARR) / sizeof((ARR)[0])) + +// Fixed-point arithmetic for (i.P) numbers. +#define FP_MUL(A,B,P) (((A) * (B)) >> (P)) +#define FP_DIV(A,B,P) (((A) << (P)) / (B)) +#define FP_LERP(Y0,Y1,X,P) ((Y0) + FP_MUL((X), ((Y1) - (Y0)), P)) + +// +// Memory section macros for devkitARM. +// + +#define IWRAM_DATA __attribute__((section(".iwram"))) +#define IWRAM_CODE __attribute__((section(".iwram"), long_call, target("arm"))) +#define EWRAM_DATA __attribute__((section(".ewram"))) +#define EWRAM_CODE __attribute__((section(".ewram"), long_call)) +#define EWRAM_BSS __attribute__((section(".sbss"))) + +#endif // GBA_H diff --git a/src/gba/interrupts.c b/src/gba/interrupts.c new file mode 100644 index 0000000..3b11335 --- /dev/null +++ b/src/gba/interrupts.c @@ -0,0 +1,91 @@ +#include "gba.h" + +IrsFunc irs_table[] = { + [IRQ_VBLANK ] = NULL, + [IRQ_HBLANK ] = NULL, + [IRQ_VCOUNT ] = NULL, + [IRQ_TIMER_0] = NULL, + [IRQ_TIMER_1] = NULL, + [IRQ_TIMER_2] = NULL, + [IRQ_TIMER_3] = NULL, + [IRQ_SERIAL ] = NULL, + [IRQ_DMA_0 ] = NULL, + [IRQ_DMA_1 ] = NULL, + [IRQ_DMA_2 ] = NULL, + [IRQ_DMA_3 ] = NULL, + [IRQ_KEYPAD ] = NULL, + [IRQ_GAMEPAK] = NULL, +}; + +// External irs_main function, has to be written in ARM assembly. +void irs_main(void); +#define IRS_MAIN *(IrsFunc*)(0x03007FFC) + +void +irq_enable(IrqIndex idx) { + switch (idx) { + case IRQ_VBLANK: { DISP_STATUS |= DISP_VBLANK_IRQ; } break; + case IRQ_HBLANK: { DISP_STATUS |= DISP_HBLANK_IRQ; } break; + case IRQ_VCOUNT: { DISP_STATUS |= DISP_VCOUNT_IRQ; } break; + case IRQ_TIMER_0: { TIMER_CTRL_0 |= TIMER_CTRL_IRQ; } break; + case IRQ_TIMER_1: { TIMER_CTRL_1 |= TIMER_CTRL_IRQ; } break; + case IRQ_TIMER_2: { TIMER_CTRL_2 |= TIMER_CTRL_IRQ; } break; + case IRQ_TIMER_3: { TIMER_CTRL_3 |= TIMER_CTRL_IRQ; } break; + case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; + case IRQ_DMA_0: { DMA_CTRL(0) |= DMA_IRQ; } break; + case IRQ_DMA_1: { DMA_CTRL(1) |= DMA_IRQ; } break; + case IRQ_DMA_2: { DMA_CTRL(2) |= DMA_IRQ; } break; + case IRQ_DMA_3: { DMA_CTRL(3) |= DMA_IRQ; } break; + case IRQ_KEYPAD: { KEY_CTRL |= KEY_IRQ; } break; + case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; + } + IRQ_ENABLE |= (1 << idx); +} + +void +irq_disable(IrqIndex idx) { + switch (idx) { + case IRQ_VBLANK: { DISP_STATUS &= ~DISP_VBLANK_IRQ; } break; + case IRQ_HBLANK: { DISP_STATUS &= ~DISP_HBLANK_IRQ; } break; + case IRQ_VCOUNT: { DISP_STATUS &= ~DISP_VCOUNT_IRQ; } break; + case IRQ_TIMER_0: { TIMER_CTRL_0 &= ~TIMER_CTRL_IRQ; } break; + case IRQ_TIMER_1: { TIMER_CTRL_1 &= ~TIMER_CTRL_IRQ; } break; + case IRQ_TIMER_2: { TIMER_CTRL_2 &= ~TIMER_CTRL_IRQ; } break; + case IRQ_TIMER_3: { TIMER_CTRL_3 &= ~TIMER_CTRL_IRQ; } break; + case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; + case IRQ_DMA_0: { DMA_CTRL(0) &= ~DMA_IRQ; } break; + case IRQ_DMA_1: { DMA_CTRL(1) &= ~DMA_IRQ; } break; + case IRQ_DMA_2: { DMA_CTRL(2) &= ~DMA_IRQ; } break; + case IRQ_DMA_3: { DMA_CTRL(3) &= ~DMA_IRQ; } break; + case IRQ_KEYPAD: { KEY_CTRL &= ~KEY_IRQ; } break; + case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; + } + IRQ_ENABLE &= ~(1 << idx); +} + +void +irs_set(IrqIndex idx, IrsFunc func) { + // Store IRQ_CTRL status and disable interrupts for now. + u16 irq_ctrl = IRQ_CTRL; + IRQ_CTRL = 0; + + // Update the IRS table and enable/disable the given IRQ. + irs_table[idx] = func; + if (func == NULL) { + irq_disable(idx); + } else { + irq_enable(idx); + } + + // Restore previous irq_ctrl. + IRQ_CTRL = irq_ctrl; +} + +void +irq_init(void) { + IRS_MAIN = irs_main; + IRQ_CTRL = 1; +} + +void +irs_stub(void) {} diff --git a/src/gba/interrupts.s b/src/gba/interrupts.s new file mode 100644 index 0000000..67b9fe9 --- /dev/null +++ b/src/gba/interrupts.s @@ -0,0 +1,89 @@ + .file "interrupts.s" + .extern irs_table; + .section .iwram, "ax", %progbits + .arm + .align + .global irs_main + +irs_main: + @ Get the contents of IRQ_ENABLE, IRQ_ACK, and IRQ_CTRL + ldr ip, mem_irq_base_reg @ ip = (IRQ_ENABLE << 16) | IRQ_ACK + ldr r0, [ip] @ r0 = irq_enable + and r1, r0, r0, lsr #16 @ r1 = irq_enable & irq_ack + + @ Disable IRQ_CTRL for now. + mov r3, #0 @ r3 = 0 + strh r3, [ip, #8] @ *(ip + 0x8) = r3 + + @ r0 = irs_table address pointer + @ r2 = tmp + @ r3 = 0 (for r3; r3 < 14; r3++) + ldr r0, = irs_table +irs_main_fp_search: + @ Check that the current index is an active IRQ. + mov r2, #1 + and r2, r1, r2, lsl r3 + cmp r2, #0 + beq irs_main_fp_search_not_enabled + + @ Extract the function pointer for this IRS if available. + ldr r2, [r0] + cmp r2, #0 + bne irs_main_handle_irs + +irs_main_fp_search_not_enabled: + add r0, r0, #4 + add r3, #1 + cmp r3, #14 + bne irs_main_fp_search + b irs_main_exit + +irs_main_handle_irs: + @ r2: Contains IRQ function pointer. + @ r3: Stores the IRQ index. + + @ Acknowledge that we are handling this interrupt writing to IRQ_ACK and + @ IRQ_ACK_BIOS. + mov r0, #1 + lsl r0, r0, r3 + strh r0, [ip, #2] + ldr r1, mem_irq_ack_bios @ r1 = IRQ_ACK_BIOS + str r0, [r1] + + @ Store the SPSR in one of the free registers and save it along with the + @ return pointer. + mrs r3, spsr + stmfd sp!, {r3, lr} + + @ Set CPU to system mode + mrs r3, cpsr + bic r3, r3, #0xDF + orr r3, r3, #0x1F + msr cpsr, r3 + + @ Call isr function pointer + stmfd sp!, {lr} + mov lr, pc + bx r2 + ldmfd sp!, {lr} + + @ Set CPU to irq mode + mrs r3, cpsr + bic r3, r3, #0xDF + orr r3, r3, #0x92 + msr cpsr, r3 + + @ Restore the SPSR and the registers. + ldmfd sp!, {r3, lr} + msr spsr, r3 + +irs_main_exit: + @ Restore IRQ_CTRL. + mov r3, #1 @ r3 = 0 + strh r3, [ip, #8] @ *(ip + 0x8) = r3 + bx lr + +mem_irq_base_reg: + .word 0x04000200 @ IRQ_ENABLE +mem_irq_ack_bios: + .word 0x03007FF8 @ IRQ_ACK_BIOS diff --git a/src/interrupts.c b/src/interrupts.c deleted file mode 100644 index 8e560dc..0000000 --- a/src/interrupts.c +++ /dev/null @@ -1,91 +0,0 @@ -#include "common.h" - -IrsFunc irs_table[] = { - [IRQ_VBLANK ] = NULL, - [IRQ_HBLANK ] = NULL, - [IRQ_VCOUNT ] = NULL, - [IRQ_TIMER_0] = NULL, - [IRQ_TIMER_1] = NULL, - [IRQ_TIMER_2] = NULL, - [IRQ_TIMER_3] = NULL, - [IRQ_SERIAL ] = NULL, - [IRQ_DMA_0 ] = NULL, - [IRQ_DMA_1 ] = NULL, - [IRQ_DMA_2 ] = NULL, - [IRQ_DMA_3 ] = NULL, - [IRQ_KEYPAD ] = NULL, - [IRQ_GAMEPAK] = NULL, -}; - -// External irs_main function, has to be written in ARM assembly. -void irs_main(void); -#define IRS_MAIN *(IrsFunc*)(0x03007FFC) - -void -irq_enable(IrqIndex idx) { - switch (idx) { - case IRQ_VBLANK: { DISP_STATUS |= DISP_VBLANK_IRQ; } break; - case IRQ_HBLANK: { DISP_STATUS |= DISP_HBLANK_IRQ; } break; - case IRQ_VCOUNT: { DISP_STATUS |= DISP_VCOUNT_IRQ; } break; - case IRQ_TIMER_0: { TIMER_CTRL_0 |= TIMER_CTRL_IRQ; } break; - case IRQ_TIMER_1: { TIMER_CTRL_1 |= TIMER_CTRL_IRQ; } break; - case IRQ_TIMER_2: { TIMER_CTRL_2 |= TIMER_CTRL_IRQ; } break; - case IRQ_TIMER_3: { TIMER_CTRL_3 |= TIMER_CTRL_IRQ; } break; - case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; - case IRQ_DMA_0: { DMA_CTRL(0) |= DMA_IRQ; } break; - case IRQ_DMA_1: { DMA_CTRL(1) |= DMA_IRQ; } break; - case IRQ_DMA_2: { DMA_CTRL(2) |= DMA_IRQ; } break; - case IRQ_DMA_3: { DMA_CTRL(3) |= DMA_IRQ; } break; - case IRQ_KEYPAD: { KEY_CTRL |= KEY_IRQ; } break; - case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; - } - IRQ_ENABLE |= (1 << idx); -} - -void -irq_disable(IrqIndex idx) { - switch (idx) { - case IRQ_VBLANK: { DISP_STATUS &= ~DISP_VBLANK_IRQ; } break; - case IRQ_HBLANK: { DISP_STATUS &= ~DISP_HBLANK_IRQ; } break; - case IRQ_VCOUNT: { DISP_STATUS &= ~DISP_VCOUNT_IRQ; } break; - case IRQ_TIMER_0: { TIMER_CTRL_0 &= ~TIMER_CTRL_IRQ; } break; - case IRQ_TIMER_1: { TIMER_CTRL_1 &= ~TIMER_CTRL_IRQ; } break; - case IRQ_TIMER_2: { TIMER_CTRL_2 &= ~TIMER_CTRL_IRQ; } break; - case IRQ_TIMER_3: { TIMER_CTRL_3 &= ~TIMER_CTRL_IRQ; } break; - case IRQ_SERIAL: { /* TODO: Set REG_SERIAL? */ } break; - case IRQ_DMA_0: { DMA_CTRL(0) &= ~DMA_IRQ; } break; - case IRQ_DMA_1: { DMA_CTRL(1) &= ~DMA_IRQ; } break; - case IRQ_DMA_2: { DMA_CTRL(2) &= ~DMA_IRQ; } break; - case IRQ_DMA_3: { DMA_CTRL(3) &= ~DMA_IRQ; } break; - case IRQ_KEYPAD: { KEY_CTRL &= ~KEY_IRQ; } break; - case IRQ_GAMEPAK: { /* Nothing to do here...*/ } break; - } - IRQ_ENABLE &= ~(1 << idx); -} - -void -irs_set(IrqIndex idx, IrsFunc func) { - // Store IRQ_CTRL status and disable interrupts for now. - u16 irq_ctrl = IRQ_CTRL; - IRQ_CTRL = 0; - - // Update the IRS table and enable/disable the given IRQ. - irs_table[idx] = func; - if (func == NULL) { - irq_disable(idx); - } else { - irq_enable(idx); - } - - // Restore previous irq_ctrl. - IRQ_CTRL = irq_ctrl; -} - -void -irq_init(void) { - IRS_MAIN = irs_main; - IRQ_CTRL = 1; -} - -void -irs_stub(void) {} diff --git a/src/interrupts.s b/src/interrupts.s deleted file mode 100644 index 67b9fe9..0000000 --- a/src/interrupts.s +++ /dev/null @@ -1,89 +0,0 @@ - .file "interrupts.s" - .extern irs_table; - .section .iwram, "ax", %progbits - .arm - .align - .global irs_main - -irs_main: - @ Get the contents of IRQ_ENABLE, IRQ_ACK, and IRQ_CTRL - ldr ip, mem_irq_base_reg @ ip = (IRQ_ENABLE << 16) | IRQ_ACK - ldr r0, [ip] @ r0 = irq_enable - and r1, r0, r0, lsr #16 @ r1 = irq_enable & irq_ack - - @ Disable IRQ_CTRL for now. - mov r3, #0 @ r3 = 0 - strh r3, [ip, #8] @ *(ip + 0x8) = r3 - - @ r0 = irs_table address pointer - @ r2 = tmp - @ r3 = 0 (for r3; r3 < 14; r3++) - ldr r0, = irs_table -irs_main_fp_search: - @ Check that the current index is an active IRQ. - mov r2, #1 - and r2, r1, r2, lsl r3 - cmp r2, #0 - beq irs_main_fp_search_not_enabled - - @ Extract the function pointer for this IRS if available. - ldr r2, [r0] - cmp r2, #0 - bne irs_main_handle_irs - -irs_main_fp_search_not_enabled: - add r0, r0, #4 - add r3, #1 - cmp r3, #14 - bne irs_main_fp_search - b irs_main_exit - -irs_main_handle_irs: - @ r2: Contains IRQ function pointer. - @ r3: Stores the IRQ index. - - @ Acknowledge that we are handling this interrupt writing to IRQ_ACK and - @ IRQ_ACK_BIOS. - mov r0, #1 - lsl r0, r0, r3 - strh r0, [ip, #2] - ldr r1, mem_irq_ack_bios @ r1 = IRQ_ACK_BIOS - str r0, [r1] - - @ Store the SPSR in one of the free registers and save it along with the - @ return pointer. - mrs r3, spsr - stmfd sp!, {r3, lr} - - @ Set CPU to system mode - mrs r3, cpsr - bic r3, r3, #0xDF - orr r3, r3, #0x1F - msr cpsr, r3 - - @ Call isr function pointer - stmfd sp!, {lr} - mov lr, pc - bx r2 - ldmfd sp!, {lr} - - @ Set CPU to irq mode - mrs r3, cpsr - bic r3, r3, #0xDF - orr r3, r3, #0x92 - msr cpsr, r3 - - @ Restore the SPSR and the registers. - ldmfd sp!, {r3, lr} - msr spsr, r3 - -irs_main_exit: - @ Restore IRQ_CTRL. - mov r3, #1 @ r3 = 0 - strh r3, [ip, #8] @ *(ip + 0x8) = r3 - bx lr - -mem_irq_base_reg: - .word 0x04000200 @ IRQ_ENABLE -mem_irq_ack_bios: - .word 0x03007FF8 @ IRQ_ACK_BIOS diff --git a/src/main.c b/src/main.c index e228034..1e1ef11 100644 --- a/src/main.c +++ b/src/main.c @@ -9,12 +9,11 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. */ -#include "common.h" -// #include "text.h" +#include "gba/gba.h" -#include "interrupts.c" #include "filesystem.c" #include "renderer.c" +// #include "text.h" // // Config parameters. @@ -62,6 +61,10 @@ int main(void) { irq_init(); irs_set(IRQ_VBLANK, irs_stub); + draw_pixel(10, 0, 1); + draw_pixel(10, 1, 2); + draw_pixel(10, 2, 3); + draw_pixel(10, 3, 4); draw_pixel(0, 0, 1); draw_pixel(0, 1, 2); draw_pixel(0, 2, 3); diff --git a/src/renderer.c b/src/renderer.c index 9fe55b2..ec54411 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -1,14 +1,28 @@ -// TODO: For now we pack front/backbuffers together but this make it so that we -// can only use 2 backgrounds. Instead we can move the backbuffer to the end of -// the VRAM. This will give us 3 backgrounds but eats into the available memory -// for sprites but should be fine for non sprite intensive applications. -#define FRONTBUFFER ((u32*)(MEM_VRAM)) -#define BACKBUFFER ((u32*)(MEM_VRAM + KB(96) - KB(20))) +#include "bd-font.c" + +// The frontbuffer is located at the beginning of the VRAM, and requires 20KB of +// video memory for 32 * 20 tiles at 4bpp. +#define FRONTBUF ((u32*)(MEM_VRAM)) // Adjust both of these if the location of the map changes. Each screnblock -// requires 2K. -#define FRONTBUFFER_TILEMAP ((u16*)(MEM_VRAM + KB(20))) -#define FRONTBUFFER_SCREENBLOCK 10 +// requires less than 2KB. +#define FRONTBUF_TILEMAP ((u16*)(MEM_VRAM + KB(20))) +#define FRONTBUF_SB 10 + +// The backbuffer is located at the end of the VRAM. This can allow us to use +// more backgrounds but eats into the available memory for sprites. This should +// be fine for non sprite intensive applications. If more sprite memory is +// needed, the backbuffer can be located at the end of the background memory +// instead (64KB - 20KB). +#define BACKBUF ((u32*)(MEM_VRAM + KB(96) - KB(20))) + +// The font data is located at the end of the frontbuffer memory, after the tile +// map and requires 8KB for 256 8x8 characters at 4bpp. This, along with the +// tilemap information allow us to store the frontbuffer and font for a text +// background in the first 2 charblocks (32KB). +#define FONT_DATA ((u32*)(MEM_VRAM + KB(22))) +#define FONT_TILEMAP ((u16*)(MEM_VRAM + KB(30))) +#define FONT_SB 15 static u32 dirty_tiles[21] = {0}; @@ -29,7 +43,7 @@ draw_pixel(u16 x, u16 y, u8 color) { // Update backbuffer. size_t shift = start_col * sizeof(u32); - BACKBUFFER[pos] = (BACKBUFFER[pos] & ~(0xF << shift)) | color << shift; + BACKBUF[pos] = (BACKBUF[pos] & ~(0xF << shift)) | color << shift; // Mark tile as dirty. dirty_tiles[tile_y] |= 1 << tile_x; @@ -39,8 +53,8 @@ IWRAM_CODE void flip_buffer(void) { // Copy dirty tiles from the backbuffer to the frontbuffer. - Tile *dst = FRONTBUFFER; - Tile *src = BACKBUFFER; + Tile *dst = FRONTBUF; + Tile *src = BACKBUF; for (size_t j = 0; j < 20; ++j) { if (dirty_tiles[j] == 0) { continue; @@ -54,19 +68,23 @@ flip_buffer(void) { } } +static u16 font_map[256] = {0}; + void renderer_init(void) { // Initialize display mode and bg palette. - DISP_CTRL = DISP_MODE_0 | DISP_BG_0 | DISP_OBJ; + DISP_CTRL = DISP_MODE_0 | DISP_BG_0 | DISP_BG_1 | DISP_OBJ; // Initialize backgrounds. - BG_CTRL(0) = BG_CHARBLOCK(0) | BG_SCREENBLOCK(FRONTBUFFER_SCREENBLOCK); + BG_CTRL(0) = BG_CHARBLOCK(0) | BG_SCREENBLOCK(FRONTBUF_SB) | BG_PRIORITY(1); + BG_CTRL(1) = BG_CHARBLOCK(1) | BG_SCREENBLOCK(FONT_SB) | BG_PRIORITY(0); - // TODO: Initialize other backgrounds if needed. - - // Use DMA to clear front and back buffers. - dma_fill(FRONTBUFFER, 0, KB(20), 3); - dma_fill(BACKBUFFER, 0, KB(20), 3); + // Use DMA to clear front and back buffers as well as the font memory map. + dma_fill(FRONTBUF, 0, KB(20), 3); + dma_fill(FRONTBUF_TILEMAP, 0, KB(2), 3); + dma_fill(FONT_DATA, 0, KB(8), 3); + dma_fill(FONT_TILEMAP, 0, KB(2), 3); + dma_fill(BACKBUF, 0, KB(20), 3); // Initialize default palette. PAL_BUFFER_BG[0] = COLOR_BLACK; @@ -76,11 +94,18 @@ renderer_init(void) { PAL_BUFFER_BG[4] = COLOR_CYAN; PAL_BUFFER_BG[5] = COLOR_GREY; - // Initialize background memory map. + // Initialize background memory map for frontbuffer and font backgorund. for (size_t i = 0; i < 32 * 20; ++i) { - FRONTBUFFER_TILEMAP[i] = i; + FRONTBUF_TILEMAP[i] = i; } -// // Load font data into VRAM. -// unpack_tiles(&bd_font, FONT_DATA, 256); + // Load font data into VRAM. + unpack_tiles(&bd_font, FONT_DATA, 256); + + // Initialize the font map translation table. That way we can write + // a character on the text background layer with: + // FONT_TILEMAP[tile_x + 32 * tile_y] = font_map['A']; + for (size_t i = 0; i < 256; ++i) { + font_map[i] = 192 + i; + } } -- cgit v1.2.1