diff --git a/CMakeLists.txt b/CMakeLists.txt index aeb538f..1407480 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.5) project(mtl LANGUAGES CXX C ASM) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(TARGET_SRC_FILES src/memcpy.s src/string.cpp) +set(TARGET_SRC_FILES src/memcpy.s src/string.cpp src/fixed.s) set(TARGET_PUB_INCLUDE_FILES include/mtl/utility.hpp include/mtl/string.hpp include/mtl/fsm.hpp) add_library(${PROJECT_NAME} STATIC ${TARGET_SRC_FILES} ${TARGET_PUB_INCLUDE_FILES}) diff --git a/include/mtl/fixed.hpp b/include/mtl/fixed.hpp new file mode 100644 index 0000000..000f85f --- /dev/null +++ b/include/mtl/fixed.hpp @@ -0,0 +1,83 @@ +#pragma once + +#include + +/** + * \brief Fixed point multiply assembly implementation + * + * DO NOT USE DIRECTLY! Use fixed::operator* instead + */ +extern "C" int32_t fixed_mul(int32_t x, int32_t y); + +namespace mtl { +/** + * \brief 32-bit Fixed point number + * + * Uses a base of 64. ie. the lower 6 bits are after the decimal place, + * the other 26 bits are before the decimal place. + * + * Valid values are in the range ~[-33'554'431.01, 33'554'432.98] + * + * Has a maximum error of +/- 1/128 (~0.0078), integers are always + * exactly. + */ +class fixed { + int32_t x; + + /** + * \brief Raw constructor + * + * Creates a new fixed point number with the raw data of x. + * DO NOT use to set the fixed number to an integer value, use + * the public constructor instead. + */ + constexpr fixed(int32_t _x, bool) : x(_x) {} + +public: + constexpr fixed() : x(0) {} + /** + * \brief 32-bit integer constructor + * + * Creates a new fixed point number with the value of the integer. + * Must be within the range represented by fixed point numbers, see + * the class description for more detail. + */ + constexpr fixed(int32_t _i) : x(_i * 64) {} + /** + * \brief Floating point constructor + * + * Creates a new fixed point number with the closest number to + * the floating point number. Must be within the range represented by + * fixed point numbers, see the class description for more detail. + */ + constexpr fixed(float _f) + // 0.5 offset accounts for truncating to integer, round instead + : x((_f * 64) + 0.5f) {} + + /** + * \brief Fixed point addition + * + * Addition with fixed point numbers is the same as with a 32-bit + * integer, so should be extremely quick. + */ + fixed operator+(fixed rhs) const { + return fixed(x + rhs.x, true); + } + + /** + * \brief Fixed point multiplication + * + * Uses an assembly implementation to multiply the two numbers. + * Not as quick as an integer multiplication. Use sparringly. + * + * Tested on the MGBA Gameboy Advance emulator, takes around 90 + * cycles when the assembly routine is placed in IWRAM. + * The Gameboy Advance uses an armv7tdmi, and IWRAM is the fastest + * available RAM. + */ + fixed operator*(fixed rhs) const { + return fixed(fixed_mul(x, rhs.x), true); + } +}; + +} // namespace mtl diff --git a/src/fixed.s b/src/fixed.s new file mode 100644 index 0000000..88c2682 --- /dev/null +++ b/src/fixed.s @@ -0,0 +1,11 @@ +.section .iwram, "ax", %progbits +.arm +.align 2 + +.global mtl_fixed_mul +.type mtl_fixed_mul STT_FUNC +mtl_fixed_mul: +smull r2, r3, r0, r1 +lsr r0, r2, #6 +orr r0, r3, lsl #26 +bx lr