note / article
< Notes Notas
Updated April 19, 2026
PlayStationCRetroGame DevPSYQ

Developing Applications for PlayStation 1 with PSYQ

How games were made for PS1 in the 90s using PSYQ, SN Systems' SDK that ran on DOS and connected your PC to a real console over serial.

Editado 19 de abril de 2026
PlayStationCRetroGame DevPSYQ

Desarrollo de Aplicaciones para PlayStation 1 con PSYQ

Cómo se desarrollaban juegos para PS1 en los 90 usando PSYQ, el SDK de SN Systems que corría en DOS y conectaba tu PC a una consola real por puerto serial.

The kit that made the PS1 possible

Before Unity, before modern engines, studios developing for PlayStation 1 used PSYQ. An SDK built by SN Systems that ran on DOS or Windows 95 and compiled C for Sony’s MIPS R3000 processor.

It wasn’t glamorous. A GCC fork targeting Sony’s architecture, a set of libraries for graphics and audio, and debugging tools that connected to a development kit over serial. That was it. Some of the most important games of the 90s were built with exactly that.


How the environment worked

The workflow was simple by necessity: write C in a text editor, compile with mipsel-psx-elf-gcc, transfer the binary to a Sony devkit over a serial or parallel cable. The devkit emulated the real console, so you were running on actual hardware, not a software emulator.

A basic Makefile looked like this:

CC = mipsel-psx-elf-gcc
CFLAGS = -O2 -Wall
LDFLAGS = -Llib -lgs

SRC = $(wildcard src/*.c)
OBJ = $(SRC:.c=.o)

my_game.exe: $(OBJ)
    $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)

Project structure was minimal: source in src/, headers in include/, PSYQ libraries like libgs.a in lib/. No project generators, no IDEs. Makefiles and a compiler.


Graphics: 2D primitives to render 3D

The PS1 graphics API works with 2D primitives (triangles, quads, sprites) that get pushed into an Ordering Table (OT) and sent to the GPU. To render 3D objects you used the GTE (Geometry Transformation Engine), a fixed-function matrix math unit baked into the chip.

Initializing the graphics system was the first thing any program did:

#include <libgte.h>
#include <libgpu.h>

void initGraphics() {
    SetDispMask(1);
    SetDefDrawEnv(&drawEnvironment, 0, 0, 320, 240);
    SetDefDispEnv(&dispEnvironment, 0, 0, 320, 240);
}

The PS1 used double buffering. While one framebuffer was being displayed, the other was being drawn into. Coordinating the two environments (display and draw) was something you managed by hand.


Audio: SPU and format conversion

Audio ran on the SPU (Sound Processing Unit), a dedicated chip supporting up to 24 simultaneous voices. PSYQ’s libraries exposed an API for loading samples, setting volume, and playback:

#include <libsnd.h>

void playSound() {
    SpuCommonAttr attr;
    attr.mask = SPU_COMMON_MVOLL | SPU_COMMON_MVOLR;
    attr.mvol.left = 0x3fff;
    attr.mvol.right = 0x3fff;
    SpuSetCommonAttr(&attr);
}

The API itself wasn’t hard. The painful part was the pipeline before it. Samples had to be converted to Sony’s VAG format before they could be used, which meant running additional tools on the PC before every build.


The constraints were the design

The PS1 had 2 MB of RAM and 1 MB of VRAM. The R3000 ran at 33 MHz with no data cache. Every byte mattered, every CPU cycle mattered.

A lot of PS1 development was memory management: what lives in RAM, what goes in VRAM, when things stream from the CD, how the OT gets organized to minimize GPU state changes. Debugging without access to the original devkit was hard because emulators of the era were imprecise, and timing bugs didn’t always show up the same way on real hardware.

Those constraints shaped how you thought about development. Not as a problem to overcome, but as the system you worked with.

El kit que hizo posible la PS1

Antes de Unity, antes de los motores modernos, los estudios que desarrollaban para PlayStation 1 usaban PSYQ. Un SDK creado por SN Systems que corría en DOS o Windows 95 y compilaba C para el procesador MIPS R3000 de la consola.

No era glamoroso. Era un GCC modificado para la arquitectura de Sony, un conjunto de librerías para gráficos y audio, y herramientas de depuración que te conectaban a un kit de desarrollo por puerto serial. Eso era todo. Con eso se hicieron algunos de los juegos más importantes de los 90.


Cómo funcionaba el entorno

El flujo era simple por necesidad: escribías C en un editor de texto, lo compilabas con mipsel-psx-elf-gcc, y transferías el binario a un kit de desarrollo Sony por cable serial o paralelo. El kit emulaba la consola real, así que tenías acceso al hardware de verdad, no a un emulador de software.

Un Makefile básico se veía así:

CC = mipsel-psx-elf-gcc
CFLAGS = -O2 -Wall
LDFLAGS = -Llib -lgs

SRC = $(wildcard src/*.c)
OBJ = $(SRC:.c=.o)

my_game.exe: $(OBJ)
    $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)

La estructura del proyecto era mínima: código fuente en src/, headers en include/, y las librerías de PSYQ (como libgs.a para gráficos) en lib/. Sin generadores de proyecto, sin IDEs. Makefiles y un compilador.


Gráficos: primitivas 2D para renderizar 3D

La API de gráficos de la PS1 trabaja con primitivas 2D (triángulos, quads, sprites) que se empujan a una lista de ordenación (OT, Ordering Table) y se envían a la GPU. Para renderizar objetos 3D usabas la GTE (Geometry Transformation Engine), una unidad de cálculo matricial hardcodeada en el chip.

Inicializar el sistema gráfico era el primer paso en cualquier programa:

#include <libgte.h>
#include <libgpu.h>

void initGraphics() {
    SetDispMask(1);
    SetDefDrawEnv(&drawEnvironment, 0, 0, 320, 240);
    SetDefDispEnv(&dispEnvironment, 0, 0, 320, 240);
}

La PS1 usaba double buffering. Mientras una pantalla se mostraba, la otra se estaba dibujando. Coordinar los dos entornos (display y draw) era algo que manejabas manualmente.


Audio: SPU y conversión de formatos

El audio corría en la SPU (Sound Processing Unit), una unidad dedicada con soporte para hasta 24 voces simultáneas. Las librerías de PSYQ exponían una API para cargar samples, configurar volumen y reproducir:

#include <libsnd.h>

void playSound() {
    SpuCommonAttr attr;
    attr.mask = SPU_COMMON_MVOLL | SPU_COMMON_MVOLR;
    attr.mvol.left = 0x3fff;
    attr.mvol.right = 0x3fff;
    SpuSetCommonAttr(&attr);
}

Lo complicado no era la API sino el pipeline previo. Los samples tenían que convertirse al formato VAG de Sony antes de poder usarse, lo que implicaba herramientas adicionales corriendo en la PC antes de cada build.


Las restricciones eran el diseño

La PS1 tenía 2 MB de RAM y 1 MB de VRAM. El R3000 corría a 33 MHz sin cache de datos. Cada byte importaba, cada ciclo de CPU importaba.

Gran parte del trabajo de un desarrollador de PS1 era gestión de memoria: qué va en RAM, qué va en VRAM, cuándo se carga desde el CD, cómo se organiza la OT para minimizar state changes en la GPU. La depuración sin acceso al kit original era difícil porque los emuladores de la época eran imprecisos, y los errores de timing no se manifestaban igual en hardware real.

Esas restricciones moldearon cómo se pensaba el desarrollo. No como un problema a superar, sino como el sistema con el que trabajabas.