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_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;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.testing— HVSC tune fetch/resolve for test suites (fetch_tune,resolve_tune,make_tune_fixtures), andmake_oracle_fixtures/oracle_grid— a reusable byte-exact player-vs-sidtrace-oracle test (docs/oracle-testing.md).survey— whole-song HVSC survey and failure triage of any format's renderer against thesidtraceoracle, 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).MemPlayer, theoracleregister_grid, andirqhostall start from it, so a tune reading uninitialised memory reads the power-on byte, not zero.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;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 aMemPlayerstraight to WAV through an emulated SID (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.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 format package registers aSidFormatinstead of shipping its own CLI.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.17.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 | |
|---|---|---|---|
| pysidtracker-0.17.0.tar.gz | 224.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pysidtracker-0.17.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 390.3 kB
Release files / pysidtracker-0.17.0.tar.gz
| Download URL | pysidtracker-0.17.0.tar.gz |
|---|---|
| Size | 224.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9ec150ef637f62d6d0af20b6bb2bebf1857db37bba84c2c88d1d0ce8d34ff405
|
|
BLAKE2b-256 checksum How to use checksums |
a2d23deda8c03e69909bb63846f80e45102fe7f7beeb50840d84f165ce45c079
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 15, 2026.
Transparency logRelease files / pysidtracker-0.17.0-py3-none-any.whl
| Download URL | pysidtracker-0.17.0-py3-none-any.whl |
|---|---|
| Size | 165.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0e9029768c23f421a7c07ec0f3965a2d1b6232053d4099997b3c47654b3008ad
|
|
BLAKE2b-256 checksum How to use checksums |
5b7802789f1ac77f07c8be979776f877bd86b624bdb0429ca11b6db052bdc3f6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 15, 2026.
Transparency log