OpenSNES
Modern Open-Source SNES Development SDK
Loading...
Searching...
No Matches
math.h File Reference

SNES Fixed-Point Math. More...

#include <snes/types.h>

Go to the source code of this file.

Macros

#define FIX(x)
 Convert integer to fixed-point.
#define FIX_FRAC(x)
 Get fractional part of fixed-point.
#define FIX_MAKE(i, f)
 Create fixed-point from integer and fraction.
#define UNFIX(x)
 Convert fixed-point to integer (truncate).
#define UNFIX_ROUND(x)
 Convert fixed-point to integer (rounded).

Typedefs

typedef s16 fixed
 8.8 signed fixed-point type

Functions

u8 atan2_8 (s16 dy, s16 dx)
 8-bit two-argument arctangent
u16 div16 (u16 dividend, u16 divisor)
 Safe 16-bit division.
u8 ease_in_quad (u8 t)
 Ease-in quadratic: t² curve, output [0, 255].
u8 ease_out_quad (u8 t)
 Ease-out quadratic: 1 - (1-t)² curve, output [0, 255].
fixed fixAbs (fixed x)
 Absolute value of fixed-point.
fixed fixClamp (fixed x, fixed min, fixed max)
 Clamp value to range.
fixed fixCos (u8 angle)
 Get cosine value for angle.
fixed fixDiv (fixed a, fixed b)
 Divide two fixed-point values.
fixed fixLerp (fixed a, fixed b, u8 t)
 Linear interpolation.
fixed fixMul (fixed a, fixed b)
 Multiply two fixed-point values.
fixed fixSin (u8 angle)
fixed fixSqrt (fixed x)
 Square root in 8.8 fixed-point.
u16 mod16 (u16 dividend, u16 divisor)
 Get remainder of division.
u16 mul16 (u16 a, u16 b)
 Safe 16-bit multiplication.
u16 sqrt16 (u16 n)
 Integer square root.

Variables

const u8 ease_quad_table [256]
 256-byte quadratic easing LUT — i² normalised to [0, 255]
const s16 sine_table [256]
 Get sine value for angle.

Detailed Description

SNES Fixed-Point Math.

Fixed-point arithmetic for smooth movement and physics.

Overview

This module provides:

  • 8.8 fixed-point type and macros
  • Sine/cosine lookup tables (256 angles = full circle)
  • Fixed-point multiplication
  • Integer multiplication (safe alternative to compiler's *)

Fixed-Point Format

The fixed type is 16-bit signed (s16) in 8.8 format:

  • High byte: integer part (-128 to 127)
  • Low byte: fractional part (0-255, representing 0.0 to 0.996)
fixed pos_x = FIX(100); // 100.0
fixed velocity = FIX(1) / 4; // 0.25 (64 in fixed)
pos_x = pos_x + velocity; // 100.25
s16 screen_x = UNFIX(pos_x); // 100 (truncated)
static s16 pos_x
Definition main.c:177
signed short s16
16-bit signed integer (-32768 to 32767)
Definition types.h:50
#define UNFIX(x)
Convert fixed-point to integer (truncate).
Definition math.h:83
#define FIX(x)
Convert integer to fixed-point.
Definition math.h:71
s16 fixed
8.8 signed fixed-point type
Definition math.h:59

Angles

Angles are 8-bit values (0-255) representing 0-360 degrees:

  • 0 = 0°, 64 = 90°, 128 = 180°, 192 = 270°
u8 angle = 64; // 90 degrees
fixed dx = fixSin(angle); // 1.0 (256)
fixed dy = fixCos(angle); // 0.0 (0)
player_x = player_x + fixMul(speed, dx);
static s16 player_x
Player X position in screen coordinates.
Definition main.c:56
unsigned char u8
8-bit unsigned integer (0 to 255)
Definition types.h:47
fixed fixCos(u8 angle)
Get cosine value for angle.
Definition math.h:197
fixed fixMul(fixed a, fixed b)
Multiply two fixed-point values.
fixed fixSin(u8 angle)
Definition math.h:181
static u16 angle
Rotation step index, 0..47.
Definition main.c:53
Author
OpenSNES Team

Macro Definition Documentation

◆ FIX

#define FIX ( x)
Value:
((fixed)((x) << 8))

Convert integer to fixed-point.

Parameters
xInteger value (-128 to 127)
Returns
Fixed-point representation
fixed pos = FIX(50); // 50.0 in fixed-point
fixed half = FIX(1) / 2; // 0.5

◆ FIX_FRAC

#define FIX_FRAC ( x)
Value:
((u8)((x) & 0xFF))

Get fractional part of fixed-point.

Parameters
xFixed-point value
Returns
Fractional part (0-255)

◆ FIX_MAKE

#define FIX_MAKE ( i,
f )
Value:
((fixed)(((i) << 8) | (f)))
static u8 i
Definition main.c:156

