Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,6 @@ dmypy.json
# ROMs
roms/

# Pypy tarball
pypy3.10-v7.3.17-linux64*

# auto-generated imgui file
imgui.ini

Expand Down
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pypy@3.10
pypy@3.11
9 changes: 4 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@

## Project Goal

A SNES emulator written in Python, targeting real-time emulation speed while keeping elegant Python syntax. The performance strategy is Cython (pure Python mode) compiled with PyPy 3.10, or a combination of both.
A SNES emulator written in Python, targeting real-time emulation speed while keeping elegant Python syntax. The performance strategy is Cython (pure Python mode) compiled with PyPy 3.11, or a combination of both.

The codebase is in active development. CPU and SPC700 instruction tests pass against the SingleStepTests suite. Super Mario World boots past the SPC700 IPL handshake and second-stage audio upload and renders the animated title screen (branch `apu-sync-timing-fixes`).

## Build & Run

### Prerequisites
- PyPy 3.10 (from tarball, NOT snap — snap version had window display issues)
- PyPy 3.11
- uv (package manager)
- SDL2 system library (`libsdl2-dev`)

### Commands
```bash
# Install dependencies (interpreter is pinned in .python-version → pypy@3.10)
# Install dependencies (interpreter is pinned in .python-version)
uv sync

# Build (Cython compile all .py files into a .so)
make build # uses PyPy 3.10 by default
make build

# Run
uv run pysnes roms/game.sfc # script entry point (recommended)
Expand Down Expand Up @@ -134,7 +134,6 @@ On startup, `pysnes.py` opens `cpu_trace.log` and compares CPU execution against
### Build System Notes
- **Cython compilation is on hold.** PyPy alone is ~20× faster than CPython+Cython for this codebase as it currently stands, so the project runs from pure-Python source under PyPy. The `make build_pysnes` target and Cython decorators in the source still work, but you do not need to run `make build_pysnes` to develop or test — pure-Python source under PyPy is the supported path. If `*.so` files exist (left over from a previous build), Python imports them in preference to the `.py` source; `find pysnes -name "*.so" -delete` to fall back to the `.py` files.
- **Cython version is pinned to 3.1.1** — DO NOT upgrade to 3.1.2, it has a "multiple definitions of function" bug: https://stackoverflow.com/questions/79687815/cython-multiple-definitions-of-function
- **Python version must be ~3.10** — ImGui (now removed but still in pyproject.toml) didn't compile in 3.11 with PyPy
- All `.py` files in `pysnes/` are compiled into a single monolithic Cython `.so` extension
- `setup.py` compiles everything and links against SDL2
- `cythonize()` uses `cache=True` and `nthreads=cpu_count()` — only changed `.py` files are re-transpiled; Cython step is parallelised
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: build clean run install test benchmark docs

PY ?= --python pypy@3.10
PY ?= --python pypy@3.11

all: build

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PySNES

## Virtualenv (3.10)
## Virtualenv

```
snap install pypy3 --classic
Expand Down Expand Up @@ -34,7 +34,7 @@ uv run pytest
### Inplace

```
uv run --python pypy@3.10 setup.py build_ext --inplace
uv run --python pypy@3.11 setup.py build_ext --inplace
```

