Skip to content

MichaelTroelsen/SIDDetector-II

Repository files navigation

SID Detector v1.5.05

A Commodore 64 diagnostic utility that identifies 24+ variants of the SID (Sound Interface Device) chip — including real hardware, FPGA clones, microcontroller emulators, and PC emulators.

Original release: https://csdb.dk/release/?id=176909 Author: funfun/triangle 3532 Syntax: KickAssembler (converted from ACME original)

STORY.md — Full technical article: detection methods, reverse-engineered protocols, SIDFX secondary probing, self-modifying code patterns, hardware testing methodology, and lessons learned. For sceners, SID musicians, and hardware developers.


Screenshot

SID Detector running in VICE


Hardware test rig

Representative photos of the chips and boards the detector is validated against — see pictures/ for the full set.

6581 / 8580 reference chips

Reference chip collection: 6581 R2 / R3 / R4AR and 8580 R5 silicon on foam.

C64 with FM-YAM and CBM SFX Sound Expander

C64 with FM-YAM cartridge, Commodore FM Sound Module (SFX Sound Expander), and Ultimate II+ — used to verify the V1.4.x OPL $DF40/$DF50/$DF60 detection path.


What it does

When run on a C64 (or emulator), the program probes SID hardware registers, measures timing characteristics, and displays the results on screen. It detects:

Category Chips / Variants
Real chips 6581 R2, R3, R4, R4AR · 8580
FPGA clones FPGASID (6581 mode · 8580 mode)
Microcontroller ARMSID · ARM2SID · Swinsid Ultimate · Swinsid Nano · Swinsid Micro · SIDKick-pico · KungFuSID · BackSID · PD SID · SIDFX · ULTISID (U64) · uSID64
Emulators VICE 3.3 ResID 6581/8580 · VICE 3.3 FastSID · HOXS64 · Frodo · YACE64 · EMU64 · C64DBG
FM expansion CBM SFX Sound Expander · FM-YAM (Yamaha OPL2 / YM3812 at $DF40)
MIDI interfaces Sequential Circuits · Namesoft · DATEL/Siel/JMS · Passport · Maplin (6850 ACIA)
Machine type C64 · C128 · TC64 (Turbo Chameleon 64)
Clock PAL · NTSC
Stereo Scans D400/D500/D600/D700/DE00–DFFF for additional SID chips
Info pages Press I on the result screen for per-chip detail; CRSR LEFT/RIGHT to flip pages
Quality page Press Q for a per-slot audio-quality fingerprint (sidcheck grade + $D418 decay) — one row per detected SID

Keys on the result screen

Key Action
SPACE Restart the full detection sequence (raster-stable; silences all voices)
I Per-chip info pages (CRSR LEFT/RIGHT or B/M to flip; SPACE returns)
Q Quality Fingerprint page (sidcheck grade + $D418 decay per slot)
D Debug page — raw detection values, UCI/SIDFX state (D again → page 2)
R README / help viewer (W/S to scroll)
T SID sound test — plays the 3-voice melody on every detected slot
P SID Tracker View — live per-voice display while music plays
L TLR sid-detect2 second-SID detector (copied to $0801 and run)

Detection chain

The program runs a sequential detection pipeline at startup. Each step either identifies the chip and jumps to the result display, or falls through to the next step.

Step 1 — SIDFX (DETECTSIDFX)

Communicates with the SIDFX cartridge using its SCI serial protocol over the D41E/D41F register pins. Sends a "PNP" login packet ($80 $50 $4E $50) and reads back a 4-byte vendor/product ID. Checks for the expected signature $45 $4C $12 $58.

  • data1 = $30 → SIDFX found
  • data1 = $31 → SIDFX not present

When SIDFX is found, D41D (SW2/SW1/PLY) and D41E (SID1/SID2 types) are saved. After detection, sidfx_populate_sid_list populates sid_list: slot 1 = D400 with SID1 type, slot 2 = secondary address (D420/D500/DE00 per SW1 bits 5:4) with SID2 type. Before writing the secondary type, sfx_probe_skpico probes the secondary for SIDKick Pico identity: enters config mode by writing $FF to secondary+$1F, then reads VERSION_STR[0]='S' and [1]='K' at secondary+$1D — if confirmed, the slot type becomes $0B/$0E (SIDKick Pico 8580/6581) instead of the generic 6581/8580 from SIDFX D41E.

Step 2 — ARMSID / ARM2SID / Swinsid Ultimate (Checkarmsid)

Writes the ASCII string "DIS" to voice-3 registers (D41D/D41E/D41F), waits, then reads back D41B/D41C/D41D. Real SID chips do not echo writes; clone chips do:

Echo in D41B Meaning
'S' = $53 Swinsid Ultimate
'N' = $4E ARMSID family

