JSIDPlay2 is a highly compatible and cycle-exact Commodore 64 (C64) emulator. It is written in Java and features:
Use the following command to build the project. Replace javafx.platform with your platform (linux, linux-aarch64,
win, mac, or mac-aarch64).
mvn -Djavafx.platform=linux clean install
target/standalonetarget/deploymvn exec:java -Dexec.mainClass=ui.JSidPlay2Main
mvn exec:java -Dexec.mainClass=server.restful.JSIDPlay2Server
Then open start page http://127.0.0.1:8080 in your browser.
... or open demo
page http://127.0.0.1:8080/static/teavm/c64jukebox.vue in your
browser.
For detailed server setup (NGINX, MySQL, WhatsSID), see docs/SERVER_SETUP.md.
For the developer release process, see docs/RELEASE_HOWTO.md.
For the JCommander usage, see docs/JCOMMANDER_USAGE.md.
For the latest changes, see CHANGELOG.md.
For plans, see TODO.md.
The JavaFX and browser emulation settings offer VIC Model beside C64 Model. DEFAULT follows the machine preset: C64 uses MOS6569R3 for PAL and MOS6567R8 for NTSC; C64C uses MOS8565 for PAL and MOS8562 for NTSC. With DEFAULT, the forced video standard, tune metadata and default video standard retain their existing precedence.
Explicit choices are MOS6569R3, MOS8565 and MOS6567R8. The selected chip determines PAL/NTSC, overriding forced clock settings and tune metadata. Manual clock controls are disabled while an explicit chip is selected, and their saved values are retained for a return to DEFAULT. The C64 preset continues to select the CIA and glue logic.
Java/console configuration supports VicModel=DEFAULT in the INI [Emulation] section and, for example, --c64Model C64C --vicModel MOS6569R3. JavaFX changes require restarting the application; browser changes apply on the next playback/open. Status reports the active VIC and clock while a saved change is pending. Older saved configurations use DEFAULT.
Chip selection applies the currently implemented timing behavior and preserves the existing palette. The C64C NTSC default retains its existing MOS8562 identity and behavior; separate MOS8562 revision emulation is not added here.
The program tests run headless, sequentially, with a fresh emulator per test and hardware variant. No desktop, display server, or audio device is needed. Model-sensitive contracts select C64/MOS6526 and C64C/MOS6526A within one run; independent programs run once. Existing fixture-specific hardware baselines and known exclusions are retained.
# Run the test bench and generate the published Surefire report.
mvn -Djavafx.platform=linux -Prelease clean verify
# Inspect mismatch screens and the complete outcome summaries.
# target/deploy/tests/visual/index.html
# Surefire report: target/deploy/tests/index.html
The latest published test report is updated at the same URL. The Surefire report shows Pass Rate (without skipped) (passed / executed) and Pass Rate (with skipped) (passed / total), where executed = total - skipped and passed = executed - failures - errors. For example, 75 passed, 5 failed and 20 skipped means 93.75% without skipped tests and 75% with skipped tests. Rates describe the selected tests in that run, not code coverage. A zero denominator is shown as N/A. Both percentages reuse the same recorded results; report generation does not run the tests again.
The gallery is created on every Surefire run and opens directly in a browser. Headless program tests update it after each result. A component-only selection can leave the gallery empty because those tests do not capture screens; the Surefire report still lists every test. The gallery defaults to failures, while its pass/fail/skip outcomes remain available in the filters and summaries. Exact visual differences show expected, actual and difference images with a differing-pixel count and first differing coordinate. Captions identify hardware, clock, source provenance and capture cycle. Passing and skipped checks have textual results without screenshot panels. Raw frames and reference artifacts remain available through visual/artifacts.json, including frames consumed by browser-parity checks. Archive the gallery before another invocation replaces it.
Reference-backed diagnostic checkpoints are automated visual assertions. A complete frame is captured at a scheduled emulated checkpoint with filtering disabled; every VIC color pixel must match, using fixed PAL/NTSC palette mappings and the established viewport alignment. There is no similarity threshold, rescaling or search for a better-matching later frame. Exact matches report JUnit PASS, differences report FAIL, and missing or non-comparable visual oracles remain SKIPPED with specific reasons. Photographs remain manual because their pixel coordinates cannot be compared directly. Visual acceptance covers the captured screen; audio, analog measurements and interactive requirements remain documented separately.
The Surefire report embeds comparison images only for failures. Keep the visual/ directory with the report when copying or publishing it. Saved artifact manifests retain the observed JUnit outcomes, so offline regeneration never upgrades an old skip into a pass. Reference bindings and recorded image paths are verified before refreshing a current report; invalid or missing evidence stops regeneration without replacing the published report. Rerun the diagnostic suite to measure new outcomes.
Every existing manual VICE checkpoint has an audited reference decision, including specialized CPU, CIA and SID tests. Hardware settings, checkpoint identities, program/ROM hashes and reference provenance are verified explicitly. Matching upstream images take precedence; other suitable visual states have independently repeated VICE examples. Nonvisual diagnostics retain an explanation of why a C64 screen cannot validate their output. The gallery reports reference availability and actual visual PASS/FAIL counts separately.
Diagnostic coverage is shown in both reports and exported to visual/manual-reference-coverage.json. manualCases counts the documented inventory; it is not a test-debt count. Audited not-applicable and photograph decisions have category external-oracle-required and contribute to externalOracleRequired. These require audio, analog, physical-input or human measurement evidence. Valid unavailable decisions have category reference-unavailable. Both categories are excluded from unresolved test debt. unresolved counts missing, unknown or invalid reference decisions. Reference comparisons are classified as visual-reference-covered or visual-reference-difference; an audited reference without a recorded pixel verdict is visual-reference-unchecked. Photographs still contribute to withReferences, which describes reference availability. Recorded JUnit outcomes and pass rates are preserved; visual differences and reference-validation failures remain failures.
Invalid references and configuration mismatches fail explicitly. Offline report generation preserves an already-recorded validation failure, while rejecting newly corrupted evidence; it never converts an old skip into a visual pass.
Every VICE image is labeled “VICE-generated emulator capture — not recorded on a real machine.” in both reports and in the standalone PNG's provenance metadata. Publication upstream alone does not prove a real-machine recording: images without documented capture provenance say “Upstream reference — capture method/hardware origin unverified.” Labels preserve the image pixels. Reference decisions, commands, hardware, capture timing, source revisions and SHA-256 hashes are stored in src/test/resources/vice/manual-references/, separately from the pinned upstream fixtures.
Run the complete manual inventory with mvn -Prelease compiler:testCompile surefire:test -Dmaven.test.skip=false -Dtest=ViceManualSuite. This opt-in suite reuses the existing runners and exports actual configurations as visual/*/manual-captures.json. For a new audit or changed configuration, add -Dvice.manual.collectOnly=true to collect previews without applying stale reference decisions; that run explicitly reports unresolved references. Then regenerate references with python3 src/test/scripts/vice-manual-references.py using VICE x64sc and Python Pillow (--vice selects another executable; --refresh recaptures verified VICE examples). The generator uses repository ROMs and disposable media, verifies two identical VICE captures per state and adds provenance metadata without changing pixels. Generation errors remain errors, not applicability exclusions. Configuration limits are reported separately as unavailable references. Ordinary tests require neither VICE nor network access. The report-generation command below renders recorded comparison results without rerunning tests or changing their frames and JUnit outcomes. Rerun the manual suite when its capture or launch configuration changes. Measured decisions and outcomes are also written to visual/manual-reference-coverage.json.
Standalone Wolfgang Lorenz programs can show OK followed by a LOAD ERROR for the next program in their original disk-suite chain. The current program's stable green verdict determines its result; each next program is tested independently.
For an already built checkout, these direct goals avoid unrelated web packaging steps and override the development profile's default test skip:
mvn compiler:compile compiler:testCompile surefire:test -Dmaven.test.skip=false
# Select a suite or an individual Java test method; all applicable variants remain selected.
mvn compiler:testCompile surefire:test -Dmaven.test.skip=false -Dtest=FrodoTest
mvn compiler:testCompile surefire:test -Dmaven.test.skip=false '-Dtest=ViceCIATest#detectCIA6526'
# Optional focused model override. Contracts pinned to another model are excluded.
mvn compiler:testCompile surefire:test -Dmaven.test.skip=false -Dc64model=C64C
The existing vice.cpu.filter and vice.lorenz.filter regular-expression filters still select program subsets. To archive results in CI, collect target/deploy/tests/ and target/surefire-reports/ even when the test command fails. After a failed test run, or to regenerate the report from an existing run, use:
mvn -Prelease compiler:testCompile surefire-report:report-only exec:java@publish-test-statistics
This compiles the report helpers and regenerates the percentages and manual-test comparisons from target/surefire-reports/ and the existing gallery, without running tests or replacing screenshots. Release verify runs the tests once, then generates and processes the report automatically.
Enable JaCoCo for the direct-goal test workflow with:
mvn -Pcoverage jacoco:prepare-agent@coverage-agent resources:resources resources:testResources compiler:compile compiler:testCompile surefire:test jacoco:report
The HTML report is target/deploy/tests/coverage/index.html, with jacoco.xml
and jacoco.csv alongside it. Uploading the deploy directory publishes it at
tests/coverage/. Execution data is target/jacoco.exec; archive it with the
reports and Surefire results to retain a baseline. Each test run replaces the
execution data.
If tests fail, Maven stops before the report goal. Generate coverage from that
run without rerunning tests:
mvn -Pcoverage jacoco:report
For the regular release build, use -Prelease,coverage; verify generates the
report after passing tests. Add -Dtest=REUTest,REUBusArbitrationTest to the test
command for a focused run, whose coverage is only a partial measurement.
The report includes all production Java code, including unexecuted code, and
excludes test code and dependency JARs. Coverage is opt-in, preserves custom
-DargLine options, and adds no percentage gate or changes to assertions.
It measures Java execution; hardware-backed tests establish C64 compatibility.
For new program tests, extend common.HeadlessTest. Use @C64Models for method/class requirements or common.ModelCase for parameterized cases; mark fixture baselines with fixed = true when another preset is inapplicable. Old/new @ViceSource pairs expand automatically. Parameterized classes must use @UseParametersRunnerFactory(ViceSourceParametersRunnerFactory.class) alongside @RunWith(Parameterized.class). Use startProgram, scheduled hardware operations, bounded waits, and checkpoint("name"); the harness owns final capture and shutdown. Polling and observations use emulated-cycle boundaries, holding the clock while a sample is inspected so host scheduling cannot consume a program's cycle budget. Use the harness helpers for waits and program restarts. Existing logical-color screenshot comparisons remain exact and produce readable gallery images.
# Deploy the existing build and reports without re-running tests.
mvn -Djavafx.platform=linux -DskipTests -DserverUrl=scp://<user>@<hostname>/home/ken deploy -Prelease
Allow the Docker container to open a window (once):
xhost +local:docker
docker compose -f src/main/docker/docker-compose.XXX.linux.yml updocker compose -f src/main/docker/docker-compose.XXX.linux.yml downExamples:
Start Server: docker compose -f src/main/docker/docker-compose.server.linux.yml up
Start JavaFX UI: docker compose -f src/main/docker/docker-compose.ui.linux.yml up
Start Network SID Device: docker compose -f src/main/docker/docker-compose.netsiddev.linux.yml up