Files
project-solana/games/rac1/host/include/vu.h
T
2026-10-13 20:04:31 -04:00

147 lines
5.5 KiB
C

/* vu.h: runtime for the VU microprograms translated by tools/vu_recomp.py.
*
* Each microprogram the game uploads (VU0 through VIF0, VU1 through VIF1) is
* translated to C at build time from the user's executable. A translation is a
* function over an RcVu0 (VU0 uses rc_vu0, the registers it shares with the
* EE's macro mode; VU1 has rc_vu1). Data memory is the real address range:
* VU0 at 0x11004000 (4 KB), VU1 at 0x1100C000 (16 KB).
*
* The VU pipeline is visible to these programs in three places and modelled
* here: MAC/status and clip flags written by an op are seen by flag reads 4
* cycles later, and Q from DIV/SQRT (RSQRT) 7 (13) cycles later. The
* translator counts cycles (with the stalls it can see statically) in a local
* `cyc`; results are queued with the cycle they become visible at. */
#ifndef VU_H
#define VU_H
#include "rc.h"
extern RcVu0 rc_vu1;
#ifndef VU_TRACE
#define VU_TRACE(pc) /* testing hook: each instruction pair's address */
#endif
/* VU data memory, by quadword address */
#define VU0_MEM(qa) ((u32 *)(uintptr_t)(0x11004000u + (((u32)(qa) & 0xFFu) << 4)))
#define VU1_MEM(qa) ((u32 *)(uintptr_t)(0x1100C000u + (((u32)(qa) & 0x3FFu) << 4)))
RC_INLINE u32 vu_u(f32 f) { u32 u; __builtin_memcpy(&u, &f, 4); return u; }
RC_INLINE f32 vu_f(u32 u) { f32 f; __builtin_memcpy(&f, &u, 4); return f; }
/* MAC flag bits for one component (x = 0 .. w = 3) of a result */
/* an FMAC result as the VU has it: no inf or NaN, +-MAX instead (counted: FPSLOG) */
extern u32 vu_nonfinite;
RC_INLINE f32 vu_fc(f32 x)
{
u32 b = vu_u(x);
if (__builtin_expect((b & 0x7F800000u) == 0x7F800000u, 0)) { vu_nonfinite++; b = (b & 0x80000000u) | 0x7F7FFFFFu; __builtin_memcpy(&x, &b, 4); }
return x;
}
RC_INLINE u32 vu_macbits(f32 r, int c)
{
u32 u = vu_u(r), m = 0;
if ((u & 0x7FFFFFFFu) == 0) m |= 8u >> c;
if (u >> 31) m |= 0x80u >> c;
return m;
}
/* ---- MAC/status flag pipeline ---- */
RC_INLINE void vu_fl_sync(RcVu0 *v, u32 cyc)
{
while (v->fp_n && (s32)(cyc - v->fp_rdy[v->fp_h]) >= 0) {
v->mac = v->fp_mac[v->fp_h]; v->status = v->fp_st[v->fp_h];
v->fp_h = (v->fp_h + 1) & 3; v->fp_n--;
}
}
RC_INLINE void vu_fl_push(RcVu0 *v, u32 cyc, u32 mac)
{
u32 st = (v->st_int & 0xFC0u) | ((mac & 0xF) ? 1 : 0) | ((mac & 0xF0) ? 2 : 0);
st |= (st & 3) << 6;
v->st_int = st;
if (v->fp_n == 4) { v->mac = v->fp_mac[v->fp_h]; v->status = v->fp_st[v->fp_h]; v->fp_h = (v->fp_h + 1) & 3; v->fp_n--; }
u32 k = (v->fp_h + v->fp_n) & 3;
v->fp_rdy[k] = cyc + 4; v->fp_mac[k] = mac; v->fp_st[k] = st; v->fp_n++;
}
RC_INLINE u32 vu_status(RcVu0 *v, u32 cyc) { vu_fl_sync(v, cyc); return v->status; }
RC_INLINE u32 vu_mac(RcVu0 *v, u32 cyc) { vu_fl_sync(v, cyc); return v->mac; }
RC_INLINE void vu_fsset(RcVu0 *v, u32 cyc, u32 imm)
{
vu_fl_sync(v, cyc + 8);
v->status = (v->status & 0x3F) | (imm & 0xFC0); v->st_int = v->status;
}
/* ---- clip flag pipeline ---- */
RC_INLINE void vu_cl_sync(RcVu0 *v, u32 cyc)
{
while (v->cp_n && (s32)(cyc - v->cp_rdy[v->cp_h]) >= 0) {
v->clip = v->cp_val[v->cp_h]; v->cp_h = (v->cp_h + 1) & 3; v->cp_n--;
}
}
RC_INLINE void vu_clipw(RcVu0 *v, u32 cyc, const f32 *fs, f32 w)
{
f32 a = __builtin_fabsf(w);
u32 f = (fs[0] > a) | ((fs[0] < -a) << 1) | ((fs[1] > a) << 2) | ((fs[1] < -a) << 3) | ((fs[2] > a) << 4) | ((fs[2] < -a) << 5);
v->cl_int = ((v->cl_int << 6) | f) & 0xFFFFFF;
if (v->cp_n == 4) { v->clip = v->cp_val[v->cp_h]; v->cp_h = (v->cp_h + 1) & 3; v->cp_n--; }
u32 k = (v->cp_h + v->cp_n) & 3;
v->cp_rdy[k] = cyc + 4; v->cp_val[k] = v->cl_int; v->cp_n++;
}
RC_INLINE u32 vu_clip(RcVu0 *v, u32 cyc) { vu_cl_sync(v, cyc); return v->clip; }
RC_INLINE void vu_fcset(RcVu0 *v, u32 cyc, u32 imm)
{
vu_cl_sync(v, cyc + 8);
v->clip = v->cl_int = imm & 0xFFFFFF;
}
/* ---- Q ---- */
RC_INLINE f32 vu_q(RcVu0 *v, u32 cyc)
{
if (v->q_pend && (s32)(cyc - v->q_rdy) >= 0) { v->q = v->q_new; v->q_pend = 0; }
return v->q;
}
/* WAITQ, or a new DIV while one is in flight: stall until Q is written */
RC_INLINE u32 vu_qwait(RcVu0 *v, u32 cyc)
{
if (v->q_pend) { if ((s32)(cyc - v->q_rdy) < 0) cyc = v->q_rdy; v->q = v->q_new; v->q_pend = 0; }
return cyc;
}
RC_INLINE void vu_qset(RcVu0 *v, u32 cyc, int lat, f32 val) { v->q_new = val; v->q_pend = 1; v->q_rdy = cyc + lat; }
/* end of a microprogram: everything in flight lands */
RC_INLINE void vu_settle(RcVu0 *v)
{
while (v->fp_n) { v->mac = v->fp_mac[v->fp_h]; v->status = v->fp_st[v->fp_h]; v->fp_h = (v->fp_h + 1) & 3; v->fp_n--; }
while (v->cp_n) { v->clip = v->cp_val[v->cp_h]; v->cp_h = (v->cp_h + 1) & 3; v->cp_n--; }
if (v->q_pend) { v->q = v->q_new; v->q_pend = 0; }
v->fp_h = v->cp_h = 0;
}
RC_INLINE f32 vu_div(f32 a, f32 b)
{
if (b == 0.0f) return ((vu_u(a) ^ vu_u(b)) & 0x80000000u) ? -3.4028235e38f : 3.4028235e38f;
return a / b;
}
RC_INLINE f32 vu_sqrt(f32 a) { return __builtin_sqrtf(__builtin_fabsf(a)); }
RC_INLINE f32 vu_rsqrt(f32 a, f32 b)
{
f32 s = __builtin_sqrtf(__builtin_fabsf(b));
if (s == 0.0f) return (vu_u(a) & 0x80000000u) ? -3.4028235e38f : 3.4028235e38f;
return a / s;
}
RC_INLINE s32 vu_ftoi(f32 f, f32 scale)
{
f32 x = f * scale;
if (x >= 2147483647.0f) return 0x7FFFFFFF;
if (x <= -2147483648.0f) return (s32)0x80000000u;
if (x != x) return 0;
return (s32)x;
}
RC_INLINE u32 vu_itof(u32 i, f32 inv) { return vu_u((f32)(s32)i * inv); }
/* host side (host/src/gfx/vu.c) */
void vu_xgkick(u32 qaddr);
u32 vu_vif_top(int vu);
u32 vu_vif_itop(int vu);
void vu_bad(int vu, u32 pc, const char *what);
#endif