initial commit, stripped-down version of gba-sprite-engine

This commit is contained in:
wgroeneveld 2020-07-08 09:05:02 +02:00
parent 62dbfddd6b
commit 27e6a9065c
41 changed files with 7000 additions and 2 deletions

121
.gitignore vendored
View File

@ -1,4 +1,83 @@
CMakeLists.txt.user
.DS_Store
build/
.idea/
# Created by https://www.gitignore.io/api/clion,cmake
### CLion ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
### CLion Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/sonarlint
### CMake ###
CMakeCache.txt
CMakeFiles
CMakeScripts
@ -8,4 +87,42 @@ cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
# End of https://www.gitignore.io/api/clion,cmake
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
#VisualStudio code
.vscode

31
CMakeLists.txt Executable file
View File

@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.12)
# use the GBA cross-compiler.
# WARNING: CMAKE_AR and RANLIB didn't use find_program() to scan the PATH yet.
# If using CMake < 3.12, consider using an absolute path. https://gitlab.kitware.com/cmake/cmake/merge_requests/1720
SET(CMAKE_SYSTEM_NAME Generic)
SET(CMAKE_GENERATOR "Unix Makefiles")
SET(CMAKE_C_COMPILER arm-none-eabi-gcc)
SET(CMAKE_CXX_COMPILER arm-none-eabi-g++)
SET(CMAKE_OBJCOPY arm-none-eabi-objcopy)
SET(CMAKE_AR arm-none-eabi-ar CACHE FILEPATH "Archiver")
SET(CMAKE_RANLIB arm-none-eabi-ranlib)
SET(BASE_CMAKE_LINK_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
SET(CMAKE_EXE_LINKER_FLAGS "${BASE_CMAKE_LINK_FLAGS} -mthumb-interwork -mthumb -specs=gba.specs")
SET(CMAKE_CXX_STANDARD 11)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing -O3")
set(CMAKE_VERBOSE_MAKEFILE on)
project(gba-bitmap-engine-project VERSION 0.1 LANGUAGES CXX)
# Must use GNUInstallDirs to install libraries into correct locations on all platforms.
include(GNUInstallDirs)
add_subdirectory(engine)
# this should be a part of the engine CMakeLists.txt file but cross-compiling and gtest doesn't work
add_subdirectory(test)
add_subdirectory(demos/demo1-wireframes)

5
README.md Normal file
View File

@ -0,0 +1,5 @@
## A high-level object-oriented Game Boy Advance bitmap 3D software engine
Engine layout: stripped-down version of [https://github.com/wgroeneveld/gba-sprite-engine/](https://github.com/wgroeneveld/gba-sprite-engine/)
**Work in progress**, using mode4 to create a software 3D engine from scratch.

View File

@ -0,0 +1,13 @@
project(wireframes)
add_executable(${PROJECT_NAME}.elf
src/main.cpp
src/wirescene.cpp
src/wirescene.h
)
target_link_libraries(${PROJECT_NAME}.elf gba-bitmap-engine)
add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -v -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.gba
)

View File

@ -0,0 +1,19 @@
#include <libgba-sprite-engine/scene.h>
#include <libgba-sprite-engine/gba_engine.h>
#include <libgba-sprite-engine/palette/palette_manager.h>
#include "wirescene.h"
int main() {
std::shared_ptr<GBAEngine> engine(new GBAEngine());
WireScene* startScene = new WireScene(engine);
engine->setScene(startScene);
while (true) {
engine->update();
engine->delay(1000);
}
return 0;
}

View File

@ -0,0 +1,21 @@
//
// Created by Wouter Groeneveld on 02/08/18.
//
#include <libgba-sprite-engine/gba/tonc_memdef.h>
#include <libgba-sprite-engine/gba_engine.h>
#include "wirescene.h"
const unsigned short pal[3] __attribute__((aligned(4))) = {
0x0000, 0xFFFF, 0x3AE2
};
void WireScene::load() {
foregroundPalette = std::unique_ptr<ForegroundPaletteManager>(new ForegroundPaletteManager());
backgroundPalette = std::unique_ptr<BackgroundPaletteManager>(new BackgroundPaletteManager(pal, sizeof(pal)));
}
void WireScene::tick(u16 keys) {
}

View File

@ -0,0 +1,23 @@
//
// Created by Wouter Groeneveld on 02/08/18.
//
#ifndef GBA_SPRITE_ENGINE_SAMPLE_START_SCENE_H
#define GBA_SPRITE_ENGINE_SAMPLE_START_SCENE_H
#include <libgba-sprite-engine/scene.h>
class WireScene : public Scene {
private:
public:
WireScene(std::shared_ptr<GBAEngine> engine) : Scene(engine) {}
void load() override;
void tick(u16 keys) override;
};
#endif //GBA_SPRITE_ENGINE_SAMPLE_START_SCENE_H

22
engine/CMakeLists.txt Normal file
View File

@ -0,0 +1,22 @@
project(gba-bitmap-engine)
set_property(SOURCE src/gba/sin_lut.s PROPERTY LANGUAGE C)
set_property(SOURCE src/gba/tonc_bios.s PROPERTY LANGUAGE C)
set_source_files_properties(src/gba/tonc_bios.s PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp")
add_library(${PROJECT_NAME}
src/palette/palette_manager.cpp
src/palette/combined_palette.cpp
src/gba/sin_lut.s
src/gba/tonc_bios.s
src/gba_engine.cpp
src/sound_control.cpp src/scene.cpp src/timer.cpp include/libgba-sprite-engine/gbavector.h src/gbavector.cpp)
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
PRIVATE src)
install(TARGETS ${PROJECT_NAME} EXPORT GbaSpriteEngineTargets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

View File

@ -0,0 +1,132 @@
//
// tonc_asminc.h : header file with goodies for assembly.
//
//! \file tonc_asminc.h
//! \author J Vijn
//! \date 20081019 - 20120519
//
/* === NOTES ===
* Cleaned up the macros so that they work with comma-directives as well.
* For use in assembly only!
*/
#ifndef TONC_ASMINC_H
#define TONC_ASMINC_H
#if !__ASSEMBLER__
#error This header file is for use in assembly only!
#endif // /asm only
// --------------------------------------------------------------------
// MACROS
// --------------------------------------------------------------------
#define DEF_SIZE(_name) .size _name, .-_name
//! \name Section definitions for assembly.
//\{
#define CSEC_TEXT .text //!< Standard code section directive.
#define CSEC_EWRAM .section .ewram , "ax", %progbits //!< EWRAM code section directive.
#define CSEC_IWRAM .section .iwram, "ax", %progbits //!< IWRAM code section directive.
#define DSEC_DATA .data //<! Standard data section directive.
#define DSEC_ROM .section .rodata //!< ROM data section directive.
#define DSEC_BSS .section .bss //!< Uninited data (RAM) section directive.
#define DSEC_SBSS .section .sbss //!< Uninited data (DTCM?) section directive.
#define ARM_FUNC .arm //!< Indicates an ARM function.
#define THUMB_FUNC .thumb_func //!< Indicates a Thumb function.
//# NOTE: because these use commas, I can't pass them through CPP macros.
//# Yes, this is stupid, but do you have a better idea?
#undef CSEC_EWRAM
.macro CSEC_EWRAM
.section .ewram , "ax", %progbits
.endm
#undef CSEC_IWRAM
.macro CSEC_IWRAM
.section .iwram , "ax", %progbits
.endm
//\}
//! \name Function definition macros.
//\{
//! Start an assembly function.
/*!
\param _name Name of function.
\param _section Section to place function in (like .text)
*/
#define BEGIN_FUNC(_name, _section, _iset) \
_section; \
_iset; \
.align 2; \
.global _name; \
.type _name STT_FUNC; \
_name:
//! End of a function.
#define END_FUNC(_name) DEF_SIZE(_name)
//! Begin an ARM function
/*!
\param _name Name of function.
\param _section Section to place function in (like .text)
*/
#define BEGIN_FUNC_ARM(_name, _section) BEGIN_FUNC(_name, _section, ARM_FUNC)
//! Begin a THUMB function.
/*!
\param _name Name of function.
\param _section Section to place function in (like .text)
*/
#define BEGIN_FUNC_THUMB(_name, _section) BEGIN_FUNC(_name, _section, THUMB_FUNC)
//\}
//! \name Data definition macros.
//\{
#define BEGIN_SYMBOL(_name, _section) \
_section; \
.align; \
.global _name; \
_name:
#define END_SYMBOL(_name) DEF_SIZE(_name)
//\}
// --------------------------------------------------------------------
// CONSTANTS
// --------------------------------------------------------------------
//! \name TSurface member offsets.
//\{
#define TSRF_data 0
#define TSRF_pitch 4
#define TSRF_width 8
#define TSRF_height 10
#define TSRF_bpp 12
#define TSRF_type 13
#define TSRF_palSize 14
#define TSRF_pal 16
//\}
// --------------------------------------------------------------------
// GLOBALS
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// PROTOTYPES
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// INLINES
// --------------------------------------------------------------------
#endif // /TONC_ASMINC_H
// EOF

View File

@ -0,0 +1,355 @@
//
// BIOS call functions
// MODIFIED by Wouter Groeneveld: extern "C" - no C++ name mangling, can't find assembly methods!
//
//! \file tonc_bios.h
//! \author J Vijn
//! \date 20060508 - 20070208
//
// === NOTES ===
//
// Pretty much copied verbatim from Pern and dkARM's libgba
// (which in turn is copied from CowBite Spec (which got its info from
// GBATek))
//
//
// === NOTES ===
// * Make SURE your data is aligned to 32bit boundaries. Defining data
// as u32 (and I do mean define; not merely cast) ensures this. Either
// that or use __attribute__(( aligned(4) ))
// * There is a large (70 cycle in and out) overhead for SWIs. If you
// know what they do, consider creating replacement code
// * div by 0 locks up GBA.
// * Cpu(Fast)Set's count is in chunks, not bytes. CpuFastSet REQUIRES
// n*32 byte data
// * SoftReset is funky with interrupts on.
// * VBlankIntrWait is your friend. If you have a VBlank isr that clears
// REG_IFBIOS as well. Use this instead of REG_VCOUNT polling for
// VSync.
// * I haven't tested many of these functions. The ones that are have a
// plus (+) behind their numbers.
// * I've switched to the standard BIOS names.
#ifndef TONC_BIOS
#define TONC_BIOS
#include "tonc_types.h"
/*!
\addtogroup grpBios
\brief Interfaces and constants for the GBA BIOS routines.
For details, see
<a href="http://www.coranac.com/tonc/text/keys.htm">tonc:keys</a>
and especially
<a href="http://nocash.emubase.de/gbatek.htm#biosfunctions">gbatek:bios</a>.
\note While the speeds of the routines are fair, there
is a large overhead in calling the functions.
*/
/*! \defgroup grpBiosDef BIOS informalities
\ingroup grpBios
*/
/*! \{ */
// --------------------------------------------------------------------
// CONSTANTS
// --------------------------------------------------------------------
//! \name SoftReset flags
//\{
#define ROM_RESTART 0x00 //!< Restart from ROM entry point.
#define RAM_RESTART 0x01 //!< Restart from RAM entry point.
//\}
//! \name RegisterRamReset flags
//\{
#define RESET_EWRAM 0x0001 //!< Clear 256K on-board WRAM
#define RESET_IWRAM 0x0002 //!< Clear 32K in-chip WRAM
#define RESET_PALETTE 0x0004 //!< Clear Palette
#define RESET_VRAM 0x0008 //!< Clear VRAM
#define RESET_OAM 0x0010 //!< Clear OAM. does NOT disable OBJs!
#define RESET_REG_SIO 0x0020 //!< Switches to general purpose mode
#define RESET_REG_SOUND 0x0040 //!< Reset Sound registers
#define RESET_REG 0x0080 //!< All other registers
//#define RESET_REG_VIDEO 0x0100 //!< video regs, 00h-60h (non standard!)
//#define RESET_REG_DMA 0x0200 //!< DMA regs, B0h-100h (non standard!)
//#define RESET_REG_TIMER 0x0400 //!< Timer regs (100h-110h) (non standard!)
#define RESET_MEM_MASK 0x001F
#define RESET_REG_MASK 0x00E0
#define RESET_GFX 0x001C //!< Clear all gfx-related memory
//\}
//! \name Cpu(Fast)Set flags
//\{
#define CS_CPY 0 //!< Copy mode
#define CS_FILL (1<<24) //!< Fill mode
#define CS_CPY16 0 //!< Copy in halfwords
#define CS_CPY32 (1<<26) //!< Copy words
#define CS_FILL32 (5<<24) //!< Fill words
#define CFS_CPY CS_CPY //!< Copy words
#define CFS_FILL CS_FILL //!< Fill words
//\}
//! \name ObjAffineSet P-element offsets
//\{
#define BG_AFF_OFS 2 //!< BgAffineDest offsets
#define OBJ_AFF_OFS 8 //!< ObjAffineDest offsets
//\}
//! \name Decompression routines
#define BUP_ALL_OFS (1<<31)
#define LZ_TYPE 0x00000010
#define LZ_SIZE_MASK 0xFFFFFF00
#define LZ_SIZE_SHIFT 8
#define HUF_BPP_MASK 0x0000000F
#define HUF_TYPE 0x00000020
#define HUF_SIZE_MASK 0xFFFFFF00
#define HUF_SIZE_SHIFT 8
#define RL_TYPE 0x00000030
#define RL_SIZE_MASK 0xFFFFFF00
#define RL_SIZE_SHIFT 8
#define DIF_8 0x00000001
#define DIF_16 0x00000002
#define DIF_TYPE 0x00000080
#define DIF_SIZE_MASK 0xFFFFFF00
#define DIF_SIZE_SHIFT 8
//\}
//! \name Multiboot modes
//\{
#define MBOOT_NORMAL 0x00
#define MBOOT_MULTI 0x01
#define MBOOT_FAST 0x02
//\}
// --------------------------------------------------------------------
// MACROS
// --------------------------------------------------------------------
//! BIOS calls from C
/*! You can use this macro in a C BIOS-call wrapper. The wrapper
* should declare the flags, then this call will do the rest.
* \param x Number of swi call (THUMB number)
* \note It checks the __thumb__ \#define to see whether we're
* in ARM or THUMB mode and fixes the swi number accordingly.
* Huzzah for the C proprocessor!
* \deprecated This macro will not work properly for functions that have IO.
*/
#if defined ( __thumb__ )
#define swi_call(x) __asm("swi\t"#x ::: "r0", "r1", "r2", "r3")
#else
#define swi_call(x) __asm("swi\t"#x"<<16" ::: "r0", "r1", "r2", "r3")
#endif
// --------------------------------------------------------------------
// CLASSES
// --------------------------------------------------------------------
// --- affine function 0x0E and 0x0F ---
/*
* Notational convention: postfix underscore is 2d vector
*
* p_ = (px, py) = texture coordinates
* q_ = (qx, qy) = screen coordinates
* P = | pa pb | = affine matrix
* | pc pd |
* d_ = (dx, dy) = background displacement
*
* Then:
*
* (1) p_ = P*q_ + d_
*
* For transformation around a different point
* (texture point p0_ and screen point q0_), do
*
* (2) p_ - p0_ = P*(q_-q0_)
*
* Subtracting eq 2 from eq1 we immediately find:
*
* (3) _d = p0_ - P*q0_
*
* For the special case of a texture->screen scale-then-rotate
* transformation with
* s_ = (sx, sy) = inverse scales (s>1 shrinks)
* a = alpha = Counter ClockWise (CCW) angle
*
* (4) P = | sx*cos(a) -sx*sin(a) |
* | sy*sin(a) sy*cos(a) |
*
*
* ObjAffineSet takes a and s_ as input and gives P
* BgAffineSet does that and fills in d_ as well
*
*/
// affine types in tonc_types.h
//! BitUpPack ( for swi 10h)
typedef struct BUP
{
u16 src_len; //!< source length (bytes)
u8 src_bpp; //!< source bitdepth (1,2,4,8)
u8 dst_bpp; //!< destination bitdepth (1,2,4,8,16,32)
u32 dst_ofs; //!< {0-30}: added offset {31}: zero-data offset flag
} BUP;
//! Multiboot struct
typedef struct
{
u32 reserved1[5];
u8 handshake_data;
u8 padding;
u16 handshake_timeout;
u8 probe_count;
u8 client_data[3];
u8 palette_data;
u8 response_bit;
u8 client_bit;
u8 reserved2;
u8 *boot_srcp;
u8 *boot_endp;
u8 *masterp;
u8 *reserved3[3];
u32 system_work2[4];
u8 sendflag;
u8 probe_target_bit;
u8 check_wait;
u8 server_type;
} MultiBootParam;
/*! \} */
// --------------------------------------------------------------------
// BASIC BIOS ROUTINES
// --------------------------------------------------------------------
/*! \defgroup grpBiosMain BIOS functions
* \ingroup grpBios
*/
/*! \{ */
//! \name Reset functions
//\{
extern "C" void SoftReset(void);
extern "C" void RegisterRamReset(u32 flags);
//\}
//! \name Halt functions
//\{
extern "C" void Halt(void);
extern "C" void Stop(void);
extern "C" void IntrWait(u32 flagClear, u32 irq);
extern "C" void VBlankIntrWait(void);
//\}
//! \name Math functions
//\{
extern "C" s32 Div(s32 num, s32 den);
extern "C" s32 DivArm(s32 den, s32 num);
extern "C" u32 Sqrt(u32 num);
extern "C" s16 ArcTan(s16 dydx);
extern "C" s16 ArcTan2(s16 x, s16 y);
//\}
//! \name Memory copiers/fillers
//\{
// Technically, these are misnomers. The convention is that
// xxxset is used for fills (comp memset, strset). Or perhaps
// the C library functions are misnomers, since set can be applied
// to both copies and fills.
extern "C" void CpuSet(const void *src, void *dst, u32 mode);
extern "C" void CpuFastSet(const void *src, void *dst, u32 mode);
//\}
extern "C" u32 BiosCheckSum(void);
//! \name Rot/scale functions
//\{
// These functions are misnomers, because ObjAffineSet is merely
// a special case of/precursor to BgAffineSet. Results from either
// can be used for both objs and bgs. Oh well.
extern "C" void ObjAffineSet(const ObjAffineSource *src, void *dst, s32 num, s32 offset);
extern "C" void BgAffineSet(const BgAffineSource *src, BgAffineDest *dst, s32 num);
//\}
//! \name Decompression (see GBATek for format details)
//\{
extern "C" void BitUnPack(const void *src, void *dst, const BUP *bup);
extern "C" void LZ77UnCompWram(const void *src, void *dst);
extern "C" void LZ77UnCompVram(const void *src, void *dst);
extern "C" void HuffUnComp(const void *src, void *dst);
extern "C" void RLUnCompWram(const void *src, void *dst);
extern "C" void RLUnCompVram(const void *src, void *dst);
extern "C" void Diff8bitUnFilterWram(const void *src, void *dst);
extern "C" void Diff8bitUnFilterVram(const void *src, void *dst);
extern "C" void Diff16bitUnFilter(const void *src, void *dst);
//\}
//! \name Sound Functions
//\{
// (I have even less of a clue what these do than for the others ---
extern "C" void SoundBias(u32 bias);
extern "C" void SoundDriverInit(void *src);
extern "C" void SoundDriverMode(u32 mode);
extern "C" void SoundDriverMain(void);
extern "C" void SoundDriverVSync(void);
extern "C" void SoundChannelClear(void);
extern "C" u32 MidiKey2Freq(void *wa, u8 mk, u8 fp);
extern "C" void SoundDriverVSyncOff(void);
extern "C" void SoundDriverVSyncOn(void);
//\}
//! \name Multiboot handshake
//\{
extern "C" int MultiBoot(MultiBootParam* mb, u32 mode);
//\}
/*! \} */
/*! \defgroup grpBiosEx More BIOS functions
* \ingroup grpBios
*/
/*! \{ */
//\{
// You can find these in swi_ex.s
extern "C" void VBlankIntrDelay(u32 count);
extern "C" int DivSafe(int num, int den);
extern "C" int Mod(int num, int den);
extern "C" u32 DivAbs(int num, int den);
extern "C" int DivArmMod(int den, int num);
extern "C" u32 DivArmAbs(int den, int num);
extern "C" void CpuFastFill(u32 wd, void *dst, u32 count);
#define DivMod Mod
//\}
/*! \} */
#endif // TONC_BIOS

View File

@ -0,0 +1,573 @@
//
// Core functionality
//
//! \file tonc_core.h
//! \author J Vijn
//! \date 20060508 - 20080128
//
/* === NOTES ===
* Contents: bits, random, dma, timer
* 20080129,jv: added tonccpy/set routines.
*/
#ifndef TONC_CORE
#define TONC_CORE
#include "tonc_memmap.h"
#include "tonc_memdef.h"
// --------------------------------------------------------------------
// BITS and BITFIELDS
// --------------------------------------------------------------------
/*! \defgroup grpCoreBit Bit(field) macros
\ingroup grpCore
*/
/*! \{ */
//! \name Simple bit macros
//\{
//! Create value with bit \a n set
#define BIT(n) ( 1<<(n) )
//! Shift \a a by \a n
#define BIT_SHIFT(a, n) ( (a)<<(n) )
//! Create a bitmask \a len bits long
#define BIT_MASK(len) ( BIT(len)-1 )
//! Set the \a flag bits in \a word
#define BIT_SET(y, flag) ( y |= (flag) )
//! Clear the \a flag bits in \a word
#define BIT_CLEAR(y, flag) ( y &= ~(flag) )
//! Flip the \a flag bits in \a word
#define BIT_FLIP(y, flag) ( y ^= (flag) )
//! Test whether all the \a flag bits in \a word are set
#define BIT_EQ(y, flag) ( ((y)&(flag)) == (flag) )
//! Create a bitmask of length \a len starting at bit \a shift.
#define BF_MASK(shift, len) ( BIT_MASK(len)<<(shift) )
//! Retrieve a bitfield mask of length \a starting at bit \a shift from \a y.
#define _BF_GET(y, shift, len) ( ((y)>>(shift))&BIT_MASK(len) )
//! Prepare a bitmask for insertion or combining.
#define _BF_PREP(x, shift, len) ( ((x)&BIT_MASK(len))<<(shift) )
//! Insert a new bitfield value \a x into \a y.
#define _BF_SET(y, x, shift, len) \
( y= ((y) &~ BF_MASK(shift, len)) | _BF_PREP(x, shift, len) )
//\}
/*! \name some EVIL bit-field operations, >:)
* These allow you to mimic bitfields with macros. Most of the
* bitfields in the registers have <i>foo</i>_SHIFT and
* <i>foo</i>_SHIFT macros indicating the mask and shift values
* of the bitfield named <i>foo</i> in a variable.
* These macros let you prepare, get and set the bitfields.
*/
//\{
//! Prepare a named bit-field for for insterion or combination.
#define BFN_PREP(x, name) ( ((x)<<name##_SHIFT) & name##_MASK )
//! Get the value of a named bitfield from \a y. Equivalent to (var=) y.name
#define BFN_GET(y, name) ( ((y) & name##_MASK)>>name##_SHIFT )
//! Set a named bitfield in \a y to \a x. Equivalent to y.name= x.
#define BFN_SET(y, x, name) (y = ((y)&~name##_MASK) | BFN_PREP(x,name) )
//! Compare a named bitfield to named literal \a x.
#define BFN_CMP(y, x, name) ( ((y)&name##_MASK) == (x) )
//! Massage \a x for use in bitfield \a name with pre-shifted \a x
#define BFN_PREP2(x, name) ( (x) & name##_MASK )
//! Get the value of bitfield \a name from \a y, but don't down-shift
#define BFN_GET2(y, name) ( (y) & name##_MASK )
//! Set bitfield \a name from \a y to \a x with pre-shifted \a x
#define BFN_SET2(y,x,name) ( y = ((y)&~name##_MASK) | BFN_PREP2(x,name) )
//\}
INLINE u32 bf_get(u32 y, uint shift, uint len);
INLINE u32 bf_merge(u32 y, u32 x, uint shift, uint len);
INLINE u32 bf_clamp(int x, uint len);
INLINE int bit_tribool(u32 x, uint plus, uint minus);
INLINE u32 ROR(u32 x, uint ror);
/*! \} */
// --------------------------------------------------------------------
// DATA
// --------------------------------------------------------------------
/*! \defgroup grpData Data routines
\ingroup grpCore
*/
/*! \{ */
//! Get the number of elements in an array
#define countof(_array) ( sizeof(_array)/sizeof(_array[0]) )
//! Align \a x to the next multiple of \a width.
INLINE uint align(uint x, uint width);
//! \name Copying and filling routines
//\{
//! Simplified copier for GRIT-exported data.
#define GRIT_CPY(dst, name) memcpy16(dst, name, name##Len/2)
// Base memcpy/set replacements.
void *tonccpy(void *dst, const void *src, uint size);
void *__toncset(void *dst, u32 fill, uint size);
INLINE void *toncset(void *dst, u8 src, uint count);
INLINE void *toncset16(void *dst, u16 src, uint count);
INLINE void *toncset32(void *dst, u32 src, uint count);
// Fast memcpy/set
void memset16(void *dst, u16 hw, uint hwcount);
void memcpy16(void *dst, const void* src, uint hwcount);
IWRAM_CODE void memset32(void *dst, u32 wd, uint wcount);
IWRAM_CODE void memcpy32(void *dst, const void* src, uint wcount);
//! Fastfill for halfwords, analogous to memset()
/*! Uses <code>memset32()</code> if \a hwcount>5
* \param dst Destination address.
* \param hw Source halfword (not address).
* \param hwcount Number of halfwords to fill.
* \note \a dst <b>must</b> be halfword aligned.
* \note \a r0 returns as \a dst + \a hwcount*2.
*/
void memset16(void *dst, u16 hw, uint hwcount);
//! \brief Copy for halfwords.
/*! Uses <code>memcpy32()</code> if \a hwn>6 and
\a src and \a dst are aligned equally.
\param dst Destination address.
\param src Source address.
\param hwcount Number of halfwords to fill.
\note \a dst and \a src <b>must</b> be halfword aligned.
\note \a r0 and \a r1 return as
\a dst + \a hwcount*2 and \a src + \a hwcount*2.
*/
void memcpy16(void *dst, const void* src, uint hwcount);
//! Fast-fill by words, analogous to memset()
/*! Like CpuFastSet(), only without the requirement of
32byte chunks and no awkward store-value-in-memory-first issue.
\param dst Destination address.
\param wd Fill word (not address).
\param wdcount Number of words to fill.
\note \a dst <b>must</b> be word aligned.
\note \a r0 returns as \a dst + \a wdcount*4.
*/
IWRAM_CODE void memset32(void *dst, u32 wd, uint wdcount);
//! \brief Fast-copy by words.
/*! Like CpuFastFill(), only without the requirement of 32byte chunks
\param dst Destination address.
\param src Source address.
\param wdcount Number of words.
\note \a src and \a dst <b>must</b> be word aligned.
\note \a r0 and \a r1 return as
\a dst + \a wdcount*4 and \a src + \a wdcount*4.
*/
IWRAM_CODE void memcpy32(void *dst, const void* src, uint wdcount);
//\}
/*! \name Repeated-value creators
These function take a hex-value and duplicate it to all fields,
like 0x88 -> 0x88888888.
*/
//\{
INLINE u16 dup8(u8 x);
INLINE u32 dup16(u16 x);
INLINE u32 quad8(u8 x);
INLINE u32 octup(u8 x);
//\}
//! \name Packing routines.
//\{
INLINE u16 bytes2hword(u8 b0, u8 b1);
INLINE u32 bytes2word(u8 b0, u8 b1, u8 b2, u8 b3);
INLINE u32 hword2word(u16 h0, u16 h1);
//\}
/*! \} */
// --------------------------------------------------------------------
// DMA
// --------------------------------------------------------------------
/*! \addtogroup grpDma */
/*! \{ */
//! General purpose DMA transfer macro
/*! \param _dst Destination address.
\param _src Source address.
\param count Number of transfers.
\param ch DMA channel.
\param mode DMA mode.
*/
#define DMA_TRANSFER(_dst, _src, count, ch, mode) \
do { \
REG_DMA[ch].cnt= 0; \
REG_DMA[ch].src= (const void*)(_src); \
REG_DMA[ch].dst= (void*)(_dst); \
REG_DMA[ch].cnt= (count) | (mode); \
} while(0)
INLINE void dma_cpy(void *dst, const void *src, uint count, uint ch, u32 mode);
INLINE void dma_fill(void *dst, volatile u32 src, uint count, uint ch, u32 mode);
INLINE void dma3_cpy(void *dst, const void *src, uint size);
INLINE void dma3_fill(void *dst, volatile u32 src, uint size);
/*! \} */
// --------------------------------------------------------------------
// TIMER
// --------------------------------------------------------------------
INLINE void profile_start(void);
INLINE uint profile_stop(void);
// --------------------------------------------------------------------
// TONE GENERATOR
// --------------------------------------------------------------------
typedef enum
{
NOTE_C=0, NOTE_CIS, NOTE_D, NOTE_DIS,
NOTE_E, NOTE_F, NOTE_FIS, NOTE_G,
NOTE_GIS, NOTE_A, NOTE_BES, NOTE_B
} eSndNoteId;
extern const uint __snd_rates[12];
//! Gives the period of a note for the tone-gen registers.
/*! GBA sound range: 8 octaves: [-2, 5]; 8*12= 96 notes (kinda).
* \param note ID (range: [0,11>). See eSndNoteId.
* \param oct octave (range [-2,4)>).
*/
#define SND_RATE(note, oct) ( 2048-(__snd_rates[note]>>(4+(oct))) )
// --------------------------------------------------------------------
// MISC
// --------------------------------------------------------------------
/*! \defgroup grpCoreMisc Miscellaneous routines
* \ingroup grpCore
*/
/*! \{ */
#define STR(x) #x
//! Create text string from a literal
#define XSTR(x) STR(x)
//! \name Inline assembly
//\{
//! Assembly comment
#define ASM_CMT(str) asm volatile("@# " str)
//! No$gba breakpoint
#define ASM_BREAK() asm volatile("\tmov\t\tr11, r11")
//! No-op; wait a bit.
#define ASM_NOP() asm volatile("\tnop")
//\}
//! \name Sector checking
//\{
u32 octant(int x, int y);
u32 octant_rot(int x0, int y0);
//\}
//! \name Random numbers
//\{
#define QRAN_SHIFT 15
#define QRAN_MASK ((1<<QRAN_SHIFT)-1)
#define QRAN_MAX QRAN_MASK
int sqran(int seed);
INLINE int qran(void);
INLINE int qran_range(int min, int max);
//\}
/*! \} */
// --------------------------------------------------------------------
// GLOBALS
// --------------------------------------------------------------------
extern const u8 oam_sizes[3][4][2];
extern const BG_AFFINE bg_aff_default;
extern COLOR *vid_page;
extern int __qran_seed;
// --------------------------------------------------------------------
// INLINES
// --------------------------------------------------------------------
// --- Bit and bitfields -----------------------------------------------
//! Get \a len long bitfield from \a y, starting at \a shift.
/*! \param y Value containing bitfield.
\param shift Bitfield Start;
\param len Length of bitfield.
\return Bitfield between bits \a shift and \a shift + \a length.
*/
INLINE u32 bf_get(u32 y, uint shift, uint len)
{ return (y>>shift) & ( (1<<len)-1 ); }
//! Merge \a x into an \a len long bitfield from \a y, starting at \a shift.
/*! \param y Value containing bitfield.
\param x Value to merge (will be masked to fit).
\param shift Bitfield Start;
\param len Length of bitfield.
\return Result of merger: (y&~M) | (x<<s & M)
\note Does \e not write the result back into \a y (Because pure C
does't have references, that's why)
*/
INLINE u32 bf_merge(u32 y, u32 x, uint shift, uint len)
{
u32 mask= ((u32)(1<<len)-1);
return (y &~ (mask<<shift)) | (x & mask)<<shift;
}
//! Clamp \a to within the range allowed by \a len bits
INLINE u32 bf_clamp(int x, uint len)
{
u32 y=x>>len;
if(y)
x= (~y)>>(32-len);
return x;
}
//! Gives a tribool (-1, 0, or +1) depending on the state of some bits.
/*! Looks at the \a plus and \a minus bits of \a flags, and subtracts
their status to give a +1, -1 or 0 result. Useful for direction flags.
\param flags Value with bit-flags.
\param plus Bit number for positive result.
\param minus Bit number for negative result.
\return <b>+1</b> if \a plus bit is set but \a minus bit isn't<br>
<b>-1</b> if \a minus bit is set and \a plus bit isn't<br>
<b>0</b> if neither or both are set.
*/
INLINE int bit_tribool(u32 flags, uint plus, uint minus)
{ return ((flags>>plus)&1) - ((flags>>minus)&1); }
//! Rotate bits right. Yes, this does lead to a ror instruction.
INLINE u32 ROR(u32 x, uint ror)
{ return (x<<(32-ror)) | (x>>ror); }
// --- Data -----------------------------------------------------------
INLINE uint align(uint x, uint width)
{ return (x+width-1)/width*width; }
//! VRAM-safe memset, byte version. Size in bytes.
INLINE void *toncset(void *dst, u8 src, uint count)
{ return __toncset(dst, quad8(src), count); }
//! VRAM-safe memset, halfword version. Size in hwords.
INLINE void *toncset16(void *dst, u16 src, uint count)
{ return __toncset(dst, src|src<<16, count*2); }
//! VRAM-safe memset, word version. Size in words.
INLINE void *toncset32(void *dst, u32 src, uint count)
{ return __toncset(dst, src, count*4); }
//! Duplicate a byte to form a halfword: 0x12 -> 0x1212.
INLINE u16 dup8(u8 x) { return x|(x<<8); }
//! Duplicate a halfword to form a word: 0x1234 -> 0x12341234.
INLINE u32 dup16(u16 x) { return x|(x<<16); }
//! Quadruple a byte to form a word: 0x12 -> 0x12121212.
INLINE u32 quad8(u8 x) { return x*0x01010101; }
//! Octuple a nybble to form a word: 0x1 -> 0x11111111
INLINE u32 octup(u8 x) { return x*0x11111111; }
//! Pack 2 bytes into a word. Little-endian order.
INLINE u16 bytes2hword(u8 b0, u8 b1)
{ return b0 | b1<<8; }
//! Pack 4 bytes into a word. Little-endian order.
INLINE u32 bytes2word(u8 b0, u8 b1, u8 b2, u8 b3)
{ return b0 | b1<<8 | b2<<16 | b3<<24; }
INLINE u32 hword2word(u16 h0, u16 h1)
{ return h0 | h1<<16; }
// --- DMA ------------------------------------------------------------
/*! \addtogroup grpDma */
/*! \{ */
//! Generic DMA copy routine.
/*! \param dst Destination address.
* \param src Source address.
* \param count Number of copies to perform.
* \param ch DMA channel.
* \param mode DMA transfer mode.
* \note \a count is the number of copies, not the size in bytes.
*/
INLINE void dma_cpy(void *dst, const void *src, uint count, uint ch, u32 mode)
{
REG_DMA[ch].cnt= 0;
REG_DMA[ch].src= src;
REG_DMA[ch].dst= dst;
REG_DMA[ch].cnt= mode | count;
}
//! Generic DMA fill routine.
/*! \param dst Destination address.
* \param src Source value.
* \param count Number of copies to perform.
* \param ch DMA channel.
* \param mode DMA transfer mode.
* \note \a count is the number of copies, not the size in bytes.
*/
INLINE void dma_fill(void *dst, volatile u32 src, uint count, uint ch, u32 mode)
{
REG_DMA[ch].cnt= 0;
REG_DMA[ch].src= (const void*)&src;
REG_DMA[ch].dst= dst;
REG_DMA[ch].cnt= count | mode | DMA_SRC_FIXED;
}
//! Specific DMA copier, using channel 3, word transfers.
/*! \param dst Destination address.
* \param src Source address.
* \param size Number of bytes to copy
* \note \a size is the number of bytes
*/
INLINE void dma3_cpy(void *dst, const void *src, uint size)
{ dma_cpy(dst, src, size/4, 3, DMA_CPY32); }
//! Specific DMA filler, using channel 3, word transfers.
/*! \param dst Destination address.
* \param src Source value.
* \param size Number of bytes to copy
* \note \a size is the number of bytes
*/
INLINE void dma3_fill(void *dst, volatile u32 src, uint size)
{ dma_fill(dst, src, size/4, 3, DMA_FILL32); }
/*! \} */
// --- Random ---------------------------------------------------------
//! Quick (and very dirty) pseudo-random number generator
/*! \return random in range [0,8000h>
*/
INLINE int qran(void)
{
__qran_seed= 1664525*__qran_seed+1013904223;
return (__qran_seed>>16) & QRAN_MAX;
}
//! Ranged random number
/*! \return random in range [\a min, \a max>
* \note (max-min) must be lower than 8000h
*/
INLINE int qran_range(int min, int max)
{ return (qran()*(max-min)>>QRAN_SHIFT)+min; }
// --- Timer ----------------------------------------------------------
/*! \addtogroup grpTimer */
/*! \{ */
//! Start a profiling run
/*! \note Routine uses timers 3 and 3; if you're already using these
* somewhere, chaos is going to ensue.
*/
INLINE void profile_start(void)
{
REG_TM2D= 0; REG_TM3D= 0;
REG_TM2CNT= 0; REG_TM3CNT= 0;
REG_TM3CNT= TM_ENABLE | TM_CASCADE;
REG_TM2CNT= TM_ENABLE;
}
//! Stop a profiling run and return the time since its start.
/*! \return 32bit cycle count
*/
INLINE uint profile_stop(void)
{
REG_TM2CNT= 0;
return (REG_TM3D<<16)|REG_TM2D;
}
/*! \} /addtogroup */
#endif // TONC_CORE

View File

@ -0,0 +1,573 @@
//
// Core functionality
//
//! \file tonc_core.h
//! \author J Vijn
//! \date 20060508 - 20080128
//
/* === NOTES ===
* Contents: bits, random, dma, timer
* 20080129,jv: added tonccpy/set routines.
*/
#ifndef TONC_CORE_STUB
#define TONC_CORE_STUB
#include "tonc_memmap.h"
#include "tonc_memdef.h"
// --------------------------------------------------------------------
// BITS and BITFIELDS
// --------------------------------------------------------------------
/*! \defgroup grpCoreBit Bit(field) macros
\ingroup grpCore
*/
/*! \{ */
//! \name Simple bit macros
//\{
//! Create value with bit \a n set
#define BIT(n) ( 1<<(n) )
//! Shift \a a by \a n
#define BIT_SHIFT(a, n) ( (a)<<(n) )
//! Create a bitmask \a len bits long
#define BIT_MASK(len) ( BIT(len)-1 )
//! Set the \a flag bits in \a word
#define BIT_SET(y, flag) ( y |= (flag) )
//! Clear the \a flag bits in \a word
#define BIT_CLEAR(y, flag) ( y &= ~(flag) )
//! Flip the \a flag bits in \a word
#define BIT_FLIP(y, flag) ( y ^= (flag) )
//! Test whether all the \a flag bits in \a word are set
#define BIT_EQ(y, flag) ( ((y)&(flag)) == (flag) )
//! Create a bitmask of length \a len starting at bit \a shift.
#define BF_MASK(shift, len) ( BIT_MASK(len)<<(shift) )
//! Retrieve a bitfield mask of length \a starting at bit \a shift from \a y.
#define _BF_GET(y, shift, len) ( ((y)>>(shift))&BIT_MASK(len) )
//! Prepare a bitmask for insertion or combining.
#define _BF_PREP(x, shift, len) ( ((x)&BIT_MASK(len))<<(shift) )
//! Insert a new bitfield value \a x into \a y.
#define _BF_SET(y, x, shift, len) \
( y= ((y) &~ BF_MASK(shift, len)) | _BF_PREP(x, shift, len) )
//\}
/*! \name some EVIL bit-field operations, >:)
* These allow you to mimic bitfields with macros. Most of the
* bitfields in the registers have <i>foo</i>_SHIFT and
* <i>foo</i>_SHIFT macros indicating the mask and shift values
* of the bitfield named <i>foo</i> in a variable.
* These macros let you prepare, get and set the bitfields.
*/
//\{
//! Prepare a named bit-field for for insterion or combination.
#define BFN_PREP(x, name) ( ((x)<<name##_SHIFT) & name##_MASK )
//! Get the value of a named bitfield from \a y. Equivalent to (var=) y.name
#define BFN_GET(y, name) ( ((y) & name##_MASK)>>name##_SHIFT )
//! Set a named bitfield in \a y to \a x. Equivalent to y.name= x.
#define BFN_SET(y, x, name) (y = ((y)&~name##_MASK) | BFN_PREP(x,name) )
//! Compare a named bitfield to named literal \a x.
#define BFN_CMP(y, x, name) ( ((y)&name##_MASK) == (x) )
//! Massage \a x for use in bitfield \a name with pre-shifted \a x
#define BFN_PREP2(x, name) ( (x) & name##_MASK )
//! Get the value of bitfield \a name from \a y, but don't down-shift
#define BFN_GET2(y, name) ( (y) & name##_MASK )
//! Set bitfield \a name from \a y to \a x with pre-shifted \a x
#define BFN_SET2(y,x,name) ( y = ((y)&~name##_MASK) | BFN_PREP2(x,name) )
//\}
INLINE u32 bf_get(u32 y, uint shift, uint len);
INLINE u32 bf_merge(u32 y, u32 x, uint shift, uint len);
INLINE u32 bf_clamp(int x, uint len);
INLINE int bit_tribool(u32 x, uint plus, uint minus);
INLINE u32 ROR(u32 x, uint ror);
/*! \} */
// --------------------------------------------------------------------
// DATA
// --------------------------------------------------------------------
/*! \defgroup grpData Data routines
\ingroup grpCore
*/
/*! \{ */
//! Get the number of elements in an array
#define countof(_array) ( sizeof(_array)/sizeof(_array[0]) )
//! Align \a x to the next multiple of \a width.
INLINE uint align(uint x, uint width);
//! \name Copying and filling routines
//\{
//! Simplified copier for GRIT-exported data.
#define GRIT_CPY(dst, name) memcpy16(dst, name, name##Len/2)
// Base memcpy/set replacements.
void *tonccpy(void *dst, const void *src, uint size);
void *__toncset(void *dst, u32 fill, uint size);
INLINE void *toncset(void *dst, u8 src, uint count);
INLINE void *toncset16(void *dst, u16 src, uint count);
INLINE void *toncset32(void *dst, u32 src, uint count);
// Fast memcpy/set
void memset16(void *dst, u16 hw, uint hwcount);
void memcpy16(void *dst, const void* src, uint hwcount);
void memset32(void *dst, u32 wd, uint wcount);
void memcpy32(void *dst, const void* src, uint wcount);
//! Fastfill for halfwords, analogous to memset()
/*! Uses <code>memset32()</code> if \a hwcount>5
* \param dst Destination address.
* \param hw Source halfword (not address).
* \param hwcount Number of halfwords to fill.
* \note \a dst <b>must</b> be halfword aligned.
* \note \a r0 returns as \a dst + \a hwcount*2.
*/
void memset16(void *dst, u16 hw, uint hwcount);
//! \brief Copy for halfwords.
/*! Uses <code>memcpy32()</code> if \a hwn>6 and
\a src and \a dst are aligned equally.
\param dst Destination address.
\param src Source address.
\param hwcount Number of halfwords to fill.
\note \a dst and \a src <b>must</b> be halfword aligned.
\note \a r0 and \a r1 return as
\a dst + \a hwcount*2 and \a src + \a hwcount*2.
*/
void memcpy16(void *dst, const void* src, uint hwcount);
//! Fast-fill by words, analogous to memset()
/*! Like CpuFastSet(), only without the requirement of
32byte chunks and no awkward store-value-in-memory-first issue.
\param dst Destination address.
\param wd Fill word (not address).
\param wdcount Number of words to fill.
\note \a dst <b>must</b> be word aligned.
\note \a r0 returns as \a dst + \a wdcount*4.
*/
void memset32(void *dst, u32 wd, uint wdcount);
//! \brief Fast-copy by words.
/*! Like CpuFastFill(), only without the requirement of 32byte chunks
\param dst Destination address.
\param src Source address.
\param wdcount Number of words.
\note \a src and \a dst <b>must</b> be word aligned.
\note \a r0 and \a r1 return as
\a dst + \a wdcount*4 and \a src + \a wdcount*4.
*/
void memcpy32(void *dst, const void* src, uint wdcount);
//\}
/*! \name Repeated-value creators
These function take a hex-value and duplicate it to all fields,
like 0x88 -> 0x88888888.
*/
//\{
INLINE u16 dup8(u8 x);
INLINE u32 dup16(u16 x);
INLINE u32 quad8(u8 x);
INLINE u32 octup(u8 x);
//\}
//! \name Packing routines.
//\{
INLINE u16 bytes2hword(u8 b0, u8 b1);
INLINE u32 bytes2word(u8 b0, u8 b1, u8 b2, u8 b3);
INLINE u32 hword2word(u16 h0, u16 h1);
//\}
/*! \} */
// --------------------------------------------------------------------
// DMA
// --------------------------------------------------------------------
/*! \addtogroup grpDma */
/*! \{ */
//! General purpose DMA transfer macro
/*! \param _dst Destination address.
\param _src Source address.
\param count Number of transfers.
\param ch DMA channel.
\param mode DMA mode.
*/
#define DMA_TRANSFER(_dst, _src, count, ch, mode) \
do { \
REG_DMA[ch].cnt= 0; \
REG_DMA[ch].src= (const void*)(_src); \
REG_DMA[ch].dst= (void*)(_dst); \
REG_DMA[ch].cnt= (count) | (mode); \
} while(0)
INLINE void dma_cpy(void *dst, const void *src, uint count, uint ch, u32 mode);
INLINE void dma_fill(void *dst, volatile u32 src, uint count, uint ch, u32 mode);
INLINE void dma3_cpy(void *dst, const void *src, uint size);
INLINE void dma3_fill(void *dst, volatile u32 src, uint size);
/*! \} */
// --------------------------------------------------------------------
// TIMER
// --------------------------------------------------------------------
INLINE void profile_start(void);
INLINE uint profile_stop(void);
// --------------------------------------------------------------------
// TONE GENERATOR
// --------------------------------------------------------------------
typedef enum
{
NOTE_C=0, NOTE_CIS, NOTE_D, NOTE_DIS,
NOTE_E, NOTE_F, NOTE_FIS, NOTE_G,
NOTE_GIS, NOTE_A, NOTE_BES, NOTE_B
} eSndNoteId;
extern const uint __snd_rates[12];
//! Gives the period of a note for the tone-gen registers.
/*! GBA sound range: 8 octaves: [-2, 5]; 8*12= 96 notes (kinda).
* \param note ID (range: [0,11>). See eSndNoteId.
* \param oct octave (range [-2,4)>).
*/
#define SND_RATE(note, oct) ( 2048-(__snd_rates[note]>>(4+(oct))) )
// --------------------------------------------------------------------
// MISC
// --------------------------------------------------------------------
/*! \defgroup grpCoreMisc Miscellaneous routines
* \ingroup grpCore
*/
/*! \{ */
#define STR(x) #x
//! Create text string from a literal
#define XSTR(x) STR(x)
//! \name Inline assembly
//\{
//! Assembly comment
#define ASM_CMT(str) asm volatile("@# " str)
//! No$gba breakpoint
#define ASM_BREAK() asm volatile("\tmov\t\tr11, r11")
//! No-op; wait a bit.
#define ASM_NOP() asm volatile("\tnop")
//\}
//! \name Sector checking
//\{
u32 octant(int x, int y);
u32 octant_rot(int x0, int y0);
//\}
//! \name Random numbers
//\{
#define QRAN_SHIFT 15
#define QRAN_MASK ((1<<QRAN_SHIFT)-1)
#define QRAN_MAX QRAN_MASK
int sqran(int seed);
INLINE int qran(void);
INLINE int qran_range(int min, int max);
//\}
/*! \} */
// --------------------------------------------------------------------
// GLOBALS
// --------------------------------------------------------------------
extern const u8 oam_sizes[3][4][2];
extern const BG_AFFINE bg_aff_default;
extern COLOR *vid_page;
extern int __qran_seed;
// --------------------------------------------------------------------
// INLINES
// --------------------------------------------------------------------
// --- Bit and bitfields -----------------------------------------------
//! Get \a len long bitfield from \a y, starting at \a shift.
/*! \param y Value containing bitfield.
\param shift Bitfield Start;
\param len Length of bitfield.
\return Bitfield between bits \a shift and \a shift + \a length.
*/
INLINE u32 bf_get(u32 y, uint shift, uint len)
{ return (y>>shift) & ( (1<<len)-1 ); }
//! Merge \a x into an \a len long bitfield from \a y, starting at \a shift.
/*! \param y Value containing bitfield.
\param x Value to merge (will be masked to fit).
\param shift Bitfield Start;
\param len Length of bitfield.
\return Result of merger: (y&~M) | (x<<s & M)
\note Does \e not write the result back into \a y (Because pure C
does't have references, that's why)
*/
INLINE u32 bf_merge(u32 y, u32 x, uint shift, uint len)
{
u32 mask= ((u32)(1<<len)-1);
return (y &~ (mask<<shift)) | (x & mask)<<shift;
}
//! Clamp \a to within the range allowed by \a len bits
INLINE u32 bf_clamp(int x, uint len)
{
u32 y=x>>len;
if(y)
x= (~y)>>(32-len);
return x;
}
//! Gives a tribool (-1, 0, or +1) depending on the state of some bits.
/*! Looks at the \a plus and \a minus bits of \a flags, and subtracts
their status to give a +1, -1 or 0 result. Useful for direction flags.
\param flags Value with bit-flags.
\param plus Bit number for positive result.
\param minus Bit number for negative result.
\return <b>+1</b> if \a plus bit is set but \a minus bit isn't<br>
<b>-1</b> if \a minus bit is set and \a plus bit isn't<br>
<b>0</b> if neither or both are set.
*/
INLINE int bit_tribool(u32 flags, uint plus, uint minus)
{ return ((flags>>plus)&1) - ((flags>>minus)&1); }
//! Rotate bits right. Yes, this does lead to a ror instruction.
INLINE u32 ROR(u32 x, uint ror)
{ return (x<<(32-ror)) | (x>>ror); }
// --- Data -----------------------------------------------------------
INLINE uint align(uint x, uint width)
{ return (x+width-1)/width*width; }
//! VRAM-safe memset, byte version. Size in bytes.
INLINE void *toncset(void *dst, u8 src, uint count)
{ return __toncset(dst, quad8(src), count); }
//! VRAM-safe memset, halfword version. Size in hwords.
INLINE void *toncset16(void *dst, u16 src, uint count)
{ return __toncset(dst, src|src<<16, count*2); }
//! VRAM-safe memset, word version. Size in words.
INLINE void *toncset32(void *dst, u32 src, uint count)
{ return __toncset(dst, src, count*4); }
//! Duplicate a byte to form a halfword: 0x12 -> 0x1212.
INLINE u16 dup8(u8 x) { return x|(x<<8); }
//! Duplicate a halfword to form a word: 0x1234 -> 0x12341234.
INLINE u32 dup16(u16 x) { return x|(x<<16); }
//! Quadruple a byte to form a word: 0x12 -> 0x12121212.
INLINE u32 quad8(u8 x) { return x*0x01010101; }
//! Octuple a nybble to form a word: 0x1 -> 0x11111111
INLINE u32 octup(u8 x) { return x*0x11111111; }
//! Pack 2 bytes into a word. Little-endian order.
INLINE u16 bytes2hword(u8 b0, u8 b1)
{ return b0 | b1<<8; }
//! Pack 4 bytes into a word. Little-endian order.
INLINE u32 bytes2word(u8 b0, u8 b1, u8 b2, u8 b3)
{ return b0 | b1<<8 | b2<<16 | b3<<24; }
INLINE u32 hword2word(u16 h0, u16 h1)
{ return h0 | h1<<16; }
// --- DMA ------------------------------------------------------------
/*! \addtogroup grpDma */
/*! \{ */
//! Generic DMA copy routine.
/*! \param dst Destination address.
* \param src Source address.
* \param count Number of copies to perform.
* \param ch DMA channel.
* \param mode DMA transfer mode.
* \note \a count is the number of copies, not the size in bytes.
*/
INLINE void dma_cpy(void *dst, const void *src, uint count, uint ch, u32 mode)
{
REG_DMA[ch].cnt= 0;
REG_DMA[ch].src= src;
REG_DMA[ch].dst= dst;
REG_DMA[ch].cnt= mode | count;
}
//! Generic DMA fill routine.
/*! \param dst Destination address.
* \param src Source value.
* \param count Number of copies to perform.
* \param ch DMA channel.
* \param mode DMA transfer mode.
* \note \a count is the number of copies, not the size in bytes.
*/
INLINE void dma_fill(void *dst, volatile u32 src, uint count, uint ch, u32 mode)
{
REG_DMA[ch].cnt= 0;
REG_DMA[ch].src= (const void*)&src;
REG_DMA[ch].dst= dst;
REG_DMA[ch].cnt= count | mode | DMA_SRC_FIXED;
}
//! Specific DMA copier, using channel 3, word transfers.
/*! \param dst Destination address.
* \param src Source address.
* \param size Number of bytes to copy
* \note \a size is the number of bytes
*/
INLINE void dma3_cpy(void *dst, const void *src, uint size)
{ dma_cpy(dst, src, size/4, 3, DMA_CPY32); }
//! Specific DMA filler, using channel 3, word transfers.
/*! \param dst Destination address.
* \param src Source value.
* \param size Number of bytes to copy
* \note \a size is the number of bytes
*/
INLINE void dma3_fill(void *dst, volatile u32 src, uint size)
{ dma_fill(dst, src, size/4, 3, DMA_FILL32); }
/*! \} */
// --- Random ---------------------------------------------------------
//! Quick (and very dirty) pseudo-random number generator
/*! \return random in range [0,8000h>
*/
INLINE int qran(void)
{
__qran_seed= 1664525*__qran_seed+1013904223;
return (__qran_seed>>16) & QRAN_MAX;
}
//! Ranged random number
/*! \return random in range [\a min, \a max>
* \note (max-min) must be lower than 8000h
*/
INLINE int qran_range(int min, int max)
{ return (qran()*(max-min)>>QRAN_SHIFT)+min; }
// --- Timer ----------------------------------------------------------
/*! \addtogroup grpTimer */
/*! \{ */
//! Start a profiling run
/*! \note Routine uses timers 3 and 3; if you're already using these
* somewhere, chaos is going to ensue.
*/
INLINE void profile_start(void)
{
REG_TM2D= 0; REG_TM3D= 0;
REG_TM2CNT= 0; REG_TM3CNT= 0;
REG_TM3CNT= TM_ENABLE | TM_CASCADE;
REG_TM2CNT= TM_ENABLE;
}
//! Stop a profiling run and return the time since its start.
/*! \return 32bit cycle count
*/
INLINE uint profile_stop(void)
{
REG_TM2CNT= 0;
return (REG_TM3D<<16)|REG_TM2D;
}
/*! \} /addtogroup */
#endif // TONC_CORE

View File

@ -0,0 +1,691 @@
//
// Mathematical functions
//
//! \file tonc_math.h
//! \author J Vijn
//! \date 20060508 - 20060908
//
// === NOTES ===
#ifndef TONC_MATH
#define TONC_MATH
#include "tonc_types.h"
// --- Doxygen modules ---
/*! \defgroup grpMathBase Base math
* \brief Basic math macros and functions like MIN, MAX
* \ingroup grpMath
*/
/*! \defgroup grpMathFixed Fixed point math
* \ingroup grpMath
*/
/*! \defgroup grpMathLut Look-up tables
* \brief Tonc's internal look-up tables and related routines.
* \ingroup grpMath
*/
/*! \defgroup grpMathPoint Point functions
* \ingroup grpMath
*/
/*! \defgroup grpMathVector Vector functions
* \ingroup grpMath
*/
/*! \defgroup grpMathRect Rect functions
* \ingroup grpMath
*/
// --------------------------------------------------------------------
// GENERAL
// --------------------------------------------------------------------
/*! \addtogroup grpMathBase */
/*! \{ */
// Also available as inline functions
//! \name core math macros
//\{
#ifndef ABS
//! Get the absolute value of \a x
#define ABS(x) ( (x)>=0 ? (x) : -(x) )
#endif // ABS
#ifndef SGN
//! Get the sign of \a x.
#define SGN(x) ( (x)>=0 ? 1 : -1 )
#define SGN2 SGN
#endif // SGN
#ifndef SGN3
//! Tri-state sign: -1 for negative, 0 for 0, +1 for positive.
#define SGN3(x) ( (x)>0 ? 1 : ( (x)<0 ? -1 : 0) )
#endif // SGN3
#ifndef MAX
//! Get the maximum of \a a and \a b
#define MAX(a, b) ( ((a) > (b)) ? (a) : (b) )
//! Get the minimum of \a a and \a b
#define MIN(a, b) ( ((a) < (b)) ? (a) : (b) )
#endif // MAX
#ifndef SWAP
//! In-place swap.
#define SWAP2(a, b) do { a=(a)-(b); b=(a)+(b); a=(b)-(a); } while(0)
#define SWAP SWAP2
//Alternative:
//#define SWAP2(a, b) ( (b) ^= ((a) ^= ((b) ^= (a))) )
//! Swaps \a a and \a b, using \a tmp as a temporary
#define SWAP3(a, b, tmp) do { (tmp)=(a); (a)=(b); (b)=(tmp); } while(0)
#endif // SWAP
INLINE int sgn(int x);
INLINE int sgn3(int x);
INLINE int max(int a, int b);
INLINE int min(int a, int b);
//\}
//! \name Boundary response macros
//\{
//! Range check
#define IN_RANGE(x, min, max) ( ((x)>=(min)) && ((x)<(max)) )
//! Truncates \a x to stay in range [\a min, \a max>
/*! \return Truncated value of \a x.
* \note \a max is exclusive!
*/
#define CLAMP(x, min, max) \
( (x)>=(max) ? ((max)-1) : ( ((x)<(min)) ? (min) : (x) ) )
//! Reflects \a x at boundaries \a min and \a max
/*! If \a x is outside the range [\a min, \a max>,
* it'll be placed inside again with the same distance
* to the 'wall', but on the other side. Example for lower
* border: y = \a min - (\a x- \a min) = 2*\a min + \a x.
* \return Reflected value of \a x.
* \note \a max is exclusive!
*/
#define REFLECT(x, min, max) \
( (x)>=(max) ? 2*((max)-1)-(x) : ( ((x)<(min)) ? 2*(min)-(x) : (x) ) )
//! Wraps \a x to stay in range [\a min, \a max>
#define WRAP(x, min, max) \
( (x)>=(max) ? (x)+(min)-(max) : ( ((x)<(min)) ? (x)+(max)-(min) : (x) ) )
INLINE BOOL in_range(int x, int min, int max);
INLINE int clamp(int x, int min, int max);
INLINE int reflect(int x, int min, int max);
INLINE int wrap(int x, int min, int max);
//\}
/* \} */
// --------------------------------------------------------------------
// FIXED POINT
// --------------------------------------------------------------------
/*! \addtogroup grpMathFixed */
/*! \{ */
#define FIX_SHIFT 8
#define FIX_SCALE ( 1<<FIX_SHIFT )
#define FIX_MASK ( FIX_SCALE-1 )
#define FIX_SCALEF ( (float)FIX_SCALE )
#define FIX_SCALEF_INV ( 1.0/FIX_SCALEF )
#define FIX_ONE FIX_SCALE
//! Get the fixed point reciprocal of \a a, in \a fp fractional bits.
/*!
* \param a Value to take the reciprocal of.
* \param fp Number of fixed point bits
* \note The routine does do a division, but the compiler will
* optimize it to a single constant ... \e if both \a a and \a fp
* are constants!
* \sa #FX_RECIMUL
*/
#define FX_RECIPROCAL(a, fp) ( ((1<<(fp))+(a)-1)/(a) )
//! Perform the division \a x/ \a a by reciprocal multiplication
/*! Division is slow, but you can approximate division by a constant
* by multiplying with its reciprocal: x/a vs x*(1/a). This routine
* gives the reciprocal of \a a as a fixed point number with \a fp
* fractional bits.
* \param a Value to take the reciprocal of.
* \param fp Number of fixed point bits
* \note The routine does do a division, but the compiler will
* optimize it to a single constant ... \e if both \a a and \a fp
* are constants!
* \note Rules for safe reciprocal division, using
* n = 2<sup>fp</sup> and m = (n+a-1)/a (i.e., rounding up)
* \li Maximum safe numerator \a x: x < n/(m*a-n)
* \li Minimum n for known \a x: n > x*(a-1)
*/
#define FX_RECIMUL(x, a, fp) ( ((x)*((1<<(fp))+(a)-1)/(a))>>(fp) )
INLINE FIXED int2fx(int d);
INLINE FIXED float2fx(float f);
INLINE u32 fx2uint(FIXED fx);
INLINE u32 fx2ufrac(FIXED fx);
INLINE int fx2int(FIXED fx);
INLINE float fx2float(FIXED fx);
INLINE FIXED fxadd(FIXED fa, FIXED fb);
INLINE FIXED fxsub(FIXED fa, FIXED fb);
INLINE FIXED fxmul(FIXED fa, FIXED fb);
INLINE FIXED fxdiv(FIXED fa, FIXED fb);
INLINE FIXED fxmul64(FIXED fa, FIXED fb);
INLINE FIXED fxdiv64(FIXED fa, FIXED fb);
/*! \} */
// === LUT ============================================================
/*! \addtogroup grpMathLut */
/*! \{ */
#define SIN_LUT_SIZE 514 // 512 for main lut, 2 extra for lerp
#define DIV_LUT_SIZE 257 // 256 for main lut, 1 extra for lerp
extern s32 div_lut[257]; // .16f
extern s16 sin_lut[514]; // .12f
INLINE s32 lu_sin(uint theta);
INLINE s32 lu_cos(uint theta);
INLINE uint lu_div(uint x);
INLINE int lu_lerp32(const s32 lut[], uint x, const uint shift);
INLINE int lu_lerp16(const s16 lut[], uint x, const uint shift);
/*! \} */
// === POINT ==========================================================
struct RECT;
//! \addtogroup grpMathPoint
//! \{
//! 2D Point struct
typedef struct POINT { int x, y; } POINT, POINT32;
// --- Point functions ---
INLINE POINT *pt_set(POINT *pd, int x, int y);
INLINE POINT *pt_add(POINT *pd, const POINT *pa, const POINT *pb);
INLINE POINT *pt_sub(POINT *pd, const POINT *pa, const POINT *pb);
INLINE POINT *pt_scale(POINT *pd, const POINT *pa, int c);
INLINE POINT *pt_add_eq(POINT *pd, const POINT *pb);
INLINE POINT *pt_sub_eq(POINT *pd, const POINT *pb);
INLINE POINT *pt_scale_eq(POINT *pd, int c);
INLINE int pt_cross(const POINT *pa, const POINT *pb);
INLINE int pt_dot(const POINT *pa, const POINT *pb);
int pt_in_rect(const POINT *pt, const struct RECT *rc);
//! \}
// === RECT ===========================================================
/*! \addtogroup grpMathRect */
/*! \{ */
//! Rectangle struct
typedef struct RECT
{
int left, top;
int right, bottom;
} RECT, RECT32;
INLINE RECT *rc_set(RECT *rc, int l, int t, int r, int b);
INLINE RECT *rc_set2(RECT *rc, int x, int y, int w, int h);
INLINE int rc_width(const RECT *rc);
INLINE int rc_height(const RECT *rc);
INLINE RECT *rc_set_pos(RECT *rc, int x, int y);
INLINE RECT *rc_set_size(RECT *rc, int w, int h);
INLINE RECT *rc_move(RECT *rc, int dx, int dy);
INLINE RECT *rc_inflate(RECT *rc, int dw, int dh);
INLINE RECT *rc_inflate2(RECT *rc, const RECT *dr);
RECT *rc_normalize(RECT *rc);
/*! \} */
// === VECTOR =========================================================
/*! \addtogroup grpMathVector */
/*! \{ */
//! Vector struct
typedef struct VECTOR { FIXED x, y, z; } VECTOR;
INLINE VECTOR *vec_set(VECTOR *vd, FIXED x, FIXED y, FIXED z);
INLINE VECTOR *vec_add(VECTOR *vd, const VECTOR *va, const VECTOR *vb);
INLINE VECTOR *vec_sub(VECTOR *vd, const VECTOR *va, const VECTOR *vb);
INLINE VECTOR *vec_mul(VECTOR *vd, const VECTOR *va, const VECTOR *vb);
INLINE VECTOR *vec_scale(VECTOR *vd, const VECTOR *va, FIXED c);
INLINE FIXED vec_dot(const VECTOR *va, const VECTOR *vb);
INLINE VECTOR *vec_add_eq(VECTOR *vd, const VECTOR *vb);
INLINE VECTOR *vec_sub_eq(VECTOR *vd, const VECTOR *vb);
INLINE VECTOR *vec_mul_eq(VECTOR *vd, const VECTOR *vb);
INLINE VECTOR *vec_scale_eq(VECTOR *vd, FIXED c);
VECTOR *vec_cross(VECTOR *vd, const VECTOR *va, const VECTOR *vb);
/*! \} */
// === INLINE =========================================================
// --- General --------------------------------------------------------
//! Get the sign of \a x.
INLINE int sgn(int x)
{ return (x>=0) ? +1 : -1; }
//! Tri-state sign of \a x: -1 for negative, 0 for 0, +1 for positive.
INLINE int sgn3(int x)
{ return (x>>31) - (-x>>31); }
//! Get the maximum of \a a and \a b
INLINE int max(int a, int b)
{ return (a > b) ? (a) : (b); }
//! Get the minimum of \a a and \a b
INLINE int min(int a, int b)
{ return (a < b) ? (a) : (b); }
//! Range check
INLINE BOOL in_range(int x, int min, int max)
{ return (u32)(x-min) < (u32)(max-min); }
//! Truncates \a x to stay in range [\a min, \a max>
/*! \return Truncated value of \a x.
* \note \a max is exclusive!
*/
INLINE int clamp(int x, int min, int max)
{ return (x>=max) ? (max-1) : ( (x<min) ? min : x ); }
//! Reflects \a x at boundaries \a min and \a max
/*! If \a x is outside the range [\a min, \a max>,
* it'll be placed inside again with the same distance
* to the 'wall', but on the other side. Example for lower
* border: y = \a min - (\a x- \a min) = 2*\a min + \a x.
* \return Reflected value of \a x.
* \note \a max is exclusive!
*/
INLINE int reflect(int x, int min, int max)
{ return (x>=max) ? (2*(max-1)-x) : ( (x<min) ? (2*min-x) : x ); }
//! Wraps \a x to stay in range [\a min, \a max>
INLINE int wrap(int x, int min, int max)
{ return (x>=max) ? (x+min-max) : ( (x<min) ? (x+max-min) : x ); }
// --- Fixed point ----------------------------------------------------
//! Convert an integer to fixed-point
INLINE FIXED int2fx(int d)
{ return d<<FIX_SHIFT; }
//! Convert a float to fixed-point
INLINE FIXED float2fx(float f)
{ return (FIXED)(f*FIX_SCALEF); }
//! Convert a FIXED point value to an unsigned integer (orly?).
INLINE u32 fx2uint(FIXED fx)
{ return fx>>FIX_SHIFT; }
//! Get the unsigned fractional part of a fixed point value (orly?).
INLINE u32 fx2ufrac(FIXED fx)
{ return fx&FIX_MASK; }
//! Convert a FIXED point value to an signed integer.
INLINE int fx2int(FIXED fx)
{ return fx/FIX_SCALE; }
//! Convert a fixed point value to floating point.
INLINE float fx2float(FIXED fx)
{ return fx/FIX_SCALEF; }
//! Add two fixed point values
INLINE FIXED fxadd(FIXED fa, FIXED fb)
{ return fa + fb; }
//! Subtract two fixed point values
INLINE FIXED fxsub(FIXED fa, FIXED fb)
{ return fa - fb; }
//! Multiply two fixed point values
INLINE FIXED fxmul(FIXED fa, FIXED fb)
{ return (fa*fb)>>FIX_SHIFT; }
//! Divide two fixed point values.
INLINE FIXED fxdiv(FIXED fa, FIXED fb)
{ return ((fa)*FIX_SCALE)/(fb); }
//! Multiply two fixed point values using 64bit math.
INLINE FIXED fxmul64(FIXED fa, FIXED fb)
{ return (((s64)fa)*fb)>>FIX_SHIFT; }
//! Divide two fixed point values using 64bit math.
INLINE FIXED fxdiv64(FIXED fa, FIXED fb)
{ return ( ((s64)fa)<<FIX_SHIFT)/(fb); }
// --- LUT ------------------------------------------------------------
//! Look-up a sine value (2&#960; = 0x10000)
/*! \param theta Angle in [0,FFFFh] range
* \return .12f sine value
*/
INLINE s32 lu_sin(uint theta)
{ return sin_lut[(theta>>7)&0x1FF]; }
//! Look-up a cosine value (2&#960; = 0x10000)
/*! \param theta Angle in [0,FFFFh] range
* \return .12f cosine value
*/
INLINE s32 lu_cos(uint theta)
{ return sin_lut[((theta>>7)+128)&0x1FF]; }
//! Look-up a division value between 0 and 255
/*! \param x reciprocal to look up.
* \return 1/x (.16f)
*/
INLINE uint lu_div(uint x)
{ return div_lut[x]; }
//! Linear interpolator for 32bit LUTs.
/*! A lut is essentially the discrete form of a function, f(<i>x</i>).
* You can get values for non-integer \e x via (linear)
* interpolation between f(x) and f(x+1).
* \param lut The LUT to interpolate from.
* \param x Fixed point number to interpolate at.
* \param shift Number of fixed-point bits of \a x.
*/
INLINE int lu_lerp32(const s32 lut[], uint x, const uint shift)
{
int xa, ya, yb;
xa=x>>shift;
ya= lut[xa]; yb= lut[xa+1];
return ya + ( (yb-ya)*(x-(xa<<shift))>>shift );
}
//! As lu_lerp32, but for 16bit LUTs.
INLINE int lu_lerp16(const s16 lut[], uint x, const uint shift)
{
int xa, ya, yb;
xa=x>>shift;
ya= lut[xa]; yb= lut[xa+1];
return ya + ( (yb-ya)*(x-(xa<<shift))>>shift );
}
// --- Point ----------------------------------------------------------
//! Initialize \a pd to (\a x, \a y)
INLINE POINT *pt_set(POINT *pd, int x, int y)
{
pd->x= x; pd->y= y;
return pd;
}
//! Point addition: \a pd = \a pa + \a pb
INLINE POINT *pt_add(POINT *pd, const POINT *pa, const POINT *pb)
{
pd->x= pa->x + pb->x;
pd->y= pa->x + pb->y;
return pd;
}
//! Point subtraction: \a pd = \a pa - \a pb
INLINE POINT *pt_sub(POINT *pd, const POINT *pa, const POINT *pb)
{
pd->x= pa->x - pb->x;
pd->y= pa->x - pb->y;
return pd;
}
//! Point scale: \a pd = \a c * \a pa
INLINE POINT *pt_scale(POINT *pd, const POINT *pa, int c)
{
pd->x= pa->x*c;
pd->y= pa->y*c;
return pd;
}
//! Point increment: \a pd += \a pb
INLINE POINT *pt_add_eq(POINT *pd, const POINT *pb)
{
pd->x += pb->y;
pd->y += pb->y;
return pd;
}
//! Point decrement: \a pd -= \a pb
INLINE POINT *pt_sub_eq(POINT *pd, const POINT *pb)
{
pd->x -= pb->y;
pd->y -= pb->y;
return pd;
}
//! Point scale: \a pd *= \a c
INLINE POINT *pt_scale_eq(POINT *pd, int c)
{
pd->x *= c;
pd->y *= c;
return pd;
}
//! Point 'cross'-product: \a pa \htmlonly &times; \endhtmlonly \a pb
/*! Actually, there's no such thing as a 2D cross-product, but you could
* extend it to 3D and get the value of its <i>z</i>-component,
* which can be used for a test for parallelism.
*/
INLINE int pt_cross(const POINT *pa, const POINT *pb)
{ return pa->x * pb->y - pa->y * pb->x; }
//! Point 'dot'-product:\a pa \htmlonly &middot; \endhtmlonly \a pb
INLINE int pt_dot(const POINT *pa, const POINT *pb)
{ return pa->x * pb->x + pa->y * pb->y; }
// --- Rect -----------------------------------------------------------
//! Initialize a rectangle.
/*! \param l Left side.
* \param t Top side.
* \param r Right side.
* \param b Bottom side.
*/
INLINE RECT *rc_set(RECT *rc, int l, int t, int r, int b)
{
rc->left= l; rc->top= t; rc->right= r; rc->bottom= b;
return rc;
}
//! Initialize a rectangle, with sizes inside of max boundaries.
/*! \param x Left side.
* \param y Top side.
* \param w Width.
* \param h Height.
*/
INLINE RECT *rc_set2(RECT *rc, int x, int y, int w, int h)
{
rc->left= x; rc->top= y; rc->right= x+w; rc->bottom= y+h;
return rc;
}
//! Get rectangle width.
INLINE int rc_width(const RECT *rc)
{ return rc->right - rc->left; }
//! Get rectangle height
INLINE int rc_height(const RECT *rc)
{ return rc->bottom - rc->top; }
//! Move rectangle to (\a x, \a y) position.
INLINE RECT *rc_set_pos(RECT *rc, int x, int y)
{
rc->right += x-rc->left; rc->left= x;
rc->bottom += y-rc->top; rc->top= y;
return rc;
}
//! Reside rectangle.
INLINE RECT *rc_set_size(RECT *rc, int w, int h)
{
rc->right= rc->left+w; rc->bottom= rc->top+h;
return rc;
}
//! Move rectangle by (\a dx, \a dy).
INLINE RECT *rc_move(RECT *rc, int dx, int dy)
{
rc->left += dx; rc->top += dy;
rc->right += dx; rc->bottom += dy;
return rc;
}
//! Increase size by \a dw horizontally and \a dh vertically.
INLINE RECT *rc_inflate(RECT *rc, int dw, int dh)
{
rc->left -= dw; rc->top -= dh;
rc->right += dw; rc->bottom += dh;
return rc;
}
//! Increase sizes on all sides by values of rectangle \a dr.
INLINE RECT *rc_inflate2(RECT *rc, const RECT *dr)
{
rc->left += dr->left; rc->top += dr->top;
rc->right += dr->right; rc->bottom += dr->bottom;
return rc;
}
// --- Vector ---------------------------------------------------------
//! Initialize a vector
INLINE VECTOR *vec_set(VECTOR *vd, FIXED x, FIXED y, FIXED z)
{
vd->x= x; vd->y= y; vd->z= z;
return vd;
}
//! Add vectors: \b d = \b a + \b b;
INLINE VECTOR *vec_add(VECTOR *vd, const VECTOR *va, const VECTOR *vb)
{
vd->x= va->x + vb->x;
vd->y= va->y + vb->y;
vd->z= va->z + vb->z;
return vd;
}
//! Subtract vectors: \b d = \b a - \b b;
INLINE VECTOR *vec_sub(VECTOR *vd, const VECTOR *va, const VECTOR *vb)
{
vd->x= va->x - vb->x;
vd->y= va->y - vb->y;
vd->z= va->z - vb->z;
return vd;
}
//! Multiply vectors elements: \b d = \b S(ax, ay, az) <20>\b b
INLINE VECTOR *vec_mul(VECTOR *vd, const VECTOR *va, const VECTOR *vb)
{
vd->x= fxmul(va->x, vb->x);
vd->y= fxmul(va->y, vb->y);
vd->z= fxmul(va->z, vb->z);
return vd;
}
//! Scale vector: \b d = c*\b a
INLINE VECTOR *vec_scale(VECTOR *vd, const VECTOR *va, FIXED c)
{
vd->x= fxmul(va->x, c);
vd->y= fxmul(va->y, c);
vd->z= fxmul(va->z, c);
return vd;
}
//! Dot-product: d = \b a <20>\b b
INLINE FIXED vec_dot(const VECTOR *va, const VECTOR *vb)
{
FIXED dot;
dot = fxmul(va->x, vb->x);
dot += fxmul(va->y, vb->y);
dot += fxmul(va->z, vb->z);
return dot;
}
//! Increment vector: \b d += \b b;
INLINE VECTOR *vec_add_eq(VECTOR *vd, const VECTOR *vb)
{ vd->x += vb->x; vd->y += vb->y; vd->z += vb->z; return vd; }
//! Decrease vector: \b d -= \b b;
INLINE VECTOR *vec_sub_eq(VECTOR *vd, const VECTOR *vb)
{ vd->x -= vb->x; vd->y -= vb->y; vd->z -= vb->z; return vd; }
//! Multiply vectors elements: \b d = \b S(dx, dy, dz) <20>\b b
INLINE VECTOR *vec_mul_eq(VECTOR *vd, const VECTOR *vb)
{
vd->x= fxmul(vd->x, vb->x);
vd->y= fxmul(vd->y, vb->y);
vd->z= fxmul(vd->z, vb->z);
return vd;
}
//! Scale vector: \b d = c*\b d
INLINE VECTOR *vec_scale_eq(VECTOR *vd, FIXED c)
{
vd->x= fxmul(vd->x, c);
vd->y= fxmul(vd->y, c);
vd->z= fxmul(vd->z, c);
return vd;
}
#endif // TONC_MATH

View File

@ -0,0 +1,702 @@
//
// Created by Wouter Groeneveld on 14/12/18.
//
#ifndef GBA_SPRITE_ENGINE_PROJECT_TONC_MATH_STUB_H
#define GBA_SPRITE_ENGINE_PROJECT_TONC_MATH_STUB_H
//
// Mathematical functions
//
//! \file tonc_math.h
//! \author J Vijn
//! \date 20060508 - 20060908
//
// === NOTES ===
#include <cmath>
#include "tonc_types.h"
// --- Doxygen modules ---
/*! \defgroup grpMathBase Base math
* \brief Basic math macros and functions like MIN, MAX
* \ingroup grpMath
*/
/*! \defgroup grpMathFixed Fixed point math
* \ingroup grpMath
*/
/*! \defgroup grpMathLut Look-up tables
* \brief Tonc's internal look-up tables and related routines.
* \ingroup grpMath
*/
/*! \defgroup grpMathPoint Point functions
* \ingroup grpMath
*/
/*! \defgroup grpMathVector Vector functions
* \ingroup grpMath
*/
/*! \defgroup grpMathRect Rect functions
* \ingroup grpMath
*/
// --------------------------------------------------------------------
// GENERAL
// --------------------------------------------------------------------
/*! \addtogroup grpMathBase */
/*! \{ */
// Also available as inline functions
//! \name core math macros
//\{
#ifndef ABS
//! Get the absolute value of \a x
#define ABS(x) ( (x)>=0 ? (x) : -(x) )
#endif // ABS
#ifndef SGN
//! Get the sign of \a x.
#define SGN(x) ( (x)>=0 ? 1 : -1 )
#define SGN2 SGN
#endif // SGN
#ifndef SGN3
//! Tri-state sign: -1 for negative, 0 for 0, +1 for positive.
#define SGN3(x) ( (x)>0 ? 1 : ( (x)<0 ? -1 : 0) )
#endif // SGN3
#ifndef MAX
//! Get the maximum of \a a and \a b
#define MAX(a, b) ( ((a) > (b)) ? (a) : (b) )
//! Get the minimum of \a a and \a b
#define MIN(a, b) ( ((a) < (b)) ? (a) : (b) )
#endif // MAX
#ifndef SWAP
//! In-place swap.
#define SWAP2(a, b) do { a=(a)-(b); b=(a)+(b); a=(b)-(a); } while(0)
#define SWAP SWAP2
//Alternative:
//#define SWAP2(a, b) ( (b) ^= ((a) ^= ((b) ^= (a))) )
//! Swaps \a a and \a b, using \a tmp as a temporary
#define SWAP3(a, b, tmp) do { (tmp)=(a); (a)=(b); (b)=(tmp); } while(0)
#endif // SWAP
INLINE int sgn(int x);
INLINE int sgn3(int x);
INLINE int max(int a, int b);
INLINE int min(int a, int b);
//\}
//! \name Boundary response macros
//\{
//! Range check
#define IN_RANGE(x, min, max) ( ((x)>=(min)) && ((x)<(max)) )
//! Truncates \a x to stay in range [\a min, \a max>
/*! \return Truncated value of \a x.
* \note \a max is exclusive!
*/
#define CLAMP(x, min, max) \
( (x)>=(max) ? ((max)-1) : ( ((x)<(min)) ? (min) : (x) ) )
//! Reflects \a x at boundaries \a min and \a max
/*! If \a x is outside the range [\a min, \a max>,
* it'll be placed inside again with the same distance
* to the 'wall', but on the other side. Example for lower
* border: y = \a min - (\a x- \a min) = 2*\a min + \a x.
* \return Reflected value of \a x.
* \note \a max is exclusive!
*/
#define REFLECT(x, min, max) \
( (x)>=(max) ? 2*((max)-1)-(x) : ( ((x)<(min)) ? 2*(min)-(x) : (x) ) )
//! Wraps \a x to stay in range [\a min, \a max>
#define WRAP(x, min, max) \
( (x)>=(max) ? (x)+(min)-(max) : ( ((x)<(min)) ? (x)+(max)-(min) : (x) ) )
INLINE BOOL in_range(int x, int min, int max);
INLINE int clamp(int x, int min, int max);
INLINE int reflect(int x, int min, int max);
INLINE int wrap(int x, int min, int max);
//\}
/* \} */
// --------------------------------------------------------------------
// FIXED POINT
// --------------------------------------------------------------------
/*! \addtogroup grpMathFixed */
/*! \{ */
#define FIX_SHIFT 8
#define FIX_SCALE ( 1<<FIX_SHIFT )
#define FIX_MASK ( FIX_SCALE-1 )
#define FIX_SCALEF ( (float)FIX_SCALE )
#define FIX_SCALEF_INV ( 1.0/FIX_SCALEF )
#define FIX_ONE FIX_SCALE
//! Get the fixed point reciprocal of \a a, in \a fp fractional bits.
/*!
* \param a Value to take the reciprocal of.
* \param fp Number of fixed point bits
* \note The routine does do a division, but the compiler will
* optimize it to a single constant ... \e if both \a a and \a fp
* are constants!
* \sa #FX_RECIMUL
*/
#define FX_RECIPROCAL(a, fp) ( ((1<<(fp))+(a)-1)/(a) )
//! Perform the division \a x/ \a a by reciprocal multiplication
/*! Division is slow, but you can approximate division by a constant
* by multiplying with its reciprocal: x/a vs x*(1/a). This routine
* gives the reciprocal of \a a as a fixed point number with \a fp
* fractional bits.
* \param a Value to take the reciprocal of.
* \param fp Number of fixed point bits
* \note The routine does do a division, but the compiler will
* optimize it to a single constant ... \e if both \a a and \a fp
* are constants!
* \note Rules for safe reciprocal division, using
* n = 2<sup>fp</sup> and m = (n+a-1)/a (i.e., rounding up)
* \li Maximum safe numerator \a x: x < n/(m*a-n)
* \li Minimum n for known \a x: n > x*(a-1)
*/
#define FX_RECIMUL(x, a, fp) ( ((x)*((1<<(fp))+(a)-1)/(a))>>(fp) )
INLINE FIXED int2fx(int d);
INLINE FIXED float2fx(float f);
INLINE u32 fx2uint(FIXED fx);
INLINE u32 fx2ufrac(FIXED fx);
INLINE int fx2int(FIXED fx);
INLINE float fx2float(FIXED fx);
INLINE FIXED fxadd(FIXED fa, FIXED fb);
INLINE FIXED fxsub(FIXED fa, FIXED fb);
INLINE FIXED fxmul(FIXED fa, FIXED fb);
INLINE FIXED fxdiv(FIXED fa, FIXED fb);
INLINE FIXED fxmul64(FIXED fa, FIXED fb);
INLINE FIXED fxdiv64(FIXED fa, FIXED fb);
/*! \} */
// === LUT ============================================================
/*! \addtogroup grpMathLut */
/*! \{ */
#define SIN_LUT_SIZE 514 // 512 for main lut, 2 extra for lerp
#define DIV_LUT_SIZE 257 // 256 for main lut, 1 extra for lerp
INLINE s32 lu_sin(uint theta);
INLINE s32 lu_cos(uint theta);
INLINE uint lu_div(uint x);
INLINE int lu_lerp32(const s32 lut[], uint x, const uint shift);
INLINE int lu_lerp16(const s16 lut[], uint x, const uint shift);
/*! \} */
// === POINT ==========================================================
struct RECT;
//! \addtogroup grpMathPoint
//! \{
//! 2D Point struct
typedef struct POINT { int x, y; } POINT, POINT32;
// --- Point functions ---
INLINE POINT *pt_set(POINT *pd, int x, int y);
INLINE POINT *pt_add(POINT *pd, const POINT *pa, const POINT *pb);
INLINE POINT *pt_sub(POINT *pd, const POINT *pa, const POINT *pb);
INLINE POINT *pt_scale(POINT *pd, const POINT *pa, int c);
INLINE POINT *pt_add_eq(POINT *pd, const POINT *pb);
INLINE POINT *pt_sub_eq(POINT *pd, const POINT *pb);
INLINE POINT *pt_scale_eq(POINT *pd, int c);
INLINE int pt_cross(const POINT *pa, const POINT *pb);
INLINE int pt_dot(const POINT *pa, const POINT *pb);
int pt_in_rect(const POINT *pt, const struct RECT *rc);
//! \}
// === RECT ===========================================================
/*! \addtogroup grpMathRect */
/*! \{ */
//! Rectangle struct
typedef struct RECT
{
int left, top;
int right, bottom;
} RECT, RECT32;
INLINE RECT *rc_set(RECT *rc, int l, int t, int r, int b);
INLINE RECT *rc_set2(RECT *rc, int x, int y, int w, int h);
INLINE int rc_width(const RECT *rc);
INLINE int rc_height(const RECT *rc);
INLINE RECT *rc_set_pos(RECT *rc, int x, int y);
INLINE RECT *rc_set_size(RECT *rc, int w, int h);
INLINE RECT *rc_move(RECT *rc, int dx, int dy);
INLINE RECT *rc_inflate(RECT *rc, int dw, int dh);
INLINE RECT *rc_inflate2(RECT *rc, const RECT *dr);
RECT *rc_normalize(RECT *rc);
/*! \} */
// === VECTOR =========================================================
/*! \addtogroup grpMathVector */
/*! \{ */
//! Vector struct
typedef struct VECTOR { FIXED x, y, z; } VECTOR;
INLINE VECTOR *vec_set(VECTOR *vd, FIXED x, FIXED y, FIXED z);
INLINE VECTOR *vec_add(VECTOR *vd, const VECTOR *va, const VECTOR *vb);
INLINE VECTOR *vec_sub(VECTOR *vd, const VECTOR *va, const VECTOR *vb);
INLINE VECTOR *vec_mul(VECTOR *vd, const VECTOR *va, const VECTOR *vb);
INLINE VECTOR *vec_scale(VECTOR *vd, const VECTOR *va, FIXED c);
INLINE FIXED vec_dot(const VECTOR *va, const VECTOR *vb);
INLINE VECTOR *vec_add_eq(VECTOR *vd, const VECTOR *vb);
INLINE VECTOR *vec_sub_eq(VECTOR *vd, const VECTOR *vb);
INLINE VECTOR *vec_mul_eq(VECTOR *vd, const VECTOR *vb);
INLINE VECTOR *vec_scale_eq(VECTOR *vd, FIXED c);
VECTOR *vec_cross(VECTOR *vd, const VECTOR *va, const VECTOR *vb);
/*! \} */
// === INLINE =========================================================
// --- General --------------------------------------------------------
//! Get the sign of \a x.
INLINE int sgn(int x)
{ return (x>=0) ? +1 : -1; }
//! Tri-state sign of \a x: -1 for negative, 0 for 0, +1 for positive.
INLINE int sgn3(int x)
{ return (x>>31) - (-x>>31); }
//! Get the maximum of \a a and \a b
INLINE int max(int a, int b)
{ return (a > b) ? (a) : (b); }
//! Get the minimum of \a a and \a b
INLINE int min(int a, int b)
{ return (a < b) ? (a) : (b); }
//! Range check
INLINE BOOL in_range(int x, int min, int max)
{ return (u32)(x-min) < (u32)(max-min); }
//! Truncates \a x to stay in range [\a min, \a max>
/*! \return Truncated value of \a x.
* \note \a max is exclusive!
*/
INLINE int clamp(int x, int min, int max)
{ return (x>=max) ? (max-1) : ( (x<min) ? min : x ); }
//! Reflects \a x at boundaries \a min and \a max
/*! If \a x is outside the range [\a min, \a max>,
* it'll be placed inside again with the same distance
* to the 'wall', but on the other side. Example for lower
* border: y = \a min - (\a x- \a min) = 2*\a min + \a x.
* \return Reflected value of \a x.
* \note \a max is exclusive!
*/
INLINE int reflect(int x, int min, int max)
{ return (x>=max) ? (2*(max-1)-x) : ( (x<min) ? (2*min-x) : x ); }
//! Wraps \a x to stay in range [\a min, \a max>
INLINE int wrap(int x, int min, int max)
{ return (x>=max) ? (x+min-max) : ( (x<min) ? (x+max-min) : x ); }
// --- Fixed point ----------------------------------------------------
//! Convert an integer to fixed-point
INLINE FIXED int2fx(int d)
{ return d<<FIX_SHIFT; }
//! Convert a float to fixed-point
INLINE FIXED float2fx(float f)
{ return (FIXED)(f*FIX_SCALEF); }
//! Convert a FIXED point value to an unsigned integer (orly?).
INLINE u32 fx2uint(FIXED fx)
{ return fx>>FIX_SHIFT; }
//! Get the unsigned fractional part of a fixed point value (orly?).
INLINE u32 fx2ufrac(FIXED fx)
{ return fx&FIX_MASK; }
//! Convert a FIXED point value to an signed integer.
INLINE int fx2int(FIXED fx)
{ return fx/FIX_SCALE; }
//! Convert a fixed point value to floating point.
INLINE float fx2float(FIXED fx)
{ return fx/FIX_SCALEF; }
//! Add two fixed point values
INLINE FIXED fxadd(FIXED fa, FIXED fb)
{ return fa + fb; }
//! Subtract two fixed point values
INLINE FIXED fxsub(FIXED fa, FIXED fb)
{ return fa - fb; }
//! Multiply two fixed point values
INLINE FIXED fxmul(FIXED fa, FIXED fb)
{ return (fa*fb)>>FIX_SHIFT; }
//! Divide two fixed point values.
INLINE FIXED fxdiv(FIXED fa, FIXED fb)
{ return ((fa)*FIX_SCALE)/(fb); }
//! Multiply two fixed point values using 64bit math.
INLINE FIXED fxmul64(FIXED fa, FIXED fb)
{ return (((s64)fa)*fb)>>FIX_SHIFT; }
//! Divide two fixed point values using 64bit math.
INLINE FIXED fxdiv64(FIXED fa, FIXED fb)
{ return ( ((s64)fa)<<FIX_SHIFT)/(fb); }
// --- LUT ------------------------------------------------------------
//! Look-up a sine value (2&#960; = 0x10000)
/*! \param theta Angle in [0,FFFFh] range
* \return .12f sine value
*/
INLINE s32 lu_sin(uint theta) {
// Stub expects for testing the angle in degrees
double rad = theta*M_PI/180;
auto x = sin(rad);
return (int)(x * 65536.0f / 16.0f);;
}
//! Look-up a cosine value (2&#960; = 0x10000)
/*! \param theta Angle in [0,FFFFh] range
* \return .12f cosine value
*/
INLINE s32 lu_cos(uint theta) {
// Stub expects for testing the angle in degrees
double rad = theta*M_PI/180;
auto x = cos(rad);
return (int)(x * 65536.0f / 16.0f);;
}
//! Look-up a division value between 0 and 255
/*! \param x reciprocal to look up.
* \return 1/x (.16f)
*/
INLINE uint lu_div(uint x)
{ return 0; }
//! Linear interpolator for 32bit LUTs.
/*! A lut is essentially the discrete form of a function, f(<i>x</i>).
* You can get values for non-integer \e x via (linear)
* interpolation between f(x) and f(x+1).
* \param lut The LUT to interpolate from.
* \param x Fixed point number to interpolate at.
* \param shift Number of fixed-point bits of \a x.
*/
INLINE int lu_lerp32(const s32 lut[], uint x, const uint shift)
{
int xa, ya, yb;
xa=x>>shift;
ya= lut[xa]; yb= lut[xa+1];
return ya + ( (yb-ya)*(x-(xa<<shift))>>shift );
}
//! As lu_lerp32, but for 16bit LUTs.
INLINE int lu_lerp16(const s16 lut[], uint x, const uint shift)
{
int xa, ya, yb;
xa=x>>shift;
ya= lut[xa]; yb= lut[xa+1];
return ya + ( (yb-ya)*(x-(xa<<shift))>>shift );
}
// --- Point ----------------------------------------------------------
//! Initialize \a pd to (\a x, \a y)
INLINE POINT *pt_set(POINT *pd, int x, int y)
{
pd->x= x; pd->y= y;
return pd;
}
//! Point addition: \a pd = \a pa + \a pb
INLINE POINT *pt_add(POINT *pd, const POINT *pa, const POINT *pb)
{
pd->x= pa->x + pb->x;
pd->y= pa->x + pb->y;
return pd;
}
//! Point subtraction: \a pd = \a pa - \a pb
INLINE POINT *pt_sub(POINT *pd, const POINT *pa, const POINT *pb)
{
pd->x= pa->x - pb->x;
pd->y= pa->x - pb->y;
return pd;
}
//! Point scale: \a pd = \a c * \a pa
INLINE POINT *pt_scale(POINT *pd, const POINT *pa, int c)
{
pd->x= pa->x*c;
pd->y= pa->y*c;
return pd;
}
//! Point increment: \a pd += \a pb
INLINE POINT *pt_add_eq(POINT *pd, const POINT *pb)
{
pd->x += pb->y;
pd->y += pb->y;
return pd;
}
//! Point decrement: \a pd -= \a pb
INLINE POINT *pt_sub_eq(POINT *pd, const POINT *pb)
{
pd->x -= pb->y;
pd->y -= pb->y;
return pd;
}
//! Point scale: \a pd *= \a c
INLINE POINT *pt_scale_eq(POINT *pd, int c)
{
pd->x *= c;
pd->y *= c;
return pd;
}
//! Point 'cross'-product: \a pa \htmlonly &times; \endhtmlonly \a pb
/*! Actually, there's no such thing as a 2D cross-product, but you could
* extend it to 3D and get the value of its <i>z</i>-component,
* which can be used for a test for parallelism.
*/
INLINE int pt_cross(const POINT *pa, const POINT *pb)
{ return pa->x * pb->y - pa->y * pb->x; }
//! Point 'dot'-product:\a pa \htmlonly &middot; \endhtmlonly \a pb
INLINE int pt_dot(const POINT *pa, const POINT *pb)
{ return pa->x * pb->x + pa->y * pb->y; }
// --- Rect -----------------------------------------------------------
//! Initialize a rectangle.
/*! \param l Left side.
* \param t Top side.
* \param r Right side.
* \param b Bottom side.
*/
INLINE RECT *rc_set(RECT *rc, int l, int t, int r, int b)
{
rc->left= l; rc->top= t; rc->right= r; rc->bottom= b;
return rc;
}
//! Initialize a rectangle, with sizes inside of max boundaries.
/*! \param x Left side.
* \param y Top side.
* \param w Width.
* \param h Height.
*/
INLINE RECT *rc_set2(RECT *rc, int x, int y, int w, int h)
{
rc->left= x; rc->top= y; rc->right= x+w; rc->bottom= y+h;
return rc;
}
//! Get rectangle width.
INLINE int rc_width(const RECT *rc)
{ return rc->right - rc->left; }
//! Get rectangle height
INLINE int rc_height(const RECT *rc)
{ return rc->bottom - rc->top; }
//! Move rectangle to (\a x, \a y) position.
INLINE RECT *rc_set_pos(RECT *rc, int x, int y)
{
rc->right += x-rc->left; rc->left= x;
rc->bottom += y-rc->top; rc->top= y;
return rc;
}
//! Reside rectangle.
INLINE RECT *rc_set_size(RECT *rc, int w, int h)
{
rc->right= rc->left+w; rc->bottom= rc->top+h;
return rc;
}
//! Move rectangle by (\a dx, \a dy).
INLINE RECT *rc_move(RECT *rc, int dx, int dy)
{
rc->left += dx; rc->top += dy;
rc->right += dx; rc->bottom += dy;
return rc;
}
//! Increase size by \a dw horizontally and \a dh vertically.
INLINE RECT *rc_inflate(RECT *rc, int dw, int dh)
{
rc->left -= dw; rc->top -= dh;
rc->right += dw; rc->bottom += dh;
return rc;
}
//! Increase sizes on all sides by values of rectangle \a dr.
INLINE RECT *rc_inflate2(RECT *rc, const RECT *dr)
{
rc->left += dr->left; rc->top += dr->top;
rc->right += dr->right; rc->bottom += dr->bottom;
return rc;
}
// --- Vector ---------------------------------------------------------
//! Initialize a vector
INLINE VECTOR *vec_set(VECTOR *vd, FIXED x, FIXED y, FIXED z)
{
vd->x= x; vd->y= y; vd->z= z;
return vd;
}
//! Add vectors: \b d = \b a + \b b;
INLINE VECTOR *vec_add(VECTOR *vd, const VECTOR *va, const VECTOR *vb)
{
vd->x= va->x + vb->x;
vd->y= va->y + vb->y;
vd->z= va->z + vb->z;
return vd;
}
//! Subtract vectors: \b d = \b a - \b b;
INLINE VECTOR *vec_sub(VECTOR *vd, const VECTOR *va, const VECTOR *vb)
{
vd->x= va->x - vb->x;
vd->y= va->y - vb->y;
vd->z= va->z - vb->z;
return vd;
}
//! Multiply vectors elements: \b d = \b S(ax, ay, az) <20>\b b
INLINE VECTOR *vec_mul(VECTOR *vd, const VECTOR *va, const VECTOR *vb)
{
vd->x= fxmul(va->x, vb->x);
vd->y= fxmul(va->y, vb->y);
vd->z= fxmul(va->z, vb->z);
return vd;
}
//! Scale vector: \b d = c*\b a
INLINE VECTOR *vec_scale(VECTOR *vd, const VECTOR *va, FIXED c)
{
vd->x= fxmul(va->x, c);
vd->y= fxmul(va->y, c);
vd->z= fxmul(va->z, c);
return vd;
}
//! Dot-product: d = \b a <20>\b b
INLINE FIXED vec_dot(const VECTOR *va, const VECTOR *vb)
{
FIXED dot;
dot = fxmul(va->x, vb->x);
dot += fxmul(va->y, vb->y);
dot += fxmul(va->z, vb->z);
return dot;
}
//! Increment vector: \b d += \b b;
INLINE VECTOR *vec_add_eq(VECTOR *vd, const VECTOR *vb)
{ vd->x += vb->x; vd->y += vb->y; vd->z += vb->z; return vd; }
//! Decrease vector: \b d -= \b b;
INLINE VECTOR *vec_sub_eq(VECTOR *vd, const VECTOR *vb)
{ vd->x -= vb->x; vd->y -= vb->y; vd->z -= vb->z; return vd; }
//! Multiply vectors elements: \b d = \b S(dx, dy, dz) <20>\b b
INLINE VECTOR *vec_mul_eq(VECTOR *vd, const VECTOR *vb)
{
vd->x= fxmul(vd->x, vb->x);
vd->y= fxmul(vd->y, vb->y);
vd->z= fxmul(vd->z, vb->z);
return vd;
}
//! Scale vector: \b d = c*\b d
INLINE VECTOR *vec_scale_eq(VECTOR *vd, FIXED c)
{
vd->x= fxmul(vd->x, c);
vd->y= fxmul(vd->y, c);
vd->z= fxmul(vd->z, c);
return vd;
}
#endif //GBA_SPRITE_ENGINE_PROJECT_TONC_MATH_STUB_H

View File

@ -0,0 +1,962 @@
//
// Memory map defines. All of them
//
//! \file tonc_memdef.h
//! \author J Vijn
//! \date 20060508 - 20080521
//
/* === NOTES ===
* 20080521 : comms items taken from libgba
*/
#ifndef TONC_MEMDEF
#define TONC_MEMDEF
/*! \defgroup grpMemBits Memory map bit(fields)
\ingroup grpMemmap
\brief List of all bit(field) definitions of memory mapped items.
*/
// --- Prefixes ---
// REG_DISPCNT : DCNT
// REG_DISPSTAT : DSTAT
// REG_BGxCNT : BG
// REG_WIN_x : WIN
// REG_MOSAIC : MOS
// REG_BLDCNT : BLD
// REG_SND1SWEEP : SSW
// REG_SNDxCNT, : SSQR
// REG_SNDxFREQ, : SFREQ
// REG_SNDDMGCNT : SDMG
// REG_SNDDSCNT : SDS
// REG_SNDSTAT : SSTAT
// REG_DMAxCNT : DMA
// REG_TMxCNT : TM
// REG_SIOCNT : SIO(N/M/U)
// REG_RCNT : R / GPIO
// REG_KEYINPUT : KEY
// REG_KEYCNT : KCNT
// REG_IE, REG_IF : IRQ
// REG_WSCNT : WS
// Regular SE : SE
// OAM attr 0 : ATTR0
// OAM attr 1 : ATTR1
// OAM attr 2 : ATTR2
// --- REG_DISPCNT -----------------------------------------------------
/*! \defgroup grpVideoDCNT Display Control Flags
\ingroup grpMemBits
\brief Bits for REG_DISPCNT
*/
/*! \{ */
#define DCNT_MODE0 0 //!< Mode 0; bg 0-4: reg
#define DCNT_MODE1 0x0001 //!< Mode 1; bg 0-1: reg; bg 2: affine
#define DCNT_MODE2 0x0002 //!< Mode 2; bg 2-3: affine
#define DCNT_MODE3 0x0003 //!< Mode 3; bg2: 240x160\@16 bitmap
#define DCNT_MODE4 0x0004 //!< Mode 4; bg2: 240x160\@8 bitmap
#define DCNT_MODE5 0x0005 //!< Mode 5; bg2: 160x128\@16 bitmap
#define DCNT_GB 0x0008 //!< (R) GBC indicator
#define DCNT_PAGE 0x0010 //!< Page indicator
#define DCNT_OAM_HBL 0x0020 //!< Allow OAM updates in HBlank
#define DCNT_OBJ_2D 0 //!< OBJ-VRAM as matrix
#define DCNT_OBJ_1D 0x0040 //!< OBJ-VRAM as array
#define DCNT_BLANK 0x0080 //!< Force screen blank
#define DCNT_BG0 0x0100 //!< Enable bg 0
#define DCNT_BG1 0x0200 //!< Enable bg 1
#define DCNT_BG2 0x0400 //!< Enable bg 2
#define DCNT_BG3 0x0800 //!< Enable bg 3
#define DCNT_OBJ 0x1000 //!< Enable objects
#define DCNT_WIN0 0x2000 //!< Enable window 0
#define DCNT_WIN1 0x4000 //!< Enable window 1
#define DCNT_WINOBJ 0x8000 //!< Enable object window
#define DCNT_MODE_MASK 0x0007
#define DCNT_MODE_SHIFT 0
#define DCNT_MODE(n) ((n)<<DCNT_MODE_SHIFT)
#define DCNT_LAYER_MASK 0x1F00
#define DCNT_LAYER_SHIFT 8
#define DCNT_LAYER(n) ((n)<<DCNT_LAYER_SHIFT)
#define DCNT_WIN_MASK 0xE000
#define DCNT_WIN_SHIFT 13
#define DCNT_WIN(n) ((n)<<DCNT_WIN_SHIFT)
#define DCNT_BUILD(mode, layer, win, obj1d, objhbl) \
( \
(((win)&7)<<13) | (((layer)&31)<<8) | (((obj1d)&1)<<6) \
| (((objhbl)&1)<<5) | ((mode)&7) \
)
/*! \} /defgroup */
// --- REG_DISPSTAT ----------------------------------------------------
/*! \defgroup grpVideoDSTAT Display Status Flags
\ingroup grpMemBits
\brief Bits for REG_DISPSTAT
*/
/*! \{ */
#define DSTAT_IN_VBL 0x0001 //!< Now in VBlank
#define DSTAT_IN_HBL 0x0002 //!< Now in HBlank
#define DSTAT_IN_VCT 0x0004 //!< Now in set VCount
#define DSTAT_VBL_IRQ 0x0008 //!< Enable VBlank irq
#define DSTAT_HBL_IRQ 0x0010 //!< Enable HBlank irq
#define DSTAT_VCT_IRQ 0x0020 //!< Enable VCount irq
#define DSTAT_VCT_MASK 0xFF00
#define DSTAT_VCT_SHIFT 8
#define DSTAT_VCT(n) ((n)<<DSTAT_VCT_SHIFT)
/*! \} /defgroup */
// --- REG_BGxCNT ------------------------------------------------------
/*! \defgroup grpVideoBGCNT Background Control Flags
\ingroup grpMemBits
\brief Bits for REG_BGxCNT
*/
/*! \{ */
#define BG_MOSAIC 0x0040 //!< Enable Mosaic
#define BG_4BPP 0 //!< 4bpp (16 color) bg (no effect on affine bg)
#define BG_8BPP 0x0080 //!< 8bpp (256 color) bg (no effect on affine bg)
#define BG_WRAP 0x2000 //!< Wrap around edges of affine bgs
#define BG_SIZE0 0
#define BG_SIZE1 0x4000
#define BG_SIZE2 0x8000
#define BG_SIZE3 0xC000
#define BG_REG_32x32 0 //!< reg bg, 32x32 (256x256 px)
#define BG_REG_64x32 0x4000 //!< reg bg, 64x32 (512x256 px)
#define BG_REG_32x64 0x8000 //!< reg bg, 32x64 (256x512 px)
#define BG_REG_64x64 0xC000 //!< reg bg, 64x64 (512x512 px)
#define BG_AFF_16x16 0 //!< affine bg, 16x16 (128x128 px)
#define BG_AFF_32x32 0x4000 //!< affine bg, 32x32 (256x256 px)
#define BG_AFF_64x64 0x8000 //!< affine bg, 64x64 (512x512 px)
#define BG_AFF_128x128 0xC000 //!< affine bg, 128x128 (1024x1024 px)
#define BG_PRIO_MASK 0x0003
#define BG_PRIO_SHIFT 0
#define BG_PRIO(n) ((n)<<BG_PRIO_SHIFT)
#define BG_CBB_MASK 0x000C
#define BG_CBB_SHIFT 2
#define BG_CBB(n) ((n)<<BG_CBB_SHIFT)
#define BG_SBB_MASK 0x1F00
#define BG_SBB_SHIFT 8
#define BG_SBB(n) ((n)<<BG_SBB_SHIFT)
#define BG_SIZE_MASK 0xC000
#define BG_SIZE_SHIFT 14
#define BG_SIZE(n) ((n)<<BG_SIZE_SHIFT)
#define BG_BUILD(cbb, sbb, size, bpp, prio, mos, wrap) \
( \
((size)<<14) | (((wrap)&1)<<13) | (((sbb)&31)<<8 \
| (((bpp)&8)<<4) | (((mos)&1)<<6) | (((cbb)&3)<<2) \
| ((prio)&3) \
)
/*! \} */
/*! \defgroup grpVideoGfx Graphic effects
\ingroup grpMemBits
*/
/*! \{ */
// --- REG_WIN_x ------------------------------------------------------
//! \name Window macros
//\{
#define WIN_BG0 0x0001 //!< Windowed bg 0
#define WIN_BG1 0x0002 //!< Windowed bg 1
#define WIN_BG2 0x0004 //!< Windowed bg 2
#define WIN_BG3 0x0008 //!< Windowed bg 3
#define WIN_OBJ 0x0010 //!< Windowed objects
#define WIN_ALL 0x001F //!< All layers in window.
#define WIN_BLD 0x0020 //!< Windowed blending
#define WIN_LAYER_MASK 0x003F
#define WIN_LAYER_SHIFT 0
#define WIN_LAYER(n) ((n)<<WIN_LAYER_SHIFT)
#define WIN_BUILD(low, high) \
( ((high)<<8) | (low) )
#define WININ_BUILD(win0, win1) WIN_BUILD(win0, win1)
#define WINOUT_BUILD(out, obj) WIN_BUILD(out, obj)
//\}
// --- REG_MOSAIC ------------------------------------------------------
//! \name Mosaic macros
//\{
#define MOS_BH_MASK 0x000F
#define MOS_BH_SHIFT 0
#define MOS_BH(n) ((n)<<MOS_BH_SHIFT)
#define MOS_BV_MASK 0x00F0
#define MOS_BV_SHIFT 4
#define MOS_BV(n) ((n)<<MOS_BV_SHIFT)
#define MOS_OH_MASK 0x0F00
#define MOS_OH_SHIFT 8
#define MOS_OH(n) ((n)<<MOS_OH_SHIFT)
#define MOS_OV_MASK 0xF000
#define MOS_OV_SHIFT 12
#define MOS_OV(n) ((n)<<MOS_OV_SHIFT)
#define MOS_BUILD(bh, bv, oh, ov) \
( (((ov)&15)<<12) | (((oh)&15)<<8) | (((bv)&15)<<4)| ((bh)&15) )
//\}
/* \} */
// --- REG_BLDCNT ------------------------------------------------------
/*! \defgroup grpVideoBLD Blend Flags
\ingroup grpMemBits
\brief Macros for REG_BLDCNT, REG_BLDY and REG_BLDALPHA
*/
/*! \{ */
//!\ name Blend control
//\{
#define BLD_BG0 0x0001 //!< Blend bg 0
#define BLD_BG1 0x0002 //!< Blend bg 1
#define BLD_BG2 0x0004 //!< Blend bg 2
#define BLD_BG3 0x0008 //!< Blend bg 3
#define BLD_OBJ 0x0010 //!< Blend objects
#define BLD_ALL 0x001F //!< All layers (except backdrop)
#define BLD_BACKDROP 0x0020 //!< Blend backdrop
#define BLD_OFF 0 //!< Blend mode is off
#define BLD_STD 0x0040 //!< Normal alpha blend (with REG_EV)
#define BLD_WHITE 0x0080 //!< Fade to white (with REG_Y)
#define BLD_BLACK 0x00C0 //!< Fade to black (with REG_Y)
#define BLD_TOP_MASK 0x003F
#define BLD_TOP_SHIFT 0
#define BLD_TOP(n) ((n)<<BLD_TOP_SHIFT)
#define BLD_MODE_MASK 0x00C0
#define BLD_MODE_SHIFT 6
#define BLD_MODE(n) ((n)<<BLD_MODE_SHIFT)
#define BLD_BOT_MASK 0x3F00
#define BLD_BOT_SHIFT 8
#define BLD_BOT(n) ((n)<<BLD_BOT_SHIFT)
#define BLD_BUILD(top, bot, mode) \
( (((bot)&63)<<8) | (((mode)&3)<<6) | ((top)&63) )
//\}
// --- REG_BLDALPHA ---
//! \name Blend weights
#define BLD_EVA_MASK 0x001F
#define BLD_EVA_SHIFT 0
#define BLD_EVA(n) ((n)<<BLD_EVA_SHIFT)
#define BLD_EVB_MASK 0x1F00
#define BLD_EVB_SHIFT 8
#define BLD_EVB(n) ((n)<<BLD_EVB_SHIFT)
#define BLDA_BUILD(eva, evb) \
( ((eva)&31) | (((evb)&31)<<8) )
//\}
// --- REG_BLDY ---
//! \name Fade levels
#define BLDY_MASK 0x001F
#define BLDY_SHIFT 0
#define BLDY(n) ((n)<<BLD_EY_SHIFT)
#define BLDY_BUILD(ey) \
( (ey)&31 )
//\}
/*! \} */
// --- REG_SND1SWEEP ---------------------------------------------------
/*! \defgroup grpAudioSSW Tone Generator, Sweep Flags
\ingroup grpMemBits
\brief Bits for REG_SND1SWEEP (aka REG_SOUND1CNT_L)
*/
/*! \{ */
#define SSW_INC 0 //!< Increasing sweep rate
#define SSW_DEC 0x0008 //!< Decreasing sweep rate
#define SSW_OFF 0x0008 //!< Disable sweep altogether
#define SSW_SHIFT_MASK 0x0007
#define SSW_SHIFT_SHIFT 0
#define SSW_SHIFT(n) ((n)<<SSW_SHIFT_SHIFT)
#define SSW_TIME_MASK 0x0070
#define SSW_TIME_SHIFT 4
#define SSW_TIME(n) ((n)<<SSW_TIME_SHIFT)
#define SSW_BUILD(shift, dir, time) \
( (((time)&7)<<4) | ((dir)<<3) | ((shift)&7) )
/*! \} /defgroup */
// --- REG_SND1CNT, REG_SND2CNT, REG_SND4CNT ---------------------------
/*! \defgroup grpAudioSSQR Tone Generator, Square Flags
\ingroup grpMemBits
\brief Bits for REG_SND{1,2,4}CNT
(aka REG_SOUND1CNT_H, REG_SOUND2CNT_L, REG_SOUND4CNT_L, respectively)
*/
/*! \{ */
#define SSQR_DUTY1_8 0 //!< 12.5% duty cycle (#-------)
#define SSQR_DUTY1_4 0x0040 //!< 25% duty cycle (##------)
#define SSQR_DUTY1_2 0x0080 //!< 50% duty cycle (####----)
#define SSQR_DUTY3_4 0x00C0 //!< 75% duty cycle (######--) Equivalent to 25%
#define SSQR_INC 0 //!< Increasing volume
#define SSQR_DEC 0x0800 //!< Decreasing volume
#define SSQR_LEN_MASK 0x003F
#define SSQR_LEN_SHIFT 0
#define SSQR_LEN(n) ((n)<<SSQR_LEN_SHIFT)
#define SSQR_DUTY_MASK 0x00C0
#define SSQR_DUTY_SHIFT 6
#define SSQR_DUTY(n) ((n)<<SSQR_DUTY_SHIFT)
#define SSQR_TIME_MASK 0x0700
#define SSQR_TIME_SHIFT 8
#define SSQR_TIME(n) ((n)<<SSQR_TIME_SHIFT)
#define SSQR_IVOL_MASK 0xF000
#define SSQR_IVOL_SHIFT 12
#define SSQR_IVOL(n) ((n)<<SSQR_IVOL_SHIFT)
#define SSQR_ENV_BUILD(ivol, dir, time) \
( ((ivol)<<12) | ((dir)<<11) | (((time)&7)<<8) )
#define SSQR_BUILD(_ivol, dir, step, duty, len) \
( SSQR_ENV_BUILD(ivol,dir,step) | (((duty)&3)<<6) | ((len)&63) )
/*! \} /defgroup */
// --- REG_SND1FREQ, REG_SND2FREQ, REG_SND3FREQ ------------------------
/*! \defgroup grpAudioSFREQ Tone Generator, Frequency Flags
\ingroup grpMemBits
\brief Bits for REG_SND{1-3}FREQ
(aka REG_SOUND1CNT_X, REG_SOUND2CNT_H, REG_SOUND3CNT_X)
*/
/*! \{ */
#define SFREQ_HOLD 0 //!< Continuous play
#define SFREQ_TIMED 0x4000 //!< Timed play
#define SFREQ_RESET 0x8000 //!< Reset sound
#define SFREQ_RATE_MASK 0x07FF
#define SFREQ_RATE_SHIFT 0
#define SFREQ_RATE(n) ((n)<<SFREQ_RATE_SHIFT)
#define SFREQ_BUILD(rate, timed, reset) \
( ((rate)&0x7FF) | ((timed)<<14) | ((reset)<<15) )
/*! \} /defgroup */
// --- REG_SNDDMGCNT ---------------------------------------------------
/*! \defgroup grpAudioSDMG Tone Generator, Control Flags
\ingroup grpMemBits
\brief Bits for REG_SNDDMGCNT (aka REG_SOUNDCNT_L)
*/
/*! \{ */
#define SDMG_LSQR1 0x0100 //!< Enable channel 1 on left
#define SDMG_LSQR2 0x0200 //!< Enable channel 2 on left
#define SDMG_LWAVE 0x0400 //!< Enable channel 3 on left
#define SDMG_LNOISE 0x0800 //!< Enable channel 4 on left
#define SDMG_RSQR1 0x1000 //!< Enable channel 1 on right
#define SDMG_RSQR2 0x2000 //!< Enable channel 2 on right
#define SDMG_RWAVE 0x4000 //!< Enable channel 3 on right
#define SDMG_RNOISE 0x8000 //!< Enable channel 4 on right
#define SDMG_LVOL_MASK 0x0007
#define SDMG_LVOL_SHIFT 0
#define SDMG_LVOL(n) ((n)<<SDMG_LVOL_SHIFT)
#define SDMG_RVOL_MASK 0x0070
#define SDMG_RVOL_SHIFT 4
#define SDMG_RVOL(n) ((n)<<SDMG_RVOL_SHIFT)
// Unshifted values
#define SDMG_SQR1 0x01
#define SDMG_SQR2 0x02
#define SDMG_WAVE 0x04
#define SDMG_NOISE 0x08
#define SDMG_BUILD(_lmode, _rmode, _lvol, _rvol) \
( ((_rmode)<<12) | ((_lmode)<<8) | (((_rvol)&7)<<4) | ((_lvol)&7) )
#define SDMG_BUILD_LR(_mode, _vol) SDMG_BUILD(_mode, _mode, _vol, _vol)
/*! \} /defgroup */
// --- REG_SNDDSCNT ----------------------------------------------------
/*! \defgroup grpAudioSDS Direct Sound Flags
\ingroup grpMemBits
\brief Bits for REG_SNDDSCNT (aka REG_SOUNDCNT_H)
*/
/*! \{ */
#define SDS_DMG25 0 //!< Tone generators at 25% volume
#define SDS_DMG50 0x0001 //!< Tone generators at 50% volume
#define SDS_DMG100 0x0002 //!< Tone generators at 100% volume
#define SDS_A50 0 //!< Direct Sound A at 50% volume
#define SDS_A100 0x0004 //!< Direct Sound A at 100% volume
#define SDS_B50 0 //!< Direct Sound B at 50% volume
#define SDS_B100 0x0008 //!< Direct Sound B at 100% volume
#define SDS_AR 0x0100 //!< Enable Direct Sound A on right
#define SDS_AL 0x0200 //!< Enable Direct Sound A on left
#define SDS_ATMR0 0 //!< Direct Sound A to use timer 0
#define SDS_ATMR1 0x0400 //!< Direct Sound A to use timer 1
#define SDS_ARESET 0x0800 //!< Reset FIFO of Direct Sound A
#define SDS_BR 0x1000 //!< Enable Direct Sound B on right
#define SDS_BL 0x2000 //!< Enable Direct Sound B on left
#define SDS_BTMR0 0 //!< Direct Sound B to use timer 0
#define SDS_BTMR1 0x4000 //!< Direct Sound B to use timer 1
#define SDS_BRESET 0x8000 //!< Reset FIFO of Direct Sound B
/*! \} /defgroup */
// --- REG_SNDSTAT -----------------------------------------------------
/*! \defgroup grpAudioSSTAT Sound Status Flags
\ingroup grpMemBits
\brief Bits for REG_SNDSTAT (and REG_SOUNDCNT_X)
*/
/*! \{ */
#define SSTAT_SQR1 0x0001 //!< (R) Channel 1 status
#define SSTAT_SQR2 0x0002 //!< (R) Channel 2 status
#define SSTAT_WAVE 0x0004 //!< (R) Channel 3 status
#define SSTAT_NOISE 0x0008 //!< (R) Channel 4 status
#define SSTAT_DISABLE 0 //!< Disable sound
#define SSTAT_ENABLE 0x0080 //!< Enable sound. NOTE: enable before using any other sound regs
/*! \} /defgroup */
// --- REG_DMAxCNT -----------------------------------------------------
/*! \defgroup grpAudioDMA DMA Control Flags
\ingroup grpMemBits
\brief Bits for REG_DMAxCNT
*/
/*! \{ */
#define DMA_DST_INC 0 //!< Incrementing destination address
#define DMA_DST_DEC 0x00200000 //!< Decrementing destination
#define DMA_DST_FIXED 0x00400000 //!< Fixed destination
#define DMA_DST_RELOAD 0x00600000 //!< Increment destination, reset after full run
#define DMA_SRC_INC 0 //!< Incrementing source address
#define DMA_SRC_DEC 0x00800000 //!< Decrementing source address
#define DMA_SRC_FIXED 0x01000000 //!< Fixed source address
#define DMA_REPEAT 0x02000000 //!< Repeat transfer at next start condition
#define DMA_16 0 //!< Transfer by halfword
#define DMA_32 0x04000000 //!< Transfer by word
#define DMA_AT_NOW 0 //!< Start transfer now
#define DMA_GAMEPAK 0x08000000 //!< Gamepak DRQ
#define DMA_AT_VBLANK 0x10000000 //!< Start transfer at VBlank
#define DMA_AT_HBLANK 0x20000000 //!< Start transfer at HBlank
#define DMA_AT_SPECIAL 0x30000000 //!< Start copy at 'special' condition. Channel dependent
#define DMA_AT_FIFO 0x30000000 //!< Start at FIFO empty (DMA0/DMA1)
#define DMA_AT_REFRESH 0x30000000 //!< VRAM special; start at VCount=2 (DMA3)
#define DMA_IRQ 0x40000000 //!< Enable DMA irq
#define DMA_ENABLE 0x80000000 //!< Enable DMA
#define DMA_COUNT_MASK 0x0000FFFF
#define DMA_COUNT_SHIFT 0
#define DMA_COUNT(n) ((n)<<DMA_COUNT_SHIFT)
// \name Extra
//\{
#define DMA_NOW (DMA_ENABLE | DMA_AT_NOW)
#define DMA_16NOW (DMA_NOW | DMA_16)
#define DMA_32NOW (DMA_NOW | DMA_32)
// copies
#define DMA_CPY16 (DMA_NOW | DMA_16)
#define DMA_CPY32 (DMA_NOW | DMA_32)
// fills
#define DMA_FILL16 (DMA_NOW | DMA_SRC_FIXED | DMA_16)
#define DMA_FILL32 (DMA_NOW | DMA_SRC_FIXED | DMA_32)
#define DMA_HDMA (DMA_ENABLE | DMA_REPEAT | DMA_AT_HBLANK | DMA_DST_RELOAD)
//\}
/*! \} /defgroup */
// --- REG_TMxCNT ------------------------------------------------------
/*! \defgroup grpTimerTM Timer Control Flags
\ingroup grpMemBits
\brief Bits for REG_TMxCNT
*/
/*! \{ */
#define TM_FREQ_SYS 0 //!< System clock timer (16.7 Mhz)
#define TM_FREQ_1 0 //!< 1 cycle/tick (16.7 Mhz)
#define TM_FREQ_64 0x0001 //!< 64 cycles/tick (262 kHz)
#define TM_FREQ_256 0x0002 //!< 256 cycles/tick (66 kHz)
#define TM_FREQ_1024 0x0003 //!< 1024 cycles/tick (16 kHz)
#define TM_CASCADE 0x0004 //!< Increment when preceding timer overflows
#define TM_IRQ 0x0040 //!< Enable timer irq
#define TM_ENABLE 0x0080 //!< Enable timer
#define TM_FREQ_MASK 0x0003
#define TM_FREQ_SHIFT 0
#define TM_FREQ(n) ((n)<<TM_FREQ_SHIFT)
/*! \} /defgroup */
// --- REG_SIOCNT ----------------------------------------------------------
/*! \defgroup grpSioCnt Serial I/O Control
\ingroup grpMemBits
\brief Bits for REG_TMxCNT
*/
/*! \{ */
//! \name General SIO bits.
//\{
#define SIO_MODE_8BIT 0x0000 //!< Normal comm mode, 8-bit.
#define SIO_MODE_32BIT 0x1000 //!< Normal comm mode, 32-bit.
#define SIO_MODE_MULTI 0x2000 //!< Multi-play comm mode.
#define SIO_MODE_UART 0x3000 //!< UART comm mode.
#define SIO_SI_HIGH 0x0004
#define SIO_IRQ 0x4000 //!< Enable serial irq.
#define SIO_MODE_MASK 0x3000
#define SIO_MODE_SHIFT 12
#define SIO_MODE(n) ((n)<<SIO_MODE_SHIFT)
//\}
//! \name Normal mode bits. UNTESTED.
//\{
#define SION_CLK_EXT 0x0000 //!< Slave unit; use external clock (default).
#define SION_CLK_INT 0x0001 //!< Master unit; use internal clock.
#define SION_256KHZ 0x0000 //!< 256 kHz clockspeed (default).
#define SION_2MHZ 0x0002 //!< 2 MHz clockspeed.
#define SION_RECV_HIGH 0x0004 //!< SI high; opponent ready to receive (R).
#define SION_SEND_HIGH 0x0008 //!< SO high; ready to transfer.
#define SION_ENABLE 0x0080 //!< Start transfer/transfer enabled.
//\}
//! \name Multiplayer mode bits. UNTESTED.
//\{
#define SIOM_9600 0x0000 //!< Baud rate, 9.6 kbps.
#define SIOM_38400 0x0001 //!< Baud rate, 38.4 kbps.
#define SIOM_57600 0x0002 //!< Baud rate, 57.6 kbps.
#define SIOM_115200 0x0003 //!< Baud rate, 115.2 kbps.
#define SIOM_SI 0x0004 //!< SI port (R).
#define SIOM_SLAVE 0x0004 //!< Not the master (R).
#define SIOM_SD 0x0008 //!< SD port (R).
#define SIOM_CONNECTED 0x0008 //!< All GBAs connected (R)
#define SIOM_ERROR 0x0040 //!< Error in transfer (R).
#define SIOM_ENABLE 0x0080 //!< Start transfer/transfer enabled.
#define SIOM_BAUD_MASK 0x0003
#define SIOM_BAUD_SHIFT 0
#define SIOM_BAUD(n) ((n)<<SIOM_BAUD_SHIFT)
#define SIOM_ID_MASK 0x0030 //!< Multi-player ID mask (R)
#define SIOM_ID_SHIFT 4
#define SIOM_ID(n) ((n)<<SIOM_ID_SHIFT)
//\}
//! \name UART mode bits. UNTESTED.
//!\{
#define SIOU_9600 0x0000 //!< Baud rate, 9.6 kbps.
#define SIOU_38400 0x0001 //!< Baud rate, 38.4 kbps.
#define SIOU_57600 0x0002 //!< Baud rate, 57.6 kbps.
#define SIOU_115200 0x0003 //!< Baud rate, 115.2 kbps.
#define SIOU_CTS 0x0004 //!< CTS enable.
#define SIOU_PARITY_EVEN 0x0000 //!< Use even parity.
#define SIOU_PARITY_ODD 0x0008 //!< Use odd parity.
#define SIOU_SEND_FULL 0x0010 //!< Send data is full (R).
#define SIOU_RECV_EMPTY 0x0020 //!< Receive data is empty (R).
#define SIOU_ERROR 0x0040 //!< Error in transfer (R).
#define SIOU_7BIT 0x0000 //!< Data is 7bits long.
#define SIOU_8BIT 0x0080 //!< Data is 8bits long.
#define SIOU_SEND 0x0100 //!< Start sending data.
#define SIOU_RECV 0x0200 //!< Start receiving data.
#define SIOU_BAUD_MASK 0x0003
#define SIOU_BAUD_SHIFT 0
#define SIOU_BAUD(n) ((n)<<SIOU_BAUD_SHIFT)
//\}
/*! \} */
/*! \defgroup grpCommR Comm control.
\ingroup grpMemBits
\brief Communication mode select and general purpose I/O (REG_RCNT).
*/
/*! \{ */
//! \name Communication mode select.
//\{
#define R_MODE_NORMAL 0x0000 //!< Normal mode.
#define R_MODE_MULTI 0x0000 //!< Multiplayer mode.
#define R_MODE_UART 0x0000 //!< UART mode.
#define R_MODE_GPIO 0x8000 //!< General purpose mode.
#define R_MODE_JOYBUS 0xC000 //!< JOY mode.
#define R_MODE_MASK 0xC000
#define R_MODE_SHIFT 14
#define R_MODE(n) ((n)<<R_MODE_SHIFT)
//\}
//! \name General purpose I/O data
//\{
#define GPIO_SC 0x0001 // Data
#define GPIO_SD 0x0002
#define GPIO_SI 0x0004
#define GPIO_SO 0x0008
#define GPIO_SC_IO 0x0010 // Select I/O
#define GPIO_SD_IO 0x0020
#define GPIO_SI_IO 0x0040
#define GPIO_SO_IO 0x0080
#define GPIO_SC_INPUT 0x0000 // Input setting
#define GPIO_SD_INPUT 0x0000
#define GPIO_SI_INPUT 0x0000
#define GPIO_SO_INPUT 0x0000
#define GPIO_SC_OUTPUT 0x0010 // Output setting
#define GPIO_SD_OUTPUT 0x0020
#define GPIO_SI_OUTPUT 0x0040
#define GPIO_SO_OUTPUT 0x0080
#define GPIO_IRQ 0x0100 //! Interrupt on SI.
//\}
/*! \} */
// --- REG_KEYINPUT --------------------------------------------------------
/*! \defgroup grpInputKEY Key Flags
\ingroup grpMemBits
\brief Bits for REG_KEYINPUT and REG_KEYCNT
*/
/*! \{ */
#define KEY_A 0x0001 //!< Button A
#define KEY_B 0x0002 //!< Button B
#define KEY_SELECT 0x0004 //!< Select button
#define KEY_START 0x0008 //!< Start button
#define KEY_RIGHT 0x0010 //!< Right D-pad
#define KEY_LEFT 0x0020 //!< Left D-pad
#define KEY_UP 0x0040 //!< Up D-pad
#define KEY_DOWN 0x0080 //!< Down D-pad
#define KEY_R 0x0100 //!< Shoulder R
#define KEY_L 0x0200 //!< Shoulder L
#define KEY_ACCEPT 0x0009 //!< Accept buttons: A or start
#define KEY_CANCEL 0x0002 //!< Cancel button: B (well, it usually is)
#define KEY_RESET 0x030C //!< St+Se+L+R
#define KEY_FIRE 0x0003 //!< Fire buttons: A or B
#define KEY_SPECIAL 0x000C //!< Special buttons: Select or Start
#define KEY_DIR 0x00F0 //!< Directions: left, right, up down
#define KEY_SHOULDER 0x0300 //!< L or R
#define KEY_ANY 0x03FF //!< Here's the Any key :)
#define KEY_MASK 0x03FF
/*! \} /defgroup */
// --- REG_KEYCNT ------------------------------------------------------
/*! \defgroup grpInputKCNT Key Control Flags
\ingroup grpMemBits
\brief Bits for REG_KEYCNT
*/
/*! \{ */
#define KCNT_IRQ 0x4000 //!< Enable key irq
#define KCNT_OR 0 //!< Interrupt on any of selected keys
#define KCNT_AND 0x8000 //!< Interrupt on all of selected keys
/*! \} /defgroup */
// --- REG_IE, REG_IF, REG_IF_BIOS -------------------------------------
/*! \defgroup grpIrqIRQ Interrupt Flags
\ingroup grpMemBits
\brief Bits for REG_IE, REG_IF and REG_IFBIOS
*/
/*! \{ */
#define IRQ_VBLANK 0x0001 //!< Catch VBlank irq
#define IRQ_HBLANK 0x0002 //!< Catch HBlank irq
#define IRQ_VCOUNT 0x0004 //!< Catch VCount irq
#define IRQ_TIMER0 0x0008 //!< Catch timer 0 irq
#define IRQ_TIMER1 0x0010 //!< Catch timer 1 irq
#define IRQ_TIMER2 0x0020 //!< Catch timer 2 irq
#define IRQ_TIMER3 0x0040 //!< Catch timer 3 irq
#define IRQ_SERIAL 0x0080 //!< Catch serial comm irq
#define IRQ_DMA0 0x0100 //!< Catch DMA 0 irq
#define IRQ_DMA1 0x0200 //!< Catch DMA 1 irq
#define IRQ_DMA2 0x0400 //!< Catch DMA 2 irq
#define IRQ_DMA3 0x0800 //!< Catch DMA 3 irq
#define IRQ_KEYPAD 0x1000 //!< Catch key irq
#define IRQ_GAMEPAK 0x2000 //!< Catch cart irq
/*! \} /defgroup */
// --- REG_WSCNT -------------------------------------------------------
/*! \defgroup grpMiscWS Waitstate Control Flags
\ingroup grpMemBits
\brief Bits for REG_WAITCNT
*/
/*! \{ */
#define WS_SRAM_4 0
#define WS_SRAM_3 0x0001
#define WS_SRAM_2 0x0002
#define WS_SRAM_8 0x0003
#define WS_ROM0_N4 0
#define WS_ROM0_N3 0x0004
#define WS_ROM0_N2 0x0008
#define WS_ROM0_N8 0x000C
#define WS_ROM0_S2 0
#define WS_ROM0_S1 0x0010
#define WS_ROM1_N4 0
#define WS_ROM1_N3 0x0020
#define WS_ROM1_N2 0x0040
#define WS_ROM1_N8 0x0060
#define WS_ROM1_S4 0
#define WS_ROM1_S1 0x0080
#define WS_ROM2_N4 0
#define WS_ROM2_N3 0x0100
#define WS_ROM2_N2 0x0200
#define WS_ROM2_N8 0x0300
#define WS_ROM2_S8 0
#define WS_ROM2_S1 0x0400
#define WS_PHI_OFF 0
#define WS_PHI_4 0x0800
#define WS_PHI_2 0x1000
#define WS_PHI_1 0x1800
#define WS_PREFETCH 0x4000
#define WS_GBA 0
#define WS_CGB 0x8000
#define WS_STANDARD 0x4317
/*! \} /defgroup */
// --- Reg screen entries ----------------------------------------------
/*! \defgroup grpVideoSE Screen-entry Flags
\ingroup grpMemBits
*/
/*! \{ */
#define SE_HFLIP 0x0400 //!< Horizontal flip
#define SE_VFLIP 0x0800 //!< Vertical flip
#define SE_ID_MASK 0x03FF
#define SE_ID_SHIFT 0
#define SE_ID(n) ((n)<<SE_ID_SHIFT)
#define SE_FLIP_MASK 0x0C00
#define SE_FLIP_SHIFT 10
#define SE_FLIP(n) ((n)<<SE_FLIP_SHIFT)
#define SE_PALBANK_MASK 0xF000
#define SE_PALBANK_SHIFT 12
#define SE_PALBANK(n) ((n)<<SE_PALBANK_SHIFT)
#define SE_BUILD(id, PALBANK, hflip, vflip) \
( ((id)&0x03FF) | (((hflip)&1)<<10) | (((vflip)&1)<<11) | ((PALBANK)<<12) )
/*! \} /defgroup */
// --- OAM attribute 0 -------------------------------------------------
/*! \defgroup grpVideoAttr0 Object Attribute 0 Flags
\ingroup grpMemBits
*/
/*! \{ */
#define ATTR0_REG 0 //!< Regular object
#define ATTR0_AFF 0x0100 //!< Affine object
#define ATTR0_HIDE 0x0200 //!< Inactive object
#define ATTR0_AFF_DBL 0x0300 //!< Double-size affine object
#define ATTR0_AFF_DBL_BIT 0x0200
#define ATTR0_BLEND 0x0400 //!< Enable blend
#define ATTR0_WINDOW 0x0800 //!< Use for object window
#define ATTR0_MOSAIC 0x1000 //!< Enable mosaic
#define ATTR0_4BPP 0 //!< Use 4bpp (16 color) tiles
#define ATTR0_8BPP 0x2000 //!< Use 8bpp (256 color) tiles
#define ATTR0_SQUARE 0 //!< Square shape
#define ATTR0_WIDE 0x4000 //!< Tall shape (height &gt; width)
#define ATTR0_TALL 0x8000 //!< Wide shape (height &lt; width)
#define ATTR0_Y_MASK 0x00FF
#define ATTR0_Y_SHIFT 0
#define ATTR0_Y(n) ((n)<<ATTR0_Y_SHIFT)
#define ATTR0_MODE_MASK 0x0300
#define ATTR0_MODE_SHIFT 8
#define ATTR0_MODE(n) ((n)<<ATTR0_MODE_SHIFT)
#define ATTR0_SHAPE_MASK 0xC000
#define ATTR0_SHAPE_SHIFT 14
#define ATTR0_SHAPE(n) ((n)<<ATTR0_SHAPE_SHIFT)
#define ATTR0_BUILD(y, shape, bpp, mode, mos, bld, win) \
( \
((y)&255) | (((mode)&3)<<8) | (((bld)&1)<<10) | (((win)&1)<<11) \
| (((mos)&1)<<12) | (((bpp)&8)<<10)| (((shape)&3)<<14) \
)
/*! \} /defgroup */
// --- OAM attribute 1 -------------------------------------------------
/*! \defgroup grpVideoAttr1 Object Attribute 1 Flags
\ingroup grpMemBits
*/
/*! \{ */
#define ATTR1_HFLIP 0x1000 //!< Horizontal flip (reg obj only)
#define ATTR1_VFLIP 0x2000 //!< Vertical flip (reg obj only)
// Base sizes
#define ATTR1_SIZE_8 0
#define ATTR1_SIZE_16 0x4000
#define ATTR1_SIZE_32 0x8000
#define ATTR1_SIZE_64 0xC000
// Square sizes
#define ATTR1_SIZE_8x8 0 //!< Size flag for 8x8 px object
#define ATTR1_SIZE_16x16 0x4000 //!< Size flag for 16x16 px object
#define ATTR1_SIZE_32x32 0x8000 //!< Size flag for 32x32 px object
#define ATTR1_SIZE_64x64 0xC000 //!< Size flag for 64x64 px object
// Tall sizes
#define ATTR1_SIZE_8x16 0 //!< Size flag for 8x16 px object
#define ATTR1_SIZE_8x32 0x4000 //!< Size flag for 8x32 px object
#define ATTR1_SIZE_16x32 0x8000 //!< Size flag for 16x32 px object
#define ATTR1_SIZE_32x64 0xC000 //!< Size flag for 32x64 px object
// Wide sizes
#define ATTR1_SIZE_16x8 0 //!< Size flag for 16x8 px object
#define ATTR1_SIZE_32x8 0x4000 //!< Size flag for 32x8 px object
#define ATTR1_SIZE_32x16 0x8000 //!< Size flag for 32x16 px object
#define ATTR1_SIZE_64x32 0xC000 //!< Size flag for 64x64 px object
#define ATTR1_X_MASK 0x01FF
#define ATTR1_X_SHIFT 0
#define ATTR1_X(n) ((n)<<ATTR1_X_SHIFT)
#define ATTR1_AFF_ID_MASK 0x3E00
#define ATTR1_AFF_ID_SHIFT 9
#define ATTR1_AFF_ID(n) ((n)<<ATTR1_AFF_ID_SHIFT)
#define ATTR1_FLIP_MASK 0x3000
#define ATTR1_FLIP_SHIFT 12
#define ATTR1_FLIP(n) ((n)<<ATTR1_FLIP_SHIFT)
#define ATTR1_SIZE_MASK 0xC000
#define ATTR1_SIZE_SHIFT 14
#define ATTR1_SIZE(n) ((n)<<ATTR1_SIZE_SHIFT)
#define ATTR1_BUILDR(x, size, hflip, vflip) \
( ((x)&511) | (((hflip)&1)<<12) | (((vflip)&1)<<13) | (((size)&3)<<14) )
#define ATTR1_BUILDA(x, size, affid) \
( ((x)&511) | (((affid)&31)<<9) | (((size)&3)<<14) )
/*! \} /defgroup */
// --- OAM attribute 2 -------------------------------------------------
/*! \defgroup grpVideoAttr2 Object Attribute 2 Flags
\ingroup grpMemBits
*/
/*! \{ */
#define ATTR2_ID_MASK 0x03FF
#define ATTR2_ID_SHIFT 0
#define ATTR2_ID(n) ((n)<<ATTR2_ID_SHIFT)
#define ATTR2_PRIO_MASK 0x0C00
#define ATTR2_PRIO_SHIFT 10
#define ATTR2_PRIO(n) ((n)<<ATTR2_PRIO_SHIFT)
#define ATTR2_PALBANK_MASK 0xF000
#define ATTR2_PALBANK_SHIFT 12
#define ATTR2_PALBANK(n) ((n)<<ATTR2_PALBANK_SHIFT)
#define ATTR2_BUILD(id, pb, prio) \
( ((id)&0x3FF) | (((pb)&15)<<12) | (((prio)&3)<<10) )
/*! \} //defgroup */
#endif // TONC_MEMDEF

View File

@ -0,0 +1,583 @@
//
// GBA Memory map
//
//! \file tonc_memmap.h
//! \author J Vijn
//! \date 20060508 - 20060508
//
//
// === NOTES ===
//
// * The REG_BGxy registers for affine backgrounds
// should be _signed_ (vs16 / vs32), not unsigned (vu16 / vu32)
// * I have removed several REG_x_L, REG_x_H pairs because all they
// do is clutter up the file
// * C++ doesn't seem to like struct copies if the type specifiers
// don't match (e.g., volatile, non-volatile). Most registers
// don't really need the volatile specifier anyway, so if this
// presents a problem consider removing it.
// * I'm using defines for the memory map here, but GCC cannot optimize
// these properly and they will often appear inside a loop, potentially
// slowing it down to up 50% or so, depending on how much you do
// in the loop. Possible remedy: use a set of global pointers for the
// memory map instead of defines. It'll only be 4 or so pointers, so
// it should be ok. (PONDER: system with void pointers?)
#ifndef TONC_MEMMAP
#define TONC_MEMMAP
#ifndef __ASM__
#include "tonc_types.h"
#endif
/*! \defgroup grpReg IO Registers */
/*! \defgroup grpRegAlt IO Alternates */
// === MEMORY SECTIONS ================================================
/*! \addtogroup grpMemmap
\brief Basic memory map
*/
/*! \{ */
//! \name Main sections
//\{
#define MEM_EWRAM 0x02000000 //!< External work RAM
#define MEM_IWRAM 0x03000000 //!< Internal work RAM
#define MEM_IO 0x04000000 //!< I/O registers
#define MEM_PAL 0x05000000 //!< Palette. Note: no 8bit write !!
#define MEM_VRAM 0x06000000 //!< Video RAM. Note: no 8bit write !!
#define MEM_OAM 0x07000000 //!< Object Attribute Memory (OAM) Note: no 8bit write !!
#define MEM_ROM 0x08000000 //!< ROM. No write at all (duh)
#define MEM_SRAM 0x0E000000 //!< Static RAM. 8bit write only
//\}
//! \name Main section sizes
//\{
#define EWRAM_SIZE 0x40000
#define IWRAM_SIZE 0x08000
#define PAL_SIZE 0x00400
#define VRAM_SIZE 0x18000
#define OAM_SIZE 0x00400
#define SRAM_SIZE 0x10000
//\}
//! \name Sub section sizes
//\{
#define PAL_BG_SIZE 0x00200 //!< BG palette size
#define PAL_OBJ_SIZE 0x00200 //!< Object palette size
#define CBB_SIZE 0x04000 //!< Charblock size
#define SBB_SIZE 0x00800 //!< Screenblock size
#define VRAM_BG_SIZE 0x10000 //!< BG VRAM size
#define VRAM_OBJ_SIZE 0x08000 //!< Object VRAM size
#define M3_SIZE 0x12C00 //!< Mode 3 buffer size
#define M4_SIZE 0x09600 //!< Mode 4 buffer size
#define M5_SIZE 0x0A000 //!< Mode 5 buffer size
#define VRAM_PAGE_SIZE 0x0A000 //!< Bitmap page size
//\}
//! \name Sub sections
//\{
#define REG_BASE MEM_IO
#define MEM_PAL_BG (MEM_PAL) //!< Background palette address
#define MEM_PAL_OBJ (MEM_PAL + PAL_BG_SIZE) //!< Object palette address
#define MEM_VRAM_FRONT (MEM_VRAM) //!< Front page address
#define MEM_VRAM_BACK (MEM_VRAM + VRAM_PAGE_SIZE) //!< Back page address
#define MEM_VRAM_OBJ (MEM_VRAM + VRAM_BG_SIZE) //!< Object VRAM address
//\}
/*! \} */
// --------------------------------------------------------------------
// STRUCTURED MEMORY MAP
// --------------------------------------------------------------------
/*! \defgroup grpMemArray Memory mapped arrays
\ingroup grpMemmap
\brief These are some macros for easier access of various
memory sections. They're all arrays or matrices, using the
types that would be the most natural for that concept.
*/
/* \{ */
//! \name Palette
//\{
//! Background palette.
/*! pal_bg_mem[i] = color i ( COLOR )
*/
#define pal_bg_mem ((COLOR*)MEM_PAL)
//! Object palette.
/*! pal_obj_mem[i] = color i ( COLOR )
*/
#define pal_obj_mem ((COLOR*)MEM_PAL_OBJ)
//! Background palette matrix.
/*! pal_bg_bank[y] = bank y ( COLOR[ ] )<br>
pal_bg_bank[y][x] = color color y*16+x ( COLOR )
*/
#define pal_bg_bank ((PALBANK*)MEM_PAL)
//! Object palette matrix.
/*! pal_obj_bank[y] = bank y ( COLOR[ ] )<br>
pal_obj_bank[y][x] = color y*16+x ( COLOR )
*/
#define pal_obj_bank ((PALBANK*)MEM_PAL_OBJ)
//\} // End Palette
//! \name VRAM
//\{
//! Charblocks, 4bpp tiles.
/*! tile_mem[y] = charblock y ( TILE[ ] )<br>
tile_mem[y][x] = block y, tile x ( TILE )
*/
#define tile_mem ( (CHARBLOCK*)MEM_VRAM)
//! Charblocks, 8bpp tiles.
/*! tile_mem[y] = charblock y ( TILE[ ] )<br>
tile_mem[y][x] = block y, tile x ( TILE )
*/
#define tile8_mem ((CHARBLOCK8*)MEM_VRAM)
//! Object charblocks, 4bpp tiles.
/*! tile_mem[y] = charblock y ( TILE[ ] )<br>
tile_mem[y][x] = block y, tile x ( TILE )
*/
#define tile_mem_obj ( (CHARBLOCK*)MEM_VRAM_OBJ)
//! Object charblocks, 4bpp tiles.
/*! tile_mem[y] = charblock y ( TILE[ ] )<br>
tile_mem[y][x] = block y, tile x ( TILE )
*/
#define tile8_mem_obj ((CHARBLOCK8*)MEM_VRAM_OBJ)
//! Screenblocks as arrays
/*! se_mem[y] = screenblock y ( SCR_ENTRY[ ] )<br>
* se_mem[y][x] = screenblock y, entry x ( SCR_ENTRY )
*/
#define se_mem ((SCREENBLOCK*)MEM_VRAM)
//! Screenblock as matrices
/*! se_mat[s] = screenblock s ( SCR_ENTRY[ ][ ] )<br>
se_mat[s][y][x] = screenblock s, entry (x,y) ( SCR_ENTRY )
*/
#define se_mat ((SCREENMAT*)MEM_VRAM)
//! Main mode 3/5 frame as an array
/*! vid_mem[i] = pixel i ( COLOR )
*/
#define vid_mem ((COLOR*)MEM_VRAM)
//! Mode 3 frame as a matrix
/*! m3_mem[y][x] = pixel (x, y) ( COLOR )
*/
#define m3_mem ((M3LINE*)MEM_VRAM)
//! Mode 4 first page as a matrix
/*! m4_mem[y][x] = pixel (x, y) ( u8 )
* \note This is a byte-buffer. Not to be used for writing.
*/
#define m4_mem ((M4LINE*)MEM_VRAM)
//! Mode 5 first page as a matrix
/*! m5_mem[y][x] = pixel (x, y) ( COLOR )
*/
#define m5_mem ((M5LINE*)MEM_VRAM)
//! First page array
#define vid_mem_front ((COLOR*)MEM_VRAM)
//! Second page array
#define vid_mem_back ((COLOR*)MEM_VRAM_BACK)
//! Mode 4 second page as a matrix
/*! m4_mem[y][x] = pixel (x, y) ( u8 )
* \note This is a byte-buffer. Not to be used for writing.
*/
#define m4_mem_back ((M4LINE*)MEM_VRAM_BACK)
//! Mode 5 second page as a matrix
/*! m5_mem[y][x] = pixel (x, y) ( COLOR )
*/
#define m5_mem_back ((M5LINE*)MEM_VRAM_BACK)
//\} // End VRAM
//! \name OAM
//\{
//! Object attribute memory
/*! oam_mem[i] = object i ( OBJ_ATTR )
*/
#define oam_mem ((OBJ_ATTR*)MEM_OAM)
#define obj_mem ((OBJ_ATTR*)MEM_OAM)
//! Object affine memory
/*! obj_aff_mem[i] = object matrix i ( OBJ_AFFINE )
*/
#define obj_aff_mem ((OBJ_AFFINE*)MEM_OAM)
//\} // End OAM
//! \name ROM
//\{
//! ROM pointer
#define rom_mem ((u16*)MEM_ROM)
//\}
//! \name SRAM
//\{
//! SRAM pointer
#define sram_mem ((u8*)MEM_SRAM)
//\}
/*! \} */
// --------------------------------------------------------------------
// REGISTER LIST
// --------------------------------------------------------------------
/*! \addtogroup grpReg
\ingroup grpMemmap
*/
/*! \{ */
//! \name IWRAM 'registers'
//\{
// 0300:7ff[y] is mirrored at 03ff:fff[y], which is why this works out:
#define REG_IFBIOS *(vu16*)(REG_BASE-0x0008) //!< IRQ ack for IntrWait functions
#define REG_RESET_DST *(vu16*)(REG_BASE-0x0006) //!< Destination for after SoftReset
#define REG_ISR_MAIN *(fnptr*)(REG_BASE-0x0004) //!< IRQ handler address
//\}
//! \name Display registers
//\{
#define REG_DISPCNT *(vu32*)(REG_BASE+0x0000) //!< Display control
#define REG_DISPSTAT *(vu16*)(REG_BASE+0x0004) //!< Display status
#define REG_VCOUNT *(vu16*)(REG_BASE+0x0006) //!< Scanline count
//\}
//! \name Background control registers
//\{
#define REG_BGCNT ((vu16*)(REG_BASE+0x0008)) //!< Bg control array
#define REG_BG0CNT *(vu16*)(REG_BASE+0x0008) //!< Bg0 control
#define REG_BG1CNT *(vu16*)(REG_BASE+0x000A) //!< Bg1 control
#define REG_BG2CNT *(vu16*)(REG_BASE+0x000C) //!< Bg2 control
#define REG_BG3CNT *(vu16*)(REG_BASE+0x000E) //!< Bg3 control
//\}
//! \name Regular background scroll registers. (write only!)
//\{
#define REG_BG_OFS ((BG_POINT*)(REG_BASE+0x0010)) //!< Bg scroll array
#define REG_BG0HOFS *(vu16*)(REG_BASE+0x0010) //!< Bg0 horizontal scroll
#define REG_BG0VOFS *(vu16*)(REG_BASE+0x0012) //!< Bg0 vertical scroll
#define REG_BG1HOFS *(vu16*)(REG_BASE+0x0014) //!< Bg1 horizontal scroll
#define REG_BG1VOFS *(vu16*)(REG_BASE+0x0016) //!< Bg1 vertical scroll
#define REG_BG2HOFS *(vu16*)(REG_BASE+0x0018) //!< Bg2 horizontal scroll
#define REG_BG2VOFS *(vu16*)(REG_BASE+0x001A) //!< Bg2 vertical scroll
#define REG_BG3HOFS *(vu16*)(REG_BASE+0x001C) //!< Bg3 horizontal scroll
#define REG_BG3VOFS *(vu16*)(REG_BASE+0x001E) //!< Bg3 vertical scroll
//\}
//! \name Affine background parameters. (write only!)
//\{
#define REG_BG_AFFINE ((BG_AFFINE*)(REG_BASE+0x0000)) //!< Bg affine array
#define REG_BG2PA *(vs16*)(REG_BASE+0x0020) //!< Bg2 matrix.pa
#define REG_BG2PB *(vs16*)(REG_BASE+0x0022) //!< Bg2 matrix.pb
#define REG_BG2PC *(vs16*)(REG_BASE+0x0024) //!< Bg2 matrix.pc
#define REG_BG2PD *(vs16*)(REG_BASE+0x0026) //!< Bg2 matrix.pd
#define REG_BG2X *(vs32*)(REG_BASE+0x0028) //!< Bg2 x scroll
#define REG_BG2Y *(vs32*)(REG_BASE+0x002C) //!< Bg2 y scroll
#define REG_BG3PA *(vs16*)(REG_BASE+0x0030) //!< Bg3 matrix.pa.
#define REG_BG3PB *(vs16*)(REG_BASE+0x0032) //!< Bg3 matrix.pb
#define REG_BG3PC *(vs16*)(REG_BASE+0x0034) //!< Bg3 matrix.pc
#define REG_BG3PD *(vs16*)(REG_BASE+0x0036) //!< Bg3 matrix.pd
#define REG_BG3X *(vs32*)(REG_BASE+0x0038) //!< Bg3 x scroll
#define REG_BG3Y *(vs32*)(REG_BASE+0x003C) //!< Bg3 y scroll
//\}
//! \name Windowing registers
//\{
#define REG_WIN0H *(vu16*)(REG_BASE+0x0040) //!< win0 right, left (0xLLRR)
#define REG_WIN1H *(vu16*)(REG_BASE+0x0042) //!< win1 right, left (0xLLRR)
#define REG_WIN0V *(vu16*)(REG_BASE+0x0044) //!< win0 bottom, top (0xTTBB)
#define REG_WIN1V *(vu16*)(REG_BASE+0x0046) //!< win1 bottom, top (0xTTBB)
#define REG_WININ *(vu16*)(REG_BASE+0x0048) //!< win0, win1 control
#define REG_WINOUT *(vu16*)(REG_BASE+0x004A) //!< winOut, winObj control
//\}
//! \name Alternate Windowing registers
//\{
#define REG_WIN0R *(vu8*)(REG_BASE+0x0040) //!< Win 0 right
#define REG_WIN0L *(vu8*)(REG_BASE+0x0041) //!< Win 0 left
#define REG_WIN1R *(vu8*)(REG_BASE+0x0042) //!< Win 1 right
#define REG_WIN1L *(vu8*)(REG_BASE+0x0043) //!< Win 1 left
#define REG_WIN0B *(vu8*)(REG_BASE+0x0044) //!< Win 0 bottom
#define REG_WIN0T *(vu8*)(REG_BASE+0x0045) //!< Win 0 top
#define REG_WIN1B *(vu8*)(REG_BASE+0x0046) //!< Win 1 bottom
#define REG_WIN1T *(vu8*)(REG_BASE+0x0047) //!< Win 1 top
#define REG_WIN0CNT *(vu8*)(REG_BASE+0x0048) //!< window 0 control
#define REG_WIN1CNT *(vu8*)(REG_BASE+0x0049) //!< window 1 control
#define REG_WINOUTCNT *(vu8*)(REG_BASE+0x004A) //!< Out window control
#define REG_WINOBJCNT *(vu8*)(REG_BASE+0x004B) //!< Obj window control
//\}
//! \name Graphic effects
//\{
#define REG_MOSAIC *(vu32*)(REG_BASE+0x004C) //!< Mosaic control
#define REG_BLDCNT *(vu16*)(REG_BASE+0x0050) //!< Alpha control
#define REG_BLDALPHA *(vu16*)(REG_BASE+0x0052) //!< Fade level
#define REG_BLDY *(vu16*)(REG_BASE+0x0054) //!< Blend levels
//\}
// === SOUND REGISTERS ===
// sound regs, partially following pin8gba's nomenclature
//! \name Channel 1: Square wave with sweep
//\{
#define REG_SND1SWEEP *(vu16*)(REG_BASE+0x0060) //!< Channel 1 Sweep
#define REG_SND1CNT *(vu16*)(REG_BASE+0x0062) //!< Channel 1 Control
#define REG_SND1FREQ *(vu16*)(REG_BASE+0x0064) //!< Channel 1 frequency
//\}
//! \name Channel 2: Simple square wave
//\{
#define REG_SND2CNT *(vu16*)(REG_BASE+0x0068) //!< Channel 2 control
#define REG_SND2FREQ *(vu16*)(REG_BASE+0x006C) //!< Channel 2 frequency
//\}
//! \name Channel 3: Wave player
//\{
#define REG_SND3SEL *(vu16*)(REG_BASE+0x0070) //!< Channel 3 wave select
#define REG_SND3CNT *(vu16*)(REG_BASE+0x0072) //!< Channel 3 control
#define REG_SND3FREQ *(vu16*)(REG_BASE+0x0074) //!< Channel 3 frequency
//\}
//! \name Channel 4: Noise generator
//\{
#define REG_SND4CNT *(vu16*)(REG_BASE+0x0078) //!< Channel 4 control
#define REG_SND4FREQ *(vu16*)(REG_BASE+0x007C) //!< Channel 4 frequency
//\}
//! \name Sound control
//\{
#define REG_SNDCNT *(vu32*)(REG_BASE+0x0080) //!< Main sound control
#define REG_SNDDMGCNT *(vu16*)(REG_BASE+0x0080) //!< DMG channel control
#define REG_SNDDSCNT *(vu16*)(REG_BASE+0x0082) //!< Direct Sound control
#define REG_SNDSTAT *(vu16*)(REG_BASE+0x0084) //!< Sound status
#define REG_SNDBIAS *(vu16*)(REG_BASE+0x0088) //!< Sound bias
//\}
//! \name Sound buffers
//\{
#define REG_WAVE_RAM (vu32*)(REG_BASE+0x0090) //!< Channel 3 wave buffer
#define REG_WAVE_RAM0 *(vu32*)(REG_BASE+0x0090)
#define REG_WAVE_RAM1 *(vu32*)(REG_BASE+0x0094)
#define REG_WAVE_RAM2 *(vu32*)(REG_BASE+0x0098)
#define REG_WAVE_RAM3 *(vu32*)(REG_BASE+0x009C)
#define REG_FIFO_A *(vu32*)(REG_BASE+0x00A0) //!< DSound A FIFO
#define REG_FIFO_B *(vu32*)(REG_BASE+0x00A4) //!< DSound B FIFO
//\}
//! \name DMA registers
//\{
#define REG_DMA ((volatile DMA_REC*)(REG_BASE+0x00B0)) //!< DMA as DMA_REC array
#define REG_DMA0SAD *(vu32*)(REG_BASE+0x00B0) //!< DMA 0 Source address
#define REG_DMA0DAD *(vu32*)(REG_BASE+0x00B4) //!< DMA 0 Destination address
#define REG_DMA0CNT *(vu32*)(REG_BASE+0x00B8) //!< DMA 0 Control
#define REG_DMA1SAD *(vu32*)(REG_BASE+0x00BC) //!< DMA 1 Source address
#define REG_DMA1DAD *(vu32*)(REG_BASE+0x00C0) //!< DMA 1 Destination address
#define REG_DMA1CNT *(vu32*)(REG_BASE+0x00C4) //!< DMA 1 Control
#define REG_DMA2SAD *(vu32*)(REG_BASE+0x00C8) //!< DMA 2 Source address
#define REG_DMA2DAD *(vu32*)(REG_BASE+0x00CC) //!< DMA 2 Destination address
#define REG_DMA2CNT *(vu32*)(REG_BASE+0x00D0) //!< DMA 2 Control
#define REG_DMA3SAD *(vu32*)(REG_BASE+0x00D4) //!< DMA 3 Source address
#define REG_DMA3DAD *(vu32*)(REG_BASE+0x00D8) //!< DMA 3 Destination address
#define REG_DMA3CNT *(vu32*)(REG_BASE+0x00DC) //!< DMA 3 Control
//\}
//! \name Timer registers
//\{
#define REG_TM ((volatile TMR_REC*)(REG_BASE+0x0100)) //!< Timers as TMR_REC array
#define REG_TM0D *(vu16*)(REG_BASE+0x0100) //!< Timer 0 data
#define REG_TM0CNT *(vu16*)(REG_BASE+0x0102) //!< Timer 0 control
#define REG_TM1D *(vu16*)(REG_BASE+0x0104) //!< Timer 1 data
#define REG_TM1CNT *(vu16*)(REG_BASE+0x0106) //!< Timer 1 control
#define REG_TM2D *(vu16*)(REG_BASE+0x0108) //!< Timer 2 data
#define REG_TM2CNT *(vu16*)(REG_BASE+0x010A) //!< Timer 2 control
#define REG_TM3D *(vu16*)(REG_BASE+0x010C) //!< Timer 3 data
#define REG_TM3CNT *(vu16*)(REG_BASE+0x010E) //!< Timer 3 control
//\}
//! \name Serial communication
//{
#define REG_SIOCNT *(vu16*)(REG_BASE+0x0128) //!< Serial IO control (Normal/MP/UART)
#define REG_SIODATA ((vu32*)(REG_BASE+0x0120))
#define REG_SIODATA32 *(vu32*)(REG_BASE+0x0120) //!< Normal/UART 32bit data
#define REG_SIODATA8 *(vu16*)(REG_BASE+0x012A) //!< Normal/UART 8bit data
#define REG_SIOMULTI ((vu16*)(REG_BASE+0x0120)) //!< Multiplayer data array
#define REG_SIOMULTI0 *(vu16*)(REG_BASE+0x0120) //!< MP master data
#define REG_SIOMULTI1 *(vu16*)(REG_BASE+0x0122) //!< MP Slave 1 data
#define REG_SIOMULTI2 *(vu16*)(REG_BASE+0x0124) //!< MP Slave 2 data
#define REG_SIOMULTI3 *(vu16*)(REG_BASE+0x0126) //!< MP Slave 3 data
#define REG_SIOMLT_RECV *(vu16*)(REG_BASE+0x0120) //!< MP data receiver
#define REG_SIOMLT_SEND *(vu16*)(REG_BASE+0x012A) //!< MP data sender
//\}
//! \name Keypad registers
//\{
#define REG_KEYINPUT *(vu16*)(REG_BASE+0x0130) //!< Key status (read only??)
#define REG_KEYCNT *(vu16*)(REG_BASE+0x0132) //!< Key IRQ control
//\}
//! \name Joybus communication
//\{
#define REG_RCNT *(vu16*)(REG_BASE+0x0134) //!< SIO Mode Select/General Purpose Data
#define REG_JOYCNT *(vu16*)(REG_BASE+0x0140) //!< JOY bus control
#define REG_JOY_RECV *(vu32*)(REG_BASE+0x0150) //!< JOY bus receiever
#define REG_JOY_TRANS *(vu32*)(REG_BASE+0x0154) //!< JOY bus transmitter
#define REG_JOYSTAT *(vu16*)(REG_BASE+0x0158) //!< JOY bus status
//\}
//! \name Interrupt / System registers
//\{
#define REG_IE *(vu16*)(REG_BASE+0x0200) //!< IRQ enable
#define REG_IF *(vu16*)(REG_BASE+0x0202) //!< IRQ status/acknowledge
#define REG_WAITCNT *(vu16*)(REG_BASE+0x0204) //!< Waitstate control
#define REG_IME *(vu16*)(REG_BASE+0x0208) //!< IRQ master enable
#define REG_PAUSE *(vu16*)(REG_BASE+0x0300) //!< Pause system (?)
//\}
/*! \} */
// --------------------------------------------------------------------
// ALT REGISTERS
// --------------------------------------------------------------------
/*! \addtogroup grpRegAlt
\ingroup grpMemmap
\brief Alternate names for some of the registers
*/
/*! \{ */
#define REG_BLDMOD *(vu16*)(REG_BASE+0x0050) // alpha control
#define REG_COLEV *(vu16*)(REG_BASE+0x0052) // fade level
#define REG_COLEY *(vu16*)(REG_BASE+0x0054) // blend levels
// sound regs as in belogic and GBATek (mostly for compatability)
#define REG_SOUND1CNT *(vu32*)(REG_BASE+0x0060)
#define REG_SOUND1CNT_L *(vu16*)(REG_BASE+0x0060)
#define REG_SOUND1CNT_H *(vu16*)(REG_BASE+0x0062)
#define REG_SOUND1CNT_X *(vu16*)(REG_BASE+0x0064)
#define REG_SOUND2CNT_L *(vu16*)(REG_BASE+0x0068)
#define REG_SOUND2CNT_H *(vu16*)(REG_BASE+0x006C)
#define REG_SOUND3CNT *(vu32*)(REG_BASE+0x0070)
#define REG_SOUND3CNT_L *(vu16*)(REG_BASE+0x0070)
#define REG_SOUND3CNT_H *(vu16*)(REG_BASE+0x0072)
#define REG_SOUND3CNT_X *(vu16*)(REG_BASE+0x0074)
#define REG_SOUND4CNT_L *(vu16*)(REG_BASE+0x0078)
#define REG_SOUND4CNT_H *(vu16*)(REG_BASE+0x007C)
#define REG_SOUNDCNT *(vu32*)(REG_BASE+0x0080)
#define REG_SOUNDCNT_L *(vu16*)(REG_BASE+0x0080)
#define REG_SOUNDCNT_H *(vu16*)(REG_BASE+0x0082)
#define REG_SOUNDCNT_X *(vu16*)(REG_BASE+0x0084)
#define REG_SOUNDBIAS *(vu16*)(REG_BASE+0x0088)
#define REG_WAVE (vu32*)(REG_BASE+0x0090)
#define REG_FIFOA *(vu32*)(REG_BASE+0x00A0)
#define REG_FIFOB *(vu32*)(REG_BASE+0x00A4)
#define REG_DMA0CNT_L *(vu16*)(REG_BASE+0x00B8) // count
#define REG_DMA0CNT_H *(vu16*)(REG_BASE+0x00BA) // flags
#define REG_DMA1CNT_L *(vu16*)(REG_BASE+0x00C4)
#define REG_DMA1CNT_H *(vu16*)(REG_BASE+0x00C6)
#define REG_DMA2CNT_L *(vu16*)(REG_BASE+0x00D0)
#define REG_DMA2CNT_H *(vu16*)(REG_BASE+0x00D2)
#define REG_DMA3CNT_L *(vu16*)(REG_BASE+0x00DC)
#define REG_DMA3CNT_H *(vu16*)(REG_BASE+0x00DE)
#define REG_TM0CNT_L *(vu16*)(REG_BASE+0x0100)
#define REG_TM0CNT_H *(vu16*)(REG_BASE+0x0102)
#define REG_TM1CNT_L *(vu16*)(REG_BASE+0x0104)
#define REG_TM1CNT_H *(vu16*)(REG_BASE+0x0106)
#define REG_TM2CNT_L *(vu16*)(REG_BASE+0x0108)
#define REG_TM2CNT_H *(vu16*)(REG_BASE+0x010a)
#define REG_TM3CNT_L *(vu16*)(REG_BASE+0x010c)
#define REG_TM3CNT_H *(vu16*)(REG_BASE+0x010e)
#define REG_KEYS *(vu16*)(REG_BASE+0x0130) // Key status
#define REG_P1 *(vu16*)(REG_BASE+0x0130) // for backward combatibility
#define REG_P1CNT *(vu16*)(REG_BASE+0x0132) // ditto
#define REG_SCD0 *(vu16*)(REG_BASE+0x0120)
#define REG_SCD1 *(vu16*)(REG_BASE+0x0122)
#define REG_SCD2 *(vu16*)(REG_BASE+0x0124)
#define REG_SCD3 *(vu16*)(REG_BASE+0x0126)
#define REG_SCCNT *(vu32*)(REG_BASE+0x0128)
#define REG_SCCNT_L *(vu16*)(REG_BASE+0x0128)
#define REG_SCCNT_H *(vu16*)(REG_BASE+0x012A)
#define REG_R *(vu16*)(REG_BASE+0x0134)
#define REG_HS_CTRL *(vu16*)(REG_BASE+0x0140)
#define REG_JOYRE *(vu32*)(REG_BASE+0x0150)
#define REG_JOYRE_L *(vu16*)(REG_BASE+0x0150)
#define REG_JOYRE_H *(vu16*)(REG_BASE+0x0152)
#define REG_JOYTR *(vu32*)(REG_BASE+0x0154)
#define REG_JOYTR_L *(vu16*)(REG_BASE+0x0154)
#define REG_JOYTR_H *(vu16*)(REG_BASE+0x0156)
#define REG_JSTAT *(vu16*)(REG_BASE+0x0158)
#define REG_WSCNT *(vu16*)(REG_BASE+0x0204)
/*! \} */
#endif // TONC_MEMMAP
// EOF

View File

@ -0,0 +1,231 @@
//
// Basic video functions
//
//! \file tonc_oam.h
//! \author J Vijn
//! \date 20060604 - 20060604
//
// === NOTES ===
// * Basic video-IO, color, background and object functionality
#ifndef TONC_OAM
#define TONC_OAM
#include "tonc_memmap.h"
#include "tonc_memdef.h"
#include "tonc_core.h"
#ifdef CODE_COMPILED_AS_PART_OF_TEST
#include "tonc_math_stub.h"
#else
#include "tonc_math.h"
#endif
// --------------------------------------------------------------------
// OBJECTS
// --------------------------------------------------------------------
//! \addtogroup grpVideoObj
/*! \{ */
#define OAM_CLEAR() memset32(oam_mem, 0, OAM_SIZE/4)
// --- Prototypes -----------------------------------------------------
// --- Full OAM ---
void oam_init(OBJ_ATTR *obj, uint count);
INLINE void oam_copy(OBJ_ATTR *dst, const OBJ_ATTR *src, uint count);
// --- Obj attr only ---
INLINE OBJ_ATTR *obj_set_attr(OBJ_ATTR *obj, u16 a0, u16 a1, u16 a2);
INLINE void obj_set_pos(OBJ_ATTR *obj, int x, int y);
INLINE void obj_hide(OBJ_ATTR *oatr);
INLINE void obj_unhide(OBJ_ATTR *obj, u16 mode);
INLINE const u8 *obj_get_size(const OBJ_ATTR *obj);
INLINE int obj_get_width(const OBJ_ATTR *obj);
INLINE int obj_get_height(const OBJ_ATTR *obj);
void obj_copy(OBJ_ATTR *dst, const OBJ_ATTR *src, uint count);
void obj_hide_multi(OBJ_ATTR *obj, u32 count);
void obj_unhide_multi(OBJ_ATTR *obj, u16 mode, uint count);
// --- Obj affine only ---
void obj_aff_copy(OBJ_AFFINE *dst, const OBJ_AFFINE *src, uint count);
INLINE void obj_aff_set(OBJ_AFFINE *oaff,
FIXED pa, FIXED pb, FIXED pc, FIXED pd);
INLINE void obj_aff_identity(OBJ_AFFINE *oaff);
INLINE void obj_aff_scale(OBJ_AFFINE *oaff, FIXED sx, FIXED sy);
INLINE void obj_aff_shearx(OBJ_AFFINE *oaff, FIXED hx);
INLINE void obj_aff_sheary(OBJ_AFFINE *oaff, FIXED hy);
void obj_aff_rotate(OBJ_AFFINE *oaff, u16 alpha);
void obj_aff_rotscale(OBJ_AFFINE *oaff, FIXED sx, FIXED sy, u16 alpha);
void obj_aff_premul(OBJ_AFFINE *dst, const OBJ_AFFINE *src);
void obj_aff_postmul(OBJ_AFFINE *dst, const OBJ_AFFINE *src);
void obj_aff_rotscale2(OBJ_AFFINE *oaff, const AFF_SRC *as);
void obj_rotscale_ex(OBJ_ATTR *obj, OBJ_AFFINE *oaff, const AFF_SRC_EX *asx);
// inverse (object -> screen) functions, could be useful
// inverses (prototypes)
INLINE void obj_aff_scale_inv(OBJ_AFFINE *oa, FIXED wx, FIXED wy);
INLINE void obj_aff_rotate_inv(OBJ_AFFINE *oa, u16 theta);
INLINE void obj_aff_shearx_inv(OBJ_AFFINE *oa, FIXED hx);
INLINE void obj_aff_sheary_inv(OBJ_AFFINE *oa, FIXED hy);
/*! \} */
// --------------------------------------------------------------------
// INLINES
// --------------------------------------------------------------------
/*! \addtogroup grpVideoObj */
/*! \{ */
//! Set the attributes of an object.
INLINE OBJ_ATTR *obj_set_attr(OBJ_ATTR *obj, u16 a0, u16 a1, u16 a2)
{
obj->attr0= a0; obj->attr1= a1; obj->attr2= a2;
return obj;
}
//! Set the position of \a obj
INLINE void obj_set_pos(OBJ_ATTR *obj, int x, int y)
{
BFN_SET(obj->attr0, y, ATTR0_Y);
BFN_SET(obj->attr1, x, ATTR1_X);
}
//! Copies \a count OAM entries from \a src to \a dst.
INLINE void oam_copy(OBJ_ATTR *dst, const OBJ_ATTR *src, uint count)
{ memcpy32(dst, src, count*2); }
//! Hide an object.
INLINE void obj_hide(OBJ_ATTR *obj)
{ BFN_SET2(obj->attr0, ATTR0_HIDE, ATTR0_MODE); }
//! Unhide an object.
/*! \param obj Object to unhide.
* \param mode Object mode to unhide to. Necessary because this affects
* the affine-ness of the object.
*/
INLINE void obj_unhide(OBJ_ATTR *obj, u16 mode)
{ BFN_SET2(obj->attr0, mode, ATTR0_MODE); }
//! Get object's sizes as a byte array
INLINE const u8 *obj_get_size(const OBJ_ATTR *obj)
{ return oam_sizes[obj->attr0>>14][obj->attr1>>14]; }
//! Get object's width
INLINE int obj_get_width(const OBJ_ATTR *obj)
{ return obj_get_size(obj)[0]; }
//! Gets object's height
INLINE int obj_get_height(const OBJ_ATTR *obj)
{ return obj_get_size(obj)[1]; }
// --- Affine only ---
//! Set the elements of an \a object affine matrix.
INLINE void obj_aff_set(OBJ_AFFINE *oaff,
FIXED pa, FIXED pb, FIXED pc, FIXED pd)
{
oaff->pa= pa; oaff->pb= pb;
oaff->pc= pc; oaff->pd= pd;
}
//! Set an object affine matrix to the identity matrix
INLINE void obj_aff_identity(OBJ_AFFINE *oaff)
{
oaff->pa= 0x0100l; oaff->pb= 0;
oaff->pc= 0; oaff->pd= 0x0100;
}
//! Set an object affine matrix for scaling.
INLINE void obj_aff_scale(OBJ_AFFINE *oaff, FIXED sx, FIXED sy)
{
oaff->pa= sx; oaff->pb= 0;
oaff->pb= 0; oaff->pd= sy;
}
INLINE void obj_aff_shearx(OBJ_AFFINE *oaff, FIXED hx)
{
oaff->pa= 0x0100; oaff->pb= hx;
oaff->pc= 0; oaff->pd= 0x0100;
}
INLINE void obj_aff_sheary(OBJ_AFFINE *oaff, FIXED hy)
{
oaff->pa= 0x0100; oaff->pb= 0;
oaff->pc= hy; oaff->pd= 0x0100;
}
// --- Inverse operations ---
INLINE void obj_aff_scale_inv(OBJ_AFFINE *oaff, FIXED wx, FIXED wy)
{ obj_aff_scale(oaff, ((1<<24)/wx)>>8, ((1<<24)/wy)>>8); }
INLINE void obj_aff_rotate_inv(OBJ_AFFINE *oaff, u16 theta)
{ obj_aff_rotate(oaff, -theta); }
INLINE void obj_aff_shearx_inv(OBJ_AFFINE *oaff, FIXED hx)
{ obj_aff_shearx(oaff, -hx); }
INLINE void obj_aff_sheary_inv(OBJ_AFFINE *oaff, FIXED hy)
{ obj_aff_sheary(oaff, -hy); }
/*! \} */
void obj_aff_copy(OBJ_AFFINE *dst, const OBJ_AFFINE *src, u32 count)
{
int ii;
for(ii=0; ii<count; ii++)
{
dst->pa= src->pa;
dst->pb= src->pb;
dst->pc= src->pc;
dst->pd= src->pd;
src++;
dst++;
}
}
//! Set obj matrix to counter-clockwise rotation.
/*!
\param oaff Object affine struct to set.
\param alpha CCW angle. full-circle is 10000h.
*/
void obj_aff_rotate(OBJ_AFFINE *oaff, u16 alpha)
{
int ss= lu_sin(alpha)>>4, cc= lu_cos(alpha)>>4;
oaff->pa= cc; oaff->pb= -ss;
oaff->pc= ss; oaff->pd= cc;
}
//! Post-multiply \a dst by \a src: D= D*S
void obj_aff_postmul(OBJ_AFFINE *dst, const OBJ_AFFINE *src)
{
FIXED tmp_a, tmp_b, tmp_c, tmp_d;
tmp_a= dst->pa; tmp_b= dst->pb;
tmp_c= dst->pc; tmp_d= dst->pd;
dst->pa= (tmp_a*src->pa + tmp_b*src->pc)>>8;
dst->pb= (tmp_a*src->pb + tmp_b*src->pd)>>8;
dst->pc= (tmp_c*src->pa + tmp_d*src->pc)>>8;
dst->pd= (tmp_c*src->pb + tmp_d*src->pd)>>8;
}
#endif // TONC_OAM

View File

@ -0,0 +1,377 @@
//
// Basic structs and typedefs
//
//! \file tonc_types.h
//! \author J Vijn
//! \date 20060508 - 20080111
//
// === NOTES ===
// * When doing anything, always, ALWAYS!!! check the type.
// Especially when you're combining things from different sources.
// Look around on the forum and count the number of times people
// have copied, say, from a u32 source to a u16 destination.
#ifndef TONC_TYPES
#define TONC_TYPES
/*! \defgroup grpTypes Types and attributes */
// --------------------------------------------------------------------
// GCC ATTRIBUTES
// --------------------------------------------------------------------
/*! \defgroup grpTypeAttr Type attributes
* \ingroup grpTypes
*/
/*! \{ */
// If you want your data in specific sections, add this
// to your variables or functions.
// Example:
//
// //Declaration
// IWRAM_CODE void function(int x, int y, etc);
//
// //Definition
// IWRAM_CODE void function(int x, int y, etc)
// {
// // code
// }
//! Put variable in IWRAM (default).
#define IWRAM_DATA __attribute__((section(".iwram")))
//! Put variable in EWRAM.
#define EWRAM_DATA __attribute__((section(".ewram")))
//! Put <b>non</b>-initialized variable in EWRAM.
#define EWRAM_BSS __attribute__((section(".sbss")))
//! Put function in IWRAM.
#define IWRAM_CODE __attribute__((section(".iwram"), long_call))
//! Put function in EWRAM.
#define EWRAM_CODE __attribute__((section(".ewram"), long_call))
//! Force a variable to an \a n-byte boundary
#define ALIGN(n) __attribute__((aligned(n)))
//! Force word alignment.
/*! \note In the old days, GCC aggregates were always word aligned.
In the EABI environment (devkitPro r19 and higher), they are
aligned to their widest member. While technically a good thing,
it may cause problems for struct-copies. If you have aggregates
that can multiples of 4 in size but don't have word members,
consider using this attribute to make struct-copies possible again.
*/
#define ALIGN4 __attribute__((aligned(4)))
//! Pack aggregate members
/*! By default, members in aggregates are aligned to their native
boundaries. Adding this prevents that. It will slow access though.
*/
#define PACKED __attribute__((packed))
//! Deprecated notice.
/*! Indicates that this function/type/variable should not be used anymore.
Replacements are (usually) present somewhere as well.
*/
#define DEPRECATED __attribute__((deprecated))
//! Inline function declarator
/*! `inline' inlines the function when -O > 0 when called,
but also creates a body for the function itself
`static' removes the body as well
*/
#define INLINE static inline
/* \} */
// --------------------------------------------------------------------
// TYPES
// --------------------------------------------------------------------
// === primary typedefs ===============================================
/*! \defgroup grpTypePrim Primary types
\ingroup grpTypes
*/
/*! \{ */
/*! \name Base types
Basic signed and unsigned types for 8, 16, 32 and 64-bit integers.
<ul>
<li>s# : signed #-bit integer. </li>
<li>u#/u{type} : unsigned #-bit integer.</li>
<li>e{type} : enum'ed #-bit integer.</li>
</ul>
*/
//\{
typedef unsigned char u8, byte, uchar, echar;
typedef unsigned short u16, hword, ushort, eshort;
typedef unsigned int u32, word, uint, eint;
typedef unsigned long long u64;
typedef signed char s8;
typedef signed short s16;
typedef signed int s32;
typedef signed long long s64;
//\}
/*! \name Volatile types
* Volatile types for registers
*/
//\{
typedef volatile u8 vu8;
typedef volatile u16 vu16;
typedef volatile u32 vu32;
typedef volatile u64 vu64;
typedef volatile s8 vs8;
typedef volatile s16 vs16;
typedef volatile s32 vs32;
typedef volatile s64 vs64;
//\}
/*! \name Const types
* Const types for const function aprameters
*/
//\{
typedef const u8 cu8;
typedef const u16 cu16;
typedef const u32 cu32;
typedef const u64 cu64;
typedef const s8 cs8;
typedef const s16 cs16;
typedef const s32 cs32;
typedef const s64 cs64;
//\}
//! 8-word type for fast struct-copies
typedef struct { u32 data[8]; } BLOCK;
//! Type for consting a string as well as the pointer than points to it.
typedef const char * const CSTR;
/* \} */
// === secondary typedefs =============================================
/*! \defgroup grpTypeSec Secondary types
* \ingroup grpTypes
*/
/*! \{ */
typedef s32 FIXED; //!< Fixed point type
typedef u16 COLOR; //!< Type for colors
typedef u16 SCR_ENTRY, SE; //!< Type for screen entries
typedef u8 SCR_AFF_ENTRY, SAE; //!< Type for affine screen entries
//! 4bpp tile type, for easy indexing and copying of 4-bit tiles
typedef struct { u32 data[8]; } TILE, TILE4;
//! 8bpp tile type, for easy indexing and 8-bit tiles
typedef struct { u32 data[16]; } TILE8;
#ifndef __cplusplus
//! Boolean type
typedef enum { false, true } bool;
#endif
#ifndef BOOL
typedef u8 BOOL; // C++ bool == u8 too, that's why
#define TRUE 1
#define FALSE 0
#endif
// --- function pointer ---
typedef void (*fnptr)(void); //!< void foo() function pointer
typedef void (*fn_v_i)(int); //!< void foo(int x) function pointer
typedef int (*fn_i_i)(int); //!< int foo(int x) function pointer
//! \name affine structs
//\{
//! Simple scale-rotation source struct.
/*! This can be used with ObjAffineSet, and several of tonc's
* affine functions
*/
typedef struct AFF_SRC
{
s16 sx; //!< Horizontal zoom (8.8f)
s16 sy; //!< Vertical zoom (8.8f)
u16 alpha; //!< Counter-clockwise angle ( range [0, 0xFFFF] )
} ALIGN4 AFF_SRC, ObjAffineSource;
//! Extended scale-rotate source struct
/*! This is used to scale/rotate around an arbitrary point. See
* tonc's main text for all the details.
*/
typedef struct AFF_SRC_EX
{
s32 tex_x; //!< Texture-space anchor, x coordinate (.8f)
s32 tex_y; //!< Texture-space anchor, y coordinate (.8f)
s16 scr_x; //!< Screen-space anchor, x coordinate (.0f)
s16 scr_y; //!< Screen-space anchor, y coordinate (.0f)
s16 sx; //!< Horizontal zoom (8.8f)
s16 sy; //!< Vertical zoom (8.8f)
u16 alpha; //!< Counter-clockwise angle ( range [0, 0xFFFF] )
} ALIGN4 AFF_SRC_EX, BgAffineSource;
//! Simple scale-rotation destination struct, BG version.
/*! This is a P-matrix with continuous elements, like the BG matrix.
* It can be used with ObjAffineSet.
*/
typedef struct AFF_DST
{
s16 pa, pb;
s16 pc, pd;
} ALIGN4 AFF_DST, ObjAffineDest;
//! Extended scale-rotate destination struct
/*! This contains the P-matrix and a fixed-point offset , the
* combination can be used to rotate around an arbitrary point.
* Mainly intended for BgAffineSet, but the struct cna be used
* for object transforms too.
*/
typedef struct AFF_DST_EX
{
s16 pa, pb;
s16 pc, pd;
s32 dx, dy;
} ALIGN4 AFF_DST_EX, BgAffineDest;
//\}
/* \} */
// === memory map structs ============================================
/*! \defgroup grpTypeTert Tertiary types
* These types are used for memory mapping of VRAM, affine registers
* and other areas that would benefit from logical memory mapping.
* \ingroup grpTypes
*/
/*! \{ */
//! \name IO register types
//\{
//! Regular bg points; range: :0010 - :001F
typedef struct POINT16 { s16 x, y; } ALIGN4 POINT16, BG_POINT;
//! Affine parameters for backgrounds; range : 0400:0020 - 0400:003F
typedef struct AFF_DST_EX BG_AFFINE;
//! DMA struct; range: 0400:00B0 - 0400:00DF
typedef struct DMA_REC
{
const void *src;
void *dst;
u32 cnt;
} DMA_REC;
//! Timer struct, range: 0400:0100 - 0400:010F
/*! \note The attribute is required, because union's counted as u32 otherwise.
*/
typedef struct TMR_REC
{
union { u16 start, count; } PACKED;
u16 cnt;
} ALIGN4 TMR_REC;
//\}
//! \name PAL types
//\{
//! Palette bank type, for 16-color palette banks
typedef COLOR PALBANK[16];
//\}
/*! \name VRAM array types
* These types allow VRAM access as arrays or matrices in their
* most natural types.
*/
//\{
typedef SCR_ENTRY SCREENLINE[32];
typedef SCR_ENTRY SCREENMAT[32][32];
typedef SCR_ENTRY SCREENBLOCK[1024];
typedef COLOR M3LINE[240];
typedef u8 M4LINE[240]; // NOTE: u8, not u16!!
typedef COLOR M5LINE[160];
typedef TILE CHARBLOCK[512];
typedef TILE8 CHARBLOCK8[256];
//\}
/*! \name OAM structs
* \note These OBJ_ATTR and OBJ_AFFINE structs are interlaced in OAM.
* When using affine objs, struct/DMA/mem copies will give bad results.
*/
//\{
//! Object attributes.
/*! \note attribute 3 is padding for the interlace with OBJ_AFFINE. If
* not using affine objects, it can be used as a free field
*/
typedef struct OBJ_ATTR
{
u16 attr0;
u16 attr1;
u16 attr2;
s16 fill;
} ALIGN4 OBJ_ATTR;
//! Object affine parameters.
/*! \note most fields are padding for the interlace with OBJ_ATTR.
*/
typedef struct OBJ_AFFINE
{
u16 fill0[3]; s16 pa;
u16 fill1[3]; s16 pb;
u16 fill2[3]; s16 pc;
u16 fill3[3]; s16 pd;
} ALIGN4 OBJ_AFFINE;
//\}
/*! \} */
// --------------------------------------------------------------------
// DEFINES
// --------------------------------------------------------------------
#ifndef NULL
#define NULL (void*)0
#endif
#endif // TONC_TYPES

View File

@ -0,0 +1,60 @@
//
// Created by Wouter Groeneveld on 28/07/18.
//
#ifndef GBA_SPRITE_ENGINE_GBAENGINE_H
#define GBA_SPRITE_ENGINE_GBAENGINE_H
#include <libgba-sprite-engine/gba/tonc_memmap.h>
#include <libgba-sprite-engine/gba/tonc_memdef.h>
#include "scene.h"
#include "sound_control.h"
#include "timer.h"
#define GBA_SCREEN_WIDTH 240
#define GBA_SCREEN_HEIGHT 160
class GBAEngine {
private:
// WHY raw pointers? the engine does the transition and cleanup work itself
Scene* currentScene;
Scene* sceneToTransitionTo;
static std::unique_ptr<Timer> timer;
static std::unique_ptr<SoundControl> activeChannelA;
static std::unique_ptr<SoundControl> activeChannelB;
void vsync();
void cleanupPreviousScene();
void enqueueSound(const s8 *data, int totalSamples, int sampleRate, SoundChannel channel);
void enableTimer0AndVBlank();
void disableTimer0AndVBlank();
static void startOnVBlank() { REG_IME = 1; }
static void stopOnVBlank() { REG_IME = 0; }
static void onVBlank();
public:
GBAEngine();
Timer* getTimer();
void setScene(Scene* scene);
void dequeueAllSounds();
void enqueueMusic(const s8 *data, int totalSamples, int sampleRate = 16000) {
enqueueSound(data, totalSamples, sampleRate, ChannelA);
}
void enqueueSound(const s8 *data, int totalSamples, int sampleRate = 16000) {
enqueueSound(data, totalSamples, sampleRate, ChannelB);
}
u16 readKeys();
void update();
void delay(int times) {
for(int i = 0; i < times; i++){}
}
};
#endif //GBA_SPRITE_ENGINE_GBAENGINE_H

View File

@ -0,0 +1,33 @@
//
// Created by Wouter Groeneveld on 14/12/18.
//
#ifndef GBA_SPRITE_ENGINE_PROJECT_MATH_H
#define GBA_SPRITE_ENGINE_PROJECT_MATH_H
#include <deque>
#ifdef CODE_COMPILED_AS_PART_OF_TEST
#include <libgba-sprite-engine/gba/tonc_math_stub.h>
#else
#include <libgba-sprite-engine/gba/tonc_math.h>
#endif
#include <string>
class GBAVector {
private:
VECTOR v;
public:
GBAVector() : v({}) {}
GBAVector(VECTOR v) : v(v) {}
std::deque<VECTOR> bresenhamLineTo(VECTOR dest);
VECTOR rotateAsCenter(VECTOR point, uint angle);
std::string to_string() {
return "(" + std::to_string(v.x) + "," + std::to_string(v.y) + ")";
}
};
#endif //GBA_SPRITE_ENGINE_PROJECT_MATH_H

View File

@ -0,0 +1,25 @@
//
// Created by Wouter Groeneveld on 05/08/18.
//
#ifndef GBA_SPRITE_ENGINE_COMBINED_PALETTE_H
#define GBA_SPRITE_ENGINE_COMBINED_PALETTE_H
class PaletteManager;
class CombinedPalette {
private:
// WHY use references here? lifetimes not bound, not owned by CombinedPalette
PaletteManager& palette1;
PaletteManager& palette2;
void increaseBrightness(PaletteManager& palette, int bank, int index, u32 intensity);
public:
CombinedPalette(PaletteManager& one, PaletteManager& two) : palette1(one), palette2(two) {}
CombinedPalette(const CombinedPalette& other) = delete;
void increaseBrightness(u32 intensity);
};
#endif //GBA_SPRITE_ENGINE_COMBINED_PALETTE_H

View File

@ -0,0 +1,79 @@
//
// Created by Wouter Groeneveld on 27/07/18.
//
#ifndef GBA_SPRITE_ENGINE_PALETTE_MANAGER_H
#define GBA_SPRITE_ENGINE_PALETTE_MANAGER_H
#include <libgba-sprite-engine/gba/tonc_memmap.h>
#include <libgba-sprite-engine/gba/tonc_types.h>
#include "combined_palette.h"
#define PALETTE_BANK_SIZE 16
#define PALETTE_MAX_SIZE 256
int getBits(int number, int k, int p);
class CombinedPalette;
class PaletteManager {
protected:
const COLOR *data;
int size;
virtual void* paletteAddress() = 0;
virtual PALBANK* paletteBank() = 0;
public:
PaletteManager();
PaletteManager(const COLOR paletteData[]) : PaletteManager(paletteData, PALETTE_MAX_SIZE) {}
PaletteManager(const COLOR paletteData[], int size);
CombinedPalette* operator+(const PaletteManager& other);
void persist();
void persistToBank(int bank);
COLOR change(int bank, int index, COLOR newColor);
COLOR get(int bank, int index) { return paletteBank()[bank][index]; }
void increaseBrightness(u32 intensity);
static COLOR modify(COLOR color, u32 intensity);
static COLOR color(u32 r, u32 g, u32 b);
static u32 red(COLOR r);
static u32 green(COLOR r);
static u32 blue(COLOR r);
};
class BackgroundPaletteManager : public PaletteManager {
protected:
void *paletteAddress() override {
return pal_bg_mem;
}
PALBANK *paletteBank() override {
return pal_bg_bank;
}
public:
BackgroundPaletteManager() : PaletteManager() {}
BackgroundPaletteManager(const COLOR paletteData[]) : PaletteManager(paletteData) {}
BackgroundPaletteManager(const COLOR paletteData[], int size) : PaletteManager(paletteData, size) {}
};
class ForegroundPaletteManager : public PaletteManager {
protected:
void *paletteAddress() override {
return pal_obj_mem;
}
PALBANK *paletteBank() override {
return pal_obj_bank;
}
public:
ForegroundPaletteManager() : PaletteManager() {}
ForegroundPaletteManager(const COLOR paletteData[]) : PaletteManager(paletteData) {}
ForegroundPaletteManager(const COLOR paletteData[], int size) : PaletteManager(paletteData, size) {}
};
#endif //GBA_SPRITE_ENGINE_PALETTE_MANAGER_H

View File

@ -0,0 +1,39 @@
//
// Created by Wouter Groeneveld on 28/07/18.
//
#ifndef GBA_SPRITE_ENGINE_SCRENE_H
#define GBA_SPRITE_ENGINE_SCRENE_H
#include <vector>
#include <memory>
#include <functional>
#include <libgba-sprite-engine/palette/palette_manager.h>
class GBAEngine;
class Scene {
protected:
std::unique_ptr<ForegroundPaletteManager> foregroundPalette;
std::unique_ptr<BackgroundPaletteManager> backgroundPalette;
std::shared_ptr<GBAEngine> engine;
public:
ForegroundPaletteManager* getForegroundPalette() { return foregroundPalette.get(); }
BackgroundPaletteManager* getBackgroundPalette() { return backgroundPalette.get(); }
virtual void load() = 0;
virtual void tick(u16 keys) = 0;
Scene(std::shared_ptr<GBAEngine> engine) :
engine(engine),
foregroundPalette(std::unique_ptr<ForegroundPaletteManager>(new ForegroundPaletteManager())),
backgroundPalette(std::unique_ptr<BackgroundPaletteManager>(new BackgroundPaletteManager())) { }
virtual ~Scene() {
// scenes should manage their own resources - use std::unique_ptr
}
};
#endif //GBA_SPRITE_ENGINE_SCRENE_H

View File

@ -0,0 +1,67 @@
//
// Created by Wouter Groeneveld on 07/08/18.
//
#ifndef GBA_SPRITE_ENGINE_SOUND_H
#define GBA_SPRITE_ENGINE_SOUND_H
#include <libgba-sprite-engine/gba/tonc_types.h>
#include <libgba-sprite-engine/gba/tonc_memdef.h>
#include <memory>
#include <libgba-sprite-engine/gba/tonc_memmap.h>
#define CLOCK 16777216
#define CYCLES_PER_BLANK 280806
#define OVERFLOW_16_BIT_VALUE 65536
#define DISPLAY_INTERRUPT_VBLANK_ENABLE 0x08
#define INTERRUPT_VBLANK 0x1
#define DMA_SYNC_TO_TIMER 0x30000000
#define IRQ_CALLBACK ((volatile unsigned int*) 0x3007FFC)
enum SoundChannel {
ChannelA, ChannelB
};
class SoundControl {
private:
vu32* DMAControl; // ex. &REG_DMA1CNT
vu32* DMASourceAddress; // ex. &REG_DMA1SAD
vu32* DMADestinationAddress; // ex. &REG_DMA1DAD
vu32* FiFoBuffer; // ex. &REG_FIFOA
u16 controlFlags;
u32 vblanksRemaning; // updated each vblank, counts down to 0
u32 vblanksTotal; // calculated once when enqueueing
const void* data;
public:
SoundControl(vu32* dma, vu32* src, vu32* dest, vu32* fifo, u16 flags)
: DMAControl(dma), DMASourceAddress(src), DMADestinationAddress(dest), FiFoBuffer(fifo), controlFlags(flags), vblanksRemaning(0), vblanksTotal(0) {}
u16 getControlFlags() { return controlFlags; }
u32 getVBlanksRemaning() { return vblanksRemaning; }
void reset();
void step() { vblanksRemaning--; }
bool done() { return vblanksRemaning <= 0; }
u32 getVBlanksTotal() { return vblanksTotal; }
void disable() {
*(DMAControl) = 0;
vblanksRemaning = 0;
REG_SNDDSCNT &= ~(controlFlags);
};
void enable() {
*DMAControl = DMA_DST_FIXED | DMA_REPEAT | DMA_32 | DMA_SYNC_TO_TIMER | DMA_ENABLE;
};
void accept(const void* data, int totalSamples, int ticksPerSample);
static std::unique_ptr<SoundControl> channelAControl();
static std::unique_ptr<SoundControl> channelBControl();
static std::unique_ptr<SoundControl> soundControl(SoundChannel channel) {
return channel == ChannelA ? channelAControl() : channelBControl();
};
};
#endif //GBA_SPRITE_ENGINE_SOUND_H

View File

@ -0,0 +1,42 @@
//
// Created by Wouter Groeneveld on 06/12/18.
//
#ifndef GBA_SPRITE_ENGINE_PROJECT_TIMER_H
#define GBA_SPRITE_ENGINE_PROJECT_TIMER_H
#include <ostream>
class Timer {
private:
int microsecs, msecs, secs, minutes, hours;
bool active;
public:
Timer() : active(false) {
reset();
}
void reset();
void start();
void toggle() {
if(isActive()) stop();
else start();
}
bool isActive() { return active; }
void stop();
void onvblank();
std::string to_string();
int getTotalMsecs();
int getMsecs() { return msecs; }
int getSecs() { return secs; }
int getMinutes() { return minutes; }
int getHours() { return hours; }
friend std::ostream& operator<<(std::ostream& os, Timer& timer);
};
#endif //GBA_SPRITE_ENGINE_PROJECT_TIMER_H

90
engine/src/gba/sin_lut.s Normal file
View File

@ -0,0 +1,90 @@
@ ======================================================================
@ Look-Up Tables
@ sin_lut: sin(x*pi/256)
@
@ Exported by Cearn's excellut v1.0
@ (comments, kudos, flames to daytshen@hotmail.com)
@
@ ======================================================================
.section .rodata
@ -----------------------------------------------------------------------
@ sin_lut: a 514 long LUT of 16bit values in 4.12 format
@ sin(x*pi/256)
.global sin_lut
.align 2
sin_lut:
.hword 0x0000,0x0032,0x0064,0x0096,0x00C8,0x00FB,0x012D,0x015F
.hword 0x0191,0x01C3,0x01F5,0x0227,0x0259,0x028A,0x02BC,0x02ED
.hword 0x031F,0x0350,0x0381,0x03B2,0x03E3,0x0413,0x0444,0x0474
.hword 0x04A5,0x04D5,0x0504,0x0534,0x0563,0x0593,0x05C2,0x05F0
.hword 0x061F,0x064D,0x067B,0x06A9,0x06D7,0x0704,0x0731,0x075E
.hword 0x078A,0x07B7,0x07E2,0x080E,0x0839,0x0864,0x088F,0x08B9
.hword 0x08E3,0x090D,0x0936,0x095F,0x0987,0x09B0,0x09D7,0x09FF
.hword 0x0A26,0x0A4D,0x0A73,0x0A99,0x0ABE,0x0AE3,0x0B08,0x0B2C
.hword 0x0B50,0x0B73,0x0B96,0x0BB8,0x0BDA,0x0BFC,0x0C1D,0x0C3E
.hword 0x0C5E,0x0C7D,0x0C9D,0x0CBB,0x0CD9,0x0CF7,0x0D14,0x0D31
.hword 0x0D4D,0x0D69,0x0D84,0x0D9F,0x0DB9,0x0DD2,0x0DEB,0x0E04
.hword 0x0E1C,0x0E33,0x0E4A,0x0E60,0x0E76,0x0E8B,0x0EA0,0x0EB4
.hword 0x0EC8,0x0EDB,0x0EED,0x0EFF,0x0F10,0x0F21,0x0F31,0x0F40
.hword 0x0F4F,0x0F5D,0x0F6B,0x0F78,0x0F85,0x0F91,0x0F9C,0x0FA7
.hword 0x0FB1,0x0FBA,0x0FC3,0x0FCB,0x0FD3,0x0FDA,0x0FE1,0x0FE7
.hword 0x0FEC,0x0FF0,0x0FF4,0x0FF8,0x0FFB,0x0FFD,0x0FFE,0x0FFF
.hword 0x1000,0x0FFF,0x0FFE,0x0FFD,0x0FFB,0x0FF8,0x0FF4,0x0FF0
.hword 0x0FEC,0x0FE7,0x0FE1,0x0FDA,0x0FD3,0x0FCB,0x0FC3,0x0FBA
.hword 0x0FB1,0x0FA7,0x0F9C,0x0F91,0x0F85,0x0F78,0x0F6B,0x0F5D
.hword 0x0F4F,0x0F40,0x0F31,0x0F21,0x0F10,0x0EFF,0x0EED,0x0EDB
.hword 0x0EC8,0x0EB4,0x0EA0,0x0E8B,0x0E76,0x0E60,0x0E4A,0x0E33
.hword 0x0E1C,0x0E04,0x0DEB,0x0DD2,0x0DB9,0x0D9F,0x0D84,0x0D69
.hword 0x0D4D,0x0D31,0x0D14,0x0CF7,0x0CD9,0x0CBB,0x0C9D,0x0C7D
.hword 0x0C5E,0x0C3E,0x0C1D,0x0BFC,0x0BDA,0x0BB8,0x0B96,0x0B73
.hword 0x0B50,0x0B2C,0x0B08,0x0AE3,0x0ABE,0x0A99,0x0A73,0x0A4D
.hword 0x0A26,0x09FF,0x09D7,0x09B0,0x0987,0x095F,0x0936,0x090D
.hword 0x08E3,0x08B9,0x088F,0x0864,0x0839,0x080E,0x07E2,0x07B7
.hword 0x078A,0x075E,0x0731,0x0704,0x06D7,0x06A9,0x067B,0x064D
.hword 0x061F,0x05F0,0x05C2,0x0593,0x0563,0x0534,0x0504,0x04D5
.hword 0x04A5,0x0474,0x0444,0x0413,0x03E3,0x03B2,0x0381,0x0350
.hword 0x031F,0x02ED,0x02BC,0x028A,0x0259,0x0227,0x01F5,0x01C3
.hword 0x0191,0x015F,0x012D,0x00FB,0x00C8,0x0096,0x0064,0x0032
.hword 0x0000,0xFFCE,0xFF9C,0xFF6A,0xFF38,0xFF05,0xFED3,0xFEA1
.hword 0xFE6F,0xFE3D,0xFE0B,0xFDD9,0xFDA7,0xFD76,0xFD44,0xFD13
.hword 0xFCE1,0xFCB0,0xFC7F,0xFC4E,0xFC1D,0xFBED,0xFBBC,0xFB8C
.hword 0xFB5B,0xFB2B,0xFAFC,0xFACC,0xFA9D,0xFA6D,0xFA3E,0xFA10
.hword 0xF9E1,0xF9B3,0xF985,0xF957,0xF929,0xF8FC,0xF8CF,0xF8A2
.hword 0xF876,0xF849,0xF81E,0xF7F2,0xF7C7,0xF79C,0xF771,0xF747
.hword 0xF71D,0xF6F3,0xF6CA,0xF6A1,0xF679,0xF650,0xF629,0xF601
.hword 0xF5DA,0xF5B3,0xF58D,0xF567,0xF542,0xF51D,0xF4F8,0xF4D4
.hword 0xF4B0,0xF48D,0xF46A,0xF448,0xF426,0xF404,0xF3E3,0xF3C2
.hword 0xF3A2,0xF383,0xF363,0xF345,0xF327,0xF309,0xF2EC,0xF2CF
.hword 0xF2B3,0xF297,0xF27C,0xF261,0xF247,0xF22E,0xF215,0xF1FC
.hword 0xF1E4,0xF1CD,0xF1B6,0xF1A0,0xF18A,0xF175,0xF160,0xF14C
.hword 0xF138,0xF125,0xF113,0xF101,0xF0F0,0xF0DF,0xF0CF,0xF0C0
.hword 0xF0B1,0xF0A3,0xF095,0xF088,0xF07B,0xF06F,0xF064,0xF059
.hword 0xF04F,0xF046,0xF03D,0xF035,0xF02D,0xF026,0xF01F,0xF019
.hword 0xF014,0xF010,0xF00C,0xF008,0xF005,0xF003,0xF002,0xF001
.hword 0xF000,0xF001,0xF002,0xF003,0xF005,0xF008,0xF00C,0xF010
.hword 0xF014,0xF019,0xF01F,0xF026,0xF02D,0xF035,0xF03D,0xF046
.hword 0xF04F,0xF059,0xF064,0xF06F,0xF07B,0xF088,0xF095,0xF0A3
.hword 0xF0B1,0xF0C0,0xF0CF,0xF0DF,0xF0F0,0xF101,0xF113,0xF125
.hword 0xF138,0xF14C,0xF160,0xF175,0xF18A,0xF1A0,0xF1B6,0xF1CD
.hword 0xF1E4,0xF1FC,0xF215,0xF22E,0xF247,0xF261,0xF27C,0xF297
.hword 0xF2B3,0xF2CF,0xF2EC,0xF309,0xF327,0xF345,0xF363,0xF383
.hword 0xF3A2,0xF3C2,0xF3E3,0xF404,0xF426,0xF448,0xF46A,0xF48D
.hword 0xF4B0,0xF4D4,0xF4F8,0xF51D,0xF542,0xF567,0xF58D,0xF5B3
.hword 0xF5DA,0xF601,0xF629,0xF650,0xF679,0xF6A1,0xF6CA,0xF6F3
.hword 0xF71D,0xF747,0xF771,0xF79C,0xF7C7,0xF7F2,0xF81E,0xF849
.hword 0xF876,0xF8A2,0xF8CF,0xF8FC,0xF929,0xF957,0xF985,0xF9B3
.hword 0xF9E1,0xFA10,0xFA3E,0xFA6D,0xFA9D,0xFACC,0xFAFC,0xFB2B
.hword 0xFB5B,0xFB8C,0xFBBC,0xFBED,0xFC1D,0xFC4E,0xFC7F,0xFCB0
.hword 0xFCE1,0xFD13,0xFD44,0xFD76,0xFDA7,0xFDD9,0xFE0B,0xFE3D
.hword 0xFE6F,0xFEA1,0xFED3,0xFF05,0xFF38,0xFF6A,0xFF9C,0xFFCE
.hword 0x0000,0x0032
.size sin_lut, .-sin_lut

288
engine/src/gba/tonc_bios.s Normal file
View File

@ -0,0 +1,288 @@
//
// Main GBA BIOS functions.
//
//! \file tonc_bios.s
//! \author J Vijn
//! \date 20071130 - 20090801
#include <libgba-sprite-engine/gba/tonc_asminc.h>
@ === SoftReset [00h] =================================================
@ DECL: void SoftReset();
@ DESC:
BEGIN_FUNC_THUMB(SoftReset, CSEC_TEXT)
swi 0x00
bx lr
END_FUNC(SoftReset)
@ === RegisterRamReset [01h] ==========================================
@ DECL: void RegisterRamReset(u32 flags);
@ DESC:
BEGIN_FUNC_THUMB(RegisterRamReset, CSEC_TEXT)
swi 0x01
bx lr
END_FUNC(RegisterRamReset)
@ === Halt [02h] ======================================================
@ DECL: void Halt();
@ DESC:
BEGIN_FUNC_THUMB(Halt, CSEC_TEXT)
swi 0x02
bx lr
END_FUNC(Halt)
@ === Stop [03h] ======================================================
@ DECL: void Stop();
@ DESC:
BEGIN_FUNC_THUMB(Stop, CSEC_TEXT)
swi 0x03
bx lr
END_FUNC(Stop)
@ === IntrWait [04h] ==================================================
@ DECL: void IntrWait(u32 flagClear, u32 irq);
@ DESC:
BEGIN_FUNC_THUMB(IntrWait, CSEC_TEXT)
swi 0x04
bx lr
END_FUNC(IntrWait)
@ === VBlankIntrWait [05h] ============================================
@ DECL: void VBlankIntrWait();
@ DESC:
BEGIN_FUNC_THUMB(VBlankIntrWait, CSEC_TEXT)
swi 0x05
bx lr
END_FUNC(VBlankIntrWait)
@ === Div [06h] =======================================================
@ DECL: s32 Div(s32 num, s32 den);
@ DESC:
BEGIN_FUNC_THUMB(Div, CSEC_TEXT)
swi 0x06
bx lr
END_FUNC(Div)
@ === DivArm [07h] ====================================================
@ DECL: s32 DivArm(s32 den, s32 num);
@ DESC:
BEGIN_FUNC_THUMB(DivArm, CSEC_TEXT)
swi 0x07
bx lr
END_FUNC(DivArm)
@ === Sqrt [08h] ======================================================
@ DECL: u32 Sqrt(u32 num);
@ DESC:
BEGIN_FUNC_THUMB(Sqrt, CSEC_TEXT)
swi 0x08
bx lr
END_FUNC(Sqrt)
@ === ArcTan [09h] ====================================================
@ DECL: s16 ArcTan(s16 dydx);
@ DESC:
BEGIN_FUNC_THUMB(ArcTan, CSEC_TEXT)
swi 0x09
bx lr
END_FUNC(ArcTan)
@ === ArcTan2 [0Ah] ===================================================
@ DECL: s16 ArcTan2(s16 x, s16 y);
@ DESC:
BEGIN_FUNC_THUMB(ArcTan2, CSEC_TEXT)
swi 0x0A
bx lr
END_FUNC(ArcTan2)
@ === CpuSet [0Bh] ====================================================
@ DECL: void CpuSet(const void *src, void *dst, u32 mode);
@ DESC:
BEGIN_FUNC_THUMB(CpuSet, CSEC_TEXT)
swi 0x0B
bx lr
END_FUNC(CpuSet)
@ === CpuFastSet [0Ch] ================================================
@ DECL: void CpuFastSet(const void *src, void *dst, u32 mode);
@ DESC:
BEGIN_FUNC_THUMB(CpuFastSet, CSEC_TEXT)
swi 0x0C
bx lr
END_FUNC(CpuFastSet)
@ === BiosCheckSum [0Dh] ================================================
@ DECL: u32 BiosCheckSum();
@ DESC:
BEGIN_FUNC_THUMB(BiosCheckSum, CSEC_TEXT)
swi 0x0D
bx lr
END_FUNC(BiosCheckSum)
@ === BgAffineSet [0Eh] ===============================================
@ DECL: void ObjAffineSet(const ObjAffineSource *src, void *dst, s32 num, s32 offset);
@ DESC:
BEGIN_FUNC_THUMB(BgAffineSet, CSEC_TEXT)
swi 0x0E
bx lr
END_FUNC(BgAffineSet)
@ === ObjAffineSet [0Fh] ==============================================
@ DECL: void BgAffineSet(const BGAffineSource *src, BGAffineDest *dst, s32 num);
@ DESC:
BEGIN_FUNC_THUMB(ObjAffineSet, CSEC_TEXT)
swi 0x0F
bx lr
END_FUNC(ObjAffineSet)
@ === BitUnPack [10h] =================================================
@ DECL: void BitUnPack(const void *src, void *dst, BUP *bup);
@ DESC:
BEGIN_FUNC_THUMB(BitUnPack, CSEC_TEXT)
swi 0x10
bx lr
END_FUNC(BitUnPack)
@ === LZ77UnCompWram [11h] ============================================
@ DECL: void LZ77UnCompWram(const void *src, void *dst);
@ DESC:
BEGIN_FUNC_THUMB(LZ77UnCompWram, CSEC_TEXT)
swi 0x11
bx lr
END_FUNC(LZ77UnCompWram)
@ === LZ77UnCompVram [12h] ============================================
@ DECL: void LZ77UnCompVram(const void *src, void *dst);
@ DESC:
BEGIN_FUNC_THUMB(LZ77UnCompVram, CSEC_TEXT)
swi 0x12
bx lr
END_FUNC(LZ77UnCompVram)
@ === HuffUnComp [13h] ================================================
@ DECL: void HuffUnComp(const void *src, void *dst);
@ DESC:
BEGIN_FUNC_THUMB(HuffUnComp, CSEC_TEXT)
swi 0x13
bx lr
END_FUNC(HuffUnComp)
@ === RLUnCompWram [14h] ==============================================
@ DECL: void RLUnCompWram(const void *src, void *dst);
@ DESC:
BEGIN_FUNC_THUMB(RLUnCompWram, CSEC_TEXT)
swi 0x14
bx lr
END_FUNC(RLUnCompWram)
@ === RLUnCompVram [15h] ==============================================
@ DECL: void RLUnCompVram(const void *src, void *dst);
@ DESC:
BEGIN_FUNC_THUMB(RLUnCompVram, CSEC_TEXT)
swi 0x15
bx lr
END_FUNC(RLUnCompVram)
@ === Diff8bitUnFilterWram [16h] ======================================
@ DECL: void Diff8bitUnFilterWram(const void *src, void *dst);
@ DESC:
BEGIN_FUNC_THUMB(Diff8bitUnFilterWram, CSEC_TEXT)
swi 0x16
bx lr
END_FUNC(Diff8bitUnFilterWram)
@ === Diff8bitUnFilterVram [17h] ======================================
@ DECL: void Diff8bitUnFilterVram(const void *src, void *dst);
@ DESC:
BEGIN_FUNC_THUMB(Diff8bitUnFilterVram, CSEC_TEXT)
swi 0x17
bx lr
END_FUNC(Diff8bitUnFilterVram)
@ === Diff16bitUnFilter [18h] =========================================
@ DECL: void Diff16bitUnFilter(const void *src, void *dst);
@ DESC:
BEGIN_FUNC_THUMB(Diff16bitUnFilter, CSEC_TEXT)
swi 0x18
bx lr
END_FUNC(Diff16bitUnFilter)
@ === SoundBias [19h] =================================================
@ DECL: void SoundBias(u32 bias);
@ DESC:
BEGIN_FUNC_THUMB(SoundBias, CSEC_TEXT)
swi 0x19
bx lr
END_FUNC(SoundBias)
@ === SoundDriverInit [1Ah] ===========================================
@ DECL: void SoundDriverInit(void *src);
@ DESC:
BEGIN_FUNC_THUMB(SoundDriverInit, CSEC_TEXT)
swi 0x1A
bx lr
END_FUNC(SoundDriverInit)
@ === SoundDriverMode [1Bh] ===========================================
@ DECL: void SoundDriverMode(u32 mode);
@ DESC:
BEGIN_FUNC_THUMB(SoundDriverMode, CSEC_TEXT)
swi 0x1B
bx lr
END_FUNC(SoundDriverMode)
@ === SoundDriverMain [1Ch] ===========================================
@ DECL: void SoundDriverMain();
@ DESC:
BEGIN_FUNC_THUMB(SoundDriverMain, CSEC_TEXT)
swi 0x1C
bx lr
END_FUNC(SoundDriverMain)
@ === SoundDriverVSync [1Dh] ==========================================
@ DECL: void SoundDriverVSync();
@ DESC:
BEGIN_FUNC_THUMB(SoundDriverVSync, CSEC_TEXT)
swi 0x1D
bx lr
END_FUNC(SoundDriverVSync)
@ === SoundChannelClear [1Eh] =========================================
@ DECL: void SoundChannelClear();
@ DESC:
BEGIN_FUNC_THUMB(SoundChannelClear, CSEC_TEXT)
swi 0x1E
bx lr
END_FUNC(SoundChannelClear)
@ === MidiKey2Freq [1Fh] ==============================================
@ DECL: u32 MidiKey2Freq(void *wa, u8 mk, u8 fp);
@ DESC:
BEGIN_FUNC_THUMB(MidiKey2Freq, CSEC_TEXT)
swi 0x1F
bx lr
END_FUNC(MidiKey2Freq)
@ === MultiBoot [25h] =================================================
@ DECL: int MultiBoot(MultiBootParam* mb, u32 mode);
@ DESC:
BEGIN_FUNC_THUMB(MultiBoot, CSEC_TEXT)
swi 0x25
bx lr
END_FUNC(MultiBoot)
@ === SoundDriverVSyncOff [28h] =======================================
@ DECL: void SoundDriverVSyncOff();
@ DESC:
BEGIN_FUNC_THUMB(SoundDriverVSyncOff, CSEC_TEXT)
swi 0x28
bx lr
END_FUNC(SoundDriverVSyncOff)
@ === SoundDriverVSyncOn [29h] ========================================
@ DECL: void SoundDriverVSyncOn();
@ DESC:
BEGIN_FUNC_THUMB(SoundDriverVSyncOn, CSEC_TEXT)
swi 0x29
bx lr
END_FUNC(SoundDriverVSyncOn)

150
engine/src/gba_engine.cpp Normal file
View File

@ -0,0 +1,150 @@
//
// Created by Wouter Groeneveld on 28/07/18.
//
#include <libgba-sprite-engine/gba/tonc_memdef.h>
#include <libgba-sprite-engine/gba_engine.h>
std::unique_ptr<SoundControl> GBAEngine::activeChannelA;
std::unique_ptr<SoundControl> GBAEngine::activeChannelB;
std::unique_ptr<Timer> GBAEngine::timer;
void GBAEngine::vsync() {
while (REG_VCOUNT >= 160);
while (REG_VCOUNT < 160);
}
Timer* GBAEngine::getTimer() {
return GBAEngine::timer.get();
}
void GBAEngine::onVBlank() {
// WARNING this is a very dangerous piece of code.
// GBA IRQs seem eager to crash or eat up CPU. Get in, disable stuff, work, enable, get out!
stopOnVBlank();
unsigned short tempInterruptState = REG_IF;
if((REG_IF & INTERRUPT_VBLANK) == INTERRUPT_VBLANK) {
GBAEngine::timer->onvblank();
if(GBAEngine::activeChannelA) {
if(GBAEngine::activeChannelA->done()) {
GBAEngine::activeChannelA->reset();
} else {
GBAEngine::activeChannelA->step();
}
}
if(GBAEngine::activeChannelB) {
if(GBAEngine::activeChannelB->done()) {
GBAEngine::activeChannelB->disable();
GBAEngine::activeChannelB = nullptr; // never delete, let unique_ptr do that, known to flip here
} else {
GBAEngine::activeChannelB->step();
}
}
}
REG_IF = tempInterruptState;
startOnVBlank();
}
u16 GBAEngine::readKeys() {
return ~REG_KEYS & KEY_ANY;
}
void GBAEngine::dequeueAllSounds() {
if(GBAEngine::activeChannelA) {
GBAEngine::activeChannelA->disable();
} if(GBAEngine::activeChannelB) {
GBAEngine::activeChannelB->disable();
}
}
void GBAEngine::enqueueSound(const s8 *data, int totalSamples, int sampleRate, SoundChannel channel) {
SoundControl* control;
if(channel == ChannelA) { // repeating bg music can be restarted
GBAEngine::activeChannelA = SoundControl::channelAControl();
control = GBAEngine::activeChannelA.get();
} else { // sound still playing, don't stop that
if(GBAEngine::activeChannelB) {
if(!GBAEngine::activeChannelB->done()) return;
GBAEngine::activeChannelB = nullptr;
}
GBAEngine::activeChannelB = SoundControl::channelBControl();
control = GBAEngine::activeChannelB.get();
}
disableTimer0AndVBlank();
control->disable();
REG_SNDDSCNT |= control->getControlFlags(); // output to both sides, reset fifo
REG_SNDSTAT = SSTAT_ENABLE; // enable all sound
u16 ticksPerSample = CLOCK / sampleRate; // divide the clock (ticks/second) by the sample rate (samples/second)
control->accept(data, totalSamples, ticksPerSample);
control->enable();
REG_TM0D = OVERFLOW_16_BIT_VALUE - ticksPerSample;
enableTimer0AndVBlank();
}
void GBAEngine::disableTimer0AndVBlank() {
stopOnVBlank();
REG_TM0CNT = 0;
}
void GBAEngine::enableTimer0AndVBlank() {
REG_TM0CNT = TM_ENABLE | TM_FREQ_1; // enable timer - dma auto-syncs to this thanks to DMA_SYNC_TO_TIMER
startOnVBlank();
}
GBAEngine::GBAEngine() {
GBAEngine::timer = std::unique_ptr<Timer>(new Timer());
// setup screen control flags
REG_DISPCNT = DCNT_MODE4 | DCNT_OBJ | DCNT_OBJ_1D | DCNT_BG0 | DCNT_BG1 | DCNT_BG2 | DCNT_BG3;
// setup interrupt control flags for vblank IRQing (started only when sound played)
REG_DISPSTAT |= DISPLAY_INTERRUPT_VBLANK_ENABLE;
REG_IE |= INTERRUPT_VBLANK;
*IRQ_CALLBACK = (u32) &GBAEngine::onVBlank;
enableTimer0AndVBlank();
REG_SNDDSCNT = 0;
}
void GBAEngine::update() {
// main update loop, in while(true) {}.
// WARNING - keep amount of instructions as minimal as possible in here!
u16 keys = readKeys();
// main scene update loop call. This *might* take a while.
currentScene->tick(keys);
// TODO use software interrupt Vsyncing instead of 2 wasteful whiles
vsync();
}
void GBAEngine::cleanupPreviousScene() {
delete currentScene;
sceneToTransitionTo = nullptr;
}
void GBAEngine::setScene(Scene* scene) {
dequeueAllSounds();
if(this->currentScene) {
cleanupPreviousScene();
}
scene->load();
auto fgPalette = scene->getForegroundPalette();
fgPalette->persist();
auto bgPalette = scene->getBackgroundPalette();
bgPalette->persist();
this->currentScene = scene;
}

88
engine/src/gbavector.cpp Normal file
View File

@ -0,0 +1,88 @@
//
// Created by Wouter Groeneveld on 14/12/18.
//
#include <libgba-sprite-engine/gbavector.h>
VECTOR GBAVector::rotateAsCenter(VECTOR point, uint angle) {
auto center = this->v;
s32 centerx = center.x, centery = center.y;
s32 defaultx = point.x, defaulty = point.y;
s32 cos = lu_cos(angle) >> 4;
s32 sin = lu_sin(angle) >> 4;
// affine matriches are 8.8 fixed point numbers, so shift all input 8 spaces up and forth
// possibilities: instead of between [-1.0, 1.0] it's between [-256, +256]
// 90° rotation in inversed y-axis needs to flip sin sign
return {
( cos * (defaultx - centerx) + sin * (defaulty - centery) + (centerx << 8)) >> 8,
(-sin * (defaultx - centerx) + cos * (defaulty - centery) + (centery << 8)) >> 8};
}
std::deque<VECTOR> GBAVector::bresenhamLineTo(VECTOR dest) {
// https://www.coranac.com/tonc/text/bitmaps.htm - Bresenham's line algorithm with fixed points
VECTOR src = this->v;
VECTOR step, delta;
std::deque<VECTOR> coords;
if(src.x > dest.x) {
step.x = -1;
delta.x = (src.x - dest.x);
} else {
step.x = +1;
delta.x = (dest.x - src.x);
}
if(src.y > dest.y) {
step.y = -1;
delta.y = (src.y - dest.y);
} else {
step.y = +1;
delta.y = (dest.y - src.y);
}
int dd, x = src.x, y = src.y, ii;
if(delta.y == 0) {
// horizontal
for(ii = 0; ii <= delta.x; ii++) {
coords.push_back({x, y});
x += step.x;
}
} else if(delta.x == 0) {
// vertical
for(ii = 0; ii <= delta.y; ii++) {
coords.push_back({x, y});
y += step.y;
}
} else if(delta.x >= delta.y) {
// Diagonal, slope <= 1
dd = 2 * delta.y - delta.x;
for(ii = 0; ii <= delta.x; ii++) {
coords.push_back({x, y});
if(dd >= 0) {
dd -= 2 * delta.x;
y += step.y;
}
dd += 2 * delta.y;
x += step.x;
}
} else {
// Diagonal, slope > 1
dd = 2 * delta.x - delta.y;
for(ii = 0; ii <= delta.y; ii++) {
coords.push_back({x, y});
if(dd >= 0) {
dd -= 2 * delta.y;
x += step.x;
}
dd += 2 * delta.x;
y += step.y;
}
}
return coords;
}

View File

@ -0,0 +1,22 @@
//
// Created by Wouter Groeneveld on 05/08/18.
//
#include <libgba-sprite-engine/palette/palette_manager.h>
void CombinedPalette::increaseBrightness(PaletteManager& palette, int bank, int index, u32 intensity) {
auto current = palette.get(bank, index);
auto next = PaletteManager::modify(current, intensity);
palette.change(bank, index, next);
}
void CombinedPalette::increaseBrightness(u32 intensity) {
for(int bank = 0; bank < PALETTE_BANK_SIZE; bank++) {
for(int index = 0; index < PALETTE_BANK_SIZE; index++) {
increaseBrightness(palette1, bank, index, intensity);
increaseBrightness(palette2, bank, index, intensity);
}
}
}

View File

@ -0,0 +1,96 @@
//
// Created by Wouter Groeneveld on 27/07/18.
//
#ifdef CODE_COMPILED_AS_PART_OF_TEST
#include <libgba-sprite-engine/gba/tonc_core_stub.h>
#else
#include <libgba-sprite-engine/gba/tonc_core.h>
#endif
#include <libgba-sprite-engine/palette/palette_manager.h>
const COLOR defaultPaletteData[PALETTE_MAX_SIZE] __attribute__((aligned(4))) = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
PaletteManager::PaletteManager() : PaletteManager(defaultPaletteData, PALETTE_MAX_SIZE) {
}
int getBits(int number, int k, int p) {
return (((1 << k) - 1) & (number >> p));
}
PaletteManager::PaletteManager(const COLOR *paletteData, int size) : data(paletteData), size(size) {
}
CombinedPalette* PaletteManager::operator+(const PaletteManager &other) {
return new CombinedPalette(*this, const_cast<PaletteManager&>(other));
}
void PaletteManager::persist() {
dma3_cpy(this->paletteAddress(), this->data, this->size);
}
COLOR PaletteManager::change(int bank, int index, COLOR newColor) {
auto palBank = this->paletteBank();
COLOR oldColor = palBank[bank][index];
palBank[bank][index] = newColor;
return oldColor;
}
COLOR PaletteManager::color(u32 red, u32 green, u32 blue) {
if(red > 31) red = 31;
if(green > 31) green = 31;
if(blue > 31) blue = 31;
return red | (green<<5) | (blue<<10);
}
u32 PaletteManager::red(COLOR r) {
return getBits(r, 5, 0);
}
u32 PaletteManager::green(COLOR r) {
return getBits(r, 5, 5);
}
u32 PaletteManager::blue(COLOR r) {
return getBits(r, 5, 10);
}
COLOR PaletteManager::modify(COLOR color, u32 intensity) {
return PaletteManager::color(PaletteManager::red(color) + intensity,
PaletteManager::green(color) + intensity, PaletteManager::blue(color) + intensity);
}
void PaletteManager::increaseBrightness(u32 intensity) {
for(int bank = 0; bank < PALETTE_BANK_SIZE; bank++) {
for(int index = 0; index < PALETTE_BANK_SIZE; index++) {
auto current = get(bank, index);
auto next = PaletteManager::modify(current, intensity);
change(bank, index, next);
}
}
}
void PaletteManager::persistToBank(int bank) {
auto palBank = this->paletteBank();
dma3_cpy(palBank[bank], this->data, PALETTE_BANK_SIZE);
}

7
engine/src/scene.cpp Normal file
View File

@ -0,0 +1,7 @@
//
// Created by Wouter Groeneveld on 09/08/18.
//
#include <libgba-sprite-engine/scene.h>
#include <libgba-sprite-engine/gba_engine.h>

View File

@ -0,0 +1,42 @@
//
// Created by Wouter Groeneveld on 07/08/18.
//
#include <libgba-sprite-engine/gba/tonc_memmap.h>
#include <libgba-sprite-engine/sound_control.h>
void SoundControl::accept(const void *data, int totalSamples, int ticksPerSample) {
this->data = data;
*DMASourceAddress = (u32) data;
*DMADestinationAddress = (u32) FiFoBuffer;
vblanksTotal = vblanksRemaning = totalSamples * ticksPerSample * (1.0 / CYCLES_PER_BLANK);
};
void SoundControl::reset() {
vblanksRemaning = vblanksTotal;
*(DMAControl) = 0;
*DMASourceAddress = (u32) data;
enable();
}
std::unique_ptr<SoundControl> SoundControl::channelAControl() {
return std::unique_ptr<SoundControl>(new SoundControl{
&REG_DMA1CNT,
&REG_DMA1SAD,
&REG_DMA1DAD,
&REG_FIFOA,
// Left/Right, timed, tone 100% vol, channel A 50%, channel B 100% (only one directsound reg)
SDS_AR | SDS_AL | SDS_ARESET | SDS_DMG100 | SDS_A50 | SDS_B100
});
}
std::unique_ptr<SoundControl> SoundControl::channelBControl() {
return std::unique_ptr<SoundControl>(new SoundControl{
&REG_DMA2CNT,
&REG_DMA2SAD,
&REG_DMA2DAD,
&REG_FIFOB,
// Left/Right, timed, tone 100% vol, channel A 50%, channel B 100% (only one directsound reg)
SDS_BR | SDS_BL | SDS_BRESET | SDS_DMG100 | SDS_A50 | SDS_B100
});
}

61
engine/src/timer.cpp Normal file
View File

@ -0,0 +1,61 @@
//
// Created by Wouter Groeneveld on 06/12/18.
//
#include <libgba-sprite-engine/timer.h>
#include <sstream>
void Timer::onvblank() {
if(!active) return;
// each VBlank occurs every 280806 cycles (CYCLES_PER_BLANK), each cycle is 59.59ns
// So, each VBlank occurs every 16.73322954 miliseconds or 16733.22954 microseconds
// So, each second is 59,76132686219041 vblanks.
// to avoid any big rounding errors, divisions and modulos, estimate!
msecs += 16;
microsecs += 733;
if(microsecs >= 1000) {
msecs++;
microsecs -= 1000;
}
if(msecs >= 1000) {
secs++;
msecs -= 1000;
}
if(secs >= 60) {
minutes++;
secs -= 60;
}
if(minutes >= 60) {
hours++;
minutes -= 60;
}
}
int Timer::getTotalMsecs() {
return msecs + (hours * 60 * 60 + minutes * 60 + secs) * 1000;
}
std::string Timer::to_string() {
std::ostringstream stringStream;
stringStream << *this;
return stringStream.str();
}
std::ostream& operator<<(std::ostream &os, Timer &timer) {
os << timer.hours << "h:" << timer.minutes << "m:" << timer.secs << "s:" << timer.msecs;
return os;
}
void Timer::reset() {
microsecs = msecs = secs = minutes = hours = 0;
}
void Timer::start() {
active = true;
}
void Timer::stop() {
active = false;
}

35
test/CMakeLists.txt Normal file
View File

@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.12)
SET(CMAKE_CXX_STANDARD 11)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing -Wno-int-to-pointer-cast")
set(CMAKE_VERBOSE_MAKEFILE on)
project(Unittest)
enable_testing()
SET(GTEST_LIBRARY "$ENV{GTEST_DIR}")
# reset linker flags; ARM + GTest doesn't work
SET(CMAKE_EXE_LINKER_FLAGS "${BASE_CMAKE_LINK_FLAGS}")
SET(CMAKE_C_COMPILER gcc)
SET(CMAKE_CXX_COMPILER g++)
add_definitions(-DCODE_COMPILED_AS_PART_OF_TEST)
include_directories(${GTEST_LIBRARY}/include)
include_directories(../engine/include)
# compile including library source because it's cross-compiled
add_executable(unittest
maintest.cpp
gbatest.cpp
palettetest.cpp
real_data.h
../engine/src/gba/sin_lut.s
../engine/src/palette/palette_manager.cpp
../engine/src/palette/combined_palette.cpp
../engine/src/timer.cpp
../engine/src/gbavector.cpp
timertest.cpp gbavectortest.cpp)
target_link_libraries(unittest ${GTEST_LIBRARY}/build/libgtest.a ${GTEST_LIBRARY}/build/libgtest_main.a)

16
test/gbatest.cpp Normal file
View File

@ -0,0 +1,16 @@
#include "gtest/gtest.h"
class GBASuite : public ::testing::Test {
protected:
protected:
virtual void TearDown() {
}
virtual void SetUp() {
}
};
TEST_F(GBASuite, AssertionWorks) {
ASSERT_TRUE(true);
}

154
test/gbavectortest.cpp Normal file
View File

@ -0,0 +1,154 @@
//
// Created by Wouter Groeneveld on 14/12/18.
//
#include <gtest/gtest.h>
#include <libgba-sprite-engine/gbavector.h>
class GBAVectorSuite : public ::testing::Test {
protected:
GBAVector vector;
VECTOR bottomHalf;
virtual void TearDown() {
}
virtual void SetUp() {
vector = GBAVector({120, 80});
bottomHalf = { 120, 200 };
}
};
// Angle in DEGREES for readability - converted in tonc_math_stub!
TEST_F(GBAVectorSuite, Rotate_FromBottomHalf_0_Degrees) {
auto result = vector.rotateAsCenter(bottomHalf, 0);
ASSERT_EQ(120, result.x);
ASSERT_EQ(200, result.y);
}
TEST_F(GBAVectorSuite, Rotate_FromBottomHalf_90_Degrees) {
auto result = vector.rotateAsCenter(bottomHalf, 90);
ASSERT_EQ(240, result.x);
ASSERT_EQ(80, result.y);
}
TEST_F(GBAVectorSuite, Rotate_FromBottomHalf_180_Degrees) {
auto result = vector.rotateAsCenter(bottomHalf, 180);
ASSERT_EQ(120, result.x);
ASSERT_EQ(-40, result.y);
}
TEST_F(GBAVectorSuite, Rotate_FromBottomHalf_270_Degrees) {
auto result = vector.rotateAsCenter(bottomHalf, 270);
ASSERT_EQ(0, result.x);
ASSERT_EQ(80, result.y);
}
// ---- //
TEST_F(GBAVectorSuite, ToString) {
ASSERT_EQ(std::string("(120,80)"), vector.to_string());
}
TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToTopRightCorner) {
auto points = vector.bresenhamLineTo({240, 0});
ASSERT_EQ(121, points.size());
VECTOR pt;
// some sampling
pt = points.at(1);
ASSERT_EQ(121, pt.x);
ASSERT_EQ(79, pt.y);
pt = points.at(2);
ASSERT_EQ(122, pt.x);
ASSERT_EQ(79, pt.y);
pt = points.at(10);
ASSERT_EQ(130, pt.x);
ASSERT_EQ(73, pt.y);
pt = points.at(100);
ASSERT_EQ(220, pt.x);
ASSERT_EQ(13, pt.y);
}
TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToTopLeftCorner) {
auto points = vector.bresenhamLineTo({0, 0});
ASSERT_EQ(121, points.size());
VECTOR pt;
// some sampling
pt = points.at(1);
ASSERT_EQ(119, pt.x);
ASSERT_EQ(79, pt.y);
pt = points.at(2);
ASSERT_EQ(118, pt.x);
ASSERT_EQ(79, pt.y);
pt = points.at(10);
ASSERT_EQ(110, pt.x);
ASSERT_EQ(73, pt.y);
pt = points.at(100);
ASSERT_EQ(20, pt.x);
ASSERT_EQ(13, pt.y);
}
TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToBottomLeftCorner) {
auto points = vector.bresenhamLineTo({0, 160});
ASSERT_EQ(121, points.size());
VECTOR pt;
// some sampling
pt = points.at(1);
ASSERT_EQ(119, pt.x);
ASSERT_EQ(81, pt.y);
pt = points.at(2);
ASSERT_EQ(118, pt.x);
ASSERT_EQ(81, pt.y);
pt = points.at(10);
ASSERT_EQ(110, pt.x);
ASSERT_EQ(87, pt.y);
pt = points.at(100);
ASSERT_EQ(20, pt.x);
ASSERT_EQ(147, pt.y);
}
TEST_F(GBAVectorSuite, LineBetween_Diagonal_ToBottomRightCorner) {
auto points = vector.bresenhamLineTo({240, 160});
ASSERT_EQ(121, points.size());
VECTOR pt;
// some sampling
pt = points.at(1);
ASSERT_EQ(121, pt.x);
ASSERT_EQ(81, pt.y);
pt = points.at(2);
ASSERT_EQ(122, pt.x);
ASSERT_EQ(81, pt.y);
pt = points.at(10);
ASSERT_EQ(130, pt.x);
ASSERT_EQ(87, pt.y);
pt = points.at(100);
ASSERT_EQ(220, pt.x);
ASSERT_EQ(147, pt.y);
}
TEST_F(GBAVectorSuite, LineBetween_Horizontal_FromCenterToHalfYZeroX) {
auto points = vector.bresenhamLineTo({0, 80});
ASSERT_EQ(121, points.size());
for(int i = 0; i <= 120; i++) {
auto &vec = points.front();
ASSERT_EQ(80, vec.y);
ASSERT_EQ(120 - i, vec.x);
points.pop_front();
}
}
TEST_F(GBAVectorSuite, LineBetween_Vertical_FromCenterToHalfXFullY) {
auto points = vector.bresenhamLineTo({120, 160});
ASSERT_EQ(81, points.size());
for(int i = 0; i <= (160 - 80); i++) {
auto &vec = points.front();
ASSERT_EQ(120, vec.x);
ASSERT_EQ(80 + i, vec.y);
points.pop_front();
}
}

