Add initial fixed point number implementation

This commit is contained in:
Myles Busig 2024-03-06 23:42:48 -07:00
parent 516644ef9a
commit 6f74f94aee
3 changed files with 95 additions and 1 deletions

View File

@ -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})

83
include/mtl/fixed.hpp Normal file
View File

@ -0,0 +1,83 @@
#pragma once
#include <cstdint>
/**
* \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

11
src/fixed.s Normal file
View File

@ -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