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

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

121
.gitignore vendored
View File

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

31
CMakeLists.txt Executable file
View File

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

5
README.md Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

22
engine/CMakeLists.txt Normal file
View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,573 @@
//
// Core functionality
//
//! \file tonc_core.h
//! \author J Vijn
//! \date 20060508 - 20080128
//
/* === NOTES ===
* Contents: bits, random, dma, timer
* 20080129,jv: added tonccpy/set routines.
*/
#ifndef TONC_CORE_STUB
#define TONC_CORE_STUB
#include "tonc_memmap.h"
#include "tonc_memdef.h"
// --------------------------------------------------------------------
// BITS and BITFIELDS
// --------------------------------------------------------------------
/*! \defgroup grpCoreBit Bit(field) macros
\ingroup grpCore
*/
/*! \{ */
//! \name Simple bit macros
//\{
//! Create value with bit \a n set
#define BIT(n) ( 1<<(n) )
//! Shift \a a by \a n
#define BIT_SHIFT(a, n) ( (a)<<(n) )
//! Create a bitmask \a len bits long
#define BIT_MASK(len) ( BIT(len)-1 )
//! Set the \a flag bits in \a word
#define BIT_SET(y, flag) ( y |= (flag) )
//! Clear the \a flag bits in \a word
#define BIT_CLEAR(y, flag) ( y &= ~(flag) )
//! Flip the \a flag bits in \a word
#define BIT_FLIP(y, flag) ( y ^= (flag) )
//! Test whether all the \a flag bits in \a word are set
#define BIT_EQ(y, flag) ( ((y)&(flag)) == (flag) )
//! Create a bitmask of length \a len starting at bit \a shift.
#define BF_MASK(shift, len) ( BIT_MASK(len)<<(shift) )
//! Retrieve a bitfield mask of length \a starting at bit \a shift from \a y.
#define _BF_GET(y, shift, len) ( ((y)>>(shift))&BIT_MASK(len) )
//! Prepare a bitmask for insertion or combining.
#define _BF_PREP(x, shift, len) ( ((x)&BIT_MASK(len))<<(shift) )
//! Insert a new bitfield value \a x into \a y.
#define _BF_SET(y, x, shift, len) \
( y= ((y) &~ BF_MASK(shift, len)) | _BF_PREP(x, shift, len) )
//\}
/*! \name some EVIL bit-field operations, >:)
* These allow you to mimic bitfields with macros. Most of the
* bitfields in the registers have <i>foo</i>_SHIFT and
* <i>foo</i>_SHIFT macros indicating the mask and shift values
* of the bitfield named <i>foo</i> in a variable.
* These macros let you prepare, get and set the bitfields.
*/
//\{
//! Prepare a named bit-field for for insterion or combination.
#define BFN_PREP(x, name) ( ((x)<<name##_SHIFT) & name##_MASK )
//! Get the value of a named bitfield from \a y. Equivalent to (var=) y.name
#define BFN_GET(y, name) ( ((y) & name##_MASK)>>name##_SHIFT )
//! Set a named bitfield in \a y to \a x. Equivalent to y.name= x.
#define BFN_SET(y, x, name) (y = ((y)&~name##_MASK) | BFN_PREP(x,name) )
//! Compare a named bitfield to named literal \a x.
#define BFN_CMP(y, x, name) ( ((y)&name##_MASK) == (x) )
//! Massage \a x for use in bitfield \a name with pre-shifted \a x
#define BFN_PREP2(x, name) ( (x) & name##_MASK )
//! Get the value of bitfield \a name from \a y, but don't down-shift
#define BFN_GET2(y, name) ( (y) & name##_MASK )
//! Set bitfield \a name from \a y to \a x with pre-shifted \a x
#define BFN_SET2(y,x,name) ( y = ((y)&~name##_MASK) | BFN_PREP2(x,name) )
//\}
INLINE u32 bf_get(u32 y, uint shift, uint len);
INLINE u32 bf_merge(u32 y, u32 x, uint shift, uint len);
INLINE u32 bf_clamp(int x, uint len);
INLINE int bit_tribool(u32 x, uint plus, uint minus);
INLINE u32 ROR(u32 x, uint ror);
/*! \} */
// --------------------------------------------------------------------
// DATA
// --------------------------------------------------------------------
/*! \defgroup grpData Data routines
\ingroup grpCore
*/
/*! \{ */
//! Get the number of elements in an array
#define countof(_array) ( sizeof(_array)/sizeof(_array[0]) )
//! Align \a x to the next multiple of \a width.
INLINE uint align(uint x, uint width);
//! \name Copying and filling routines
//\{
//! Simplified copier for GRIT-exported data.
#define GRIT_CPY(dst, name) memcpy16(dst, name, name##Len/2)
// Base memcpy/set replacements.
void *tonccpy(void *dst, const void *src, uint size);
void *__toncset(void *dst, u32 fill, uint size);
INLINE void *toncset(void *dst, u8 src, uint count);
INLINE void *toncset16(void *dst, u16 src, uint count);
INLINE void *toncset32(void *dst, u32 src, uint count);
// Fast memcpy/set
void memset16(void *dst, u16 hw, uint hwcount);
void memcpy16(void *dst, const void* src, uint hwcount);
void memset32(void *dst, u32 wd, uint wcount);
void memcpy32(void *dst, const void* src, uint wcount);
//! Fastfill for halfwords, analogous to memset()
/*! Uses <code>memset32()</code> if \a hwcount>5
* \param dst Destination address.
* \param hw Source halfword (not address).
* \param hwcount Number of halfwords to fill.
* \note \a dst <b>must</b> be halfword aligned.
* \note \a r0 returns as \a dst + \a hwcount*2.
*/
void memset16(void *dst, u16 hw, uint hwcount);
//! \brief Copy for halfwords.
/*! Uses <code>memcpy32()</code> if \a hwn>6 and
\a src and \a dst are aligned equally.
\param dst Destination address.
\param src Source address.
\param hwcount Number of halfwords to fill.
\note \a dst and \a src <b>must</b> be halfword aligned.
\note \a r0 and \a r1 return as
\a dst + \a hwcount*2 and \a src + \a hwcount*2.
*/
void memcpy16(void *dst, const void* src, uint hwcount);
//! Fast-fill by words, analogous to memset()
/*! Like CpuFastSet(), only without the requirement of
32byte chunks and no awkward store-value-in-memory-first issue.
\param dst Destination address.
\param wd Fill word (not address).
\param wdcount Number of words to fill.
\note \a dst <b>must</b> be word aligned.
\note \a r0 returns as \a dst + \a wdcount*4.
*/
void memset32(void *dst, u32 wd, uint wdcount);
//! \brief Fast-copy by words.
/*! Like CpuFastFill(), only without the requirement of 32byte chunks
\param dst Destination address.
\param src Source address.
\param wdcount Number of words.
\note \a src and \a dst <b>must</b> be word aligned.
\note \a r0 and \a r1 return as
\a dst + \a wdcount*4 and \a src + \a wdcount*4.
*/
void memcpy32(void *dst, const void* src, uint wdcount);
//\}
/*! \name Repeated-value creators
These function take a hex-value and duplicate it to all fields,
like 0x88 -> 0x88888888.
*/
//\{
INLINE u16 dup8(u8 x);
INLINE u32 dup16(u16 x);
INLINE u32 quad8(u8 x);
INLINE u32 octup(u8 x);
//\}
//! \name Packing routines.
//\{
INLINE u16 bytes2hword(u8 b0, u8 b1);
INLINE u32 bytes2word(u8 b0, u8 b1, u8 b2, u8 b3);
INLINE u32 hword2word