For ARMSID, D41C and D41D further discriminate:

  • D41C = 'O' ($4F) and D41D = 'R' ($53) → ARM2SID
  • D41C = 'O' ($4F) and D41D ≠ 'R'ARMSID

Self-modifying code: Checkarmsid patches the high-byte of all its SID register addresses at runtime (e.g. cas_d418+2, cas_d41D+2) so the same routine works for both D400 and D500 (stereo second-SID scan). This is the most complex routine in the codebase.

Steps 3a–3c — PD SID / BackSID / SIDKick-pico

These three checks run in sequence after ARMSID. All use D41D–D41F register probe protocols.

PD SID (checkpdsid): Writes 'P' to D41D, 'D' to D41E, reads D41E back — expects 'S'. data1 = $09.

BackSID (checkbacksid): Writes $42 to D41C, $B5 to D41D, $1D to D41E, reads D41F — expects $42 echoed. data1 = $0A.

SIDKick-pico (checkskpico): Enters config mode by writing $FF to D41F. The VERSION_STR pointer is manual — write $E0+i to D41E to select byte i, then read D41D. No auto-increment on reads.

D41E write D41D read Meaning
$E0 $53 = 'S' byte[0] of VERSION_STR
$E1 $4B = 'K' byte[1] of VERSION_STR

If both match → SIDKick-pico (data1 = $0B). Confirmed against firmware v0.22 DAC64. Full VERSION_STR: SK\x10\x09\x03\x0F0.22/DAC64.

Step 3d — FPGASID (checkfpgasid)

Enters FPGASID configuration mode by writing the magic cookie $81/$65 to D419/D41A, then sets bit 7 of D41E. Reads D419 and D41A back and checks for the identify signature $1D/$F5. If matched:

  • D41F = $3FFPGASID 8580 mode (data1 = $06)
  • D41F = $00FPGASID 6581 mode (data1 = $07)

Step 4e — Real SID (checkrealsid, normal position)

Uses the voice-3 oscillator readback technique (reference: 1541 Ultimate detection code):

  1. Writes $48 (gate bit set) to D412 (voice 3 control)
  2. Shifts right and writes the sawtooth waveform value to D412
  3. Reads D41B (oscillator 3 output) — real SIDs echo predictable values; emulators/empty sockets do not

Then reads D41B a second time and checks for $03. From that value, MODE6581/MODE8580 look-up tables classify the exact sub-revision.

Step 5 — Second SID scan (checksecondsid)

Uses the noise-waveform mirror test: activates noise waveform on voice 3, then reads D41B at $D400 + $20 offsets. A real SID at that address produces non-zero random values; a mirrored (non-SID) address always reads zero.

Scans: D400, D420, D440 … DE00, DE20, DEE0, DF00 … DFE0

Step 3e — uSID64 (checkusid64)

Runs after FPGASID, before the real SID check. Writes the config unlock sequence $F0 $10 $63 $00 $FF to D41F, then reads D41F twice with a ~3 ms gap.

  • Both reads must be in $E0–$FC range (not $FF)
  • Both reads must agree within $02 of each other (stable — the chip holds the value)

A decaying NOSID bus typically reads $FF (the last written value), or if it has drifted into $E0–$FE, the two reads will differ by more than $02. Either condition rejects the chip.

  • data1 = $0D → uSID64

Step 0.25 — Real SID pre-check (checkrealsid, early)

Runs before SwinSID Nano to prevent false positives on real 6581/8580. Uses the voice-3 oscillator readback (see Step 4e). checkrealsid only writes to D412/D40F — never D41F — so it is safe to run early. If a real SID is confirmed here, checkswinsidnano is skipped entirely.

Step 5c — SwinSID Nano (checkswinsidnano)

Runs at Step 0.5 (before SIDFX), provided no real SID was found at Step 0.25. Uses D41B (OSC3) activity counting with noise waveform at maximum frequency ($FFFF):

Stage 1 — Change-count gate: Reads D41B 8 times back-to-back and counts how many consecutive pairs differ. A real 6581/8580 LFSR advances every CPU clock at $FFFF frequency — all 7 pairs always change (cnt = 7). The SwinSID Nano AVR updates at ~44 kHz (much slower than the 985 kHz C64 clock), so some reads catch the same LFSR value (cnt = 3–7, hits 7 in ~40% of windows). Stage 1 retries up to 3 times and only rejects if all attempts give cnt = 7 (guaranteed real-SID speed). P(all 3 fail for SwinSID Nano) ≈ 6%.

Stage 2 — Activity confirmation at 62 ms: After a 50 ms wait, counts changes in another 8-read window. Requires cnt ≥ 3. Filters out a fully-dead NOSID bus that happens to have passed Stage 1.

Known limitation: A C64 with an Ultimate II+ cartridge and virtual SID disabled generates FPGA-sourced bus noise at ~44 kHz that is indistinguishable from the SwinSID Nano oscillator. Such a setup will be reported as SwinSID Nano rather than NOSID.

  • data1 = $08 → SwinSID Nano (or NOSID + U2+ with virtual SID off)