### Release
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ name = "pysnes"
version = "0.1.0"
description = "SNES emulator written in Python."
readme = "README.md"
requires-python = "~=3.10.0" # imgui didn't compile in 3.11 with pypy
requires-python = "~=3.11.0" # imgui didn't compile in 3.11 with pypy
dependencies = [
"attrs>=25.3.0",
"black>=25.1.0",
Expand All @@ -24,7 +24,7 @@ dependencies = [
"greenlet>=3.2.3",
"idna>=3.10",
"ijson>=3.3.0",
"imgui",
#"imgui",
"importlib-metadata>=8.7.0",
"iniconfig>=2.1.0",
"mypy-extensions>=1.1.0",
Expand Down
2 changes: 1 addition & 1 deletion pysnes/ppu/test_ppu_bg_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
- Brightness = 15 (colours pass through unmodified)

Run:
uv run --python pypy3.10 pytest pysnes/ppu/test_ppu_bg_window.py -v
uv run --python pypy pytest pysnes/ppu/test_ppu_bg_window.py -v
"""

import pytest
Expand Down
2 changes: 1 addition & 1 deletion pysnes/ppu/test_ppu_color_math.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
direct-color mode.

Run:
uv run --python pypy3.10 pytest pysnes/ppu/test_ppu_color_math.py -v
uv run --python pypy pytest pysnes/ppu/test_ppu_color_math.py -v
"""

import pytest
Expand Down
2 changes: 1 addition & 1 deletion pysnes/ppu/test_ppu_color_math_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
math gating controls whether the backdrop picks up the sub-screen pixel.

Run:
uv run --python pypy3.10 pytest pysnes/ppu/test_ppu_color_math_window.py -v
uv run --python pypy pytest pysnes/ppu/test_ppu_color_math_window.py -v
"""

import pytest
Expand Down
2 changes: 1 addition & 1 deletion pysnes/ppu/test_ppu_mosaic.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
pixels inside each NxN block to share the top-left pixel's color.

Run:
uv run --python pypy3.10 pytest pysnes/ppu/test_ppu_mosaic.py -v
uv run --python pypy pytest pysnes/ppu/test_ppu_mosaic.py -v
"""

import pytest
Expand Down
2 changes: 1 addition & 1 deletion pysnes/ppu/test_ppu_registers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
NotImplementedError, and VRAM address remapping modes.

Run:
uv run --python pypy3.10 pytest pysnes/ppu/test_ppu_registers.py -v
uv run --python pypy pytest pysnes/ppu/test_ppu_registers.py -v
"""

import pytest
Expand Down
2 changes: 1 addition & 1 deletion pysnes/ppu/test_ppu_scroll.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
- Row 1 of tilemap: all tile 1 (BLUE)

Run:
uv run --python pypy3.10 pytest pysnes/ppu/test_ppu_scroll.py -v
uv run --python pypy pytest pysnes/ppu/test_ppu_scroll.py -v
"""

import pytest
Expand Down
2 changes: 1 addition & 1 deletion pysnes/ppu/test_ppu_sprites.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
- v_counter = 6 → sprite row 1 drawn, output at main_bgs row 5

Run:
uv run --python pypy3.10 pytest pysnes/ppu/test_ppu_sprites.py -v
uv run --python pypy pytest pysnes/ppu/test_ppu_sprites.py -v
"""

import pytest
Expand Down
6 changes: 3 additions & 3 deletions pysnes/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
Tier 2 — instruction-level: compare CPU trace line by line to find the exact diverging instruction.

Run:
uv run --python pypy@3.10 pytest pysnes/test_integration.py::test_frame_divergence -v -s
uv run --python pypy@3.10 pytest pysnes/test_integration.py::test_instruction_divergence -v -s
uv run --python pypy pytest pysnes/test_integration.py::test_frame_divergence -v -s
uv run --python pypy pytest pysnes/test_integration.py::test_instruction_divergence -v -s

Skip in normal suite:
uv run --python pypy@3.10 pytest pysnes/ -m "not integration"
uv run --python pypy pytest pysnes/ -m "not integration"

Mesen binary expected at: tools/Mesen (relative to repo root)
Override with env var: MESEN_BIN=/path/to/Mesen
Expand Down
4 changes: 2 additions & 2 deletions scripts/bench_bus.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
Bus read/write micro-benchmark.

Run from the project root:
uv run --python pypy3.10 scripts/bench_bus.py # without Cython build
make build && uv run --python pypy3.10 scripts/bench_bus.py # with Cython build
uv run --python pypy scripts/bench_bus.py # without Cython build
make build && uv run --python pypy scripts/bench_bus.py # with Cython build

Reports ns/op for each hot-path branch in Bus.__getitem__ / __setitem__.
"""
Expand Down
Loading