Create fixed-point from integer and fraction.

Parameters
iInteger part
fFractional part (0-255)
Returns
Fixed-point value
fixed half = FIX_MAKE(0, 128); // 0.5
fixed quarter = FIX_MAKE(0, 64); // 0.25
#define FIX_MAKE(i, f)
Create fixed-point from integer and fraction.
Definition math.h:115

◆ UNFIX

#define UNFIX ( x)
Value:
((s16)((x) >> 8))

Convert fixed-point to integer (truncate).

Parameters
xFixed-point value
Returns
Integer part (truncated toward zero)
fixed pos = FIX(50) + 128; // 50.5
s16 screen_pos = UNFIX(pos); // 50

◆ UNFIX_ROUND

#define UNFIX_ROUND ( x)
Value:
((s16)(((x) + 128) >> 8))

Convert fixed-point to integer (rounded).

Parameters
xFixed-point value
Returns
Integer part (rounded to nearest)
fixed pos = FIX(50) + 128; // 50.5
s16 screen_pos = UNFIX_ROUND(pos); // 51
#define UNFIX_ROUND(x)
Convert fixed-point to integer (rounded).
Definition math.h:95

Typedef Documentation

◆ fixed

typedef s16 fixed

8.8 signed fixed-point type

Range: -128.0 to 127.996 (approximately) Precision: 1/256 = 0.00390625

Function Documentation

◆ atan2_8()

u8 atan2_8 ( s16 dy,
s16 dx )

8-bit two-argument arctangent

Returns the angle of the vector (dx, dy) measured from the positive X axis, in the SDK's 8-bit angle convention (0–255 = 0°–360°, same scale as fixSin / fixCos).

  • 0 → +X (east, dy=0, dx>0)
  • 64 → +Y (south on a screen, dy>0, dx=0)
  • 128 → −X (west)
  • 192 → −Y (north)

Implementation: 65-entry LUT covering the first octant (atan(t) for t ∈ [0, 1] mapped to angle [0, 32]), with symmetry handling for the other 7 octants. Inputs are reduced by power-of-two right shifts when their magnitudes exceed 255, so the function works for any 16-bit signed input without overflowing the internal 16-bit divide.

Parameters
dyY component of the vector
dxX component of the vector
Returns
8-bit angle of (dx, dy). Returns 0 if both inputs are zero (the angle is mathematically undefined; the lib chooses defined-but-zero).
u8 aim_angle = atan2_8(dy, dx);
fire_projectile(aim_angle);
static s16 enemy_y[4]
Y positions of the four static enemy sprites.
Definition main.c:65
static s16 player_y
Player Y position in screen coordinates.
Definition main.c:58
static s16 enemy_x[4]
X positions of the four static enemy sprites.
Definition main.c:63
u8 atan2_8(s16 dy, s16 dx)
8-bit two-argument arctangent
Note
Precision is ≤ 1 angle unit (≈ 1.4°) for inputs whose magnitudes fit in 8 bits; coarser for larger inputs after reduction (still useful for game-side aiming logic).