Step 5b — KungFuSID (checkkungfusid)

Runs after all other hardware checks have failed (last chance before SwinSID Nano / NOSID). Writes $A5 (firmware-update magic) to D41D, waits ~6 ms, reads D41D back.

Read back Meaning
$5A New firmware: ARM acknowledged the update-start magic → KungFuSID
$A5 Old firmware: register echoes the last write (all SID registers are stored in RAM array) → KungFuSID
anything else Not KungFuSID

Old firmware reason: kff_read_handler returns SID[register_addr] for every address except the firmware-update register on new firmware. Writing $A5 stores it; reading back returns $A5. Real SID chips, ARMSID, FPGASID, and SIDKick all produce different values.

  • data1 = $0C → KungFuSID

Step 6 — $D418 decay fingerprint (calcandloop + ArithmeticMean)

Sets the volume register D418 = $1F then counts CPU cycles until it decays to zero. Different emulators decay at different rates. The routine:

  1. Samples 6 times (controlled by NumberInts = $06)
  2. Averages the samples with ArithmeticMean (16-bit accumulator, integer division)
  3. Compares the average against a MODE6581/MODE8580/MODEUNKN table to print the emulator name

This fingerprint distinguishes VICE ResID, VICE FastSID, HOXS64, Frodo, YACE64, and EMU64.

Step 7 — TLR baseline sweep (tlr_sweep, V1.5.01)

Runs only when no primary chip was identified (data4=$00). A family-agnostic scan adapted from TLR's sid-detect2: walks $D400–$D7E0 and $DE00–$DFE0 in $20 strides and, at each unclassified slot, runs a retriggered-sawtooth OSC3 count-up test (the readback must increment across three reads — proof an oscillator is advancing there). New finds are appended to sid_list as type $11 ("TLR-generic"); a dedupe pass collapses duplicates against the family-specific results, keeping the refined type.

Step 8 — FM expansion (checkfmyam, V1.4.x)

Probes $DF40/$DF50/$DF60 for a Yamaha OPL family chip (YM3526 / YM3812) — the CBM SFX Sound Expander and FM-YAM both decode there identically. Detection uses an (status & $E0) == 0 two-read check: a real OPL drives status into $00–$1F; open bus has the high bits set. Reported on the next free stereo row as DF40 SFX/FM FOUND. One detection path covers both products (see STORY.md §23).

Step 9 — MIDI cartridge (checkmidi, V1.4.45)

Probes the four documented C64 MIDI interfaces for a 6850 ACIA reset signature: write $03 (master reset) to the control register, then status reads & $73 == $02 (transmit-empty, nothing received, no errors). Address pairs, first-hit-wins: Sequential/Namesoft $DE00/$DE02, DATEL/Siel/JMS $DE04/$DE06, Passport $DE08, Maplin $DF00. A two-read consistency check rejects open-bus jitter; the $DF00 probe is guarded against SIDFX / U64 / SIDKick-pico FM / ARM2SID SFX- ownership. When no SID was found, the cart name overwrites the NOSID row. See STORY.md §24.


Machine type detection

PAL / NTSC (checkpalntsc)

Patches the NMI vector to RTI, then checks whether a raster IRQ fires at line $137 (which only exists on PAL machines with 312 lines). Result stored in KERNAL variable $02A6:

  • 1 = PAL (~50 Hz)
  • 0 = NTSC (~60 Hz)

All timing loops in the detection chain are calibrated to this value.

C64 / C128 / TC64 (check128)

  1. Reads $D030 (C128 speed register; returns $FF on open C64 bus → C64 identified)
  2. If not $FF, writes $2A to $D0FE and reads it back:
    • Returns $FCCommodore 128
    • Returns other → Turbo Chameleon 64 (TC64)

Result stored in za7 ($A7).


Memory layout

Address Contents
$0801 BASIC stub: SYS 9216 (→ $2400)
$1800 Embedded SID tune 1 (Triangle Intro, $1806 play)
$0A00 Embedded TLR sid-detect2 (copied to $0801 on L)
$2400 Main program — start: and all detection subroutines ($2400~$5A99)
$5B00 tlr_sweep family-agnostic baseline scan (V1.5.01)
$6000 Detection result tables (num_sids, sid_list_l/h/t, sid_map), screen data, info-page text, string labels, colour table
$9200 SID Tracker View code (V1.4.33)
$A000 Embedded SID tune 2 (Delirious 9, under BASIC ROM)
$C000 Tracker shadow SID ($C000$C01F)
$C020 Tune-selector segment (V1.4.35)
$C300 Quality Fingerprint page code + tables (V1.5.02)

Zero-page variables