6
test/maintest.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "gtest/gtest.h"
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

69
test/palettetest.cpp Normal file
View File

@ -0,0 +1,69 @@
//
// Created by Wouter Groeneveld on 04/08/18.
//
#include <libgba-sprite-engine/palette/palette_manager.h>
#include "gtest/gtest.h"
class SomePaletteManager : public PaletteManager {
public:
PALBANK* palette;
SomePaletteManager() : SomePaletteManager({}) {};
SomePaletteManager(const u16 *paletteData) : PaletteManager(paletteData) {
palette = (PALBANK*) malloc(sizeof(PALBANK) * 16);
}
virtual ~SomePaletteManager() {
delete[] palette;
}
protected:
PALBANK *paletteBank() override {
return palette;
}
void *paletteAddress() override {
return nullptr;
}
};
class PaletteSuite : public ::testing::Test {
protected:
SomePaletteManager* palette;
virtual void TearDown() {
delete palette;
}
virtual void SetUp() {
palette = new SomePaletteManager();
}
};
TEST_F(PaletteSuite, Increase_Intensity_Updates_For_RGB_Values) {
palette->palette[0][0] = PaletteManager::color(10, 10, 10);
palette->palette[1][1] = PaletteManager::color(11, 11, 11);
palette->palette[2][2] = PaletteManager::color(12, 12, 12);
palette->increaseBrightness(1);
ASSERT_EQ(11, PaletteManager::red(palette->palette[0][0]));
ASSERT_EQ(11, PaletteManager::green(palette->palette[0][0]));
ASSERT_EQ(11, PaletteManager::blue(palette->palette[0][0]));
ASSERT_EQ(12, PaletteManager::red(palette->palette[1][1]));
ASSERT_EQ(12, PaletteManager::green(palette->palette[1][1]));
ASSERT_EQ(12, PaletteManager::blue(palette->palette[1][1]));
ASSERT_EQ(13, PaletteManager::red(palette->palette[2][2]));
ASSERT_EQ(13, PaletteManager::green(palette->palette[2][2]));
ASSERT_EQ(13, PaletteManager::blue(palette->palette[2][2]));
}
TEST_F(PaletteSuite, Extract_Individual_Colors) {
auto color = PaletteManager::color(10, 20, 30);
ASSERT_EQ(10, PaletteManager::red(color));
ASSERT_EQ(20, PaletteManager::green(color));
ASSERT_EQ(30, PaletteManager::blue(color));
}