◆ div16()

u16 div16 ( u16 dividend,
u16 divisor )

Safe 16-bit division.

Bounded binary long division — always 16 iterations regardless of the operands, so the cost is predictable. (A future optimisation could use the hardware divider at $4204-$4206 for divisors up to 255.)

Parameters
dividendNumber to divide
divisorNumber to divide by (must not be zero)
Returns
Quotient
Warning
Returns 0 if divisor is 0

◆ ease_in_quad()

u8 ease_in_quad ( u8 t)
inline

Ease-in quadratic: t² curve, output [0, 255].

Parameters
tInput in [0, 255]
Returns
ease_quad_table[t] — starts slow, accelerates toward 255

Canonical animation curve. Use to drive a value from 0 to its final state over N frames where the motion feels "wound up" at the start:

for (u8 t = 0; t < 255; t++) {
u8 alpha = ease_in_quad(t);
// ... draw with alpha ...
}
u8 ease_in_quad(u8 t)
Ease-in quadratic: t² curve, output [0, 255].
Definition math.h:415

◆ ease_out_quad()

u8 ease_out_quad ( u8 t)
inline

Ease-out quadratic: 1 - (1-t)² curve, output [0, 255].

Parameters
tInput in [0, 255]
Returns
255 - ease_quad_table[255 - t] — starts fast, settles toward 255

Mirror of ease_in_quad. Use when the motion should "decelerate" into its final state (e.g., a sprite sliding into position).

◆ fixAbs()

fixed fixAbs ( fixed x)

Absolute value of fixed-point.

Parameters
xFixed-point value
Returns
Absolute value

◆ fixClamp()

fixed fixClamp ( fixed x,
fixed min,
fixed max )

Clamp value to range.

Parameters
xValue to clamp
minMinimum value
maxMaximum value
Returns
Clamped value

◆ fixCos()

fixed fixCos ( u8 angle)
inline

Get cosine value for angle.

Parameters
angle8-bit angle (0-255 = 0°-360°)
Returns
Cosine value in 8.8 fixed-point (-256 to 256)
u8 angle = 0; // 0 degrees
fixed cos_val = fixCos(angle); // 256 = 1.0

Inlined for zero-call-overhead access (wave 4 retrofit).

◆ fixDiv()

fixed fixDiv ( fixed a,
fixed b )

Divide two fixed-point values.

Parameters
aDividend (fixed-point)
bDivisor (fixed-point, must not be zero)
Returns
Quotient in fixed-point format
fixed distance = FIX(100);
fixed time = FIX(5);
fixed speed = fixDiv(distance, time); // 20.0
fixed fixDiv(fixed a, fixed b)
Divide two fixed-point values.
Warning
Division by zero returns 0

◆ fixLerp()

fixed fixLerp ( fixed a,
fixed b,
u8 t )

Linear interpolation.

Parameters
aStart value
bEnd value
tInterpolation factor (0-256 = 0.0-1.0)
Returns
Interpolated value
fixed start = FIX(0);
fixed end = FIX(100);
fixed mid = fixLerp(start, end, 128); // 50.0
fixed fixLerp(fixed a, fixed b, u8 t)
Linear interpolation.

◆ fixMul()

fixed fixMul ( fixed a,
fixed b )

Multiply two fixed-point values.

Parameters
aFirst fixed-point operand
bSecond fixed-point operand
Returns
Product in fixed-point format
fixed speed = FIX(2);
fixed scale = FIX(1) / 2; // 0.5
fixed result = fixMul(speed, scale); // 1.0
Note
Uses 32-bit intermediate for accuracy
Warning
NOT safe inside an nmiSet() callback: uses the hardware multiplier (garbage during the auto-joypad window callbacks run in) plus shared WRAM temporaries. Plain C * / / / % ARE callback-safe (the runtime switches to a software path there — see .claude/notes/tech/nmi_context_hardware_muldiv.md). fixLerp() carries the same restriction.