Address Name Purpose
$A4 data1 Primary result (chip type code)
$A5 data2 Secondary result (echo char / sub-type)
$A6 data3 Tertiary result (ARM2SID 'R' discriminator)
$A7 za7 Machine type: $FF=C64, $FC=C128, other=TC64
$F7 sidnum_zp Number of SID chips found
$F9–$FA sptr_zp SID base address pointer (e.g. $D4:$00)
$FC–$FD mptr_zp Mirror-scan address pointer

Result codes (data1)

Code Chip
$01 6581
$02 8580
$04 Swinsid Ultimate
$05 ARMSID / ARM2SID
$06 FPGASID 8580
$07 FPGASID 6581
$08 Swinsid Nano
$09 PD SID
$0A BackSID
$0B SIDKick-pico (8580)
$0C KungFuSID
$0D uSID64
$0E SIDKick-pico (6581)
$10 Second SID found
$20$21/$24$26 ULTISID 8580
$22$23 ULTISID 6581
$30 SIDFX
$31 No SIDFX
$F0 No SID / Unknown

Screen layout (v1.5.05)

            siddetector v1.5.05

row  2: armsid.....:  [result]
row  3: swinsid....:  [result]
row  4: fpgasid....:  [result]
row  5: 6581 sid...:  [result]
row  6: 8580 sid...:  [result]
row  7: sidkick....:  [result]
row  8: backsid....:  [result]
row  9: kungfusid..:  [result]
row 10: pd sid.....:  [result]
row 11: nosid......:  [result]
row 12: sidfx......:  [result]
row 13: pal/ntsc...:  [PAL/NTSC]   [C64/C128/TC64]
row 14: usid64.....:  [result]
row 15: $d418 decay:  [value]
row 16: stereo sid.:  [address + chip name per slot]

Spacebar restarts the full detection sequence (raster-stable restart via $D012 spin). Pressing SPACE also silences all SID voices immediately.

Press P to launch the SID Tracker View — a dedicated screen that plays the built-in Triangle Intro (Michael Troelsen / Fun Fun 1988) and shows per-voice state live while music plays:

  • NOTE / WAVE / GATE / ADSR / FREQ columns for voices 1–3
  • VU bar per voice with a white → grey → yellow → orange → red gradient (voice 3 reads real $D41C ENV3; voices 1/2 use a software envelope follower with gate-edge detection)
  • Live OSC3 scope across rows 15–22 (40-sample burst of $D41B)

Press P, SPACE, or Q on the tracker screen to stop and return to detection.

Technical note: SID voice registers are write-only, so to display what the player is writing to voices 1 and 2 the tracker patches the player binary at $1800–$1FFF, rewriting every STA $D4xx to STA $C0xx. The raster IRQ then copies $C000–$C01F → $D400–$D41F each frame after the play call, so audio is unaffected. On exit, an undo table restores the original bytes for a clean re-entry.

Press I to enter the info page for the detected chip. Navigate with CRSR LEFT/RIGHT; SPACE returns to the main screen. 17 pages are available (one per chip type), browsable in any order.

Press Q to enter the Quality Fingerprint page (added V1.5.02). It paints one row per detected SID, combining two orthogonal accuracy fingerprints:

D400 QUALITY 4/5 (GOOD  8580  ) D418=N15
D420 QUALITY 2/5 (BAD   ARMSID) D418=N18
  • sidcheck grade (0–5, banded AWFUL / BAD / GOOD / BEST) — the combined-waveform OSC3 readback test from Wonderland XIII / Censor Designs, lifted from the VICE testprog suite and rebased to run per sid_list slot. The cycle-critical writes target absolute $D4xx operands that are runtime-patched per slot (a 36-site patch list generated at assemble time).
  • $D418 decay (Nnn) — the same volume-decay measurement used on the main screen's row 15, surfaced for every slot so implementations can be compared side by side.

The chip-name column is resolved through sid_type_index, the single code→name table shared with the debug page. SPACE or Q returns to the main screen.

Note: the decay column reads N00 under VICE because reSID doesn't model $D418 read-decay like real silicon; the sidcheck grade is meaningful in both. Real-hardware decay values are captured by make hw_test (TEST 9). The sidcheck result table is PAL-calibrated.


Build

make          # assemble siddetector.asm → siddetector.prg  (requires Java + KickAss.jar)
make run      # launch in the patched WinVICE 3.9
make clean    # remove siddetector.prg

# Variant-specific launches (patched VICE only)
make run-armsid       make run-arm2sid      make run-swinu        make run-swinnano
make run-fpgasid8580  make run-fpgasid6581  make run-pdsid        make run-kungfusid
make run-backsid      make run-usid64       make run-sidfx        make run-skpico8580
make run-skpico6581   make run-none

# MixSID / stereo (8580 @ D400 + personality @ D420)
make stereo-armsid    make stereo-arm2sid   make stereo-swinu
make stereo-fpgasid   make stereo-sidfx

