Skip to main content

pyjch

A standalone, pure-Python reader and player for JCH NewPlayer C64 SID songs (the JCH / Jens-Christian Huus tracker player used across the demoscene). It parses a JCH tune into a typed song model and runs the playroutine to produce byte-exact per-frame SID register output, plus a register-log surface for downstream tooling.

Everything (read/play/register-log) is pure stdlib — no dependencies.

pip install pyjch

Quick start

import pyjch

song = pyjch.read("tune.sid")            # PSID/RSID/.sid or bare .prg

# Per-frame SID register writes (changed registers only, after frame 0).
for writes in pyjch.iter_frames(song, max_frames=50 * 60):
    ...                                  # writes: list[(register, value)]

# Forward-filled 25-register-per-frame snapshot grid (the oracle form).
grid = pyjch.render_grid(song, nframes=400)

# Register log (clock reg val triples).
pyjch.write_reglog(
    pyjch.iter_register_writes(song, max_frames=2500), "tune.reglog"
)

CLI:

pyjch info   tune.sid
pyjch reglog tune.sid tune.reglog --seconds 30

Public API

  • read(src) -> Song / parse(bytes) -> Song — read a .sid/.prg.
  • Player(song) — .play_frame() -> list[(reg, val)], .regs.
  • iter_frames(song, max_frames) — per-frame writes.
  • render_grid(song, nframes) -> list[list[int]] — forward-filled grid.
  • iter_register_writes / read_reglog / write_reglog / RegWrite.
  • Model: Song (with orderlist_ptr / subpattern_ptr resolution).
  • Errors: JCHError, SidParseError.

The JCH NewPlayer format

Three independent per-voice opcode streams. The player binary is identical across tunes; only its DATA and the addresses that data lives at differ, so the reader discovers each per-tune immediate and table base from the player code's instruction operands (relocation-safe — no fixed-address assumption):

  • AD/SR defaults — image[load+0xAA] / image[load+0xB5].
  • Gate-on / gate-off (rest) CTRL — image[load+0x1E1] / image[load+0x199].
  • Subpattern pointer-table bases — the 16-bit operands at load+0x12C (lo) / load+0x131 (hi).
  • Orderlist pointers — three per-voice pairs at $1010, indexed by subtune * 8; the 7th byte seeds the tempo.
  • Frequency tables — $121F (lo) / $1220 (hi), 0x80 entries, indexed by note with a per-voice transpose.

Init (FUN_1060) copies the orderlist pointer pairs into per-voice cur/base pointers, loads the tempo, and seeds the SID registers. Play (FUN_10E8) walks each voice's opcode stream via the zero-page pointer $fb/$fc: transpose-set opcodes ($80–$9F), subpattern references (<$80 in the orderlist push a pointer and jump via the subpattern tables), gate/tempo counters ($80–$8F inside a subpattern), and note bytes that index the frequency table.

Byte-exact verdict

Validated against the preframr-sidtrace register oracle (PAL, 19656 cycles/frame, forward-filled, one leading silent play-call aligned over, PW-hi registers nibble-masked):

Tune Author Frames Residual
Flexible Scorpio 368 0 (byte-exact)
Simple Tune JCH 368 0 (byte-exact)

Tests

Test tunes are HVSC copyright works and are never committed. They are resolved from a local HVSC tree ($JCH_LOCAL_HVSC) or fetched on demand (python scripts/fetch_tunes.py) into a gitignored cache; the byte-exact tests validate against the live preframr-sidtrace binary when $SIDTRACE_BIN is set, else against the committed frozen oracle grids in tests/fixtures/, so CI passes with neither the binary nor the tunes.

pip install -e ".[dev]"
./run_tests.sh
SIDTRACE_BIN=/path/to/sidtrace ./run_tests.sh   # validate live, too

License

Apache 2.0 — see LICENSE.

Metadata

Release files for pyjch 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyjch 0.1.0
File Size Uploaded
pyjch-0.1.0.tar.gz 28.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyjch 0.1.0
File Interpreter ABI Platform
pyjch-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 51.6 kB

Release files / pyjch-0.1.0.tar.gz

Download URL pyjch-0.1.0.tar.gz
Size 28.0 kB
Tags Source
SHA-256 checksum
How to use checksums
04156784e120c99ad3587c82c6fe6aa2ab5e84dc342108bebba0c89e872f8960
BLAKE2b-256 checksum
How to use checksums
1840a96371a6bf70fc2745188bfbe175997f8d69a30d9a1efe297eef284655ff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 26, 2026.

Transparency log

Release files / pyjch-0.1.0-py3-none-any.whl

Download URL pyjch-0.1.0-py3-none-any.whl
Size 23.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
783cb4294cd520c4a2c291ef38f5b618481e660ae3961acf718d7e62564f2a7b
BLAKE2b-256 checksum
How to use checksums
5c1639408e9272a528a79dd2d7d95fc03e94ccf0be5c4d5686ca80ed1e84a2ee
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 26, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page