◆ fixSin()

fixed fixSin ( u8 angle)
inline

◆ fixSqrt()

fixed fixSqrt ( fixed x)

Square root in 8.8 fixed-point.

Computes the square root of an 8.8 fixed-point value. Internally delegates to sqrt16(x) and shifts the result by 4 bits to recover 5 bits of fractional precision in the answer (the remaining 3 bits are zero — this is a lib-side limit, not a mathematical one; precision can be raised once the QBE 32-bit codegen lands — see chantier A7 in the structural-defects catalogue).

Parameters
xInput value in 8.8 fixed-point. Must be ≥ 0; negative inputs return 0.
Returns
sqrt(x) in 8.8 fixed-point.
fixed area = FIX(64); // 64.0
fixed side = fixSqrt(area); // 8.0 (FIX(8))
fixed fixSqrt(fixed x)
Square root in 8.8 fixed-point.
Note
For x < 0, returns 0 (no error signalling — sqrt of negative is undefined; the lib chooses defined-but-zero over throwing through the simple API).

◆ mod16()

u16 mod16 ( u16 dividend,
u16 divisor )

Get remainder of division.

Same bounded binary long division as div16() — always 16 iterations.

Parameters
dividendNumber to divide
divisorNumber to divide by (must not be zero)
Returns
Remainder
Warning
Returns 0 if divisor is 0

◆ mul16()

u16 mul16 ( u16 a,
u16 b )

Safe 16-bit multiplication.

Multiplies two 16-bit values safely. Use this instead of the compiler's * operator for important calculations, as the compiler's runtime multiplication can have bugs.

Parameters
aFirst operand
bSecond operand
Returns
Product (may overflow for large values)
u16 result = mul16(width, height);
unsigned short u16
16-bit unsigned integer (0 to 65535)
Definition types.h:53
u16 mul16(u16 a, u16 b)
Safe 16-bit multiplication.

◆ sqrt16()

u16 sqrt16 ( u16 n)

Integer square root.

Computes floor(sqrt(n)) for any 16-bit unsigned input. Uses the canonical bit-by-bit (digit-by-digit) algorithm — no LUT, no floating point, fully deterministic in cycle count (~80 cycles worst case on the 65816).

Parameters
nInput value (0 to 65535)
Returns
Largest integer whose square is ≤ n. Range: 0 to 255.
u16 dist = sqrt16(dx * dx + dy * dy); // pixel distance
u16 sqrt16(u16 n)
Integer square root.
Note
Range guarantee: sqrt16(n) is always ≤ 255, so the result fits in u8. Useful when distance is bounded by screen size.
See also
fixSqrt for the 8.8 fixed-point variant.

Variable Documentation

◆ ease_quad_table

const u8 ease_quad_table[256]
extern

256-byte quadratic easing LUT — i² normalised to [0, 255]

ease_quad_table[i] = floor(i² / 255). Underlies ease_in_quad and ease_out_quad. Lives in the opt-in math_ease module (LIB_MODULES += math_ease) so math users who don't ease don't pay its 256 bank-$00 bytes. Exposed as extern so user code can index it directly for custom curve compositions.

Cost: ~5 cycles per lookup vs ~12 for (x * x) / 255 live. ROM cost: 256 bytes (placed in a SUPERFREE section by the linker).

◆ sine_table

const s16 sine_table[256]
extern

Get sine value for angle.

Parameters
angle8-bit angle (0-255 = 0°-360°)
Returns
Sine value in 8.8 fixed-point (-256 to 256, i.e., -1.0 to 1.0)
u8 angle = 64; // 90 degrees
fixed sin_val = fixSin(angle); // 256 = 1.0
Note
Table-based lookup, very fast