# Regression harnesses
make ci               # 32 unit tests       (~30 s)
make ci-full          # ci + 14 variant goldens  (~4 min)
make test-variants    # variant sweep alone
make update-variant-goldens    # after intentional UI or personality change

Requirements:

  • Java runtime
  • KickAssembler at C:/debugger/kickasm/KickAss.jar
  • Patched WinVICE 3.9 at C:/Users/mit/claude/c64server/vice-sidvariant/GTK3VICE-3.9-win64/bin/x64sc.exe (build recipe: docs/VICE_PROXY_BUILD.md; patch: patches/vice-sidvariant-v1.patch). All VICE-based tests and make run-* / stereo-* targets require this binary — the stock VICE doesn't know the -sidvariant flag.

Chip-variant testing without hardware

The repo ships a patched fork of VICE 3.9 (in ../vice-sidvariant/) with a -sidvariant <name> CLI flag. Each emulated SID slot can wear a chip-family personality that responds to that chip's detection magic-cookie protocol — ARMSID, ARM2SID, SwinSID U/Nano, FPGASID 6581/8580, PDsid, KungFuSID, BackSID, SIDKick-pico 6581/8580, SIDFX, uSID64. Audio still comes from ResID; only the detection protocol is intercepted.

This makes CI exercise every chip family without plugging anything into a real C64. The window title shows the active personality (e.g. VICE (C64SC) [SidVariant=sidfx]) so you always know what's loaded.

make ci-full runs 32 unit tests + a 14-case variant sweep that byte-diffs each variant's detection screen against a stored golden (in tests/variant_goldens/). If a regression changes what a variant looks like, the diff is printed and CI fails loudly.

Full design rationale and per-chip protocol tables:

  • docs/ARMSID_PROXY_PLAN.md — ultraplan with the 9-phase build-out.
  • docs/VICE_PROXY_BUILD.md — reproducible build from MSYS2.
  • docs/VICE_PROXY_USAGE.md — flag catalogue + make targets.
  • docs/test_matrix.html — combined HW + VICE test dashboard (open in browser).
  • patches/vice-sidvariant-v1.patch — ~2 kLOC source diff against pristine VICE 3.9 (upstream-ready, GPLv2+ inherited).

Unit testing

Tests are written in 6502 assembly (KickAssembler syntax) and run inside WinVICE. Each test preset the relevant zero-page inputs (data1, data2, data3, za7), calls an embedded copy of the dispatch logic, compares the returned result code against the expected value, and displays PASS / FAIL on the C64 screen. The final pass count is written to $07E8 (off-screen RAM) for inspection in the VICE monitor.

Running tests

make test           # ArithmeticMean unit tests       (4 cases)
make test_dispatch  # ARMSID / FPGASID dispatch tests (8 cases)
make test_suite     # Full suite — all scenarios      (43 cases)

After VICE opens, all results are visible on screen immediately. To check the pass count in the VICE monitor (Alt+M):

mem $07E8 $07E8    # shows pass count; $2B (43) = all passed for test_suite

Test files

File Tests Covers
tests/test_arith.asm 4 ArithmeticMean — pure computation
tests/test_dispatch.asm 8 ARMSID / ARM2SID / FPGASID dispatch
tests/test_suite.asm 43 All dispatch stages (see table below)

Each file has a matching .mon VICE moncommands file that loads symbols and sets a breakpoint at td_spin (the completion spin-loop).

Full suite coverage (test_suite.asm)

Section Tests Stage Inputs → Expected result
S1 T01–T03 Machine type za7=$FF → C64 · $FC → C128 · other → TC64
S2 T04–T05 SIDFX data1=$30 → found · $31 → not found
S3 T06–T10 Swinsid/ARMSID $04 → Swinsid-U · $05/$4F/$53 → ARM2SID · $05/$4F/other → ARMSID · no-match cases
S4 T11–T13 FPGASID $06 → 8580 · $07 → 6581 · other → no match
S5 T14–T16 Real SID $01 → 6581 · $02 → 8580 · other → no match
S6 T17–T18 Second SID / no sound $10 → second SID · other → no sound
S7 T19–T22 ArithmeticMean [10,20,30]=20 · [5×6]=5 · [100,50,75,25]=62 · empty=0
S8 T23 FPGA stereo data1=$06 at $D500 → recorded in sid_list
S9 T24–T27 New chips $09 → PDsid · $0A → BackSID · $0B → SIDKick-pico · $0C → KungFuSID
S10 T28–T29 ARM2SID SFX emul_mode=$01 → SFX only · $02 → SID+SFX
S11 T30–T31 SKpico FM skpico_fm=$04/$05 → FM Sound Expander at $DF00
S12 T32 FM-YAM OPL2 fmyam_detected=$01 → FM-YAM/OPL2 at $DF40
S13 T33–T35 Quality band score 0 → AWFUL · 5 → BEST · $FF → BAD-clamp
S14 T36–T43 Chip-type index sid_type_index code→slot: $01→6581 · $02→8580 · $08→Nano · $09→PDsid · $0E→SKpico-6581 · $30→SIDFX · $F0→NoSID · $0F→UNKWN

