DSP-1 coprocessor (NEC µPD77C25) — fixed-point 3D math from C. More...
#include <snes/types.h>Go to the source code of this file.
Classes | |
| struct | Dsp1Camera |
| The camera of the projection: the seven inputs of DSP-1 command $02, "Parameter", under their names in the manual. More... | |
Macros | |
Bridges to the SDK fixed-point types | |
The SDK's fixed (snes/math.h) is signed 8.8 and its angles are 8-bit (256 = full turn); the DSP-1 uses 1.15 fractions (T) and 16-bit angles (A). These macros convert. Note the T range is −1..+1: converting a fixed outside −1.0..+0.999 overflows — clamp first if your value can exceed it. | |
| #define | DSP1_A_FROM_FIX8(a) |
| SDK 8-bit angle (256 = full turn) → DSP-1 A (2^16 = full turn). | |
| #define | DSP1_FIX_FROM_T(t) |
| DSP-1 T (1.15) → SDK 8.8 fixed: an arithmetic shift, so it rounds toward minus infinity (floor), not toward zero (the word "truncating" stood here until 2026-10-04). | |
| #define | DSP1_T_FROM_FIX(f) |
| SDK 8.8 fixed → DSP-1 T (1.15). Valid for −1.0 ≤ f < 1.0. | |
Functions | |
| void | dsp1Attitude (u16 scale, u16 az, u16 ay, u16 ax) |
| Build a 3D rotation matrix into slot A (DSP-1 command $01, "Attitude"). | |
| u16 | dsp1Distance (s16 x, s16 y, s16 z) |
| Euclidean length of a 3D vector (DSP-1 command $28, "Distance"). | |
| void | dsp1Init (void) |
| Resynchronise the DSP-1 to a known command-wait state. | |
| u8 | dsp1IsPresent (void) |
| Is a DSP-1 responding? Known-answer self-test. | |
| s16 | dsp1Multiply (s16 a, s16 b) |
| Signed 1.15 × 1.15 multiply (DSP-1 command $00). | |
| void | dsp1Objective (s16 x, s16 y, s16 z) |
| Transform a point by matrix A: local → world (DSP-1 command $0D, "Objective"). | |
| void | dsp1Project (s16 x, s16 y, s16 z) |
| Project a world point to the screen (DSP-1 command $06, "Project"). | |
| s16 | dsp1Range (s16 x, s16 y, s16 z, u16 r) |
| Sphere test (DSP-1 command $18, "Range"). | |
| void | dsp1Raster (u8 FAR *ab, u8 FAR *cd, s16 vs, u16 count) |
| Stream per-scanline Mode 7 matrices into two HDMA payloads (DSP-1 command $0A, "Raster"). | |
| void | dsp1Rotate (u16 angle, s16 x, s16 y) |
| Rotate a 2D point about the origin (DSP-1 command $0C, "Rotate"). | |
| void | dsp1SetCamera (const Dsp1Camera *cam) |
| Set up the projection plane (DSP-1 command $02, "Parameter"). | |
| void | dsp1Target (s16 h, s16 v) |
| Screen point → ground plane (DSP-1 command $0E, "Target"). | |
| void | dsp1Triangle (u16 angle, s16 radius) |
| Scaled sine and cosine of an angle (DSP-1 command $04, "Triangle"). | |
Variables | |
| volatile s16 | dsp1_o0 |
| Multi-word DSP-1 result registers (written by multi-output commands). | |
| volatile s16 | dsp1_o1 |
| volatile s16 | dsp1_o2 |
| volatile s16 | dsp1_o3 |
DSP-1 coprocessor (NEC µPD77C25) — fixed-point 3D math from C.
The DSP-1 is a fixed-function math coprocessor found in Pilotwings, Super Mario Kart and other pseudo-3D games. It runs Sony's fixed firmware; you do not program it — you invoke its ~30 built-in commands (matrix, vector, projection, trig) over a two-register port. This module wraps that command interface as ordinary C calls.
| #define DSP1_A_FROM_FIX8 | ( | a | ) |
| #define DSP1_FIX_FROM_T | ( | t | ) |
| #define DSP1_T_FROM_FIX | ( | f | ) |
Build a 3D rotation matrix into slot A (DSP-1 command $01, "Attitude").
| scale | matrix scale (T, signed 1.15; 0x7FFF ≈ 1.0) |
| az | rotation about Z (A) |
| ay | rotation about Y (A) |
| ax | rotation about X (A) |
Produces no output — it loads the matrix the transform commands consume. Call once per frame (or whenever the orientation changes), then transform your points with dsp1Objective.
Euclidean length of a 3D vector (DSP-1 command $28, "Distance").
| x | vector X (I) |
| y | vector Y (I) |
| z | vector Z (I) |
Hardware square root — handy for homing missiles, audio attenuation, anything that needs a true distance rather than a compare.
| void dsp1Init | ( | void | ) |
Resynchronise the DSP-1 to a known command-wait state.
Issues the $80 Sync/Reset byte repeatedly (128×, matching the stock-game boot handshake). $80 flushes any pending command, so this recovers the chip even if a previous command was interrupted mid-parameter. Call once at startup before your first DSP-1 command; harmless to call again to recover.
| u8 dsp1IsPresent | ( | void | ) |
Is a DSP-1 responding? Known-answer self-test.
Unlike the other calls this one cannot hang on a missing chip (bounded poll). Use it at boot the way sa1IsReady() / gsuIsPresent() are used — e.g. to fall back to software math when running on the wrong board or on an emulator without the firmware. (Named dsp1Present() until 2026-09-22; that name was removed on 2026-10-05.)
Signed 1.15 × 1.15 multiply (DSP-1 command $00).
Typed s16 like every other signed DSP-1 quantity (and like DSP1_T_FROM_FIX(), which produces one). Until 2026-09-21 the three were u16: dsp1Multiply(x, y) < 0 was always false and >> 7 on the result was a logical shift. Same stack slots, same bits.
Project a world point to the screen (DSP-1 command $06, "Project").
| x | world X (I) |
| y | world Y (I) |
| z | world Z (I) |
Writes dsp1_o0 = H (screen X), dsp1_o1 = V (screen Y), dsp1_o2 = M (scale/depth — use it to size sprites with distance). Requires a prior dsp1SetCamera. Takes ~627 DSP cycles (~83 µs); the driver polls, so timing is handled for you.
Sphere test (DSP-1 command $18, "Range").
| x | offset X from the sphere centre (I) |
| y | offset Y (I) |
| z | offset Z (I) |
| r | sphere radius (I) |
One call replaces three multiplies and two adds — cheap 3D proximity / LOD tests without ever leaving 16-bit C.
Stream per-scanline Mode 7 matrices into two HDMA payloads (DSP-1 command $0A, "Raster").
| ab | destination for A,B: 4 bytes per raster (A lo, A hi, B lo, B hi) |
| cd | destination for C,D: 4 bytes per raster (C lo, C hi, D lo, D hi) |
| vs | first raster number (relative to the imaginary centre; use Vva + 2 from dsp1SetCamera for the first ground line) |
| count | number of rasters to stream (at most 127 per HDMA repeat block) |
This is the Super Mario Kart / Pilotwings ground: the chip computes, for each raster, the Mode 7 matrix that projects the ground plane under the camera set by dsp1SetCamera — perspective, and rotation when aas is non-zero. The two buffers are laid out as the payload of an HDMA_MODE_2REG_2X repeat block (M7A/M7B on one channel, M7C/M7D on the other); point the block's header at them and put the block on screen line 112 + Vof + vs. Set M7VOFS so the Mode 7 pivot (M7X/M7Y = Cx/Cy) sits on the imaginary centre line: the D values are secant slopes relative to that line, not derivatives.
The stream is closed the way the manual specifies ($8000 written in place of a D read), so the chip is back in command mode on return. Cost is CPU-bound: about 80 µs per raster, roughly 100 rasters in a frame's budget — refresh the ground at that size, or every other frame.
Rotate a 2D point about the origin (DSP-1 command $0C, "Rotate").
| angle | 16-bit rotation angle (A) |
| x | point X (I) |
| y | point Y (I) |
Writes dsp1_o0 = x' and dsp1_o1 = y'. Rotation is clockwise in the SNES screen convention (Y down): (100,0) at 90° gives (0,-99) — the chip's sine of 90° is 0x7FFF, not 1.0, so a full-scale result reads one short.
| void dsp1SetCamera | ( | const Dsp1Camera * | cam | ) |
Set up the projection plane (DSP-1 command $02, "Parameter").
| cam | the seven inputs (see Dsp1Camera); read during the call, not kept |
Writes four words (order per the official manual §5.4.1, verified on luna):
Raster numbers are down-positive and relative to the screen centre; note that dsp1Project's V is the opposite (up-positive). Call once (and again whenever the camera moves), then dsp1Project points or dsp1Raster the ground. lfe/les are best tuned empirically — see the values used by examples/chips/dsp1_cube; les is also the vertical focal length.
Screen point → ground plane (DSP-1 command $0E, "Target").
| h | screen X relative to the centre (I; mirrored like dsp1Project's H) |
| v | screen raster relative to the imaginary centre (I; down-positive) |
The inverse of dsp1Project for the ground plane: which ground (x, y) is under a cursor, a crosshair, a missile scope. Writes dsp1_o0 = ground X and dsp1_o1 = ground Y, in the same convention as Cx/Cy — dsp1Target(0, 0) returns exactly the Cx/Cy pair of the last dsp1SetCamera. Requires a prior dsp1SetCamera.
|
extern |
|
extern |