53
test/real_data.h Normal file
View File

@ -0,0 +1,53 @@
const unsigned short sharedPal[84] __attribute__((aligned(4)))=
{
0x0000,0x3AE2,0x7BBD,0x4042,0x008C,0x3614,0x633B,0x1151,
0x36FD,0x4AB8,0x21B2,0x77BE,0x3E56,0x0D0F,0x1971,0x56FA,
0x6B7D,0x2DD3,0x7FFF,0x04EE,0x1550,0x1593,0x4697,0x677C,
0x52D9,0x5B1A,0x112F,0x1DF5,0x08EE,0x31F4,0x3635,0x08EF,
0x739D,0x7BDF,0x371D,0x29B3,0x4677,0x00CD,0x2192,0x5F1A,
0x1950,0x675B,0x1130,0x77DE,0x4276,0x3214,0x04CE,0x4EB8,
0x4A97,0x56F9,0x25B2,0x3A35,0x5F3B,0x635B,0x3615,0x73BE,
0x2DF4,0x1D91,0x25B3,0x6F9D,0x4ED8,0x6B5C,0x1530,0x56D9,
0x6B7C,0x29D3,0x04CD,0x3A36,0x7BFF,0x00AD,0x4277,0x675C,
0x1D71,0x73BD,0x5F1B,0x4256,0x1951,0x5AFA,0x7BDE,0x4A98,
0x6F7D,0x2DF3,0x090F,
};
const unsigned short bg_palette [] = {
0x7c1f, 0x7e4b, 0x0539, 0x1e7f, 0x0000, 0x5aff, 0x7fff, 0x7ee7, 0x02a0,
0x0b50, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000
};

46
test/timertest.cpp Normal file
View File

@ -0,0 +1,46 @@
//
// Created by Wouter Groeneveld on 06/12/18.
//
#include <gtest/gtest.h>
#include <libgba-sprite-engine/timer.h>
class TimerSuite : public ::testing::Test {
protected:
Timer *t;
virtual void TearDown() {
delete t;
}
virtual void SetUp() {
t = new Timer();
}
};
TEST_F(TimerSuite, OnVBlankIncreasesMsecsAmountOfTimeIfStarted) {
t->start();
t->onvblank();
t->stop();
t->onvblank();
ASSERT_EQ(t->getMsecs(), 16);
}
TEST_F(TimerSuite, OnVBlankAfterEnoughTimesToGetToSec) {
t->start();
for(int i = 0; i < 100; i++) {
t->onvblank();
}
ASSERT_EQ(t->getSecs(), 1);
ASSERT_EQ(t->getMsecs(), 673);
}
TEST_F(TimerSuite, OnVBlankDoesNothingIfNotEnabled) {
t->onvblank();
ASSERT_EQ(t->getMsecs(), 0);
}