Design approach

Because the actual detection routines (Checkarmsid, checkfpgasid, etc.) probe real SID hardware registers, they cannot be tested deterministically inside VICE — VICE's SID emulates an 8580 and returns fixed values. The tests therefore target the dispatch logic: the code that reads data1/data2/data3 (already set by the detection routines) and branches to the correct chip identification. Each dispatch routine is an embedded copy of the relevant branch block from siddetector.asm, with jsr $AB1E / jsr $E50C / jmp end replaced by a result-code write to dispatch_result.

Adding a test

  1. Identify the zero-page inputs for the scenario
  2. Add a dispatch routine that mirrors the branch logic from siddetector.asm
  3. Write a test block: set inputs → call dispatch → compare dispatch_result → print PASS/FAIL
  4. Increment pass_count on pass
  5. Update the total expected count in test_done (cmp #N)

What remains untested

Area Why
Checkarmsid hardware probe Writes to SID regs; VICE returns fixed 8580 values
checkfpgasid magic-cookie Config mode only exists on real FPGASID hardware
checkrealsid OSC3 readback Depends on real sawtooth waveform decay
checksecondsid noise mirror Depends on real noise-waveform $D41B randomness
calcandloop decay timing Emulator timing differs from hardware by design

Known issues / TODO

  • Added V1.5.05: Single source of truth for chip-type → name. The Q page and the debug page previously carried two independent code→name mappings that had drifted (that drift produced the V1.5.04 $01/$02 swap and $0E mismap). They now share one sid_type_index resolver + a 17-byte sid_code_to_slot table feeding row-aligned sidname_short_* (6-char Q page) and sidname_long_* (debug page) tables — adding a chip is one row in each and they cannot disagree. ULTISID ($20–$26) stays special-cased in each printer (debug names all 7 filter-curve variants; the 6-char Q page only distinguishes 8580 vs 6581 family). Unit suite grown to 43 tests (T36–T43 exercise sid_type_index directly — the keystone guard against this drift class); hw_test.py TEST 9 captures the per-slot Q-page sidcheck grade + $D418 decay off real hardware into the run report. Binary shrank 49222 → 49184 bytes.
  • Fixed V1.5.04: Q-page chip-name table holes + a pre-existing debug-page swap. quality_print_chiptype routed five valid type codes to "UNKWN" — $09 PDsid, $0E SIDKick-pico 6581 (was mislabelled "USID64"), $10 secondsid, and $20/$21/$22 ULTISID — now PDSID/SKPI65/2NDSID/ULTI85/ULTI65. Separately, dbg_print_sid_typename had type $01 and $02 mapped to the wrong strings (6581 shown as "8580 FOUND" and vice-versa) since the routine was written; the canonical convention is $01=6581, $02=8580 (line 753 / 5894). Also retired do_quit/goodbye_text/EXITINTRO (~30 bytes dead code since V1.5.02 rebound the Q key), and fixed the taskkill //F quoting in the new smoke scripts.
  • Fixed V1.5.03: Two latent Q-page bugs surfaced by interactive WinVICE smoke-testing (neither was touched by the unit suite or the variant goldens). (1) qc_pt_ptr was declared in the $C300 code segment, not zero page — KickAssembler silently truncates a non-ZP label in (label),y indirect-indexed addressing to its low byte, so sta (qc_pt_ptr),y assembled as sta ($A9),y and corrupted memory on every Q-press; moved to $C1. (2) calcandloop_q inherited the txs/tsx "save X" trick from calcandloop, which is only safe because the original tail-calls jmp funny_print and never returns — the Q-page copy returns via rts, so each iteration buried the return address; fixed by restoring X from a zero-page slot instead.
  • Added V1.5.02: Quality Fingerprint page (Q key). One row per detected SID combining two orthogonal accuracy fingerprints: a sidcheck combined-waveform OSC3 grade (0–5, banded AWFUL/BAD/GOOD/BEST, lifted from the Wonderland XIII / Censor Designs VICE testprog) and the $D418 volume decay. The cycle-critical sidcheck writes target absolute $D4xx operands that are runtime-patched per sid_list slot via a 36-site patch list generated at assemble time by a qrec() macro, so the original's timing is preserved across all eight possible slots. Lives in a fresh $C300 RAM segment. SPACE or Q returns to the main screen.
  • Added V1.5.01: TLR family-agnostic baseline sweep (tlr_sweep, adapted from TLR's sid-detect2). Walks $D400–$D7E0 and $DE00–$DFE0 in $20 strides and, at every unclassified slot, runs a retriggered-sawtooth count-up test (OSC3 must increment across three reads) before the family-specific scans. New finds are appended to sid_list as type $11 ("TLR-generic"); a dedupe pass collapses duplicates, keeping the refined type. Gated to data4=$00 (no primary chip identified) — a broader gate false-positives on mirrors and disturbs ARMSID DIS state.
  • Added V1.4.45: MIDI cartridge detection. New checkmidi routine probes the four documented C64 MIDI carts (codebase.c64.org reference) via the 6850 ACIA master-reset signature: Sequential Circuits / Namesoft at $DE00/$DE02, DATEL/Siel/JMS at $DE04/$DE06, Passport at $DE08, Maplin at $DF00. First-hit wins (only one MIDI cart can be attached at a time per the codebase reference). Result is printed on row 11 (the NOSID line) at column 13, aligned with the other detection rows. Sequential and Namesoft share the polled-read fingerprint (only IRQ vs NMI line routing differs) and are both reported as SEQUENTIAL MIDI. The $DF00 (Maplin) probe is guarded against SIDFX / U64 / SIDKick-pico FM / ARM2SID SFX- ownership. The patched WinVICE 3.9 was rebuilt with --enable-midi to support headless make run-midi-{sequential,passport,datel,namesoft,maplin} and 5 new variant_smoke.py golden-fingerprinted test cases. Side-fix: the legacy diagnostic write at $5801 (which was clobbering bytes overlapping the MIDI string area) is now stored at a labelled diag_step4_result byte. variant_smoke.py also gained a -default flag to insulate runs from vice.ini settings persisted by interactive make stereo-* / make sfx sessions.
  • Added V1.4.44: Polish + test infra. Restart progress bar shares row 24 with the "*** RESTARTING ***" banner instead of using a separate row (bar fills outside the banner range, recolours banner letters green inside it — one row total). Behavioural U64 fallback: is_u64 = 1 when the fingerprint scan finds 4+ slots even if the $DF1F UCI probe at start: reads $FF (UCI disabled / remapped). "TUNEFUL EIGHT READY ON U64" banner overlays row 11 (NOSID...:) when sidnum_zp == 8 AND is_u64 == 1. $01 = 6581 / $02 = 8580 is now canonical across code + docs; six stale comment sites and two reversed branches in u64_fingerprint_scan / uci_type_for_addr corrected (your ULTISID slots now correctly display as 6581 INT). utfa_drain capped at 256 iterations (was unbounded — could hang when UCI was unresponsive). New scripts/check_memorymap.py [--fix] verifies addresses in docs/MEMORYMAP.md against the live siddetector.sym; wired into make ci as a doc-drift guard. New make test-tuneful-eight runs a non-destructive end-to-end U64 8-SID hardware test: snapshot the live config, apply bin/tt8-ultimate.cfg, run siddetector, verify sidnum_zp == 8 + is_u64 = 1 (all 8 slots labelled 6581 INT), restore the pre-test config in a finally block.
  • Added V1.4.37: Write-coupling scan (u64_fingerprint_scan) for 8-SID Ultimate 64 "Tuneful Eight" configurations — independently verified to find all 8 slots (D400 D420 D480 D4A0 D500 D520 D580 D5A0) on real hardware across three consecutive runs. The existing checksecondsid noise-mirror trick falsely rejects U64 UltiSID slots because every FPGA slot runs its voice 3 oscillator continuously, so reads of candidate $D41B are non-zero even when primary noise is enabled. The new scan instead silences primary D400 voice 3, snapshots each already-found slot's OSC3 baseline per candidate, writes a saw+gate activation to the candidate, and rejects only if the activation propagates to primary or any found slot's OSC3 (i.e. the candidate is a write-mirror). is_u64 ($DF1F != $FF) gate dropped because U64 configs that disable UCI return $FF there yet still have multi-SID hardware; on a single-SID C64 every candidate write mirrors to primary, so the test correctly rejects all candidates anyway. Bypassed uci_type_for_addr's UCI-FIFO drain loop (hangs forever when $DF1C/$DF1E both return $FF) — chip-type comes from a direct checkrealsid call.
  • Added V1.4.36: 8-SID enumeration for Ultimate 64 "Tuneful Eight" config. num_sids / sid_list_l/h/t extended from 8 to 9 bytes (slot 0 reserved, slots 1..8 active); s_s_add cap raised from 7 to 8; csfp_l_l_found (FPGASID-stereo path) cap raised from 7 to 8; sidstereo_print now renders up to 8 SID rows at screen rows 16..23. Row 24 stays as the action bar. U64 detection itself was already in place via $DF1F != $FFis_u64 = 1. Verified the hardware exposes 8 addressable SIDs by cross-checking with TLR's sid-detect2.
  • Added V1.4.35: Second tune in tracker view (1/2 keys) and a progress bar across the SPACE-restart wait. Triangle Intro stays at $1800; Delirious 9 (Troelsen / Genesis Project, 1990) is SIDwinder-relocated to $A000–$B39B and embedded as a separate segment. tune_select patches the IRQ play / init JSR operands, the tracker_patch_once scan range, and the title row pointer; pressing 1 / 2 in the tracker view stops IRQ play, silences SID, repatches the new player and re-inits in place. BASIC ROM is banked out ($01=$36) only inside the IRQ play call and around tune_player_init / tracker_patch_once so $AB1E (BASIC STROUT) stays usable elsewhere. The new tune-management block lives in a fresh $C020 segment above the shadow SID. The 2 s SPACE-restart dwell now fills row 23 with green solid blocks (one cell per ~54 ms) instead of busy-waiting silently.
  • Added V1.4.33: SID Tracker View (P key) — dedicated screen during music playback showing per-voice NOTE/WAVE/GATE/ADSR/FREQ, VU bars with white→red gradient, and live OSC3 scope for voice 3. Player writes are redirected to shadow RAM at $C000–$C01F via one-shot binary patch so voice 1/2 registers can be read back (normally write-only). Tracker code lives at $9200 (below BASIC ROM) to stay CPU-visible without bank switching. See MEMORYMAP.md.
  • Fixed V1.3.84: SIDFX secondary D420 probing: (1) removed D41D echo test — SIDFX write-buffers unmapped registers ($1D–$1F), causing any chip at D420 to echo back the written value (not chip-specific). (2) When primary SID is ARMSID, skip DIS probe at D420 entirely — ARMSID snoops CS2 DIS writes and drives $4E aggressively on all D4xx bus reads, contaminating D43B. Falls back to SIDFX-reported type (6581/8580). DIS probe still works for D420 with non-ARMSID primary, and for D5xx+ regardless of primary. Added D41B ACK in sfx_probe_dis_echo before secondary reads (harmless for non-ARMSID primaries).
  • Fixed V1.3.83: Detection confidence indicator — if checkrealsid needed retries due to VIC bad-line DMA steals, a * is appended after "6581 FOUND"/"8580 FOUND" on the main screen. retry_zp ($B2) tracks how many of the 3 attempts were used.
  • Fixed V1.3.81: Multi-SID sound test now plays the full 3-voice melody on every detected SID slot (not just a triangle tone). snd_patch_page self-modifies all 31 sta $D4xx instructions in st_soundtest to the target SID page.
  • Fixed V1.3.80: Stereo ARMSID@D400 + SwinSID U/ARMSID@D5xx — s_s_arm_call_real now allows sfx_probe_dis_echo when primary is ARMSID (data4=$05); the probe reads from candidate+$1B so D400 ARMSID snooping the DIS writes does not corrupt the result. Requires dual ARMSID/SwinSID U hardware to verify.
  • Fixed V1.3.79: SwinSID Ultimate fiktivloop false positive — AVR OSC3 returns 0 with noise enabled, causing checksecondsid to falsely detect D500 as a second SID. Fixed by skipping fiktivloop when primary is SwinSID U (data4=$04).
  • Fixed V1.3.79: Stereo 6581@D400 + SwinSID U@D500 — s_s_arm_call_real now tries sfx_probe_dis_echo before checkrealsid when primary is a real SID; SwinSID U echo returns 'S' and is correctly identified.
  • Fixed V1.3.73: 8580@D400 + ARMSID@D420 (MixSID, C09 config) now correctly detected
  • Fixed V1.3.74: 6581@D400 + ARMSID@D420 (MixSID, C08 config) now correctly detected
  • ARM2SID stereo D400+D500 not yet verified on hardware
  • Stereo slots D700 and DF00 not yet tested on hardware
  • Swinsid Nano with NOSID+U2+ (Ultimate II+ with virtual SID off) is indistinguishable — reported as SwinSID Nano; accepted limitation
  • FPGASID in SIDFX SID1 slot is undetectable: SIDFX drives D419/D41A (POT registers) with real joystick data, masking FPGASID's identify-mode readback signature; additionally, the SIDFX SCI state machine reacts to D41E writes, disrupting the magic-cookie handshake. No software workaround is possible. Reported as SIDFX with unknown SID type.
  • Quality page $D418 decay reads N00 under VICE — reSID emulates the audio path faithfully but does not model the analog volume-register read-decay the measurement depends on. The sidcheck grade is meaningful under emulation; real decay values come from make hw_test on a U64. Not yet captured/calibrated on hardware.
  • Quality page sidcheck result table is PAL-calibrated; NTSC scores may shift. Per-family expected-grade reference not yet built (awaiting hardware decay data).

About

Commodore 64 SID chip identifier — detects 24+ variants of real hardware, FPGA clones and emulators

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors