From 27e6a9065ccef167c28ea662013b27051cbdc83f Mon Sep 17 00:00:00 2001 From: wgroeneveld Date: Wed, 8 Jul 2020 09:05:02 +0200 Subject: [PATCH] initial commit, stripped-down version of gba-sprite-engine --- .gitignore | 121 ++- CMakeLists.txt | 31 + README.md | 5 + demos/demo1-wireframes/CMakeLists.txt | 13 + demos/demo1-wireframes/src/main.cpp | 19 + demos/demo1-wireframes/src/wirescene.cpp | 21 + demos/demo1-wireframes/src/wirescene.h | 23 + engine/CMakeLists.txt | 22 + .../libgba-sprite-engine/gba/tonc_asminc.h | 132 +++ .../libgba-sprite-engine/gba/tonc_bios.h | 355 +++++++ .../libgba-sprite-engine/gba/tonc_core.h | 573 +++++++++++ .../libgba-sprite-engine/gba/tonc_core_stub.h | 573 +++++++++++ .../libgba-sprite-engine/gba/tonc_math.h | 691 +++++++++++++ .../libgba-sprite-engine/gba/tonc_math_stub.h | 702 +++++++++++++ .../libgba-sprite-engine/gba/tonc_memdef.h | 962 ++++++++++++++++++ .../libgba-sprite-engine/gba/tonc_memmap.h | 583 +++++++++++ .../libgba-sprite-engine/gba/tonc_oam.h | 231 +++++ .../libgba-sprite-engine/gba/tonc_types.h | 377 +++++++ .../include/libgba-sprite-engine/gba_engine.h | 60 ++ .../include/libgba-sprite-engine/gbavector.h | 33 + .../palette/combined_palette.h | 25 + .../palette/palette_manager.h | 79 ++ engine/include/libgba-sprite-engine/scene.h | 39 + .../libgba-sprite-engine/sound_control.h | 67 ++ engine/include/libgba-sprite-engine/timer.h | 42 + engine/src/gba/sin_lut.s | 90 ++ engine/src/gba/tonc_bios.s | 288 ++++++ engine/src/gba_engine.cpp | 150 +++ engine/src/gbavector.cpp | 88 ++ engine/src/palette/combined_palette.cpp | 22 + engine/src/palette/palette_manager.cpp | 96 ++ engine/src/scene.cpp | 7 + engine/src/sound_control.cpp | 42 + engine/src/timer.cpp | 61 ++ test/CMakeLists.txt | 35 + test/gbatest.cpp | 16 + test/gbavectortest.cpp | 154 +++ test/maintest.cpp | 6 + test/palettetest.cpp | 69 ++ test/real_data.h | 53 + test/timertest.cpp | 46 + 41 files changed, 7000 insertions(+), 2 deletions(-) create mode 100755 CMakeLists.txt create mode 100644 README.md create mode 100644 demos/demo1-wireframes/CMakeLists.txt create mode 100644 demos/demo1-wireframes/src/main.cpp create mode 100644 demos/demo1-wireframes/src/wirescene.cpp create mode 100644 demos/demo1-wireframes/src/wirescene.h create mode 100644 engine/CMakeLists.txt create mode 100644 engine/include/libgba-sprite-engine/gba/tonc_asminc.h create mode 100644 engine/include/libgba-sprite-engine/gba/tonc_bios.h create mode 100644 engine/include/libgba-sprite-engine/gba/tonc_core.h create mode 100644 engine/include/libgba-sprite-engine/gba/tonc_core_stub.h create mode 100644 engine/include/libgba-sprite-engine/gba/tonc_math.h create mode 100644 engine/include/libgba-sprite-engine/gba/tonc_math_stub.h create mode 100644 engine/include/libgba-sprite-engine/gba/tonc_memdef.h create mode 100644 engine/include/libgba-sprite-engine/gba/tonc_memmap.h create mode 100644 engine/include/libgba-sprite-engine/gba/tonc_oam.h create mode 100644 engine/include/libgba-sprite-engine/gba/tonc_types.h create mode 100644 engine/include/libgba-sprite-engine/gba_engine.h create mode 100644 engine/include/libgba-sprite-engine/gbavector.h create mode 100644 engine/include/libgba-sprite-engine/palette/combined_palette.h create mode 100644 engine/include/libgba-sprite-engine/palette/palette_manager.h create mode 100644 engine/include/libgba-sprite-engine/scene.h create mode 100644 engine/include/libgba-sprite-engine/sound_control.h create mode 100644 engine/include/libgba-sprite-engine/timer.h create mode 100644 engine/src/gba/sin_lut.s create mode 100644 engine/src/gba/tonc_bios.s create mode 100644 engine/src/gba_engine.cpp create mode 100644 engine/src/gbavector.cpp create mode 100644 engine/src/palette/combined_palette.cpp create mode 100644 engine/src/palette/palette_manager.cpp create mode 100644 engine/src/scene.cpp create mode 100644 engine/src/sound_control.cpp create mode 100644 engine/src/timer.cpp create mode 100644 test/CMakeLists.txt create mode 100644 test/gbatest.cpp create mode 100644 test/gbavectortest.cpp create mode 100644 test/maintest.cpp create mode 100644 test/palettetest.cpp create mode 100644 test/real_data.h create mode 100644 test/timertest.cpp diff --git a/.gitignore b/.gitignore index 46f42f8..f6ed92e 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..fb23eb7 --- /dev/null +++ b/CMakeLists.txt @@ -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) diff --git a/README.md b/README.md new file mode 100644 index 0000000..f8830a0 --- /dev/null +++ b/README.md @@ -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. diff --git a/demos/demo1-wireframes/CMakeLists.txt b/demos/demo1-wireframes/CMakeLists.txt new file mode 100644 index 0000000..ba3f0fd --- /dev/null +++ b/demos/demo1-wireframes/CMakeLists.txt @@ -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 + ) diff --git a/demos/demo1-wireframes/src/main.cpp b/demos/demo1-wireframes/src/main.cpp new file mode 100644 index 0000000..fcd8c2d --- /dev/null +++ b/demos/demo1-wireframes/src/main.cpp @@ -0,0 +1,19 @@ +#include +#include +#include + +#include "wirescene.h" + +int main() { + std::shared_ptr engine(new GBAEngine()); + + WireScene* startScene = new WireScene(engine); + engine->setScene(startScene); + + while (true) { + engine->update(); + engine->delay(1000); + } + + return 0; +} \ No newline at end of file diff --git a/demos/demo1-wireframes/src/wirescene.cpp b/demos/demo1-wireframes/src/wirescene.cpp new file mode 100644 index 0000000..cd9a825 --- /dev/null +++ b/demos/demo1-wireframes/src/wirescene.cpp @@ -0,0 +1,21 @@ +// +// Created by Wouter Groeneveld on 02/08/18. +// + +#include +#include +#include "wirescene.h" + + +const unsigned short pal[3] __attribute__((aligned(4))) = { + 0x0000, 0xFFFF, 0x3AE2 +}; + +void WireScene::load() { + foregroundPalette = std::unique_ptr(new ForegroundPaletteManager()); + backgroundPalette = std::unique_ptr(new BackgroundPaletteManager(pal, sizeof(pal))); +} + +void WireScene::tick(u16 keys) { + +} diff --git a/demos/demo1-wireframes/src/wirescene.h b/demos/demo1-wireframes/src/wirescene.h new file mode 100644 index 0000000..93e7fb4 --- /dev/null +++ b/demos/demo1-wireframes/src/wirescene.h @@ -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 + +class WireScene : public Scene { +private: + +public: + + WireScene(std::shared_ptr engine) : Scene(engine) {} + + void load() override; + void tick(u16 keys) override; + +}; + + +#endif //GBA_SPRITE_ENGINE_SAMPLE_START_SCENE_H diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt new file mode 100644 index 0000000..31cd043 --- /dev/null +++ b/engine/CMakeLists.txt @@ -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 + $ + 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}) diff --git a/engine/include/libgba-sprite-engine/gba/tonc_asminc.h b/engine/include/libgba-sprite-engine/gba/tonc_asminc.h new file mode 100644 index 0000000..b1bc28a --- /dev/null +++ b/engine/include/libgba-sprite-engine/gba/tonc_asminc.h @@ -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 //tonc:keys + and especially + gbatek:bios. + + \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 \ No newline at end of file diff --git a/engine/include/libgba-sprite-engine/gba/tonc_core.h b/engine/include/libgba-sprite-engine/gba/tonc_core.h new file mode 100644 index 0000000..c3ce2f3 --- /dev/null +++ b/engine/include/libgba-sprite-engine/gba/tonc_core.h @@ -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 foo_SHIFT and +* foo_SHIFT macros indicating the mask and shift values +* of the bitfield named foo 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 ) + +//! 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 memset32() if \a hwcount>5 +* \param dst Destination address. +* \param hw Source halfword (not address). +* \param hwcount Number of halfwords to fill. +* \note \a dst must 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 memcpy32() 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 must 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 must 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 must 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<>shift) & ( (1<>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 +1 if \a plus bit is set but \a minus bit isn't
+ -1 if \a minus bit is set and \a plus bit isn't
+ 0 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 + diff --git a/engine/include/libgba-sprite-engine/gba/tonc_core_stub.h b/engine/include/libgba-sprite-engine/gba/tonc_core_stub.h new file mode 100644 index 0000000..cb35d9b --- /dev/null +++ b/engine/include/libgba-sprite-engine/gba/tonc_core_stub.h @@ -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 foo_SHIFT and +* foo_SHIFT macros indicating the mask and shift values +* of the bitfield named foo 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 ) + +//! 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 memset32() if \a hwcount>5 +* \param dst Destination address. +* \param hw Source halfword (not address). +* \param hwcount Number of halfwords to fill. +* \note \a dst must 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 memcpy32() 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 must 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 must 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 must 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<>shift) & ( (1<>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 +1 if \a plus bit is set but \a minus bit isn't
+ -1 if \a minus bit is set and \a plus bit isn't
+ 0 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 + diff --git a/engine/include/libgba-sprite-engine/gba/tonc_math.h b/engine/include/libgba-sprite-engine/gba/tonc_math.h new file mode 100644 index 0000000..087ed7a --- /dev/null +++ b/engine/include/libgba-sprite-engine/gba/tonc_math.h @@ -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<fp 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, +* 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 +INLINE int wrap(int x, int min, int max) +{ return (x>=max) ? (x+min-max) : ( (x>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)<>7)&0x1FF]; } + +//! Look-up a cosine value (2π = 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(x). +* 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 ); +} + +//! 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 ); +} + + +// --- 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 × \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 z-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 · \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) �\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 �\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) �\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 diff --git a/engine/include/libgba-sprite-engine/gba/tonc_math_stub.h b/engine/include/libgba-sprite-engine/gba/tonc_math_stub.h new file mode 100644 index 0000000..0e0f85b --- /dev/null +++ b/engine/include/libgba-sprite-engine/gba/tonc_math_stub.h @@ -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 +#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<fp 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, +* 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 +INLINE int wrap(int x, int min, int max) +{ return (x>=max) ? (x+min-max) : ( (x>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)<x). +* 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 ); +} + +//! 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 ); +} + + +// --- 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 × \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 z-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 · \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) �\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 �\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) �\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 diff --git a/engine/include/libgba-sprite-engine/gba/tonc_memdef.h b/engine/include/libgba-sprite-engine/gba/tonc_memdef.h new file mode 100644 index 0000000..ed9f9a4 --- /dev/null +++ b/engine/include/libgba-sprite-engine/gba/tonc_memdef.h @@ -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)< + 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[ ] )
+ 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[ ] )
+ tile_mem[y][x] = block y, tile x ( TILE ) +*/ +#define tile_mem ( (CHARBLOCK*)MEM_VRAM) + +//! Charblocks, 8bpp tiles. +/*! tile_mem[y] = charblock y ( TILE[ ] )
+ 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[ ] )
+ 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[ ] )
+ 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[ ] )
+* 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[ ][ ] )
+ 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 diff --git a/engine/include/libgba-sprite-engine/gba/tonc_oam.h b/engine/include/libgba-sprite-engine/gba/tonc_oam.h new file mode 100644 index 0000000..f3212c1 --- /dev/null +++ b/engine/include/libgba-sprite-engine/gba/tonc_oam.h @@ -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; iipa= 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 + diff --git a/engine/include/libgba-sprite-engine/gba/tonc_types.h b/engine/include/libgba-sprite-engine/gba/tonc_types.h new file mode 100644 index 0000000..38c4d77 --- /dev/null +++ b/engine/include/libgba-sprite-engine/gba/tonc_types.h @@ -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 non-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. +
    +
  • s# : signed #-bit integer.
  • +
  • u#/u{type} : unsigned #-bit integer.
  • +
  • e{type} : enum'ed #-bit integer.
  • + +
