pysidtracker
Shared base for the pure-Python C64 SID tracker parsers (pygoattracker, pysidwizard, pydmcsid, pyfuturecomposer, pymusicassembler, pydefmon, pyjch, pysoundmonitor).
Reads .sid containers (PSID/RSID) and bare .prg images into a 64 KiB C64
memory model, and detects packed/relocating playroutines by running the tune's
init in a 6502 emulator — container headers are not trusted. Each format
package subclasses BaseSidParser for a consistent read / parse / detect
API.
Components
parse_sid_header/SidHeader— PSID/RSID container header parsing.SidImage— a loaded 64 KiB C64 memory image with absolute-addressed accessors, from a.sidcontainer or a bare.prg.read_bytes— path /bytes/ file-like source dispatch.SidErrorhierarchy —SidParseError,SidFormatError,EmulatorUnavailable.detect_playroutine/PlayroutineKind— static signature recognition, then an emulated init run to classifyDIRECT/RELOCATED/PACKED/UNKNOWN.BaseSidParser— the base class each format subclasses.CodePattern/find_code_all/find_code_first— masked 6502 code-fragment search with operand capture.registers— SID register semantics, PSID flag helpers andfind_register_stores; the C64 memory map is vagrant-moses'memmap(the frame timing and CIA latch names dependents import are re-exported).trace_init/InitTrace— run a tune's init under a write observer to report CIA timer cadence and the IRQ/NMI vectors an IRQ-driven header hides.init_effects/subtune_selection/init_diff— run one song's init on a private snapshot and record what it left in RAM (cell stores/reads with their PCs, the installed vectors), mapping header songs to a selection cell or listing the cells that differ between songs.emu— the shared jennings host every init/replay path runs on:wire_mpu(vagrant-moses' bankedcpu.flat_mpuplus reSIDfp osc3/env3 reads) andrun_to_rts.playroutine_cadence/Cadence/TriggerSource— derive the play-routine cadence (raster calls per frame, CIA/NMI timer period) from how the tune programs its interrupts in sidplayfp's PSID driver environment (docs/cadence.md);cadence_infois its surveyinfofields andcadence_checkvalidates it against sidtrace interrupt rows.irqhost—InterruptHost/InterruptPlayerboot and run a PSID or RSID as the sidtrace oracle's sidplayfp does, on vagrant-moses' cycle-exactMachinewith reSIDfp SID devices, its trace identical to the oracle's;TunePlayerplays any PSID/RSID's own driver (hosting play address 0);host_cadencemeasures its play cadence andhost_checkcompares traces (docs/irqhost.md).native_decrunch— native exomizer unpack (pydexomizer), an init-free alternative depack path; opt-in viadetect_playroutine(..., native=True).reglog—RegWriteregister-log format (read_reglog/write_reglog) andframe_writes, the shared per-frame SID-write framing loop.oracle— per-frame SID register grids:register_grid(jennings), the stdlibgrid_from_writesframer,read_sidwr(preframr-sidtrace.sidwr.bin),aligned_match, and thesidtrace(sidplayfp) oracle bridge —run_sidtrace,read_sidtrace,sidtrace_cadence,sidtrace_grid.- Caching — every download lands under one root,
.tunecache($PYSID_TUNECACHE;default_cache(kind)fornative/archives/disks/roms/csv), CSDB requests are serialised machine-wide, and the.github/actions/cachecomposite action persists and pre-fetches it in CI (docs/caching.md). testing— HVSC tune fetch/resolve for test suites (fetch_tune,resolve_tune,make_tune_fixtures), SHA-256-pinned release downloads (fetch_verified,fetch_archive_memberfor one zip/tar member; a changed pin raisesChecksumMismatch), andmake_oracle_fixtures/oracle_grid— a reusable byte-exact player-vs-sidtrace-oracle test (docs/oracle-testing.md);assert_matches/assert_oracle_matchfail with thewhydiffexplanation.whydiff— why aMemPlayerrender diverges: the player's traced Python lines (values read, branches, assignments) beside the jennings 6502 history, with each side's backward dataflow of the diverging value, and the state behind it chased to the frame it first parted (python -m pysidtracker.whydiff, docs/whydiff.md).paths/faultloc/pathdiff— per-frame code paths of two players (6502, a format's player or anyPySubject, e.g. a lift) over a survey corpus, each divergence keyed on the branch where the executions first part, the minimum set of fixes, and strict xfail cases run with thewhydiffexplanation (python -m pysidtracker.pathdiff build|rank|xfails, docs/pathdiff.md).pathfuzz— coverage gaps against the static path universes, data-byte mutants steered to unreached branch outcomes, and ddmin reproducers (python -m pysidtracker.pathfuzz gaps|exercise|minimise, docs/pathfuzz.md).survey— whole-song HVSC survey and failure triage of any format's renderer against thesidtraceoracle, verdict moves against a baseline (compare), an oracle-free decompile scope check (scope), and the same compare for single native files (survey file, bare PRGs wrapped);sidid— pure-Python SIDId with the bundledsidid.cfg(pysididCLI).pip install "pysidtracker[survey]", docs/survey.md.poweron— the libsidplayfp power-on C64 RAM a PSID boots onto:ram_pattern(theSystemRAMBank::resetimage, vagrant-moses'c64.poweron_ramas a mutable copy) andpower_on_ram(it with the PSID driver's$0000-$03FEcleared,PSID_DRIVER_CLEARED), andplace_tune(the tune loaded with the BASICLOADpointers in$2D-$32/$AC-$AF).MemPlayer, theoracleregister_grid, andirqhostall start from them, so a tune reading uninitialised memory or its load bounds reads what sidplayfp leaves there.loader— the flat PSID/RSID loader for hosts with their own 64 KiB memory:load_psid((mem, load, init, play), honouring the in-body load address),psid_image(load bounds),psid_songs,is_ntsc,is_rsid,psid_speedandspeed_cia— every one over the sharedparse_sid_header/SidImagecontainer parsing.roms— vagrant-moses' C64 ROM fetch, SHA-256 check and mapping (fetch_rom,resolve_rom,rom_image,apply_roms), cached beside the tunes. The ROMs are copyright works, never committed (docs/c64-roms.md).kernalhost—KernalHostruns a whole C64 program with the real KERNAL file/keyboard calls serviced on a host directory, andsys_addrreads a BASICSYSstub (vagrant-moses'kernal, raisingKernalHostError).d64— release media through vagrant-moses' readers:read_d64, anditer_prgsover nested zip/gzip, d64 (35/40/42 tracks, error block), d71, d81, t64 and p00;pysidtracker media extract SRC... -o DIRwrites every PRG with asha256 size $load pathindex line.fixtures— CSDB (csdb.dk) native-format editor files (GoatTracker/DMCSID/JCH) are copyright works and never committed:fetch_csdbscrapes release pages and downloads every file into a gitignored, resume-safe cache indexed by SHA-256 inSHA256SUMS.txt,index_mediapinning files found inside release disks and archives;fixture(sha256, cache)resolves a file by hash,make_native_fixturesis a pytest factory that resolves or skips when the cache is cold, andload_manifest/write_manifestare the committed hash-only manifest.pysidtracker fixtures fetch <manifest>(and thepysid-fixturesscript) recreate the cache from it (docs/native-fixtures.md).audio—render_player_wav/render_player_samplesdrive aMemPlayer(or anyplay_frame()player) straight to WAV through an emulated SID at the render's clock, with optional tqdm progress (pyresidfp, a core dependency); the lower-levelrender_wav/render_samplestake a bare write stream, plusresolve_device/seconds_to_frames.- Writers —
SidHeader.to_bytes/write_psid/SidImage.to_prg(the inverse of the parsers),parse_prg, andencode_cstr/decode_cstr. SidImageabsolute-address accessors —byte_at/word_at/contains/poke, relocation-safe and bounds-checked.mos6502— shared 6502 primitives:OP_LEN, opcode-class sets,s8/adc/sbc,SidWriteCapturingMemory,walk_until.MemPlayer— per-frame register-diff player scaffold; subclasses implement_init/_frame;MASK_PW_HI = Truenibble-masks the pulse-width-high registers insnapshot(asEmuPlayerdoes) to match the oracle grid.register_writes_from_playerdrives it to a reglog.notefreq—NoteFreqTable, computed PAL/NTSC pitch tables, andlocate_note_freq(find hi/lo tables from paired absolute-indexed reads).make_package_errors— per-package error hierarchy factory whose*ParseError/*FormatErrorsubclass the base ones, so baseexceptclauses catch them.disasm— disassemble any range of a.sid/.prgimage (on jennings, so the NMOS illegal opcodes render as real mnemonics), with the header's init/play entries labelled (entry_labels) andimage_after_initthe image state after the tune's init ran; backs thepysidtracker disasmcommand and replaces the per-repo disassembler scripts.formats/maincli— the onepysidtrackerCLI (info/reglog/wav/disasm) that discovers installed formats through thepysidtracker.formatsentry-point group and runs against each format's parser + player, plus any subcommands it contributes (a name several formats contribute, such asexport, runs the command of the format recognising the tune). A format package registers aSidFormatinstead of shipping its own CLI;find_format(data, name)andSidFormat.play(data, subtune)play a tune's subtune (default its start song) on the installed format, assurvey --formatandwhydiff --formatdo.cliholds the underlying argparse scaffold (run_cli,print_info,add_reglog_command,add_wav_command).registersadditions —SID_VOICES, voice/global register indices, and theattack_decay/sustain_releaseADSR nibble packers.resolve_entry_points/is_jmp_vector,cadence_from_latch, and theByteCursor/check/byte_rangeparse helpers.
Install
pip install pysidtracker # core: jennings (init emulation), numpy, pyresidfp (WAV render)
All dependencies are core: jennings (detection runs a tune's 6502 init), pydexomizer
(native_decrunch unpacks exomizer-packed images natively), pyresidfp (the
emulated SID the WAV render drives), vagrant-moses (the C64 machine model: memory
map, banked bus, ROMs, the cycle-exact machine, the KERNAL host and media
readers), and numpy (sample buffers and the image anchor scan).
Usage
from pysidtracker import BaseSidParser, PlayroutineKind
class MyParser(BaseSidParser):
def recognize(self, image):
return image.find(b"MYSIG") # truthy anchor when found
def parse(self, data, **kw):
image = self.load_image(data) # .sid or .prg -> SidImage
... # decode image.mem into a model
det = MyParser().detect("tune.sid")
if det.kind is PlayroutineKind.PACKED:
... # header was not trustworthy
Development
pip install -e ".[dev]"
pytest --cov=pysidtracker
See docs/design.md for the detection model and shared primitives.
License
Apache 2.0 — see LICENSE.
Metadata
Release files for pysidtracker 0.36.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pysidtracker-0.36.0.tar.gz | 343.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pysidtracker-0.36.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 590.4 kB
Release files / pysidtracker-0.36.0.tar.gz
| Download URL | pysidtracker-0.36.0.tar.gz |
|---|---|
| Size | 343.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
20848aa2b6febbd1b5adb655d37ae51793302afed4b90a2534b4385ac0a4f630
|
|
BLAKE2b-256 checksum How to use checksums |
5015974ebe26e0dcfaee0b2a7ad0fc88a7e1df67505ad071cf8779629935a254
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 23, 2026.
Transparency logRelease files / pysidtracker-0.36.0-py3-none-any.whl
| Download URL | pysidtracker-0.36.0-py3-none-any.whl |
|---|---|
| Size | 247.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b71ca2c48a4353fbcbf17bb412bb535dceeaa76689e7512bde000f51f4256012
|
|
BLAKE2b-256 checksum How to use checksums |
eda089f51c0ea50526b01a7723928c31304291f8cfabe0a925713bfbab33ece8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 23, 2026.
Transparency logSecurity for pysidtracker 0.36.0
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for pysidtracker-0.36.0.tar.gz
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 Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.