Adding libugba interrupt handler

This commit is contained in:
Rodrigo Alfonso 2022-05-20 05:30:56 -03:00
parent e219892664
commit e18f3232ab
66 changed files with 2686 additions and 43 deletions

View File

@ -38,8 +38,6 @@ Name | Type | Default | Description
## Documentation
![image](https://user-images.githubusercontent.com/1631752/97110827-10695a00-16ba-11eb-999a-3262ad6b24d2.png)
```
Multi-Player mode can be used to communicate between up to 4 units.

View File

@ -0,0 +1 @@
../../lib/LinkConnection.h

View File

@ -0,0 +1,90 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020 Antonio Niño Díaz
#ifndef BACKGROUND_H__
#define BACKGROUND_H__
#include "bios.h"
#include "hardware.h"
// List of all possible sizes of regular backgrounds
typedef enum {
BG_REGULAR_256x256,
BG_REGULAR_512x256,
BG_REGULAR_256x512,
BG_REGULAR_512x512,
BG_REGULAR_NUMBER,
BG_REGULAR_INVALID
} bg_regular_size;
// List of all possible sizes of affine backgrounds
typedef enum {
BG_AFFINE_128x128,
BG_AFFINE_256x256,
BG_AFFINE_512x512,
BG_AFFINE_1024x1024,
BG_AFFINE_NUMBER,
BG_AFFINE_INVALID
} bg_affine_size;
// Types of background based on their bit depth
typedef enum {
BG_16_COLORS,
BG_256_COLORS
} bg_color_mode;
// Initialize a regular background.
EXPORT_API
void BG_RegularInit(int index, bg_regular_size size, bg_color_mode colors,
uintptr_t tile_base_addr, uintptr_t map_base_addr);
// Functions to set and get the scroll of a regular background. It doesn't work
// for affine backgrounds. Note that the scroll hardware registers are
// read-only. The functions below keep a copy of the scroll value so that it can
// be recovered later.
EXPORT_API void BG_RegularScrollSet(int index, int x, int y);
EXPORT_API void BG_RegularScrollGet(int index, int *x, int *y);
// Initialize an affine background.
EXPORT_API
void BG_AffineInit(int index, bg_affine_size size, uintptr_t tile_base_addr,
uintptr_t map_base_addr, int wrap);
// Set affine transformation values for the specified affine background.
EXPORT_API void BG_AffineTransformSet(int index, bg_affine_dst *tr);
// Enable wrap mode of an affine background.
EXPORT_API void BG_AffineWrapEnable(int index, int wrap);
// Set priority of a background.
EXPORT_API void BG_PrioritySet(int index, int priority);
// Enable mosaic effect in the specified background.
EXPORT_API void BG_MosaicEnable(int index, int enabled);
// Get a pointer to the framebuffer (in mode 3).
EXPORT_API uint16_t *BG_Mode3FramebufferGet(void);
// Get a pointer to the active or non-active framebuffer (in mode 4).
EXPORT_API uint16_t *BG_Mode4FramebufferActiveGet(void);
EXPORT_API uint16_t *BG_Mode4FramebufferBackGet(void);
// Get a pointer to the active or non-active framebuffer (in mode 5).
EXPORT_API uint16_t *BG_Mode5FramebufferActiveGet(void);
EXPORT_API uint16_t *BG_Mode5FramebufferBackGet(void);
// Set active buffer (0 or 1).
EXPORT_API void BG_FramebufferSet(int backbuffer);
// Swap active framebuffer (used for modes 4 and 5).
EXPORT_API void BG_FramebufferSwap(void);
// Set backdrop color (background palette 0 color).
EXPORT_API void BG_BackdropColorSet(uint16_t color);
#endif // BACKGROUND_H__

View File

@ -0,0 +1,207 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020, 2022 Antonio Niño Díaz
#ifndef BIOS_H__
#define BIOS_H__
#include <assert.h>
#include "definitions.h"
#include "hardware.h"
// Note: Names taken from GBATEK
// Perform a soft reset. Not supported on the SDL2 port.
EXPORT_API NORETURN void SWI_SoftReset(void);
// Flags to use with SWI_RegisterRamReset()
#define SWI_RAM_RESET_EWRAM (1 << 0)
#define SWI_RAM_RESET_IWRAM (1 << 1) // Except for the last 0x200 bytes
#define SWI_RAM_RESET_PALETTE (1 << 2)
#define SWI_RAM_RESET_VRAM (1 << 3)
#define SWI_RAM_RESET_OAM (1 << 4)
#define SWI_RAM_RESET_IO_SERIAL (1 << 5)
#define SWI_RAM_RESET_IO_SOUND (1 << 6)
#define SWI_RAM_RESET_IO_OTHER (1 << 7)
// Clear different areas of memory and I/O registers
EXPORT_API void SWI_RegisterRamReset(uint32_t flags);
// Wait until an interrupt happens.
EXPORT_API void SWI_Halt(void);
// Wait until the VBlank interrupt happens.
EXPORT_API void SWI_VBlankIntrWait(void);
// Wait for any of the interrupts in the flags specified. If an interrupt has
// already happened, it is possible to discard it by setting the argument to 1.
// If it is set to 0, and the interrupt has happened already, this function will
// return right away.
//
// Note that if no flags are passed the CPU will stay in an infinite loop inside
// this function.
EXPORT_API void SWI_IntrWait(uint32_t discard_old_flags, uint16_t wait_flags);
// For SWI_CpuSet() and SWI_CpuFastSet()
#define SWI_MODE_COPY (0 << 24)
#define SWI_MODE_FILL (1 << 24)
// For SWI_CpuSet() only
#define SWI_MODE_16BIT (0 << 26)
#define SWI_MODE_32BIT (1 << 26)
// Source and destination must be aligned to 4 bytes for 32-bit copies, or 2
// bytes for 16-bit copies. The length is expressed either in 32-bit or 16-bit
// chunks (words or halfwords).
EXPORT_API void SWI_CpuSet(const void *src, void *dst, uint32_t len_mode);
// Source and destination must be aligned to 4 bytes. The length must be a
// multiple of 8 bytes.
EXPORT_API void SWI_CpuFastSet(const void *src, void *dst, uint32_t len_mode);
// Calculate result or modulus of dividing num by div.
EXPORT_API int32_t SWI_Div(int32_t num, int32_t div);
EXPORT_API int32_t SWI_DivMod(int32_t num, int32_t div);
// Calculate square root.
EXPORT_API uint16_t SWI_Sqrt(uint32_t value);
// Calculate arc tangent
EXPORT_API int16_t SWI_ArcTan(int16_t tan);
// Calculate arc tangent 2
EXPORT_API int16_t SWI_ArcTan2(int16_t x, int16_t y);
// Known values that SWI_GetBiosChecksum() can return
#define SWI_CHECKSUM_GBA (0xBAAE187F) // GBA, GBA SP, GB Micro
#define SWI_CHECKSUM_NDS (0xBAAE1880) // NDS, 3DS in NDS mode
// Returns checksum of the BIOS
EXPORT_API uint32_t SWI_GetBiosChecksum(void);
// Struct that holds the input to SWI_BgAffineSet()
typedef struct ALIGNED(4) {
int32_t bgx; // 24.8 fixed point
int32_t bgy; // 24.8 fixed point
int16_t scrx;
int16_t scry;
int16_t scalex; // 8.8 fixed point
int16_t scaley; // 8.8 fixed point
uint32_t angle; // 8.8 fixed point. Low 8 bits ignored.
// The angle is a 32 bit integer (instead of adding a padding field) for
// conveniency. Only bits 8-15 are actually used.
} bg_affine_src;
static_assert(sizeof(bg_affine_src) == 20, "Wrong bg_affine_src size");
// Struct that holds the state of a background affine transformation. It is used
// as container of the output of SWI_BgAffineSet()
typedef struct ALIGNED(4) {
int16_t pa;
int16_t pb;
int16_t pc;
int16_t pd;
int32_t xoff;
int32_t yoff;
} bg_affine_dst;
static_assert(sizeof(bg_affine_dst) == 16, "Wrong bg_affine_dst size");
// This function gets a list of background transformations and outputs the
// correct affine matrices for the GBA hardware.
EXPORT_API
void SWI_BgAffineSet(const bg_affine_src *src, bg_affine_dst *dst,
uint32_t count);
// Struct that holds the input to SWI_ObjAffineSet()
typedef struct ALIGNED(2) {
int16_t sx; // 8.8 fixed point
int16_t sy; // 8.8 fixed point
uint16_t angle; // 8.8 fixed point. Range: 0 - 0xFFFF
uint16_t padding;
} obj_affine_src;
static_assert(sizeof(obj_affine_src) == 8, "Wrong obj_affine_src size");
// This function gets a list of objects transformations and outputs the correct
// affine matrices for the GBA hardware.
EXPORT_API
void SWI_ObjAffineSet(const obj_affine_src *src, void *dst,
uint32_t count, uint32_t increment);
// Struct that holds information used by SWI_BitUnPack()
typedef struct ALIGNED(4) {
int16_t source_length;
uint8_t source_width;
uint8_t dest_width;
uint32_t data_offset;
} bit_unpack_info;
static_assert(sizeof(bit_unpack_info) == 8, "Wrong bit_unpack_info size");
// Used in data_offset in bit_unpack_info.
#define SWI_BITUNPACK_OFFSET_ZERO (1 << 31)
// Unpack data of the specified width in source into the specified width in the
// destination. Used to increase the color depth of images, for example. It
// writes the result to the destination using 32-bit writes. VRAM can be used as
// destination.
EXPORT_API
void SWI_BitUnPack(const void *source, void *dest, const bit_unpack_info *info);
// Values that specify the type of the compression in the 32-bit header of a
// compressed blob. It can be found in bits 4-7 of the header.
#define SWI_UNCOMP_TYPE_LZ77 (1)
#define SWI_UNCOMP_TYPE_HUFFMAN (2)
#define SWI_UNCOMP_TYPE_RL (3)
#define SWI_UNCOMP_TYPE_DIFF (8)
// Value specified in bits 0-3 of the header
#define SWI_DIFF_SIZE_8BIT (1)
#define SWI_DIFF_SIZE_16BIT (2)
// Decompresses LZ77 data from the source and writes the result to the
// destination using 8-bit writes. It can't be used to decompress directly to
// VRAM, as it only accepts 16 and 32-bit accesses.
EXPORT_API
void SWI_LZ77UnCompReadNormalWrite8bit(const void *source, void *dest);
// Decompresses LZ77 data from the source and writes the result to the
// destination using 16-bit writes. VRAM can be used as destination.
EXPORT_API
void SWI_LZ77UnCompReadNormalWrite16bit(const void *source, void *dest);
// Decompresses Huffman-encoded data from the source and writes the result to
// the destination using 32-bit writes. VRAM can be used as destination.
EXPORT_API void SWI_HuffUnComp(const void *source, void *dest);
// Decompresses Run-Length data from the source and writes the result to the
// destination using 8-bit writes. It can't be used to decompress directly to
// VRAM, as it only accepts 16 and 32-bit accesses.
EXPORT_API void SWI_RLUnCompWram(const void *source, void *dest);
// Decompresses Run-Length data from the source and writes the result to the
// destination using 16-bit writes. VRAM can be used as destination.
EXPORT_API void SWI_RLUnCompVram(const void *source, void *dest);
// Convert data in diff format to original data. In this version, data elements
// are 8 bits wide and the result is writen in 8-bit accesses.
EXPORT_API void SWI_Diff8bitUnFilterWram(const void *source, void *dest);
// Convert data in diff format to original data. In this version, data elements
// are 8 bits wide and the result is writen in 16-bit accesses. This means that
// VRAM can be used as destination, as it doesn't accept 8-bit writes.
EXPORT_API void SWI_Diff8bitUnFilterVram(const void *source, void *dest);
// Convert data in diff format to original data. In this version, data elements
// are 16 bits wide and the result is writen in 16-bit accesses.
EXPORT_API void SWI_Diff16bitUnFilter(const void *source, void *dest);
// Set the level of the SOUNDBIAS register by doing small changes. A level of 0
// sets the BIAS level to 0, any other value sets it to 0x200.
EXPORT_API void SWI_SoundBias(uint32_t level);
// Perform a hard reset. Not supported on the SDL2 port.
EXPORT_API NORETURN void SWI_HardReset(void);
#endif // BIOS_H__

View File

@ -0,0 +1,39 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020 Antonio Niño Díaz
#ifndef BIOS_WRAPPERS_H__
#define BIOS_WRAPPERS_H__
#include <stddef.h>
#include "bios.h"
// Note: This file contains wrappers to use some BIOS services easier than with
// the actual interface.
// Copy from source to destination in 16-bit chunks.
EXPORT_API void SWI_CpuSet_Copy16(const void *src, void *dst, size_t len);
// Fill destination with the 16-bit value from source.
EXPORT_API void SWI_CpuSet_Fill16(const void *src, void *dst, size_t len);
// Copy from source to destination in 32-bit chunks.
EXPORT_API void SWI_CpuSet_Copy32(const void *src, void *dst, size_t len);
// Fill destination with the 32-bit value from source.
EXPORT_API void SWI_CpuSet_Fill32(const void *src, void *dst, size_t len);
// Copy from source to destination in 32-bit chunks.
EXPORT_API void SWI_CpuFastSet_Copy32(const void *src, void *dst, size_t len);
// Fill destination with the 32-bit value from source.
EXPORT_API void SWI_CpuFastSet_Fill32(const void *src, void *dst, size_t len);
// Fills an array of object affine matrices from an array of transformations.
EXPORT_API void SWI_ObjAffineSet_OAM(const obj_affine_src *src,
oam_matrix_entry *dst, uint32_t count);
#endif // BIOS_WRAPPERS_H__

View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020 Antonio Niño Díaz
#ifndef CONSOLE_H__
#define CONSOLE_H__
#include "definitions.h"
// Initialize the console by loading the needed tileset and map to the last tile
// block in VRAM.
EXPORT_API void CON_InitDefault(void);
// Print a character.
EXPORT_API void CON_PutChar(char c);
// Print an unformatted string.
EXPORT_API void CON_Print(const char *string);
// Set cursor at the specified coordinates.
EXPORT_API void CON_CursorSet(int x, int y);
// Clear the console.
EXPORT_API void CON_Clear(void);
#endif // CONSOLE_H__

View File

@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020-2021 Antonio Niño Díaz
#ifndef DEBUG_H__
#define DEBUG_H__
// This file has debug utilities that don't work on GBA binaries. However, there
// are defines here that remove them automatically so that code that uses them
// can still build without errors.
#include <stdint.h>
#include "definitions.h"
// Take a screenshot. If the specified name is NULL, it defaults to
// "screenshot.png".
#ifdef __GBA__
#define Debug_Screenshot(n) do { (void)(n); } while (0)
#else
EXPORT_API void Debug_Screenshot(const char *name);
#endif
// Custom assert() that works on GBA and PC.
#ifndef UGBA_DEBUG
# define UGBA_Assert(expr) ((void)0)
#else
# define UGBA_Assert(expr) \
((expr) ? (void)0 : \
UGBA_AssertFunction(__FILE__, __LINE__, __func__, #expr))
#endif // UGBA_DEBUG
EXPORT_API void UGBA_AssertFunction(const char *file, int line,
const char *func, const char *expr);
#endif // DEBUG_H__

View File

@ -0,0 +1,72 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020 Antonio Niño Díaz
#ifndef DEFINITIONS_H__
#define DEFINITIONS_H__
#define BIT(n) (1 << (n))
// For functions with unused arguments, for example
#if defined(_MSC_VER)
# define UNUSED
#else
# define UNUSED __attribute__((unused))
#endif
#if defined(_MSC_VER)
# define ALIGNED(x) __declspec(align(x))
#else
# define ALIGNED(x) __attribute__((aligned(x)))
#endif
#if defined(_MSC_VER)
# define NORETURN __declspec(noreturn)
#else
# define NORETURN __attribute__((noreturn))
#endif
// The library is built static for GBA and shared for PC
#if defined(__GBA__)
# define EXPORT_API
#else
# if defined(_MSC_VER)
# define EXPORT_API __declspec(dllexport)
# else
# define EXPORT_API __attribute__((visibility("default")))
// TODO: Is this one below needed in MinGW?
//# define EXPORT_API __attribute__((dllexport))
# endif
#endif
// NOTE: To use the following defines:
//
// ARM_CODE IWRAM_CODE int add(int a, int b)
// {
// return a + b;
// }
//
// IWRAM_BSS int empty_array[100];
//
// IWRAM_DATA int non_empty_array[3] = { 1, 2, 3 };
#ifdef __GBA__
# define ARM_CODE __attribute__((target("arm")))
# define THUMB_CODE __attribute__((target("thumb")))
# define IWRAM_BSS // IWRAM is the default location for .bss symbols
# define IWRAM_DATA // IWRAM is the default location for .data symbols
# define IWRAM_CODE __attribute__((section(".iwram_code"), long_call))
# define EWRAM_BSS __attribute__((section(".sbss")))
# define EWRAM_DATA __attribute__((section(".ewram_data")))
# define EWRAM_CODE __attribute__((section(".ewram_code"), long_call))
#else
# define ARM_CODE
# define THUMB_CODE
# define IWRAM_BSS
# define IWRAM_DATA
# define IWRAM_CODE
# define EWRAM_BSS
# define EWRAM_DATA
# define EWRAM_CODE
#endif
#endif // DEFINITIONS_H__

View File

@ -0,0 +1,62 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020-2021 Antonio Niño Díaz
#ifndef DISPLAY_H__
#define DISPLAY_H__
#include "hardware.h"
// Set background display mode and set the other fields of the register to 0.
EXPORT_API void DISP_ModeSet(int mode);
// Enable or disable screen layers.
EXPORT_API void DISP_LayersEnable(int bg0, int bg1, int bg2, int bg3, int obj);
// Enable or disable windows.
EXPORT_API void DISP_WindowsEnable(int win0, int win1, int obj);
// Enable or disable the HBL Interval Free mode.
EXPORT_API void DISP_HBLIntervalFreeEnable(int enable);
// Enable or disable forced blank.
EXPORT_API void DISP_ForcedBlankEnable(int enable);
// Enable 1D mapping or disable it (and use 2D mapping).
EXPORT_API void DISP_Object1DMappingEnable(int enable);
// Setup window 0 dimensions.
EXPORT_API void WIN_Win0SizeSet(uint32_t left, uint32_t right,
uint32_t top, uint32_t bottom);
// Setup window 1 dimensions.
EXPORT_API void WIN_Win1SizeSet(uint32_t left, uint32_t right,
uint32_t top, uint32_t bottom);
// Set the layers enabled for window 0.
EXPORT_API void WIN_Win0LayersSet(uint16_t flags);
// Set the layers enabled for window 1 and outside of window 0.
EXPORT_API void WIN_Win1LayersSet(uint16_t flags);
// Set the layers enabled outside of windows 0 and 1.
EXPORT_API void WIN_WinOutLayersSet(uint16_t flags);
// Set the layers enabled inside object windows.
EXPORT_API void WIN_WinObjLayersSet(uint16_t flags);
// Setup blending/brightness effect and layers affected.
EXPORT_API void DISP_BlendSetup(uint16_t layers_1, uint16_t layers_2,
uint16_t effect);
// Set blending factors for BLDCNT_ALPHA_BLENDING.
EXPORT_API void DISP_BlendAlphaSet(int eva, int evb);
// Set blending factor for BLDCNT_BRIGTHNESS_INCRESE/DECREASE.
EXPORT_API void DISP_BlendYSet(int evy);
// Setup mosaic for background and objects.
EXPORT_API void DISP_MosaicSet(uint32_t bgh, uint32_t bgw,
uint32_t objh, uint32_t objw);
#endif // DISPLAY_H__

View File

@ -0,0 +1,52 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020-2021 Antonio Niño Díaz
#ifndef DMA_H__
#define DMA_H__
#include "definitions.h"
// Important note: The only DMA channel that can read from ROM is 3.
// Generic DMA transfer function. It is needed to start repeating transfers or
// anything other than a simple copy, as there are only helpers for simple
// copy scenarios.
//
// Returns 0 on success. A transfer can fail if:
// - The channel number isn't valid
// - the source address is set in ROM/SRAM and the specified channel isn't 3.
// This check only works on GBA binaries.
EXPORT_API
int DMA_Transfer(int channel, const void *src, void *dst, size_t size,
uint16_t flags);
// Stop DMA transfers that repeat. Returns 0 on success.
EXPORT_API
int DMA_Stop(int channel);
// Copy from source to destination in 16-bit chunks. Returns 0 on success.
EXPORT_API
int DMA_Copy16(int channel, const void *src, void *dst, size_t size);
// Copy from source to destination in 32-bit chunks. Returns 0 on success.
EXPORT_API
int DMA_Copy32(int channel, const void *src, void *dst, size_t size);
// During the next HBL do a copy, in 16-bit chunks. Returns 0 on success.
EXPORT_API
int DMA_HBLCopy16(int channel, const void *src, void *dst, size_t size);
// During the next HBL do a copy, in 32-bit chunks. Returns 0 on success.
EXPORT_API
int DMA_HBLCopy32(int channel, const void *src, void *dst, size_t size);
// During the next VBL do a copy, in 16-bit chunks. Returns 0 on success.
EXPORT_API
int DMA_VBLCopy16(int channel, const void *src, void *dst, size_t size);
// During the next VBL do a copy, in 32-bit chunks. Returns 0 on success.
EXPORT_API
int DMA_VBLCopy32(int channel, const void *src, void *dst, size_t size);
#endif // DMA_H__

View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020 Antonio Niño Díaz
#ifndef FP_MATH_H__
#define FP_MATH_H__
#include "definitions.h"
#define FP_PI (0x8000)
#define FP_PI_2 (FP_PI / 2)
#define FP_2_PI (2 * FP_PI)
// Input: A full cirle is 0x10000 (PI = 0x8000)
// Output: Between 1 << 16 and -1 << 16 (1.0 to -1.0)
EXPORT_API IWRAM_CODE int32_t FP_Sin(int32_t x);
EXPORT_API IWRAM_CODE int32_t FP_Cos(int32_t x);
#endif // FP_MATH_H__

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020 Antonio Niño Díaz
#ifndef INPUT_H__
#define INPUT_H__
#include <stdint.h>
#include "definitions.h"
// Update keypad state.
EXPORT_API void KEYS_Update(void);
// Returns keys that have just been pressed.
EXPORT_API uint16_t KEYS_Pressed(void);
// Returns keys that are pressed.
EXPORT_API uint16_t KEYS_Held(void);
// Returns keys that have just been released.
EXPORT_API uint16_t KEYS_Released(void);
// Enable keypad interrupt that is triggered when all specified keys are pressed
// at the same time.
EXPORT_API void KEYS_IRQEnablePressedAll(uint16_t keys);
// Enable keypad interrupt that is triggered when any of the specified keys is
// pressed at any time.
EXPORT_API void KEYS_IRQEnablePressedAny(uint16_t keys);
// Disable keypad interrupt.
EXPORT_API void KEYS_IRQDisable(void);
#endif // INPUT_H__

View File

@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020 Antonio Niño Díaz
#ifndef INTERRUPTS_H__
#define INTERRUPTS_H__
#include <stddef.h>
#include <stdint.h>
#include "definitions.h"
typedef enum {
IRQ_VBLANK = 0,
IRQ_HBLANK = 1,
IRQ_VCOUNT = 2,
IRQ_TIMER0 = 3,
IRQ_TIMER1 = 4,
IRQ_TIMER2 = 5,
IRQ_TIMER3 = 6,
IRQ_SERIAL = 7,
IRQ_DMA0 = 8,
IRQ_DMA1 = 9,
IRQ_DMA2 = 10,
IRQ_DMA3 = 11,
IRQ_KEYPAD = 12,
IRQ_GAMEPAK = 13,
IRQ_NUMBER
} irq_index;
typedef void (*irq_vector)(void);
// Initialize global interrupt handling. This is called before reaching
// GBA_main(), so it isn't normally needed to call it.
EXPORT_API void IRQ_Init(void);
// Set interrupt handler for the specified interrupt.
EXPORT_API void IRQ_SetHandler(irq_index index, irq_vector function);
// Enable the specified interrupt. For VBLANK, HBLANK and VCOUNT, this function
// also sets the corresponding flags in DISPSTAT automatically.
EXPORT_API void IRQ_Enable(irq_index index);
// Disable the specified interrupt.
EXPORT_API void IRQ_Disable(irq_index index);
// Set the reference VCOUNT that triggers the VCOUNT interrupt.
EXPORT_API void IRQ_SetReferenceVCOUNT(uint32_t y);
#endif // INTERRUPTS_H__

View File

@ -0,0 +1,90 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020 Antonio Niño Díaz
#ifndef OBJ_H__
#define OBJ_H__
#include "definitions.h"
typedef enum {
OBJ_16_COLORS,
OBJ_256_COLORS
} oam_color_mode;
typedef enum {
OBJ_MODE_NORMAL,
OBJ_MODE_TRANSPARENT,
OBJ_MODE_WINDOW
} oam_entry_mode;
typedef enum {
// Square
OBJ_SIZE_8x8,
OBJ_SIZE_16x16,
OBJ_SIZE_32x32,
OBJ_SIZE_64x64,
// Horizontal
OBJ_SIZE_16x8,
OBJ_SIZE_32x8,
OBJ_SIZE_32x16,
OBJ_SIZE_64x32,
// Vertical
OBJ_SIZE_8x16,
OBJ_SIZE_8x32,
OBJ_SIZE_16x32,
OBJ_SIZE_32x64,
OBJ_SIZE_NUMBER,
OBJ_SIZE_INVALID
} oam_entry_size;
// Regular objects functions
// -------------------------
EXPORT_API void OBJ_RegularInit(int index, int x, int y, oam_entry_size size,
oam_color_mode colors, int pal, int tile);
EXPORT_API void OBJ_RegularEnableSet(int index, int enable);
EXPORT_API void OBJ_RegularHFlipSet(int index, int enable);
EXPORT_API void OBJ_RegularVFlipSet(int index, int enable);
// Affine objects functions
// ------------------------
EXPORT_API void OBJ_AffineInit(int index, int x, int y, oam_entry_size size,
int matrix, oam_color_mode colors, int pal,
int tile, int doublesize);
EXPORT_API void OBJ_AffineMatrixSet(int index, int matrix_index);
EXPORT_API void OBJ_AffineDoubleSizeSet(int index, int enable);
// Common objects functions
// ------------------------
EXPORT_API void OBJ_PositionSet(int index, int x, int y);
EXPORT_API void OBJ_PositionGet(int index, int *x, int *y);
EXPORT_API void OBJ_ModeSet(int index, oam_entry_mode mode);
EXPORT_API void OBJ_GetShapeSize(oam_entry_size size,
uint16_t *attr0_shape, uint16_t *attr1_size);
EXPORT_API oam_entry_size OBJ_GetSizeFromDimensions(int width, int height);
EXPORT_API void OBJ_GetDimensionsFromSize(oam_entry_size size,
int *width, int *height);
EXPORT_API void OBJ_SizeSet(int index, oam_entry_size size);
// Enable or disable mosaic effect.
EXPORT_API void OBJ_MosaicSet(int index, int enable);
// Set palette of the object. Only for 16 color objects.
EXPORT_API void OBJ_Palette16Set(int index, int palette);
EXPORT_API void OBJ_PrioritySet(int index, int priority);
// This function automatically detects if the object is in 16 or 256 color mode
EXPORT_API void OBJ_TileSet(int index, int tile);
#endif // OBJ_H__

View File

@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020-2021 Antonio Niño Díaz
#ifndef SOUND_H__
#define SOUND_H__
#include "definitions.h"
// Enable or disable sound hardware. It must be enabled before doing anything
// else with sound hardware.
EXPORT_API void SOUND_MasterEnable(int enable);
// Set volume of DMA channels. Valid values are 100 and 50. Any other value will
// be treated as 50%.
EXPORT_API void SOUND_DMA_Volume(int dma_a_max, int dma_b_max);
// Set master volume of all PSG channels. Valid values are 100, 50 and 25.. Any
// other value will be treated as 25%.
EXPORT_API void SOUND_PSG_MasterVolume(int volume);
// Set left and right volume of all PSG channels. Valid values are 0 to 7.
EXPORT_API void SOUND_PSG_Volume(int volume_left, int volume_right);
// Specify which DMA channels are heard from which speakers.
EXPORT_API
void SOUND_DMA_Pan(int dma_a_left, int dma_a_right,
int dma_b_left, int dma_b_right);
// Specify which PSG channels are heard from which speakers.
EXPORT_API
void SOUND_PSG_Pan(int left_1, int right_1, int left_2, int right_2,
int left_3, int right_3, int left_4, int right_4);
// Set buffer that will be streamed to DMA channel A. The buffer needs to be
// aligned to 32 bits.
EXPORT_API void SOUND_DMA_Stream_A(const void *source);
// Set buffer that will be streamed to DMA channel B. The buffer needs to be
// aligned to 32 bits.
EXPORT_API void SOUND_DMA_Stream_B(const void *source);
// Set buffers that will be streamed to DMA channels A and B. The buffers need
// to be aligned to 32 bits.
EXPORT_API void SOUND_DMA_Setup_AB(const void *source_a, const void *source_b);
// Restart DMA transfer used for audio.
EXPORT_API void SOUND_DMA_Retrigger_AB(void);
// Specify which timer is assigned to each one of the DMA channels. Only timers
// 0 and 1 are valid.
EXPORT_API void SOUND_DMA_TimerSetup(int dma_a_timer, int dma_b_timer);
#endif // SOUND_H__

View File

@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2022 Antonio Niño Díaz
#ifndef SRAM_H__
#define SRAM_H__
#include <stddef.h>
#include "definitions.h"
// SRAM can only be accessed in 8-bits reads and writes.
//
// The functions in this file are placed in EWRAM. According to some sources,
// reading SRAM from code in ROM doesn't work (even though it hasn't been
// verified).
//
// If you are going to use SRAM, you should try to make it easier for emulators
// and flashcarts to detect your save type:
//
// - Add a string like "SRAM_V111" to your ROM, preferably close to the start.
// Magic strings like that are found in commercial games developed with the
// SDK of Nintendo. Emulators look for them when loading ROMs.
//
// - The game code of your ROM should start with "2" for the EverDrive GBA to
// detect it as SRAM:
//
// 1 - EEPROM || 2 - SRAM || 3 - FLASH-64 || 4 - FLASH-128
//
// https://krikzz.com/pub/support/everdrive-gba/OS/changelist.txt
// Writes a buffer into SRAM. The destination must be inside SRAM, and the size
// of the buffer must be at most MEM_SRAM_SIZE bytes long. Returns 0 on success.
EXPORT_API EWRAM_CODE int SRAM_Write(void *dst, const void *src, size_t size);
// Reads a buffer from SRAM. The source must be inside SRAM, and the size of the
// buffer must be at most MEM_SRAM_SIZE bytes long. Returns 0 on success.
EXPORT_API EWRAM_CODE int SRAM_Read(void *dst, const void *src, size_t size);
#endif // SRAM_H__

View File

@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020 Antonio Niño Díaz
#ifndef TIMER_H__
#define TIMER_H__
#include "definitions.h"
// Start timer. The value passed to prescaler can be 1, 64, 256 or 1024.
EXPORT_API
void TM_TimerStart(int index, uint16_t reload_value, int prescaler,
int enable_irq);
// Start timer in cascade mode. Note that the prescaler value is ignored in this
// mode, so it isn't needed.
EXPORT_API
void TM_TimerStartCascade(int index, uint16_t period_ticks, int enable_irq);
// Start timer that is triggered every `period_ms` milliseconds. Internally it
// looks for the lowest prescaler that allows this period (to have the best
// accuracy). It returns 0 on success.
EXPORT_API
int TM_TimerStartMs(int index, uint32_t period_ms, int enable_irq);
// Stop the specified timer.
EXPORT_API
void TM_TimerStop(int index);
#endif // TIMER_H__

View File

@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020 Antonio Niño Díaz
#ifndef UGBA_H__
#define UGBA_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "background.h"
#include "bios.h"
#include "bios_wrappers.h"
#include "console.h"
#include "debug.h"
#include "definitions.h"
#include "display.h"
#include "dma.h"
#include "fp_math.h"
#include "hardware.h"
#include "input.h"
#include "interrupts.h"
#include "obj.h"
#include "sound.h"
#include "sram.h"
#include "timer.h"
#include "vram.h"
// Initialize library. This function needs to be called at the start of main().
EXPORT_API void UGBA_Init(int* argc, char** argv[]);
#ifndef __GBA__
// Initialize library with no video output (for testing). This function needs to
// be called at the start of main(). Not implemented in GBA as it isn't usedul
// there.
EXPORT_API void UGBA_InitHeadless(int* argc, char** argv[]);
#endif
// This function tries to detect specific flashcarts with special needs and
// returns a value to be written to REG_WAITCNT based on the flashcart. The best
// possible returned value is WAITCNT_OPTIMIZED.
EXPORT_API uint16_t UGBA_FlashcartOptimizedWaitstates(void);
#ifdef __cplusplus
}
#endif
#endif // UGBA_H__

View File

@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020 Antonio Niño Díaz
#ifndef VERSION_H__
#define VERSION_H__
#include <ugba/ugba.h>
// This library follows the Semantic Versioning rules: https://semver.org/
#define LIBUGBA_VERSION_MAJOR (0U)
#define LIBUGBA_VERSION_MINOR (2U)
#define LIBUGBA_VERSION_PATCH (0U)
#define LIBUGBA_VERSION ((LIBUGBA_VERSION_MAJOR << 16) | \
(LIBUGBA_VERSION_MINOR << 8) | \
(LIBUGBA_VERSION_PATCH << 0))
// Returns the version of the shared library when it was compiled.
EXPORT_API uint32_t UGBA_VersionCompiledGet(void);
// This function can ve used to see if the version of the shared library is the
// same as the one that the program was compiled with. It isn't needed for the
// GBA version because it is linked statically.
//
// You should use the following right at the start of main() before UGBA_Init():
//
// if (!UGBA_VersionIsCompatible(LIBUGBA_VERSION))
// VersionCheckFailed();
//
// It follows the rules of Semantic Versioning.
EXPORT_API int UGBA_VersionIsCompatible(uint32_t version);
#endif // VERSION_H__

View File

@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020 Antonio Niño Díaz
#ifndef VRAM_H__
#define VRAM_H__
#include <stddef.h>
#include <stdint.h>
#include "definitions.h"
// Copy 16-color object tiles to VRAM (0x06010000) starting at the given
// index (0 - 1023).
EXPORT_API
void VRAM_OBJTiles16Copy(const void *src, size_t size, uint32_t tile_index);
// Copy 256-color object tiles to VRAM (0x06010000) starting at the given
// index (0 - 511).
EXPORT_API
void VRAM_OBJTiles256Copy(const void *src, size_t size, uint32_t tile_index);
// Copy a 16-bit palette to object palette memory.
EXPORT_API
void VRAM_OBJPalette16Copy(const void *src, size_t size, uint32_t pal_index);
// Copy a 256-bit palette to object palette memory.
EXPORT_API
void VRAM_OBJPalette256Copy(const void *src, size_t size);
// Copy 16-color background tiles to VRAM (0x06000000) starting at the given
// index (0 - 2047).
EXPORT_API
void VRAM_BGTiles16Copy(const void *src, size_t size, uint32_t tile_index);
// Copy 256-color background tiles to VRAM (0x06000000) starting at the given
// index (0 - 1023).
EXPORT_API
void VRAM_BGTiles256Copy(const void *src, size_t size, uint32_t tile_index);
// Copy a 16-bit palette to background palette memory.
EXPORT_API
void VRAM_BGPalette16Copy(const void *src, size_t size, uint32_t pal_index);
// Copy a 256-bit palette to background palette memory.
EXPORT_API
void VRAM_BGPalette256Copy(const void *src, size_t size);
#endif // VRAM_H__

Binary file not shown.

View File

@ -131,13 +131,13 @@ export PATH := $(DEVKITARM)/bin:$(PATH)
export PROJ ?= $(notdir $(CURDIR))
TITLE := $(PROJ)
LIBS := -ltonc
LIBS := -ltonc -lugba
BUILD := build
SRCDIRS := src
DATADIRS := data
INCDIRS := src lib
LIBDIRS := $(TONCLIB)
INCDIRS := src
LIBDIRS := $(TONCLIB) $(PWD)/../_lib/libugba
# --- switches ---

View File

@ -1 +0,0 @@
../../../lib/LinkConnection.h

View File

@ -0,0 +1,22 @@
#include "interrupt.h"
#include <ugba.h>
void interrupt_init(void) {
IRQ_Init();
}
void interrupt_set_handler(interrupt_index index, interrupt_vector function) {
IRQ_SetHandler((irq_index)index, function);
}
void interrupt_enable(interrupt_index index) {
IRQ_Enable((irq_index)index);
}
void interrupt_disable(interrupt_index index) {
IRQ_Disable((irq_index)index);
}
void interrupt_set_reference_vcount(unsigned long y) {
IRQ_SetReferenceVCOUNT(y);
}

View File

@ -0,0 +1,30 @@
#ifndef INTERRUPT_H
#define INTERRUPT_H
typedef enum {
INTR_VBLANK = 0,
INTR_HBLANK = 1,
INTR_VCOUNT = 2,
INTR_TIMER0 = 3,
INTR_TIMER1 = 4,
INTR_TIMER2 = 5,
INTR_TIMER3 = 6,
INTR_SERIAL = 7,
INTR_DMA0 = 8,
INTR_DMA1 = 9,
INTR_DMA2 = 10,
INTR_DMA3 = 11,
INTR_KEYPAD = 12,
INTR_GAMEPAK = 13,
INTR_NUMBER
} interrupt_index;
typedef void (*interrupt_vector)(void);
void interrupt_init(void);
void interrupt_set_handler(interrupt_index index, interrupt_vector function);
void interrupt_enable(interrupt_index index);
void interrupt_disable(interrupt_index index);
void interrupt_set_reference_vcount(unsigned long y);
#endif // INTERRUPT_H

View File

@ -1,9 +1,9 @@
#include <tonc.h>
#include <string>
#include "interrupt.h"
// (0) Include the header
#include "../lib/LinkConnection.h"
#include "../../_lib/LinkConnection.h"
void log(std::string text);
@ -14,12 +14,14 @@ void init() {
REG_DISPCNT = DCNT_MODE0 | DCNT_BG0;
tte_init_se_default(0, BG_CBB(0) | BG_SBB(31));
irq_init(NULL);
// (2) Add the interrupt service routines
irq_add(II_VBLANK, LINK_ISR_VBLANK);
irq_add(II_SERIAL, LINK_ISR_SERIAL);
irq_add(II_TIMER3, LINK_ISR_TIMER);
interrupt_init();
interrupt_set_handler(INTR_VBLANK, LINK_ISR_VBLANK);
interrupt_enable(INTR_VBLANK);
interrupt_set_handler(INTR_SERIAL, LINK_ISR_SERIAL);
interrupt_enable(INTR_SERIAL);
interrupt_set_handler(INTR_TIMER3, LINK_ISR_TIMER);
interrupt_enable(INTR_TIMER3);
// (3) Initialize the library
linkConnection->activate();
@ -32,7 +34,7 @@ int main() {
for (u32 i = 0; i < LINK_MAX_PLAYERS; i++)
data[i] = 0;
while (1) {
while (true) {
// (4) Send/read messages messages
u16 keys = ~REG_KEYS & KEY_ANY;
u16 message = keys + 1;
@ -44,8 +46,9 @@ int main() {
output += "Players: " + std::to_string(linkState->playerCount) + "\n";
for (u32 i = 0; i < linkState->playerCount; i++) {
while (linkState->hasMessage(i))
while (linkState->hasMessage(i)) {
data[i] = linkState->readMessage(i) - 1;
}
output += "Player " + std::to_string(i) + ": " +
std::to_string(data[i]) + "\n";
@ -53,8 +56,9 @@ int main() {
output += "_sent: " + std::to_string(message) + "\n";
output += "_self pID: " + std::to_string(linkState->currentPlayerId);
} else
} else {
output += std::string("Waiting...");
}
log(output);
VBlankIntrWait();

View File

@ -120,7 +120,7 @@ endef
export PROJ ?= $(notdir $(CURDIR))
TITLE := $(PROJ)
LIBS := -ltonc -lgba-sprite-engine
LIBS := -ltonc -lugba -lgba-sprite-engine
BUILD := build
SRCDIRS := src \
@ -129,7 +129,7 @@ SRCDIRS := src \
DATADIRS :=
INCDIRS := src
LIBDIRS := $(TONCLIB) $(BASE_DIR)/lib/libgba-sprite-engine
LIBDIRS := $(TONCLIB) $(PWD)/../_lib/libugba $(PWD)/../_lib/libgba-sprite-engine
# --- switches ---
@ -277,21 +277,13 @@ endif # End BUILD switch
# --- More targets ----------------------------------------------------
.PHONY: clean rebuild start check-env
.PHONY: clean rebuild start
check-env:
ifndef BASE_DIR
$(warning Please export a BASE_DIR environment variable pointing to this directory.)
$(warning Example:)
$(warning export BASE_DIR="D:\work\gba\projects\gba-link-connection\examples\full")
$(error "Aborting")
endif
rebuild: clean $(BUILD)
rebuild: check-env clean $(BUILD)
start: check-env
start:
start "$(TARGET).gba"
restart: check-env rebuild start
restart: rebuild start
# EOF

View File

@ -1 +0,0 @@
../../../lib/LinkConnection.h

View File

@ -0,0 +1,22 @@
#include "interrupt.h"
#include <ugba.h>
void interrupt_init(void) {
IRQ_Init();
}
void interrupt_set_handler(interrupt_index index, interrupt_vector function) {
IRQ_SetHandler((irq_index)index, function);
}
void interrupt_enable(interrupt_index index) {
IRQ_Enable((irq_index)index);
}
void interrupt_disable(interrupt_index index) {
IRQ_Disable((irq_index)index);
}
void interrupt_set_reference_vcount(unsigned long y) {
IRQ_SetReferenceVCOUNT(y);
}

View File

@ -0,0 +1,30 @@
#ifndef INTERRUPT_H
#define INTERRUPT_H
typedef enum {
INTR_VBLANK = 0,
INTR_HBLANK = 1,
INTR_VCOUNT = 2,
INTR_TIMER0 = 3,
INTR_TIMER1 = 4,
INTR_TIMER2 = 5,
INTR_TIMER3 = 6,
INTR_SERIAL = 7,
INTR_DMA0 = 8,
INTR_DMA1 = 9,
INTR_DMA2 = 10,
INTR_DMA3 = 11,
INTR_KEYPAD = 12,
INTR_GAMEPAK = 13,
INTR_NUMBER
} interrupt_index;
typedef void (*interrupt_vector)(void);
void interrupt_init(void);
void interrupt_set_handler(interrupt_index index, interrupt_vector function);
void interrupt_enable(interrupt_index index);
void interrupt_disable(interrupt_index index);
void interrupt_set_reference_vcount(unsigned long y);
#endif // INTERRUPT_H

View File

@ -1,10 +1,13 @@
#include <libgba-sprite-engine/gba_engine.h>
#include <tonc.h>
#include "../lib/LinkConnection.h"
#include "../../_lib/LinkConnection.h"
#include "interrupt.h"
#include "scenes/TestScene.h"
#include "utils/SceneUtils.h"
// FULL:
// This test has a menu and lets the user send data in different ways.
void setUpInterrupts();
void printTutorial();
static std::shared_ptr<GBAEngine> engine{new GBAEngine()};
@ -55,17 +58,19 @@ inline void ISR_reset() {
}
inline void setUpInterrupts() {
irq_init(NULL);
interrupt_init();
// LinkConnection
irq_add(II_VBLANK, LINK_ISR_VBLANK);
irq_add(II_SERIAL, LINK_ISR_SERIAL);
irq_add(II_TIMER3, LINK_ISR_TIMER);
irq_add(II_TIMER2, NULL);
interrupt_set_handler(INTR_VBLANK, LINK_ISR_VBLANK);
interrupt_enable(INTR_VBLANK);
interrupt_set_handler(INTR_SERIAL, LINK_ISR_SERIAL);
interrupt_enable(INTR_SERIAL);
interrupt_set_handler(INTR_TIMER3, LINK_ISR_TIMER);
interrupt_enable(INTR_TIMER3);
// A+B+START+SELECT
REG_KEYCNT = 0b1100000000001111;
irq_add(II_KEYPAD, ISR_reset);
interrupt_set_handler(INTR_KEYPAD, ISR_reset);
}
void printTutorial() {

View File

@ -2,7 +2,7 @@
#include <libgba-sprite-engine/background/text_stream.h>
#include "../lib/LinkConnection.h"
#include "../../_lib/LinkConnection.h"
#include "utils/InputHandler.h"
#include "utils/SceneUtils.h"

284
examples/stress/Makefile Normal file
View File

@ -0,0 +1,284 @@
#
# Template tonc makefile
#
# Yoinked mostly from DKP's template
#
# === SETUP ===========================================================
# --- No implicit rules ---
.SUFFIXES:
# --- Paths ---
export TONCLIB := ${DEVKITPRO}/libtonc
# === TONC RULES ======================================================
#
# Yes, this is almost, but not quite, completely like to
# DKP's base_rules and gba_rules
#
export PATH := $(DEVKITARM)/bin:$(PATH)
# --- Executable names ---
PREFIX ?= arm-none-eabi-
export CC := $(PREFIX)gcc
export CXX := $(PREFIX)g++
export AS := $(PREFIX)as
export AR := $(PREFIX)ar
export NM := $(PREFIX)nm
export OBJCOPY := $(PREFIX)objcopy
# LD defined in Makefile
# === LINK / TRANSLATE ================================================
%.gba : %.elf
@$(OBJCOPY) -O binary $< $@
@echo built ... $(notdir $@)
@gbafix $@ -t$(TITLE)
#----------------------------------------------------------------------
%.mb.elf :
@echo Linking multiboot
$(LD) -specs=gba_mb.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
$(NM) -Sn $@ > $(basename $(notdir $@)).map
#----------------------------------------------------------------------
%.elf :
@echo Linking cartridge
$(LD) -specs=gba.specs $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
$(NM) -Sn $@ > $(basename $(notdir $@)).map
#----------------------------------------------------------------------
%.a :
@echo $(notdir $@)
@rm -f $@
$(AR) -crs $@ $^
# === OBJECTIFY =======================================================
%.iwram.o : %.iwram.cpp
@echo $(notdir $<)
$(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) $(IARCH) -c $< -o $@
#----------------------------------------------------------------------
%.iwram.o : %.iwram.c
@echo $(notdir $<)
$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) $(IARCH) -c $< -o $@
#----------------------------------------------------------------------
%.o : %.cpp
@echo $(notdir $<)
$(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) $(RARCH) -c $< -o $@
#----------------------------------------------------------------------
%.o : %.c
@echo $(notdir $<)
$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) $(RARCH) -c $< -o $@
#----------------------------------------------------------------------
%.o : %.s
@echo $(notdir $<)
$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@
#----------------------------------------------------------------------
%.o : %.S
@echo $(notdir $<)
$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(ASFLAGS) -c $< -o $@
#----------------------------------------------------------------------
# canned command sequence for binary data
#----------------------------------------------------------------------
define bin2o
bin2s $< | $(AS) -o $(@)
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(<F) | tr . _)`.h
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(<F) | tr . _)`.h
echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(<F) | tr . _)`.h
endef
# =====================================================================
# --- Main path ---
export PATH := $(DEVKITARM)/bin:$(PATH)
# === PROJECT DETAILS =================================================
# PROJ : Base project name
# TITLE : Title for ROM header (12 characters)
# LIBS : Libraries to use, formatted as list for linker flags
# BUILD : Directory for build process temporaries. Should NOT be empty!
# SRCDIRS : List of source file directories
# DATADIRS : List of data file directories
# INCDIRS : List of header file directories
# LIBDIRS : List of library directories
# General note: use `.' for the current dir, don't leave the lists empty.
export PROJ ?= $(notdir $(CURDIR))
TITLE := $(PROJ)
LIBS := -ltonc -lugba
BUILD := build
SRCDIRS := src
DATADIRS := data
INCDIRS := src lib
LIBDIRS := $(TONCLIB) $(PWD)/../_lib/libugba
# --- switches ---
bMB := 0 # Multiboot build
bTEMPS := 0 # Save gcc temporaries (.i and .s files)
bDEBUG2 := 0 # Generate debug info (bDEBUG2? Not a full DEBUG flag. Yet)
# === BUILD FLAGS =====================================================
# This is probably where you can stop editing
# NOTE: I've noticed that -fgcse and -ftree-loop-optimize sometimes muck
# up things (gcse seems fond of building masks inside a loop instead of
# outside them for example). Removing them sometimes helps
# --- Architecture ---
ARCH := -mthumb-interwork -mthumb
RARCH := -mthumb-interwork -mthumb
IARCH := -mthumb-interwork -marm -mlong-calls
# --- Main flags ---
CFLAGS := -mcpu=arm7tdmi -mtune=arm7tdmi -O2
CFLAGS += -Wall
CFLAGS += $(INCLUDE)
CFLAGS += -ffast-math -fno-strict-aliasing
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := $(ARCH) $(INCLUDE)
LDFLAGS := $(ARCH) -Wl,-Map,$(PROJ).map
# --- switched additions ----------------------------------------------
# --- Multiboot ? ---
ifeq ($(strip $(bMB)), 1)
TARGET := $(PROJ).mb
else
TARGET := $(PROJ)
endif
# --- Save temporary files ? ---
ifeq ($(strip $(bTEMPS)), 1)
CFLAGS += -save-temps
CXXFLAGS += -save-temps
endif
# --- Debug info ? ---
ifeq ($(strip $(bDEBUG)), 1)
CFLAGS += -DDEBUG -g
CXXFLAGS += -DDEBUG -g
ASFLAGS += -DDEBUG -g
LDFLAGS += -g
else
CFLAGS += -DNDEBUG
CXXFLAGS += -DNDEBUG
ASFLAGS += -DNDEBUG
endif
# === BUILD PROC ======================================================
ifneq ($(BUILD),$(notdir $(CURDIR)))
# Still in main dir:
# * Define/export some extra variables
# * Invoke this file again from the build dir
# PONDER: what happens if BUILD == "" ?
export OUTPUT := $(CURDIR)/$(TARGET)
export VPATH := \
$(foreach dir, $(SRCDIRS) , $(CURDIR)/$(dir)) \
$(foreach dir, $(DATADIRS), $(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
# --- List source and data files ---
CFILES := $(foreach dir, $(SRCDIRS) , $(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir, $(SRCDIRS) , $(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir, $(SRCDIRS) , $(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir, $(DATADIRS), $(notdir $(wildcard $(dir)/*.*)))
# --- Set linker depending on C++ file existence ---
ifeq ($(strip $(CPPFILES)),)
export LD := $(CC)
else
export LD := $(CXX)
endif
# --- Define object file list ---
export OFILES := $(addsuffix .o, $(BINFILES)) \
$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) \
$(SFILES:.s=.o)
# --- Create include and library search paths ---
export INCLUDE := $(foreach dir,$(INCDIRS),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := -L$(CURDIR) $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
# --- Create BUILD if necessary, and run this makefile from there ---
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
arm-none-eabi-nm -Sn $(OUTPUT).elf > $(BUILD)/$(TARGET).map
all : $(BUILD)
clean:
@echo clean ...
@rm -rf $(BUILD) $(TARGET).elf $(TARGET).gba $(TARGET).sav
else # If we're here, we should be in the BUILD dir
DEPENDS := $(OFILES:.o=.d)
# --- Main targets ----
$(OUTPUT).gba : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
-include $(DEPENDS)
endif # End BUILD switch
# --- More targets ----------------------------------------------------
.PHONY: clean rebuild start
rebuild: clean $(BUILD)
start:
start "$(TARGET).gba"
restart: rebuild start
# EOF

View File

@ -0,0 +1,22 @@
#include "interrupt.h"
#include <ugba.h>
void interrupt_init(void) {
IRQ_Init();
}
void interrupt_set_handler(interrupt_index index, interrupt_vector function) {
IRQ_SetHandler((irq_index)index, function);
}
void interrupt_enable(interrupt_index index) {
IRQ_Enable((irq_index)index);
}
void interrupt_disable(interrupt_index index) {
IRQ_Disable((irq_index)index);
}
void interrupt_set_reference_vcount(unsigned long y) {
IRQ_SetReferenceVCOUNT(y);
}

View File

@ -0,0 +1,30 @@
#ifndef INTERRUPT_H
#define INTERRUPT_H
typedef enum {
INTR_VBLANK = 0,
INTR_HBLANK = 1,
INTR_VCOUNT = 2,
INTR_TIMER0 = 3,
INTR_TIMER1 = 4,
INTR_TIMER2 = 5,
INTR_TIMER3 = 6,
INTR_SERIAL = 7,
INTR_DMA0 = 8,
INTR_DMA1 = 9,
INTR_DMA2 = 10,
INTR_DMA3 = 11,
INTR_KEYPAD = 12,
INTR_GAMEPAK = 13,
INTR_NUMBER
} interrupt_index;
typedef void (*interrupt_vector)(void);
void interrupt_init(void);
void interrupt_set_handler(interrupt_index index, interrupt_vector function);
void interrupt_enable(interrupt_index index);
void interrupt_disable(interrupt_index index);
void interrupt_set_reference_vcount(unsigned long y);
#endif // INTERRUPT_H

View File

@ -0,0 +1,84 @@
#include <tonc.h>
#include <string>
#include "interrupt.h"
#include "../../_lib/LinkConnection.h"
// STRESS:
// This test sends consecutive values in a two-player setup.
// When a GBA receives something not equal to previousValue + 1, it hangs.
// It should work indefinitely (with no packet loss).
void log(std::string text);
LinkConnection* linkConnection = new LinkConnection();
void init() {
REG_DISPCNT = DCNT_MODE0 | DCNT_BG0;
tte_init_se_default(0, BG_CBB(0) | BG_SBB(31));
interrupt_init();
interrupt_set_handler(INTR_VBLANK, LINK_ISR_VBLANK);
interrupt_enable(INTR_VBLANK);
interrupt_set_handler(INTR_SERIAL, LINK_ISR_SERIAL);
interrupt_enable(INTR_SERIAL);
interrupt_set_handler(INTR_TIMER3, LINK_ISR_TIMER);
interrupt_enable(INTR_TIMER3);
linkConnection->activate();
}
int main() {
init();
u16 counter = 0;
u16 remoteCounter = 0;
bool error = false;
while (true) {
auto linkState = linkConnection->linkState.get();
std::string output = "";
if (linkState->isConnected()) {
output += "Players: " + std::to_string(linkState->playerCount) + "\n";
if (linkState->playerCount == 2) {
linkConnection->send(counter + 1);
counter++;
}
while (linkState->hasMessage(!linkState->currentPlayerId)) {
u16 msg = linkState->readMessage(!linkState->currentPlayerId) - 1;
if (msg == remoteCounter) {
remoteCounter++;
} else {
error = true;
output += "ERROR!\nExpected " + std::to_string(remoteCounter) +
" but got " + std::to_string(msg) + "\n";
}
}
output += "(" + std::to_string(counter) + ", " +
std::to_string(remoteCounter) + ")\n";
} else {
output += std::string("Waiting...");
}
log(output);
if (error) {
while (true)
;
}
VBlankIntrWait();
}
return 0;
}
void log(std::string text) {
tte_erase_screen();
tte_write("#{P:0,0}");
tte_write(text.c_str());
}

View File

@ -30,12 +30,13 @@
#define LINK_SET_HIGH(REG, BIT) REG |= 1 << BIT
#define LINK_SET_LOW(REG, BIT) REG &= ~(1 << BIT)
// --------------------------------------------------------------------------
// A Link Cable connection for Multi-player mode.
// --------------------------------------------------------------------------
// Usage:
// - 1) Include this header in your main.cpp file and add:
// LinkConnection* linkConnection = new LinkConnection();
// - 2) Add the required interrupt service routines:
// - 2) Add the required interrupt service routines: (*)
// irq_init(NULL);
// irq_add(II_VBLANK, LINK_ISR_VBLANK);
// irq_add(II_SERIAL, LINK_ISR_SERIAL);
@ -45,10 +46,15 @@
// - 4) Send/read messages by using:
// linkConnection->send(...);
// linkConnection->linkState
// --------------------------------------------------------------------------
// (*) libtonc's interrupt handler sometimes ignores interrupts due to a bug.
// That can cause packet loss. You might want to use libugba's instead.
// (see examples)
// --------------------------------------------------------------------------
// `data` restrictions:
// 0xFFFF and 0x0 are reserved values, so don't use them
// (they mean 'disconnected' and 'no data' respectively)
// --------------------------------------------------------------------------
void LINK_ISR_VBLANK();
void LINK_ISR_TIMER();