+*/ +//\{ +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 + diff --git a/engine/include/libgba-sprite-engine/gba_engine.h b/engine/include/libgba-sprite-engine/gba_engine.h new file mode 100644 index 0000000..24675bd --- /dev/null +++ b/engine/include/libgba-sprite-engine/gba_engine.h @@ -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 +#include +#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; + static std::unique_ptr activeChannelA; + static std::unique_ptr 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 diff --git a/engine/include/libgba-sprite-engine/gbavector.h b/engine/include/libgba-sprite-engine/gbavector.h new file mode 100644 index 0000000..4940dd5 --- /dev/null +++ b/engine/include/libgba-sprite-engine/gbavector.h @@ -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 + +#ifdef CODE_COMPILED_AS_PART_OF_TEST +#include +#else +#include +#endif + +#include + +class GBAVector { +private: + VECTOR v; +public: + GBAVector() : v({}) {} + GBAVector(VECTOR v) : v(v) {} + + std::deque 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 diff --git a/engine/include/libgba-sprite-engine/palette/combined_palette.h b/engine/include/libgba-sprite-engine/palette/combined_palette.h new file mode 100644 index 0000000..79709dc --- /dev/null +++ b/engine/include/libgba-sprite-engine/palette/combined_palette.h @@ -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 diff --git a/engine/include/libgba-sprite-engine/palette/palette_manager.h b/engine/include/libgba-sprite-engine/palette/palette_manager.h new file mode 100644 index 0000000..dfd6b99 --- /dev/null +++ b/engine/include/libgba-sprite-engine/palette/palette_manager.h @@ -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 +#include +#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 diff --git a/engine/include/libgba-sprite-engine/scene.h b/engine/include/libgba-sprite-engine/scene.h new file mode 100644 index 0000000..712ab92 --- /dev/null +++ b/engine/include/libgba-sprite-engine/scene.h @@ -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 +#include +#include +#include + +class GBAEngine; + +class Scene { +protected: + std::unique_ptr foregroundPalette; + std::unique_ptr backgroundPalette; + std::shared_ptr 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 engine) : + engine(engine), + foregroundPalette(std::unique_ptr(new ForegroundPaletteManager())), + backgroundPalette(std::unique_ptr(new BackgroundPaletteManager())) { } + virtual ~Scene() { + // scenes should manage their own resources - use std::unique_ptr + } + +}; + + +#endif //GBA_SPRITE_ENGINE_SCRENE_H diff --git a/engine/include/libgba-sprite-engine/sound_control.h b/engine/include/libgba-sprite-engine/sound_control.h new file mode 100644 index 0000000..8c1f275 --- /dev/null +++ b/engine/include/libgba-sprite-engine/sound_control.h @@ -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 +#include +#include +#include + +#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. ®_DMA1CNT + vu32* DMASourceAddress; // ex. ®_DMA1SAD + vu32* DMADestinationAddress; // ex. ®_DMA1DAD + vu32* FiFoBuffer; // ex. ®_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 channelAControl(); + static std::unique_ptr channelBControl(); + + static std::unique_ptr soundControl(SoundChannel channel) { + return channel == ChannelA ? channelAControl() : channelBControl(); + }; +}; + +#endif //GBA_SPRITE_ENGINE_SOUND_H diff --git a/engine/include/libgba-sprite-engine/timer.h b/engine/include/libgba-sprite-engine/timer.h new file mode 100644 index 0000000..7389318 --- /dev/null +++ b/engine/include/libgba-sprite-engine/timer.h @@ -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 + +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 diff --git a/engine/src/gba/sin_lut.s b/engine/src/gba/sin_lut.s new file mode 100644 index 0000000..21d41ae --- /dev/null +++ b/engine/src/gba/sin_lut.s @@ -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 diff --git a/engine/src/gba/tonc_bios.s b/engine/src/gba/tonc_bios.s new file mode 100644 index 0000000..d572631 --- /dev/null +++ b/engine/src/gba/tonc_bios.s @@ -0,0 +1,288 @@ +// +// Main GBA BIOS functions. +// +//! \file tonc_bios.s +//! \author J Vijn +//! \date 20071130 - 20090801 + +#include + +@ === 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) diff --git a/engine/src/gba_engine.cpp b/engine/src/gba_engine.cpp new file mode 100644 index 0000000..86869b8 --- /dev/null +++ b/engine/src/gba_engine.cpp @@ -0,0 +1,150 @@ +// +// Created by Wouter Groeneveld on 28/07/18. +// + +#include +#include + +std::unique_ptr GBAEngine::activeChannelA; +std::unique_ptr GBAEngine::activeChannelB; +std::unique_ptr 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(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; +} diff --git a/engine/src/gbavector.cpp b/engine/src/gbavector.cpp new file mode 100644 index 0000000..ae48dc6 --- /dev/null +++ b/engine/src/gbavector.cpp @@ -0,0 +1,88 @@ +// +// Created by Wouter Groeneveld on 14/12/18. +// + +#include + +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 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 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; +} \ No newline at end of file diff --git a/engine/src/palette/combined_palette.cpp b/engine/src/palette/combined_palette.cpp new file mode 100644 index 0000000..c918a67 --- /dev/null +++ b/engine/src/palette/combined_palette.cpp @@ -0,0 +1,22 @@ +// +// Created by Wouter Groeneveld on 05/08/18. +// + + +#include + +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); + } + } +} \ No newline at end of file diff --git a/engine/src/palette/palette_manager.cpp b/engine/src/palette/palette_manager.cpp new file mode 100644 index 0000000..2303ef9 --- /dev/null +++ b/engine/src/palette/palette_manager.cpp @@ -0,0 +1,96 @@ +// +// Created by Wouter Groeneveld on 27/07/18. +// + +#ifdef CODE_COMPILED_AS_PART_OF_TEST +#include +#else +#include +#endif +#include + +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(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); +} \ No newline at end of file diff --git a/engine/src/scene.cpp b/engine/src/scene.cpp new file mode 100644 index 0000000..ad9c42d --- /dev/null +++ b/engine/src/scene.cpp @@ -0,0 +1,7 @@ +// +// Created by Wouter Groeneveld on 09/08/18. +// + +#include +#include + diff --git a/engine/src/sound_control.cpp b/engine/src/sound_control.cpp new file mode 100644 index 0000000..017cd28 --- /dev/null +++ b/engine/src/sound_control.cpp @@ -0,0 +1,42 @@ +// +// Created by Wouter Groeneveld on 07/08/18. +// + +#include +#include + +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::channelAControl() { + return std::unique_ptr(new SoundControl{ + ®_DMA1CNT, + ®_DMA1SAD, + ®_DMA1DAD, + ®_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::channelBControl() { + return std::unique_ptr(new SoundControl{ + ®_DMA2CNT, + ®_DMA2SAD, + ®_DMA2DAD, + ®_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 + }); +} diff --git a/engine/src/timer.cpp b/engine/src/timer.cpp new file mode 100644 index 0000000..a8908c1 --- /dev/null +++ b/engine/src/timer.cpp @@ -0,0 +1,61 @@ +// +// Created by Wouter Groeneveld on 06/12/18. +// + +#include +#include + +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; +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..2aae0e5 --- /dev/null +++ b/test/CMakeLists.txt @@ -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) diff --git a/test/gbatest.cpp b/test/gbatest.cpp new file mode 100644 index 0000000..efa193c --- /dev/null +++ b/test/gbatest.cpp @@ -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); +} diff --git a/test/gbavectortest.cpp b/test/gbavectortest.cpp new file mode 100644 index 0000000..2b864ed --- /dev/null +++ b/test/gbavectortest.cpp @@ -0,0 +1,154 @@ +// +// Created by Wouter Groeneveld on 14/12/18. +// + + + +#include +#include + +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(); + } +} diff --git a/test/maintest.cpp b/test/maintest.cpp new file mode 100644 index 0000000..5ad3123 --- /dev/null +++ b/test/maintest.cpp @@ -0,0 +1,6 @@ +#include "gtest/gtest.h" + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/test/palettetest.cpp b/test/palettetest.cpp new file mode 100644 index 0000000..54ae733 --- /dev/null +++ b/test/palettetest.cpp @@ -0,0 +1,69 @@ +// +// Created by Wouter Groeneveld on 04/08/18. +// + +#include +#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)); +} diff --git a/test/real_data.h b/test/real_data.h new file mode 100644 index 0000000..a4bdb0c --- /dev/null +++ b/test/real_data.h @@ -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 +}; + diff --git a/test/timertest.cpp b/test/timertest.cpp new file mode 100644 index 0000000..12dee6b --- /dev/null +++ b/test/timertest.cpp @@ -0,0 +1,46 @@ +// +// Created by Wouter Groeneveld on 06/12/18. +// + +#include +#include + +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); +} \ No newline at end of file